Skip to main content

Canvas

A canvas is a Content item with a shapes array. Each entry is a single drawing primitive (line, rect, circle, path, text, …) keyed by its shape name. Naming and properties mirror SVG presentation attributes — authors fluent in SVG will recognize cx/cy/r, x1/y1/x2/y2, d, fill, stroke.

Live example

A real-world architecture diagram authored as a single shapes array: hexagon for the edge service, rounded rectangles for compute, ellipse-on-rectangle cylinders for databases, color-coded connectors. Open the Template tab to see the JSON.

Container

{
"shapes": [ ... ],
"viewBox": [720, 240],
"padding": 0,
"style": "defaultShapeStyle"
}
PropertyTypeDefaultDescription
shapesarrayDrawing commands (see below).
viewBox[W, H]auto from commandsExplicit reservation in points. Omit to derive the tight bbox from commands.
paddingnumber0Padding in points added around the auto-computed bbox. Ignored when viewBox is set.
stylestringDefault style applied to commands that don't carry their own.

Coordinates are box-relative top-left originx grows right, y grows down. Commands with negative coordinates are shifted into the box automatically when viewBox is omitted.

Built-in shapes

Every shape renders identically in PDF and DOCX — same JSON, same output.

TierShapes
Tier 1 — primitivesrect, circle, ellipse, line, arc
Tier 2 — polygonstriangle, diamond, pentagon, hexagon, octagon, plus, parallelogram, trapezoid
Tier 3 — directional arrowsrightArrow, leftArrow, upArrow, downArrow, chevron
Custompolygon — N-point closed polygon, auto-closed
Pathspath — arbitrary SVG path data; textPath — text flowed along a path
Contenttext, image, barcode
Groupingg — groups child shapes under one transform + cascaded style; use — instantiates a reusable graphics component

Each polygon and arrow fits inside the bounding box you supply via width × height; the renderer derives the vertex positions automatically. The path / textPath commands accept raw SVG d strings, which makes them the most flexible primitive — anything you can draw in SVG (Beziers, arcs, compound paths, decorative flourishes) renders directly. See the Canvas Patterns Gallery tutorial for a full reference of decorative compositions (mandala, sunburst, Greek key, Lissajous, guilloché).

Drawing commands

Each command is { "<shapeName>": { …props } } — a discriminated union keyed by the shape's property name. Every shape carries a style reference into document.styles.

Geometry primitives

Rectangle

{ "rect": { "x": 100, "y": 100, "width": 150, "height": 80, "rx": 6, "ry": 6, "style": "card" } }

rx / ry are optional corner radii — omit them for square corners.

Circle

{ "circle": { "cx": 200, "cy": 100, "r": 30, "style": "node" } }

Ellipse

{ "ellipse": { "cx": 200, "cy": 100, "rx": 55, "ry": 6, "style": "dbCap" } }

Tier 2 polygons

triangle, diamond, pentagon, hexagon, octagon, plus, parallelogram, trapezoid. All take x, y, width, height; the polygon vertices fit inside that bounding box.

{ "triangle": { "x": 0, "y": 0, "width": 80, "height": 80, "style": "shape" } }
{ "diamond": { "x": 110, "y": 0, "width": 80, "height": 80, "style": "shape" } }
{ "pentagon": { "x": 220, "y": 0, "width": 80, "height": 80, "style": "shape" } }
{ "hexagon": { "x": 330, "y": 0, "width": 80, "height": 80, "style": "shape" } }
{ "octagon": { "x": 0, "y": 100, "width": 80, "height": 80, "style": "shape" } }
{ "plus": { "x": 110, "y": 100, "width": 80, "height": 80, "style": "shape" } }
{ "parallelogram": { "x": 220, "y": 100, "width": 80, "height": 80, "style": "shape" } }
{ "trapezoid": { "x": 330, "y": 100, "width": 80, "height": 80, "style": "shape" } }

Tier 3 arrows

rightArrow, leftArrow, upArrow, downArrow, chevron. The orientation is encoded in the name; the renderer adjusts vertex math accordingly.

