Layer
A layer is fixed content on the page: shapes, text, images and barcodes, each at the position and size it is given. Use it for watermarks, stamps, page numbers, running heads, page borders, letterhead and backgrounds that reach the edge of the page. A layer is a child of the section, next to its layout.
In the section
Section titled “In the section”A section holds any number of layers and at most one layout, painted in the order they are written: each one is drawn over the ones before it.
- A layer before the layout is painted under it, and a layer after the layout is painted over it.
- Among layers, the one written later is painted on top.
- A section with no layout is one page. A second
layoutis an error. - A layer cannot hold another layer, and it holds no flow elements: text in a layer is a
text-at, not atext.
<section> <layer> <!-- under the layout: a band across the top of every page --> <rect x="0pt" y="0pt" width="612pt" height="28pt" fill="#1763cf"/> </layer> <layout margin="48pt"> <text>The flowing text, painted over the band.</text> </layout> <layer page="each"> <!-- over the layout: a page number at the foot of every page --> <text-at anchor="bottom-center" y="-24pt" font-size="9pt" color="#777777">Page {page} of {pages}</text-at> </layer></section>L.section(NoAttr, [ L.layer(NoAttr, [ L.rect({ x: '0pt', y: '0pt', width: '612pt', height: '28pt', fill: '#1763cf' }), ]), L.layout({ margin: '48pt' }, [ L.text(NoAttr, ['The flowing text, painted over the band.']), ]), L.layer({ page: 'each' }, [ L.textAt({ anchor: 'bottom-center', y: '-24pt', fontSize: '9pt', color: '#777777' }, ['Page {page} of {pages}']), ]),])L.section(NoAttr, [ L.layer(NoAttr, [ L.rect(RectAttr(x='0pt', y='0pt', width='612pt', height='28pt', fill='#1763cf')) ]), L.layout(LayoutAttr(margin='48pt'), [ L.text(NoAttr, ['The flowing text, painted over the band.']) ]), L.layer(LayerAttr(page='each'), [ L.text_at(TextAtAttr(anchor='bottom-center', y='-24pt', font_size='9pt', color='#777777'), ['Page {page} of {pages}']) ])])L::section(NoAttr, [ L::layer(NoAttr, [ L::rect(new RectAttr(x: '0pt', y: '0pt', width: '612pt', height: '28pt', fill: '#1763cf')), ]), L::layout(new LayoutAttr(margin: '48pt'), [ L::text(NoAttr, ['The flowing text, painted over the band.']), ]), L::layer(new LayerAttr(page: 'each'), [ L::textAt(new TextAtAttr(anchor: 'bottom-center', y: '-24pt', fontSize: '9pt', color: '#777777'), ['Page {page} of {pages}']), ]),])L.Section(NoAttr, [ L.Layer(NoAttr, [ L.Rect(new() { X = "0pt", Y = "0pt", Width = "612pt", Height = "28pt", Fill = "#1763cf" }), ]), L.Layout(new() { Margin = "48pt" }, [ L.Text(NoAttr, ["The flowing text, painted over the band."]), ]), L.Layer(new() { Page = "each" }, [ L.TextAt(new() { Anchor = "bottom-center", Y = "-24pt", FontSize = "9pt", Color = "#777777" }, ["Page {page} of {pages}"]), ]),])A bookmark as the section’s first child comes before every layer. Layers hold no bookmarks.
Attributes
Section titled “Attributes”| Attribute | Type | Default | Description |
|---|---|---|---|
page |
string | each |
Which pages the layer is drawn on: each first last odd even, or numbers and ranges such as 1, 2-last or 1,3-5 |
opacity |
0–1 | 1 | How opaque everything in the layer is. See Opacity |
transform |
string | — | Transform applied to everything in the layer. See Transforms |
clip |
string | — | A rectangle, x y w h, in points from the top left corner of the page. What the layer draws outside it is cut off |
font |
string | document’s font, or Helvetica |
The font of the text-at in the layer that sets none of its own. See Text style |
font-size |
token/pt | m (11pt) |
The size of the text-at in the layer that sets none of its own |
color |
color | #1a1a1a |
The color of the text-at in the layer that sets none of its own. Defaults to the text color |
data-if |
string | — | Draw the layer only when the value at the path is truthy. See Data in a layer |
data-if-not |
string | — | Draw the layer only when the value at the path is falsy |
Coordinate system
Section titled “Coordinate system”- The origin is the top left corner of the page, and y grows downward.
- Units are
pt,mmandin. A negativexoryis allowed: it is an offset from an anchor. - A layer ignores the layout’s margin. A margin tells the flow not to come closer to the edge than this, and a layer places each element where it is told, so its coordinates and anchors measure from the page edge. This is what lets a page number sit in the margin and a background run to the edge.
To put a header or a footer inside the margins, taking up space, use a region in the layout. To put it on the page, taking up none, use a layer.
Positioning with anchor
Section titled “Positioning with anchor”x and y place the element’s box. Without anchor, they are its top-left corner.
With anchor, the element is placed against one of nine points of the page, and x and y become signed offsets from that point. The box’s 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 |
<layer page="each"> <!-- the box's bottom-right corner 36pt in from the page's, on any page size --> <text-at anchor="bottom-right" x="-36pt" y="-36pt" font-size="9pt">Footer</text-at></layer>L.layer({ page: 'each' }, [ L.textAt({ anchor: 'bottom-right', x: '-36pt', y: '-36pt', fontSize: '9pt' }, ['Footer']),])L.layer(LayerAttr(page='each'), [ L.text_at(TextAtAttr(anchor='bottom-right', x='-36pt', y='-36pt', font_size='9pt'), ['Footer'])])L::layer(new LayerAttr(page: 'each'), [ L::textAt(new TextAtAttr(anchor: 'bottom-right', x: '-36pt', y: '-36pt', fontSize: '9pt'), ['Footer']),])L.Layer(new() { Page = "each" }, [ L.TextAt(new() { Anchor = "bottom-right", X = "-36pt", Y = "-36pt", FontSize = "9pt" }, ["Footer"]),])A line and a path have no box, and no anchor.
Elements
Section titled “Elements”| 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-at |
Text at a position |
img-at |
Image at a position |
barcode-at |
QR code, Code 128 or EAN-13 at a position |
A stroke is centred on the outline of its shape, so half of stroke-width falls outside the box. A circle at x="0pt" with a 2pt stroke loses 1pt of it off the page.
Opacity
Section titled “Opacity”opacity fades everything in the layer as one: where its shapes overlap, they do not show through each other. A layer at 0 draws nothing.
To fade one element, put it in a layer of its own. The shapes and the -at elements have no opacity of their own, and the engine stops with an error that says so. A box of the layout has one: see Opacity.
<section> <layer opacity="0.15"> <circle x="360pt" y="40pt" r="120pt" fill="#1763cf"/> <circle x="440pt" y="120pt" r="80pt" fill="#cc0000"/> </layer> <layer> <rect x="48pt" y="48pt" width="120pt" height="60pt" fill="#1763cf"/> </layer></section>L.section(NoAttr, [ L.layer({ opacity: '0.15' }, [ L.circle({ x: '360pt', y: '40pt', r: '120pt', fill: '#1763cf' }), L.circle({ x: '440pt', y: '120pt', r: '80pt', fill: '#cc0000' }), ]), L.layer(NoAttr, [ L.rect({ x: '48pt', y: '48pt', width: '120pt', height: '60pt', fill: '#1763cf' }), ]),])L.section(NoAttr, [ L.layer(LayerAttr(opacity='0.15'), [ L.circle(CircleAttr(x='360pt', y='40pt', r='120pt', fill='#1763cf')), L.circle(CircleAttr(x='440pt', y='120pt', r='80pt', fill='#cc0000')) ]), L.layer(NoAttr, [ L.rect(RectAttr(x='48pt', y='48pt', width='120pt', height='60pt', fill='#1763cf')) ])])L::section(NoAttr, [ L::layer(new LayerAttr(opacity: '0.15'), [ L::circle(new CircleAttr(x: '360pt', y: '40pt', r: '120pt', fill: '#1763cf')), L::circle(new CircleAttr(x: '440pt', y: '120pt', r: '80pt', fill: '#cc0000')), ]), L::layer(NoAttr, [ L::rect(new RectAttr(x: '48pt', y: '48pt', width: '120pt', height: '60pt', fill: '#1763cf')), ]),])L.Section(NoAttr, [ L.Layer(new() { Opacity = "0.15" }, [ L.Circle(new() { X = "360pt", Y = "40pt", R = "120pt", Fill = "#1763cf" }), L.Circle(new() { X = "440pt", Y = "120pt", R = "80pt", Fill = "#cc0000" }), ]), L.Layer(NoAttr, [ L.Rect(new() { X = "48pt", Y = "48pt", Width = "120pt", Height = "60pt", Fill = "#1763cf" }), ]),])Colors have no alpha channel. Text that is only paler can use a lighter color, but text that must show through something needs a layer, or a box with opacity.
Text style
Section titled “Text style”font, font-size and color on a layer are the defaults of its text-at, so a layer of labels is styled once. A text-at that sets its own takes its own.
<layer page="each" font-size="9pt" color="#777777"> <text-at x="48pt" y="40pt">Quarterly report</text-at> <text-at anchor="top-right" x="-48pt" y="40pt">Confidential</text-at> <text-at anchor="bottom-center" y="-24pt" color="#cc0000">Draft: both labels above take the layer's color; this one its own</text-at></layer>L.layer({ page: 'each', fontSize: '9pt', color: '#777777' }, [ L.textAt({ x: '48pt', y: '40pt' }, ['Quarterly report']), L.textAt({ anchor: 'top-right', x: '-48pt', y: '40pt' }, ['Confidential']), L.textAt({ anchor: 'bottom-center', y: '-24pt', color: '#cc0000' }, ['Draft: both labels above take the layer\'s color; this one its own']),])L.layer(LayerAttr(page='each', font_size='9pt', color='#777777'), [ L.text_at(TextAtAttr(x='48pt', y='40pt'), ['Quarterly report']), L.text_at(TextAtAttr(anchor='top-right', x='-48pt', y='40pt'), ['Confidential']), L.text_at(TextAtAttr(anchor='bottom-center', y='-24pt', color='#cc0000'), ['Draft: both labels above take the layer\'s color; this one its own'])])L::layer(new LayerAttr(page: 'each', fontSize: '9pt', color: '#777777'), [ L::textAt(new TextAtAttr(x: '48pt', y: '40pt'), ['Quarterly report']), L::textAt(new TextAtAttr(anchor: 'top-right', x: '-48pt', y: '40pt'), ['Confidential']), L::textAt(new TextAtAttr(anchor: 'bottom-center', y: '-24pt', color: '#cc0000'), ['Draft: both labels above take the layer\'s color; this one its own']),])L.Layer(new() { Page = "each", FontSize = "9pt", Color = "#777777" }, [ L.TextAt(new() { X = "48pt", Y = "40pt" }, ["Quarterly report"]), L.TextAt(new() { Anchor = "top-right", X = "-48pt", Y = "40pt" }, ["Confidential"]), L.TextAt(new() { Anchor = "bottom-center", Y = "-24pt", Color = "#cc0000" }, ["Draft: both labels above take the layer's color; this one its own"]),])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.
To combine opacity and a transform, put both on one layer.
Data in a layer
Section titled “Data in a layer”Data binding reaches layers, with two limits.
data-valuesets the text of atext-atand the data abarcode-atencodes, as it does in the layout.data-ifanddata-if-notwork ontext-at,img-atandbarcode-at. The shapes take none of them: to switch a shape on or off, put it in a layer withdata-if.data-ifanddata-if-noton thelayerswitch the whole layer on or off, such as a DRAFT watermark while a status isdraft. The check happens once, so the layer is on every page itspageselects, or on none.data-sourceis not allowed on a layer’s elements: every copy would sit in the same place. A list from data belongs in the layout.- A layer reads single values. An array item can be read by its index,
signers[0].name, which suits a number of items fixed by the design, such as two signature lines. - A layer drawn on several pages shows the same values on each. Only
{page}and{pages}change, and they count within the section.
Text that comes from data, and so can be any length, belongs in the layout. In a layer, a longer text loses its end without a warning. See text-at.
DRAFT watermark
Section titled “DRAFT watermark”<section> <layer page="each" opacity="0.12" transform="rotate(45, 306, 396)" data-if="draft"> <text-at x="100pt" y="420pt" font-size="72pt" color="#cc0000">DRAFT</text-at> </layer> <layout margin="48pt"> <text>A document with a status.</text> </layout></section>data-if="draft" draws the watermark only when the data passed to render has a truthy draft. A document rendered with no data at all is drawn as written, watermark included.