Skip to main content

SVG graphics

The SVG primitive set, direct presentation props, scoped definitions, SVG text, and the supported numeric boundary.

Nuxt PDF ships a set of SVG drawing primitives that render through the same layout and render engine as the document primitives. A PdfSvg participates in normal page flow as a flex leaf, measured from its viewBox aspect ratio.

The primitive set

ComponentRole
PdfSvgSVG root; a flex leaf in page flow
PdfGA group; presentation attributes cascade to children
PdfPathA path from a d string
PdfRectA rectangle (x, y, width, height, rx, ry)
PdfCircleA circle (cx, cy, r)
PdfEllipseAn ellipse (cx, cy, rx, ry)
PdfLineA line (x1, y1, x2, y2)
PdfPolylineAn open polyline from a points string
PdfPolygonA closed polygon from a points string
PdfDefsHolds referenceable defs (gradients, clip paths)
PdfClipPathA clip path referenced by clipPath="url(#id)"
PdfLinearGradient / PdfRadialGradientGradients referenced by fill="url(#id)"
PdfStopA gradient color stop
PdfTspanA text span inside an SVG PdfText

Full prop tables are in the primitive reference.

A basic drawing

vue
<PdfSvg viewBox="0 0 120 120" :style="{ width: 120, height: 120 }">
  <PdfRect x="8" y="8" width="104" height="104" rx="12" fill="#eef2ee" />
  <PdfCircle cx="60" cy="60" r="34" fill="#315d3b" />
  <PdfPath d="M44 62 l12 12 l22 -26" stroke="#ffffff" stroke-width="6" fill="none" />
</PdfSvg>

Presentation is direct, not a style cascade

Set SVG paint and geometry with direct props. PdfG and the shape primitives do not accept a generic style prop, so there is no prop-versus-style precedence to remember. transform is also a direct SVG prop; page-flow primitives such as PdfView put transforms in PdfStyle instead.

PdfSvg is the exception only at the page-flow boundary: its style sizes and positions the SVG root in the surrounding PDF layout. It does not become a bag of SVG presentation attributes. SVG PdfText may use style for text metrics such as fontFamily and fontSize, but its paint color is the direct fill prop.

Kebab-case attributes written in Vue templates (stroke-width, stop-color) are coerced to the camelCase keys the renderer reads, so both forms work:

vue
<!-- stroke-width and strokeWidth are equivalent on SVG nodes -->
<PdfLine x1="0" y1="0" x2="100" y2="0" stroke="#333" stroke-width="2" />

Presentation attributes set on a PdfG cascade to its children:

vue
<PdfG fill="#315d3b" transform="translate(10 10) rotate(15)">
  <PdfRect x="0" y="0" width="20" height="20" />
  <PdfRect x="30" y="0" width="20" height="20" />
</PdfG>

Numbers and transforms

Geometry accepts finite numbers or numeric strings; percentage strings are accepted only by props typed as PdfSvgLength. strokeWidth does not accept a percentage. Widths and radii must be non-negative, opacity and gradient-stop values must be between 0 and 1 (or 0% and 100%), and a viewBox must contain four finite numbers with positive width and height.

The transform surface is deliberately small: one to three space-separated translate(...) or rotate(...) operations with unitless numeric arguments. Scale, matrix, skew, angle units, and arbitrary transform strings are rejected.

Explicit zeroes keep SVG meaning despite truthiness fallbacks in the pinned serializer:

  • fillOpacity="0" paints fully transparent;
  • strokeWidth="0" paints no stroke (not a PDF hairline); and
  • a zero linear-gradient x2, or zero radial-gradient cx, cy, fx, fy, or r, remains zero.

Nuxt PDF repairs those values after layout on the disposable resolved tree, immediately before serialization. This is an engine-boundary correction, not a second authoring format.

Gradients and clip paths

Define a gradient or clip path inside a PdfDefs and reference it by url(#id):

vue
<PdfSvg viewBox="0 0 200 100" :style="{ width: 200, height: 100 }">
  <PdfDefs>
    <PdfLinearGradient id="brand" x1="0" y1="0" x2="1" y2="0">
      <PdfStop offset="0" stop-color="#315d3b" />
      <PdfStop offset="1" stop-color="#7bbf88" />
    </PdfLinearGradient>
  </PdfDefs>
  <PdfRect x="0" y="0" width="200" height="100" fill="url(#brand)" />
</PdfSvg>
A url(#id) reference resolves only against a PdfDefs in the same PdfSvg subtree. Definition ids must be safe and unique within that SVG, and each SVG accepts at most one PdfDefs. A missing, malformed, or incompatible reference (for example a clip path used as a fill) fails with PDF_TREE_INVALID; it is never rendered as an accidental fallback. The same definition id may be reused in another PdfSvg because scopes are independent.

SVG text

PdfText has two context-specific contracts. In page flow it accepts wrapping, pagination, bookmark, destination, and dynamic-text props. Inside PdfSvg, both x and y are required, fill is the direct paint prop, and page-flow-only props are rejected. SVG text may hold PdfTspan children, which join and chain along the x-axis:

vue
<PdfSvg viewBox="0 0 200 40" :style="{ width: 200, height: 40 }">
  <PdfText :x="0" :y="24" fill="#18251d">
    <PdfTspan>Total </PdfTspan>
    <PdfTspan fill="#315d3b">EUR 1,250.00</PdfTspan>
  </PdfText>
</PdfSvg>

The text content is preserved in the extracted page text, so it remains selectable and testable. PdfTspan is intentionally narrow: it accepts only x, y, and fill; it has no generic style, stroke, transform, or other shape presentation props.

Nesting rules and what is not claimed

PdfSvg is a valid child of PdfPage and PdfView but is rejected directly inside PdfText. Leaf shapes stay childless. Each container accepts only its valid children; invalid nesting fails with a targeted diagnostic.

Within SVG, the following are not claimed in the current alpha:

  • Marker (markerStart / markerMid / markerEnd);
  • alternate gradientUnits, gradientTransform, gradient inheritance, and preserveAspectRatio modes;
  • radial-gradient inner radius (fr), which the pinned renderer hardcodes to zero; and
  • SVG image files as an image source.