{ "rightArrow": { "x": 0, "y": 0, "width": 90, "height": 60 } }
{ "leftArrow": { "x": 110, "y": 0, "width": 90, "height": 60 } }
{ "upArrow": { "x": 220, "y": 0, "width": 60, "height": 90 } }
{ "downArrow": { "x": 290, "y": 0, "width": 60, "height": 90 } }
{ "chevron": { "x": 360, "y": 0, "width": 80, "height": 60 } }

Labeled nodes (group a shape + label)

A diagram node — a shape with a centered label — is a g group: the shape plus a text centered in the same box. Children draw in the group's local coordinates, so translate places the node and the group's style cascades to children that declare none (here, the shape inherits the node fill while the label keeps its own white-text style).

{ "g": {
"translate": [30, 85],
"style": "node",
"shapes": [
{ "hexagon": { "x": 0, "y": 0, "width": 110, "height": 50 } },
{ "text": { "x": 0, "y": 20.5, "width": 110, "text": "API Gateway", "style": "nodeLabel" } }
]
} }

Centre the label vertically with y = height/2 − fontSize/2. When the same node recurs across a diagram (the common case), define it once as a graphics component and use it with per-instance data — see order-lifecycle-states.json and org-chart.json.

Live examplearchitecture-diagram.json (a hexagon edge node as a g, plus four arch-node components instantiated via use).

Polygon

Closed polygon defined by N points. Useful for stars, badges, custom flowchart shapes. Auto-closed (last vertex back to first).

{
"polygon": {
"points": [
{ "x": 100, "y": 10 },
{ "x": 140, "y": 40 },
{ "x": 125, "y": 90 },
{ "x": 75, "y": 90 },
{ "x": 60, "y": 40 }
],
"style": "star"
}
}

Lines and arcs

Line

{ "line": { "x1": 50, "y1": 50, "x2": 200, "y2": 150, "style": "connector" } }

Arc

Sweep from startAngle to endAngle (degrees, CCW from +x) around (cx, cy).

{ "arc": { "cx": 200, "cy": 200, "r": 60, "startAngle": 0, "endAngle": 180, "style": "arc" } }

Paths & text on path

Path

Arbitrary SVG path data via the d attribute. Supports the standard SVG commands: M/m (moveto), L/l (lineto), H/h / V/v (horizontal/vertical lineto), C/c / S/s (cubic Bezier + smooth), Q/q / T/t (quadratic Bezier + smooth), A/a (elliptical arc), Z/z (close path). Strokes and/or fills based on which color properties the style supplies (stroke → stroke, fill → fill, both → fill+stroke).

{ "path": { "d": "M 0 50 Q 100 0 200 50 T 400 50", "style": "wave" } }

Full circle as two semicircular arcs:

{ "path": { "d": "M 100 6 A 94 94 0 0 1 100 194 A 94 94 0 0 1 100 6", "style": "outerRing" } }

textPath

Flows text along a path with per-glyph rotation. The path itself is not drawn — pair with a path command using the same d if you want a visible curve too.

PropertyTypeDescription
dstringSVG path data (same syntax as path).
textstringText to flow; supports Shortcodes ([b], [i], [color], [fontsize], [caps], [font, Family], …). Shaped at render time — complex scripts join/stack correctly (see Multi-script text and seals).
alignmentenumstart (default) · middle · end — anchors the text along the path.
startOffsetnumberDistance in points from path start before the first glyph.
sideenumabove (default) — baseline on path; below — glyph hangs below the path.
stylestringCanvas text style.
{
"textPath": {
"d": "M 24 100 A 76 76 0 0 1 176 100",
"text": "OFFICIAL · [color, #7A1F2E]STATE CORPORATION COMMISSION[/color]",
"alignment": "middle",
"style": "sealTopText"
}
}

Content

Text

{ "text": { "x": 150, "y": 200, "text": "Section header", "style": "label" } }

When width is set, (x, y) is the top-left of a text box and the style's textAlign centers/right-aligns the text within that box. Without width, the anchor is the alignment pivot — center alignment pivots on (x, y), right alignment ends at it.

{ "text": { "x": 0, "y": 10, "width": 180, "text": "EDGE", "style": "laneHeader" } }

