SVG Test Page

PeachPDF renders SVG through its own vector scene graph, reusing the same PDF path/fill/stroke/gradient/clip primitives already used for CSS backgrounds and borders — SVG content is never rasterized to a bitmap. Full SVG 1.0 coverage is now supported (minus a handful of PDF-incompatible features such as animation, scripting, and filters): all basic shapes, fill-rule, stroke dash/cap/join, rotate()/skewX()/skewY() transforms, full preserveAspectRatio and nested viewports, objectBoundingBox/spreadMethod/currentColor gradients, the style cascade (style= and <style>), <switch>/<a> links, <marker>, <pattern>, <mask>, <image>, and <text>/<tspan>/<tref>. See supported-svg-features.md for the full compatibility matrix.

1 — Path Primitives: Lines, Curves & Arcs

straight lines (M/L/Z)
path d="M50,10 L90,90 L10,90 Z"
cubic Bézier (C)
path d="M10,80 C10,20 90,20 90,80"
elliptical arc (A)
path d="M10,60 A40,40 0 0 1 90,60"
multiple subpaths
one path, two "M...Z" subpaths

2 — circle & polygon

circle
circle cx=50 cy=50 r=35
polygon (pentagon)
polygon points="..."
overlapping circles + opacity
two circles, opacity="0.7" each
polygon (5-point star)
polygon points="..."

3 — Linear & Radial Gradients

linearGradient, 2 stops
linearGradient x1/y1/x2/y2, 2 stops
linearGradient, 3 stops
diagonal, 3 stops
radialGradient, centered
radialGradient cx/cy/r
radialGradient + gradientTransform
gradientTransform squishes the radial into an ellipse

4 — Stroke Properties

stroke only, default miterlimit
stroke-width=4 stroke-miterlimit=10
thick stroke, low miterlimit
stroke-width=10 stroke-miterlimit=1
fill + stroke combined
fill and stroke on the same shape
stroke-only star
fill="none" stroke="#c0392b"
gradient stroke (rounded rect + ellipse)
stroke="url(#gradient)" on a rounded rect and an ellipse - a gradient stroke sharing a page with solid strokes
transparent gradient stroke beside opaque
left fades to transparent; the opaque green-gold frame must stay fully visible
gradient stroke inside padded wrapper
wrapper padding no longer drops the stroke
gradient stroke inside rounded bordered wrapper
border-radius + border wrapper no longer drops the stroke
two gradient-stroked panels in a row
a transparent gradient ring never masks a later one on the same page

5 — Group Opacity & Transforms

nested group opacity (0.7 × 0.7)
g opacity="0.7" > g opacity="0.7"
group transform: translate + scale
transform="translate(15,15) scale(0.6)"
horizontal mirror via scale(-1,1)
transform="scale(-1,1) translate(-100,0)"
use + per-instance transform
two <use> of the same <circle>, each transformed
AVVA
group opacity over overlapping text
g opacity="0.5" over two overlapping <text> runs — no double-darkening at the overlap
use opacity of a container
use opacity="0.5" of a <g> of overlapping rects — composited once
nested svg opacity
nested <svg opacity="0.5"> with overlapping circles

6 — clipPath + use

clip-path references a <clipPath> that itself contains a <use> of a shape defined once in <defs> — the same pattern used by the peach illustrations below. A clip shape's own transform (directly, via <use transform>, or via a wrapping <g>) is applied to the clip geometry, so one shape can be reused at several positions. With clipPathUnits="objectBoundingBox" the 0..1 clip geometry auto-scales to the referencing element's own bounding box.

gradient clipped to a circle
clipPath > use > circle
gradient clipped to a star
clipPath > use > polygon
use for simple repetition
use xlink:href="#dot" x=.. y=..
one clip shape reused at 4 positions
clipPath > use transform="translate(..)" ×4
clip + group opacity combined
g clip-path="url(#clip3)" opacity="0.8"
objectBoundingBox: clip auto-scales to the element
clipPathUnits="objectBoundingBox" (0..1 diamond)

7 — Basic Shapes: rect, ellipse, line & polyline

rect with rounded corners
rect x y width height rx=15
ellipse (percentage rx)
ellipse rx="35%" (percentage length)
line, round linecap
line x1 y1 x2 y2 stroke-linecap="round"
polyline (open, unclosed)
polyline points="..." fill="none"

8 — Stroke: Dash Arrays, Caps & Joins

stroke-dasharray + dashoffset
stroke-dasharray="14,8" stroke-dashoffset="4"
stroke-linecap: butt/round/square
stroke-linecap: butt, round, square
stroke-linejoin: miter/round/bevel
stroke-linejoin: miter, round, bevel
dashed rounded-rect border
dasharray + linecap + linejoin combined

9 — Fill Rule & Opacity

fill-rule="nonzero" (default)
same-direction inner square: solid (no hole)
fill-rule="evenodd"
identical path, evenodd: donut (hole visible)
fill-opacity, stroke fully opaque
fill-opacity="0.4" (stroke unaffected)
stroke-opacity, fill fully opaque
stroke-opacity="0.4" (fill unaffected)
invalid fill-rule inherits, doesn't reset to nonzero
g fill-rule="evenodd" > path fill-rule="not-a-rule": still a donut
invalid stroke-linecap/linejoin inherit from group
group's round cap/join survive an invalid override
invalid fill/stroke inherit from group
g fill/stroke set > circle fill/stroke="not-a-color": still purple with dark outline
invalid fill-opacity/stroke-opacity inherit from group
group's 0.4 fill/stroke opacity survive an invalid override, not reset to 1.0

