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.
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 |
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="..." |
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 |
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" |
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 |
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.
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=.. | clip + group opacity combined g clip-path="url(#clip3)" opacity="0.8" |
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" |
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 |
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) |
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)" |
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 |
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 |
style= overrides presentation attribute fill="red" style="fill:#2ecc71": style wins | currentColor fill="currentColor" resolves the ancestor's CSS color | <style> class selector .hi/.lo rules, via <img> (see note below) | <style> id selector #target rule, via <img> (see note below) |
Note: a <style> element nested inside an inline <svg> has a known limitation in PeachPDF's HTML tokenizer, so the two <style> swatches above use the standalone <img src="data:image/svg+xml"> path, which is unaffected — see supported-svg-features.md.
switch: first child wins no requiredFeatures evaluation: always shows child 1 | 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 |
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 |
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 |
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 | <text> as mask content gradient shows only through the letter shapes |
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 |
<text x y fill font-size> plain positioned text, baseline at (x, y) | text-anchor: start/middle/end all three anchored at x=50 (dashed guideline) | <tspan> restyles mid-run tspan flows right after "Hello ", own fill+weight | <tspan x y>: new line + font variety own x/y starts a new line; size/weight/style vary |
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 |
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 Peachpath (M/C/Z) body + leaf + stroked stem, radialGradient shading, radial blush with alpha stops | Peach SliceclipPath + use round the flesh, opacity-grouped striations, pit path, use for the skin outline |
Peach Branch & Blossoms<marker> blossoms/leaf tip along a curved branch, italic <text> caption | Peach Basketpattern-woven basket, rotate() on individual peaches, gradient drop-shadow via mask |