Set fontFamily on the text style to use an embedded font, and switch fonts mid-string with the [font, Family] Shortcode — both text and textPath are shaped at render time (see Multi-script text and seals).

Image

{ "image": { "x": 50, "y": 30, "width": 100, "height": 40, "href": "images/logos/acme.png" } }

Barcode

Place any supported barcode symbology at canvas coordinates — PDF417, DataMatrix, QR, Code128, Code39, MaxiCode, and the 2D specialty codes. The barcode encoder runs at render time, so the encoded value can be a $data.* reference.

{
"barcode": {
"x": 80, "y": 56,
"width": 40, "height": 40,
"spec": { "type": "datamatrix", "code": "$data.credential.payload" }
}
}
PropertyTypeDescription
x, ynumberTop-left of the barcode in canvas coordinates.
width, heightnumberRender dimensions in points. For square 2D codes, set both equal. Omit to use per-symbology defaults.
spec.typestringSymbology — datamatrix, qrcode, pdf417, code128, code39, aztec, maxicode, microqr, rmqr, micropdf417, dotcode, hanxin, code16k, codablockf, ultracode, gridmatrix, upnqr, and all 1D linear types. See Symbologies.
spec.codestringThe payload to encode. Supports $data.* and $item.* references.

This is the canvas barcode primitive — distinct from the [barcode, …] Shortcode which renders inline with text flow. Use the canvas form when you need the barcode at a precise location alongside other canvas shapes (the center of a seal, a corner of a form, an edge sidebar).

Live examplesofficial-seal.json (PDF417 verification record next to control numbers), graduation-certificate.json and achievement-certificate.json (DataMatrix at the visual center of an academic seal), stock-certificate.json (DataMatrix verification badge), void-check.json (edge-mounted Code128 watermark).

Multi-script text and seals

Canvas text and textPath are shaped with HarfBuzz at render time — the same engine that shapes flowing paragraphs. Arabic and Hebrew join into their contextual forms and set right-to-left, Devanagari builds conjuncts, Thai stacks vowel and tone marks, and Chinese/Japanese/Korean render from a CJK family. Choose the face with fontFamily on the canvas text style, and switch fonts per run with the [font, Family] Shortcode. Each textPath ring can therefore carry a different script — which is what makes a multi-script seal possible.

Live examplesworld-languages-proclamation.json (one canvas seal carrying Arabic, Latin, Greek and Devanagari on concentric textPath rings around a CJK character) and multilingual-device-guide.json (Latin + CJK outer rings, Devanagari + Thai inner rings around a bold CJK character). See Custom Fonts for the shaping and subsetting details.

Canvas styles

Canvas shapes use a different property schema than paragraph styles. Use the keys below — color/backgroundColor/border are paragraph properties and will be silently ignored on shape commands.

Naming aligns with SVG presentation attributes. Same semantics as SVG, JSON-style camelCase.

PropertyApplies toDescription
fillshapesInterior fill.
strokeshapes, lines, pathsBorder / line / stroke color.
strokeWidthshapes, lines, pathsStroke width in points.
strokeDasharrayshapes, lines, pathsDash pattern as number array: [on, off] for simple dash, [a, b, c, d, …] for dash-dot, [0.5, 2.5] paired with strokeLinecap: "round" for dotted. Use [0] or [] to explicitly reset to solid — the PDF canvas state is sticky, so a dash pattern set earlier in the command stream persists until overridden.
strokeDashoffsetpathsStarting offset into the dash pattern (defaults to 0).
strokeLinecapshapes, lines, pathsbutt (default) / round / square — line-end shape. Combined with strokeDasharray, round turns tiny on-segments into round dots instead of square pixels.
strokeLinejoinpathsmiter (default) / round / bevel — corner join style at polyline vertices.
strokeMiterlimitpathsPositive float — controls how long a miter spike can extend before being chopped to a bevel.
opacityall0.01.0 transparency.
colortext, textPathText fill color.
fontSizetext, textPathText size in points.
fontWeight / fontStyletext, textPathWeight (bold, normal, 100900) and style (italic, normal).
fontFamilytext, textPathFont family for embedded custom fonts.
letterSpacingtext, textPathInter-character tracking in points (positive widens, negative tightens).
textAligntextleft / center / right — domain is the text's width when set, the anchor point otherwise.
textRenderingModetext, textPathfill (default) / stroke / fillstroke / invisible / fillclip / strokeclip / fillstrokeclip / clip.
skewpaths[skewX] or [skewX, skewY] in degrees — applies a 2D skew transform.
transformpaths6-element affine matrix [a, b, c, d, e, f] — raw ConcatMatrix.
rotateshapesRotation in degrees around the shape's center.
{
"styles": {
"card": { "fill": "#1E40AF", "stroke": "#1E3A8A", "strokeWidth": 0.5 },
"cardLabel": { "fontSize": 9, "fontWeight": "bold", "color": "#FFFFFF", "textAlign": "center" },
"connector": { "stroke": "#16A34A", "strokeWidth": 1.5 }
}
}