10 — Transforms: rotate() & skew()

rotate(angle, cx, cy)
transform="rotate(30,50,50)"
rotate(-angle, cx, cy)
transform="rotate(-45,50,50)"
skewX()
transform="skewX(20)"
skewY()
transform="skewY(-20)"

11 — Advanced Gradients: objectBoundingBox, spreadMethod & Radial Focus

objectBoundingBox (default)
no gradientUnits: x1/x2 are 0..1 fractions of the rect's own box
spreadMethod="repeat"
narrow x1..x2 range tiles across the rect
spreadMethod="reflect"
same idea, mirrored at each repeat
radial fx/fy off-center highlight
fx/fy offset from cx/cy: sphere-shading highlight

12 — preserveAspectRatio & Nested Viewports

default: xMidYMid meet
2:1 viewBox into a square box: letterboxed
xMidYMid slice
same viewBox, slice: cropped, fills the box
preserveAspectRatio="none"
stretched independently per axis: distorted
nested <svg>, own viewport
nested svg establishes its own coordinate system

13 — Style Cascade & currentColor

style= overrides presentation attribute
fill="red" style="fill:#2ecc71": style wins
currentColor
fill="currentColor" resolves the ancestor's CSS color
<style> class selector (standalone)
.hi/.lo rules, via <img>
<style> id selector (standalone)
#target rule, via <img>
inline <svg><style>
a <style> nested in an inline <svg> now applies (issue #159)
HTML <style> cascades into SVG
a document-level rule matching SVG shapes applies (SVG 2 §6)
combinator selector
"g rect" descendant combinator, via the full selector engine
var() custom property
fill:var(--c) resolves through the CSS cascade

14 — <switch> & <a> Links

switch: first child wins
no requiredFeatures evaluation: always shows child 1
ignored
switch: skips an unbuildable child
<metadata> isn't renderable, so child 2 is used
<a href>: real PDF link
becomes a clickable PDF link annotation
<a> with no href
renders children normally, just isn't a link

15 — Markers

marker-end, orient="auto"
marker rotates to follow the path's tangent
marker-start/mid/end on every vertex
one marker def, placed at start/mid/end vertices
markerUnits="strokeWidth" (default)
same marker, scales with each line's own stroke-width
orient: fixed angle
orient="45": same fixed rotation at every vertex

16 — Pattern Fill

checkerboard
pattern of 4 <rect> tiles, patternUnits="userSpaceOnUse"
polka dots
dot pattern clipped to the circle's own geometry
patternTransform="rotate(45)"
stripe tile rotated 45° via patternTransform
pattern filling a star shape
pattern respects the star's own fill geometry

17 — Mask

linear-gradient luminance fade
gradient mask fades the purple rect over a yellow backdrop
radial vignette
radial mask: spotlight/vignette fade
vector shape as mask
a shape, not just a gradient, as the mask's luminance
PDF
<text> as mask content
gradient shows only through the letter shapes

18 — <image> Element

raster data:image/png
a real embedded raster image XObject
vector data:image/svg+xml
stays real vector content, never rasterized
preserveAspectRatio="none" on <image>
raster image stretched non-uniformly to fit its box
<image> with clip-path
clip-path applies to <image> like any other element

19 — Text, tspan & tref

Peach
<text x y fill font-size>
plain positioned text, baseline at (x, y)
startmiddleend
text-anchor: start/middle/end
all three anchored at x=50 (dashed guideline)
Hello World
<tspan> restyles mid-run
tspan flows right after "Hello ", own fill+weight
BoldItalicRegular
<tspan x y>: new line + font variety
own x/y starts a new line; size/weight/style vary
Peach1.5em
font-* inherited from an ancestor <g>
text inherits family/size/weight from <g>; 1.5em = 24
Peach
per-character x list + rotate
each glyph its own x + rotation (SVG §10.4)
Peach
nested <tspan> along a <textPath>
restyled tspan laid along the curve too
Spaced OutWord by word
letter-spacing & word-spacing
both properties reach the same GSUB/GPOS pipeline HTML text uses
shouteach wordQUIET
text-transform
uppercase / capitalize / lowercase
Small Caps
font-variant-caps: small-caps
real GSUB smcp/c2sc substitution, gated on font support
UnderlinedDecorated
text-decoration-line/-style/-color
own value only (not inherited), flows across a plain descendant tspan

20 — Inline <svg> vs <img src="data:image/svg+xml">

The identical SVG markup rendered two ways: embedded directly in the HTML, and encoded as a base64 data: URI on an <img> tag. Both go through the same vector renderer.

inline <svg>
<svg>...</svg> inline in the HTML body
<img src="data:...">
same markup, base64 data: URI

21 — Peach Showcase

Two original peach illustrations, built entirely from the elements above: cubic-curve paths, radial gradients for shading, and a clipPath + use for the cross-section.

Whole Peach

path (M/C/Z) body + leaf + stroked stem, radialGradient shading, radial blush with alpha stops

Peach Slice

clipPath + use round the flesh, opacity-grouped striations, pit path, use for the skin outline

Prunus persica

Peach Branch & Blossoms

<marker> blossoms/leaf tip along a curved branch, italic <text> caption

Peach Basket

pattern-woven basket, rotate() on individual peaches, gradient drop-shadow via mask