Canvas
canvas provides absolute positioning within a section. Use it for watermarks, stamps, page headers/footers, decorative borders, and any element that must sit at an exact position independent of the flow layout.
Structure
Section titled “Structure”All canvas primitives must be inside a layer. Bare primitives directly in canvas are not valid.
<section> <layout> <text>Main content</text> </layout> <canvas> <layer page="each"> <text x="0pt" y="756pt" font-size="9pt" color="#aaaaaa">Page footer</text> </layer> </canvas></section>L.section(NoAttr, [ L.layout(NoAttr, [ L.text(NoAttr, ['Main content']), ]), L.canvas(NoAttr, [ L.layer({ page: 'each' }, [ L.textAt({ x: '0pt', y: '756pt', fontSize: '9pt', color: '#aaaaaa' }, ['Page footer']), ]), ]),])L.section(NoAttr, [ L.layout(NoAttr, [ L.text(NoAttr, ['Main content']) ]), L.canvas(NoAttr, [ L.layer(LayerAttr(page='each'), [ L.text_at(CanvasTextAttr(x='0pt', y='756pt', font_size='9pt', color='#aaaaaa'), ['Page footer']) ]) ])])L::section(NoAttr, [ L::layout(NoAttr, [ L::text(NoAttr, ['Main content']), ]), L::canvas(NoAttr, [ L::layer(new LayerAttr(page: 'each'), [ L::textAt(new CanvasTextAttr(x: '0pt', y: '756pt', fontSize: '9pt', color: '#aaaaaa'), ['Page footer']), ]), ]),])L.Section(NoAttr, [ L.Layout(NoAttr, [ L.Text(NoAttr, ["Main content"]), ]), L.Canvas(NoAttr, [ L.Layer(new() { Page = "each" }, [ L.TextAt(new() { X = "0pt", Y = "756pt", FontSize = "9pt", Color = "#aaaaaa" }, ["Page footer"]), ]), ]),])Paint order (overlay vs underlay)
Section titled “Paint order (overlay vs underlay)”The element listed first in section is painted first:
<!-- canvas as underlay (behind layout) --><section> <canvas>…</canvas> <layout>…</layout></section>
<!-- canvas as overlay (in front of layout) --><section> <layout>…</layout> <canvas>…</canvas></section>layer attributes
Section titled “layer attributes”| Attribute | Type | Default | Description |
|---|---|---|---|
page |
string | each |
Which pages to render on: each first last odd even, or numbers and ranges such as 1, 2-last or 1,3-5 |
opacity |
0–1 | 1 | Layer-wide opacity |
transform |
string | — | Transform applied to everything in the layer; see below |
clip |
string | — | Reserved. It is accepted but has no effect yet |
A layer cannot contain another layer. To combine opacity and a transform, put both on one layer.
Transforms
Section titled “Transforms”| Form | Effect |
|---|---|
translate(x, y) |
Move by x and y |
scale(s) or scale(sx, sy) |
Scale by s, or by sx across and sy down |
rotate(deg, cx, cy) |
Rotate by deg degrees around the point cx, cy |
matrix(a, b, c, d, e, f) |
A full transform matrix |
The numbers are plain numbers in points, separated by commas or spaces. A number with a unit, such as translate(10pt 20pt), is not read, and the transform does nothing.
Coordinate system
Section titled “Coordinate system”- Origin: top-left of the full page (ignores margin)
- Y axis: downward (y=0 is top of page)
- Units:
pt,mm,in(negative values allowed for offsets from anchors)
Positioning with anchor
Section titled “Positioning with anchor”Instead of x/y coordinates, use anchor to reference one of nine points on the page. x and y become signed offsets from that point. The element is placed so its own matching point lands there: anchor="bottom-right" puts the element’s bottom-right corner on the page’s bottom-right corner, before the offset.
| Anchor values | |
|---|---|
top-left top-center top-right |
|
center-left center center-right |
|
bottom-left bottom-center bottom-right |
<!-- Footer text anchored to bottom-left, offset 10pt from edge --><canvas> <layer page="each"> <text anchor="bottom-left" x="10pt" y="-10pt" font-size="9pt">Footer</text> </layer></canvas>L.canvas(NoAttr, [ L.layer({ page: 'each' }, [ L.textAt({ anchor: 'bottom-left', x: '10pt', y: '-10pt', fontSize: '9pt' }, ['Footer']), ]),])L.canvas(NoAttr, [ L.layer(LayerAttr(page='each'), [ L.text_at(CanvasTextAttr(anchor='bottom-left', x='10pt', y='-10pt', font_size='9pt'), ['Footer']) ])])L::canvas(NoAttr, [ L::layer(new LayerAttr(page: 'each'), [ L::textAt(new CanvasTextAttr(anchor: 'bottom-left', x: '10pt', y: '-10pt', fontSize: '9pt'), ['Footer']), ]),])L.Canvas(NoAttr, [ L.Layer(new() { Page = "each" }, [ L.TextAt(new() { Anchor = "bottom-left", X = "10pt", Y = "-10pt", FontSize = "9pt" }, ["Footer"]), ]),])Primitives
Section titled “Primitives”| Element | Description |
|---|---|
rect |
Filled or stroked rectangle |
circle |
Circle |
ellipse |
Ellipse, with separate x and y radii |
line |
Straight line between two points |
path |
SVG path data |
text |
Absolutely positioned text |
img |
Absolutely positioned image |
Canvas elements are not data-bound: data-* attributes have no effect on them. For text that changes per document, use the layout, or a region for page furniture.
DRAFT watermark example
Section titled “DRAFT watermark example”<canvas> <layer page="each" opacity="0.12" transform="rotate(45, 306, 396)"> <text x="100pt" y="420pt" font-size="72pt" color="#cc0000">DRAFT</text> </layer></canvas>L.canvas(NoAttr, [ L.layer({ page: 'each', opacity: '0.12', transform: 'rotate(45, 306, 396)' }, [ L.textAt({ x: '100pt', y: '420pt', fontSize: '72pt', color: '#cc0000' }, ['DRAFT']), ]),])L.canvas(NoAttr, [ L.layer(LayerAttr(page='each', opacity='0.12', transform='rotate(45, 306, 396)'), [ L.text_at(CanvasTextAttr(x='100pt', y='420pt', font_size='72pt', color='#cc0000'), ['DRAFT']) ])])L::canvas(NoAttr, [ L::layer(new LayerAttr(page: 'each', opacity: '0.12', transform: 'rotate(45, 306, 396)'), [ L::textAt(new CanvasTextAttr(x: '100pt', y: '420pt', fontSize: '72pt', color: '#cc0000'), ['DRAFT']), ]),])L.Canvas(NoAttr, [ L.Layer(new() { Page = "each", Opacity = "0.12", Transform = "rotate(45, 306, 396)" }, [ L.TextAt(new() { X = "100pt", Y = "420pt", FontSize = "72pt", Color = "#cc0000" }, ["DRAFT"]), ]),])