Symbols & reuse — use

Draw a graphic once, instantiate it many times. A graphics component is a document whose content is a single canvas; a use command stamps it into another canvas at a destination box, scaling it from the component's viewBox and binding per-instance data.

Declaring a graphics component

A component is an ordinary document file with a metadata.name handle and a canvas payload. The name is what authors reference — it's independent of the file name and folder.

{
"component": {
"metadata": { "name": "quality-seal" },
"styles": {
"sealOuter": { "stroke": "#10243E", "strokeWidth": 2 },
"sealGrade": { "fontSize": 46, "fontWeight": "bold", "color": "#10243E", "textAlign": "center" }
},
"content": [
{
"viewBox": [150, 150],
"shapes": [
{ "circle": { "cx": 75, "cy": 75, "r": 72, "style": "sealOuter" } },
{ "text": { "x": 0, "y": 45, "width": 150, "text": "$data.grade", "style": "sealGrade" } }
]
}
]
}
}

$data.grade is a placeholder filled in per instance (see Data Binding). The component's named styles travel with it — they resolve in whatever document uses it.

Instantiating with use

Inside any canvas, a use command names the component and gives a destination box. width/height scale the component from its viewBox; params supplies that instance's values.

{
"viewBox": [472, 175],
"shapes": [
{ "use": { "name": "quality-seal", "x": 16, "y": 12, "width": 150, "height": 150, "params": { "grade": "A" } } },
{ "use": { "name": "quality-seal", "x": 206, "y": 27, "width": 120, "height": 120, "params": { "grade": "B" } } },
{ "use": { "name": "quality-seal", "x": 372, "y": 39, "width": 100, "height": 100, "params": { "grade": "A+" } } }
]
}

One definition, three sizes, three grades — no duplicated geometry. The rings, text, and any nested shapes scale as a unit.

use fieldMeaning
nameThe component's metadata.name (the handle authors write).
idOptional GUID pin; wins over name when both are present.
x / yDestination top-left, in the host canvas's coordinates.
width / heightDestination size; the scale is derived against the component's viewBox.
paramsPer-instance values bound to the component's $data.* placeholders. May also carry x/y/width/height/rotate to make this use's own geometry dynamic (e.g. "params": { "width": "$data.columnWidth" } }) — a value here wins over the literal field of the same name.
rotate / skew / transformOptional transforms applied to the instance (degrees / degrees / 6-element matrix).
styleNamed style cascaded to the instance's shapes that declare none.

Transforms compose in SVG order (translate → rotate → skew → scale, then the raw transform matrix). Components can use other components; nesting is depth-limited with a cycle guard.

DOCX note. In Word output a use becomes a nested group carrying translate, scale, and rotate. Skew and raw-matrix transforms aren't expressible on a Word group and are dropped (the PDF honors them in full), and a rotated group pivots on its center rather than its top-left.

A second live example exercising the more decorative end of the canvas API: SVG path data, quadratic and cubic Bezier curves, arcs, textPath (text flowed along a curve), polygon shapes, and overlapping compositions. Useful as a reference when authoring seals, ornamental borders, math/geometry illustrations, or anything where the layout language is "place these curves exactly here."

What's next

  • Watermarks — translucent text/image overlays that repeat across pages.
  • Tables — tabular layouts that flow with paragraphs.
  • Columns — multi-column flow layouts.