About this manual

This is the PageWright™ JSON Spec Language Reference. PageWright is a PDF page-layout engine: you describe a document as a JSON spec - its pages, text, tables, images, charts, forms and structure - and the engine flows it onto pages and writes a tagged, standards-compliant PDF.

Every page of this manual was produced by the engine itself from a JSON spec (sample/guide-spec-reference.json), so it doubles as a worked example: the cover, the table of contents, the running heads and folios, the code boxes, the reference tables, and the index and glossary at the back are all ordinary engine features described in these pages.

Note: Conventions. Monospace text in a red tint marks a JSON key, value, or command - for example "type": "paragraph" or pagewrightapp. Shaded boxes are complete, valid snippets you can paste into a spec. Bold marks the first mention of a defined term; the Glossary collects them.

1. What a spec is

A PageWright document is a single JSON file. It is not a program and there is no scripting: you describe what the document contains - its pages, text, tables, pictures, charts, forms and structure - and the engine decides where everything lands, flows it onto pages, and writes a tagged, standards-compliant PDF.

That distinction matters. You never position a line of text or ask for a page break in the middle of a paragraph. You say "here is a heading, here are four paragraphs, here is a table", and the engine paginates, wraps, justifies, hyphenates, keeps headings with their text, repeats table headers across page breaks, and numbers everything.

The smallest useful document is three lines:

{
  "blocks": [
    {
      "type": "paragraph",
      "text": "Hello."
    }
  ]
}

Render it from the command line:

pagewrightapp hello.json hello.pdf

2. The shape of a document

Every spec is a JSON object. At the top sit document-wide settings - metadata, page setup, default styling - and one of two content keys:

  • blocks - a flat list of elements, for a document with one page setup throughout.
  • sections - a list of sections, each with its own page setup and its own blocks, for a document that changes orientation, margins, column count or folio style partway through.

A document uses one or the other, never both. Everything else is optional.

{
  "title": "Quarterly Report",
  "author": "Appligent, Inc.",
  "tagged": true,
  "lang": "en-US",
  "pageSetup": {
    "size": "Letter",
    "margins": 64,
    "footer": {
      "center": "{{page}}"
    }
  },
  "defaultStyle": {
    "fontFamily": "Helvetica",
    "fontSize": 11
  },
  "blocks": [
    {
      "type": "heading",
      "level": 1,
      "text": "Summary"
    },
    {
      "type": "paragraph",
      "text": "Revenue rose nine per cent."
    }
  ]
}

Note: tagged: true is what makes the output an accessible PDF: headings become real heading structure, tables get row and cell structure, and pictures carry their alternate text. Set it on anything that will be read by a person rather than only by a machine.

3. How to read this reference

Every element gets the same treatment: what it is for, a worked example you can paste into a spec, and a table of its keys. In those tables:

  • Key is the JSON property name, spelled exactly as the engine expects it.
  • Type is string, number, integer, boolean, object, array of X, or the name of a shared definition documented in Part IV.
  • Default is what the engine uses when you leave the key out. An empty cell means the key has no default - leaving it out simply means the feature is off.
  • Description says what the key does. Required marks a key the element cannot be built without.

Keys that every block accepts - style, id, lang, position and the rest - are documented once, in their own chapter, and left out of the per-element tables so that what you see there is what is specific to that element.

Tip: This reference is generated from the engine's own JSON Schema, so the key tables cannot drift from the engine. The same schema ships with the engine and can validate your specs directly.

4. Common block keys

The keys below may appear on any element. They are listed here once and omitted from the individual element tables.

KeyTypeDefaultDescription
alignstringHorizontal alignment of the block's content within its column. One of: left, center, right, justify, start, end.
fitWidthbooleanHeading: solve the font size so the (single-line) text fills the column.
idstringCross-reference anchor: a run's "ref" renders this block's page number and links here.
keepWithNextbooleanPagination: keep this block on the same page as the start of the next block (avoids a stranded line).
langstringBCP-47 language tag for this block, emitted as /Lang so a screen reader switches pronunciation. It also decides how the block's words divide when hyphenate is on: by this language's rules rather than the document's, and not at all when the engine embeds no patterns for it.
letterSpacingnumberExtra points between glyphs (inherited).
positionpositionPlace the block at fixed coordinates instead of in the flow, measured from the trim page's top-left -- or, when the nearest enclosing container that writes "childOrigin" says "container", from that container's box (see childOrigin). A positioned block reserves NO vertical space in the flow unless it also sets "textWrap".
stylestyleStyle for this block. Text properties cascade to nested content; box properties (padding, border, spacing) apply to this block only.
textWrapstringText wrap around an absolutely-positioned block (ignored without "position"). "none" (default): the block leaves the flow entirely and reserves no space -- what a letterhead, background panel or form-field overlay needs. "topAndBottom": the flow keeps clear of the block's whole vertical band, edge to edge, so flow content AFTER it in document order drops below it. "around": the flow keeps clear only of the block's OWN RECTANGLE, so a panel narrower than its column gets text beside it and full width below -- use this rather than topAndBottom when a pinned block should not split the page across. A block pinned strictly INSIDE its column (touching neither edge) leaves the strip on one side unused; pin it to a column edge to use both. (Named textWrap because pre already owns a boolean wrap.) One of: none, topAndBottom, around.
trackingnumberAlias for letterSpacing.
unlessstringData-driven conditional: drop this block when the predicate is true.
whenstringData-driven conditional: keep this block only when the predicate is true (a path, or "path OP value").
wordSpacingnumberExtra points between words. A BLOCK property: it is read from the block being laid out, so setting it on a RUN parses and resolves but changes nothing. Inherited by nested blocks.

Note: when and unless are the data-driven conditionals: give either one a predicate and the block survives into the document only when the predicate holds. They are the per-block form of the if element.

5. Text

Elements that put words on the page.

The paragraph element

Type name: paragraph.

The workhorse. A paragraph is a run of prose that the engine wraps, justifies and paginates for you: give it text for a single unstyled string, or runs when parts of it need their own styling, a link, a footnote or a cross-reference. Whitespace is collapsed - every run of spaces, tabs and newlines becomes one space - so you can format the JSON however you like without changing the output. A paragraph splits across pages and columns at line boundaries, with widow and orphan control.

Example

{
  "type": "paragraph",
  "runs": [
    {
      "text": "Tighten the retaining bolt to "
    },
    {
      "text": "18 N·m",
      "style": {
        "weight": "bold"
      }
    },
    {
      "text": " before flight."
    }
  ]
}

Figures aligned on a decimal tab, with a dot leader

{
  "type": "paragraph",
  "style": {
    "tabs": [
      {
        "at": 60,
        "align": "left"
      },
      {
        "at": 300,
        "align": "decimal",
        "leader": "dot"
      }
    ]
  },
  "text": "Retaining bolt\t18.00"
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
boxHeightnumberVertical writing only: the height in points of the box the columns are set in. Omit to fill the height the paragraph is offered: in a horizontal page from where it stands down to the page foot (at most the text area's height); in a table cell, a columns cell, a cardGrid card or a container down to the page foot too, so a row holding it stands a page tall; in a positioned paragraph or container its position.height (without one, the room from where the text begins down to the flow's foot, above any footnote band - or, beginning in the bottom margin or within a character of that foot, down to the page's foot - at most the text area's full height, as a flowing paragraph's; it took the text area's full height from where the box was placed, running past the page foot, until 2026-10-15); in a cell of a positioned columns block, table or cardGrid with no height - or a container holding one - every such text in it (a list item's too) shares the box's room: each is given the same box, the tallest (at most the text area's height) with which the whole box ends at that foot - or, where the box's horizontal content runs past the foot however short that box is, with which every such text does - every row still drawn, a card grid's rows still uniform, and columns past a cell's width cut and said, naming the box they are set in (it took the text area's full height there, running past the page foot, until 2026-10-15). Not bounded, as before: such text in a positioned box with a position.height (a table drops its rows past that height, said; a columns block's or cardGrid's stands a text area tall past it), or positioned in a vertical section - set boxHeight there. The foot is read when the box is placed: a footnote that its own cells, or a block placed after it, add on that page is not above it. Set it for a smaller box, which a vertical block in a cell, a card or a container must do. A box shorter than one character is raised to one (a column holds at least one), and the engine says so. When the box does not fit in the room left the paragraph moves to the next page (a fresh page shrinks the box to the room there). Columns that overrun the box's width continue on the next page; in a table cell, a columns cell, a cardGrid card or a positioned box, which do not continue, they are not drawn, and the engine says how many characters were lost and that the columns ran past the width (a taller boxHeight, a wider box or shorter text holds them).
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
columnGapnumberVertical writing only: extra space in points between columns, on top of the one-em column body. Omit for the conventional half-em gap.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
counterstring | objectNamed autonumber stream (defined in numbering.counters): bump it and prefix this paragraph its value ("Step 1. "). A string names the stream; the object form restarts it.
counterRestartbooleanRestart the named counter stream before this block (shorthand alongside a string "counter").
dropCapintegerDrop cap: set the first character as an enlarged initial dropped over this many lines; the body wraps around it.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
minFontSizenumber6shrinkToFit's floor, in points: shrinking never sets any run of the text below this, and text written smaller keeps its own size.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
runsrunsThe block's text as styled inline runs, instead of "text". A run can carry a link, footnote, badge, inline maths or a cross-reference.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
shrinkToFitbooleanWith a position height: shrink the text until its lines fit the box, in 0.25 pt steps of its largest size -- every size scaled by one ratio, but no run set below minFontSize (a run written smaller keeps its own size). Solved against the box's own height, so the size is the same wherever the page breaks. A block whose own keys ask for it without a position height is told so and keeps its size; a dropCap, textPath or vertical paragraph, or one a run-in heading is drawn into, is not shrunk. Text that still does not fit at minFontSize, or that its padding leaves no room for, is clipped and reported. May also come from the block's style.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
textstringThe block's text, when it needs no inline styling.
textPathtextPath -
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
writingModestringWriting mode for this paragraph. "vertical-rl" sets it in 直排 (tategaki) columns - glyphs advance downward, columns advance right to left; "vertical-lr" runs the columns left to right. The block owns a fixed box inside the ordinary horizontal flow, so the surrounding layout is unchanged. Omit (or "inherit") to take the section's pageSetup.writingMode, itself horizontal by default. Vertical text needs an embedded font (pageSetup.embedFont or a style fontFamily) - the base-14 faces carry no vertical CMap and no Han glyphs. One of: horizontal-tb, horizontal, lr-tb, yokogaki, 横排, vertical-rl, vertical, tb-rl, tategaki, 直排, 縦書き, vertical-lr, tb-lr, inherit.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The heading element

Type name: heading.

A titled break in the flow, at level 1 to 6. Headings do more than look large: each one is collected into the table of contents, becomes a PDF bookmark, and is tagged H1-H6 for accessibility. Give a heading an id and other blocks can cross-reference it. With document numbering enabled the engine writes the number for you, so "1.2 Assembly" is something you never type.

Example

{
  "type": "heading",
  "level": 2,
  "id": "assembly",
  "text": "Assembly"
}

An appendix heading, lettered by document numbering

{
  "type": "heading",
  "level": 1,
  "appendix": true,
  "id": "app-data",
  "text": "Data tables"
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
appendixbooleanfalseOn a level-1 heading, opens the APPENDICES: the chapter counter restarts and switches to letters from here on, so this becomes A, the next B. Chapter-relative captions follow ("Figure A-1"), and a cross-reference reads "Appendix A".
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
boxHeightnumberVertical writing only (a heading whose writingMode, its own or its section's, is vertical): the height in points of the box its columns are set in - a paragraph's boxHeight, same meaning and default (since rev 2026-10-14.2). Set it for a bounded heading: in a table cell, a columns cell, a cardGrid card or a container the row or box is sized to it rather than standing a page tall, and in a horizontal page the block after it follows below it, so keepWithNext can be met. A box shorter than one character is raised to one, and the engine says so. When the box does not fit in the room left the heading moves to the next page (a fresh page shrinks the box to the room there), and a heading before it with keepWithNext moves with it, whether or not this one keeps with its own next. When its columns are wider than the width left it is split where it stands in a page's column or a positioned box (since 2026-10-15: the next column or page is no wider, so a box of the same height is just as wide there; moving it only split it there and left a heading before it alone), the rest continuing on the next page, still the heading (<Hn>, listed once) - a heading before it with keepWithNext keeps with its first part, and its own keepWithNext holds its last part to the block after it; where the next slot may be wider - a vertical section, a box narrowed by its padding or beside a float, a story's frame - it moves on whole and is split only there if it must be. In a table cell, a columns cell, a cardGrid card or a positioned box, which do not continue, columns past the width are not drawn (its <Hn> says what is drawn; its bookmark and contents row keep the title), and the engine says how many characters were lost and that the columns ran past the width. Omit to fill the height the heading is offered, as a vertical paragraph without boxHeight does: in a horizontal page from where it stands down to the page foot (at most the text area's height) - and since a heading keeps with its next block by default and nothing can follow it on that page, it moves to the next page unless it already stands at a page top, its body after it on the page after (keepWithNext: false keeps it where it stands; a vertical paragraph given keepWithNext moves the same way); in a table cell, a columns cell, a cardGrid card or a container down to the page foot, a row holding it a page tall - and in a cell of a positioned columns block, table or cardGrid with no height, its share of that box's room, as a vertical paragraph's (since 2026-10-15; not in a box with a position.height, nor in a vertical section).
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
columnGapnumberVertical writing only: extra space in points between the heading's columns, on top of the one-em column body - a paragraph's columnGap, same meaning (since rev 2026-10-14.2). Omit for the conventional half-em gap, half the heading's own font size.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
levelintegerHeading level, 1 (most prominent) to 6. Drives the tagged structure (H1-H6), the outline hierarchy and automatic heading numbering. THOSE THREE ARE SEPARABLE: outline: false keeps the H1-H6 tag while leaving the heading out of the bookmark panel and the contents list, and numbered: false drops it from the numbering stream. Use outline: false for a card title or panel label -- a heading to a reader, noise beside the document's real chapters -- rather than styling a paragraph bold, which produces a <P> and is announced as body text.
minFontSizenumber6shrinkToFit's floor, in points: shrinking never sets any run of the text below this, and text written smaller keeps its own size.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic heading numbering (when the document enables it). false leaves a title / front-matter heading unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
outlinebooleantrueWhether this heading enters the document OUTLINE -- the bookmark panel and, because both are fed by the same registration, the table of contents and the running heads. false keeps everything else about the heading, including its <Hn> tagged structure, its numbering and its style. Intended for a heading inside a card, panel or callout: real structure for assistive technology, absent from navigation.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
runInbooleanfalseDraw this heading INLINE at the start of the following paragraph's first line (a bold lead-in), the body flowing on the same line beside it. The heading stays a distinct <Hn> structure element (outline / TOC / bookmark). No effect unless a plain paragraph follows; falls back to a normal stacked heading under a baseline grid or when the next block is not a plain paragraph. Ignored on a heading set on a curve (textPath) or in vertical (直排) columns, which are shapes of their own: the heading is set on its curve or in its columns, on its own, and the engine says so once (at /pointer).
runsrunsThe block's text as styled inline runs, instead of "text". A run can carry a link, footnote, badge, inline maths or a cross-reference.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
shrinkToFitbooleanWith a position height: shrink the text until its lines fit the box, in 0.25 pt steps of its largest size -- every size scaled by one ratio, but no run set below minFontSize (a run written smaller keeps its own size). Solved against the box's own height, so the size is the same wherever the page breaks. A block whose own keys ask for it without a position height is told so and keeps its size; a textPath or vertical heading, or a side head, is not shrunk. Text that still does not fit at minFontSize, or that its padding leaves no room for, is clipped and reported. May also come from the block's style.
sideheadbooleanfalseDraw this heading in the reserved side-head band (see pageSetup.sideHeadWidth) instead of the body flow, with its top aligned to the body that follows. No effect unless the section defines a side-head area; then the heading consumes no vertical flow and the body sits beside it.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
textstringThe block's text, when it needs no inline styling.
textPathtextPath -
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
writingModestringWriting mode for this heading. "vertical-rl" sets it in 直排 (tategaki) columns at the heading's own size - glyphs advance downward, columns advance right to left; "vertical-lr" runs the columns left to right. It stays the heading: tagged <Hn>, with one bookmark and one contents row. It moves to the next page when its box is taller than the room left, and when its columns are wider than the width left where moving can help - without a boxHeight (its box grows to a fresh page's height), in a vertical section (the width left shrinks as columns fill), in a box narrowed by its padding or beside a float, or in a story's frame (the next slot may be wider); a heading in its own boxHeight given a page's whole column, or in a positioned box, whose box comes back no wider in the next column or page, is split where it stands instead (since 2026-10-15), as is any heading at a page top. The rest continues as the heading (<Hn>, at its size, listed once), never as body text. Its columns are boxHeight tall and columnGap apart - the paragraph's two keys, with the same meaning and defaults (since rev 2026-10-14.2). Without boxHeight it fills the height it is offered, exactly as a vertical paragraph without one does: in a horizontal page from where it stands down to the page foot (at most the text area's height), and in a table cell, a columns cell or a container down to the page foot too, so a table or columns row holding it stands a page tall; with boxHeight it is bounded there, its row or box sized to it, and in a horizontal page its body follows below it. In a vertical section it is a column group in the page's own flow. Its endnotes and citations are drawn in the column and linked both ways; a footnote or a margin note has no page foot or margin band beside a column, so it is not drawn and takes no number, and the engine says so. runIn is ignored on a vertical heading - a column is a shape of its own - and the engine says so once (at /pointer). Omit (or "inherit") to take the section's pageSetup.writingMode, itself horizontal by default. Vertical text needs an embedded font (pageSetup.embedFont or a style fontFamily) - the base-14 faces carry no vertical CMap and no Han glyphs. One of: horizontal-tb, horizontal, lr-tb, yokogaki, 横排, vertical-rl, vertical, tb-rl, tategaki, 直排, 縦書き, vertical-lr, tb-lr, inherit.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The list element

Type name: list.

A bulleted or numbered list. Each item is itself a list of blocks, so an item can hold several paragraphs, a nested list, an image or a table - not just one line of text. Set ordered for numbers, and nest lists to whatever depth the material needs.

Example

{
  "type": "list",
  "ordered": true,
  "items": [
    {
      "blocks": [
        {
          "type": "paragraph",
          "text": "Seat the motor."
        }
      ]
    },
    {
      "blocks": [
        {
          "type": "paragraph",
          "text": "Attach the shock cord."
        }
      ]
    }
  ]
}

A multi-level numbered outline (1, 1.1, 1.1.1)

{
  "type": "list",
  "ordered": true,
  "multiLevel": true,
  "items": [
    {
      "blocks": [
        {
          "type": "paragraph",
          "text": "Preparation"
        }
      ],
      "items": [
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "Inspect the airframe."
            }
          ]
        },
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "Check the motor mount."
            }
          ]
        }
      ]
    },
    {
      "blocks": [
        {
          "type": "paragraph",
          "text": "Assembly"
        }
      ]
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
itemsarray of listItemThe list items. A bare string is plain text; an object can carry runs, its own style, or nested blocks.
levelsarray of stringMulti-level: per-level numeral formats, outermost first (["upper-roman","upper-alpha","1"] => I, I.A, I.A.1). Inherited by nested lists; levels past the end reuse the last.
markerstringExplicit marker style. An ordered style (decimal / alpha / roman) implies ordered:true. Overrides the ordered default (decimal or bullet). One of: bullet, dash, decimal, 1, lower-alpha, a, upper-alpha, A, lower-roman, i, upper-roman, I.
markerSuffixstring.Multi-level: text drawn after the whole number ("1.1."). Set "" for "1.1".
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
multiLevelbooleanNumber nested ordered lists with their full ancestor path - 1, 1.1, 1.1.1 - instead of restarting per level (the legal / outline convention). Opt-in; nested ordered lists inherit it.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
orderedbooleanNumber the items (1. 2. 3.) instead of bulleting them.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
separatorstring.Multi-level: separator between levels ("1.1").
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
startinteger1Ordinal of the first item.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The pre element

Type name: pre. Accepted aliases: code.

Verbatim text, drawn line for line in a monospace face with every space preserved: a console session, a configuration file, a code listing. Unlike a paragraph it never reflows. fit shrinks an over-wide line to the column; wrap breaks it onto an indented continuation instead. Supplying tokens instead of text gives syntax coloring, and a caption promotes the block to a numbered listing.

Example

{
  "type": "code",
  "language": "python",
  "lineNumbers": true,
  "caption": "Reading a spec file.",
  "text": "def load(path):\n    return json.load(open(path))"
}

Syntax-colored tokens, with one line highlighted

{
  "type": "code",
  "language": "python",
  "lineNumbers": true,
  "highlightLines": [
    1
  ],
  "tokens": [
    [
      {
        "text": "def ",
        "color": "#CF222E"
      },
      {
        "text": "load",
        "color": "#8250DF"
      },
      {
        "text": "(path):",
        "color": "#1F2328"
      }
    ],
    [
      {
        "text": "    return ",
        "color": "#CF222E"
      },
      {
        "text": "json",
        "color": "#0550AE"
      },
      {
        "text": ".load(open(path))",
        "color": "#1F2328"
      }
    ]
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
captionstring | runsA caption makes this a LISTING: the text is drawn above the code (in the surrounding body face) and collected for the list of listings ("lol"). Numbered when the document enables "numbering": { "listings": ... }. Accepts a plain string, an ARRAY OF RUNS, or a SINGLE RUN OBJECT - the same run forms a paragraph's "runs" takes - so a caption can carry per-span styling, and above all a per-span "fontFamily": a caption that mixes scripts sets each span in the face that suits it instead of flattening to one. The flat text is always derived from the runs and stays the entry of record: it is what reaches the list of figures, the /Alt and the structure tree. A number ("Figure 3-2.") is prepended as the first run, unstyled, so it takes the block's own face.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
fitbooleanShrink the monospace font uniformly so the widest line fits the column width (default true); an over-wide line scales down instead of spilling past the margin.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
highlightColorcolorEmphasis band color for highlightLines (default soft blue).
highlightLinesarray of integer1-based source lines to emphasize with a background band.
languagestringInformational language label for a code block (e.g. "python"); does not affect layout.
lineNumbersbooleanDraw an aligned monospace line-number gutter to the left of the code (default false).
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic caption numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
panelColorcolorPanel fill behind a tokenized code block (default light gray).
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
textstringVerbatim preformatted text (a Unix-man-page Quick Reference, a code listing): drawn line-for-line (\n = a hard line break) with every space preserved, in a monospace font - Courier by default, but only when nobody chose a face: a fontFamily on the block wins, and so does one inherited from a container or table cell, so a pre inside a styled box sets in that box's monospace rather than falling back to Base-14 - no whitespace collapsing, wrapping or reflow. Ignored when "tokens" is present.
tokensarray of array of string | objectSyntax highlighting: a pre-tokenized color stream that supersedes "text". One inner array per source line; each token is a colored span whose whitespace is preserved so the source line reconstructs exactly. A tokenized code block paints a panel behind every line.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
wrapbooleanSoft-wrap over-wide lines onto indented continuation lines instead of shrinking (default false; takes precedence over fit). Short lines keep full size; a long line breaks at a space (or hard-breaks an unbreakable token) so it never spills past the margin.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

6. Structure and spacing

Elements that group other elements or shape the page.

The container element

Type name: container.

A group of blocks treated as one. A container is how you give several blocks a shared background, border, padding or margin - a boxed sidebar, a tinted panel, a card. It is also the unit of keepTogether, which asks the engine not to split the group across a page break.

Example

{
  "type": "container",
  "keepTogether": true,
  "style": {
    "background": "#eef2f8",
    "padding": 10,
    "border": {
      "width": 0.75,
      "color": "#2a4d7a"
    }
  },
  "blocks": [
    {
      "type": "paragraph",
      "text": "A boxed aside."
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
blocksarray of blockThe blocks held by this container, in reading order. A container is also how you give a run of content one shared style or background.
boxAlignstringleftHorizontal placement of the box when width makes it narrower than the space it is placed in (default "left"). Physical edges: "left" is the left edge in a right-to-left document too. It moves the box, not the text inside it -- that is align. No effect on a floated container, which float places. A box keeps its place on every page it splits onto. One of: left, center, right.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
childOriginstringpageWhere the position.x / position.y of this container's POSITIONED descendants are measured from. "page" (what an unwritten key means, unless an enclosing container set "container"): the trim page's top-left, as everywhere else. "container": this container's own box's top-left -- the BORDER box, not the content box inside the padding: for a positioned container its position rect (its fragment in the layout map), for one in the flow the box it paints where it landed, after pagination and in whatever column (its visual rect in the map, recorded even when it paints nothing; on a page a split container continues onto, the part placed there). So a sticker at {x: 10, y: 10} sits 10 pt in from the card's corner wherever the card is. The nearest container that WRITES the key decides: a nested "page" resets its subtree to page coordinates, and a nested container that does not write it inherits the rule (its descendants measure from the ancestor that said "container", not from it). Width and height are unaffected. The container's own angle / scale / skew / mirror, clip and groupOpacity apply to its children as before -- they are laid out in the container's upright box and turn with it; verticalAlign on the container does not move them. Everything that reports geometry reports the resulting PAGE coordinates: the layout map, TextShop line records, link rectangles, textWrap bands, clip notices. On any block other than a container, and for any other value, it is refused with a notice and ignored. One of: page, container.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
keepTogetherbooleanKeep the whole group on one page when it fits a fresh one: it moves to the next page whole rather than split. Unwritten, it is true for a container that paints a box (a style background, border, accentBar or stripe), as for a callout, and false for one that paints nothing. Write false to let a box split. A container that is split -- because it is taller than a page, or keepTogether is false -- keeps its side padding, width and boxAlign on every page it reaches, and takes its bottom padding and spaceAfter where it ends. One that paints a box is drawn on every page, open where the page cuts it, and repeats its top padding below each cut; one that paints nothing does not.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
rolestringnoneThe STRUCTURE TYPE this box announces itself as, for assistive technology. ONLY ON A container BLOCK -- "role" is an ordinary word and appears freely in a document's own DATA (a {name, role, years} record bound by a repeat, say); nothing outside a container block is touched by this key. Default "none" = no structure element at all, which is what every container was before this key existed and what a purely visual grouping (a tile, a swatch, a specimen box) should stay. "aside" is content outside the main narrative -- a sidebar, a pull quote, a closing commentary band -- and is the type a callout sets for itself; it is written as <Aside> in the PDF 2.0 namespace under PDF/UA-2, and elsewhere as <Aside> plus a /RoleMap to Div, so a pre-PDF-2.0 reader still gets a standard type. "section" is <Sect>, "div" a generic block-level grouping, "blockquote" an extended quotation. An unknown value REFUSES the document rather than being ignored -- a role that silently does nothing is precisely the defect this key exists to fix. Not offered: Figure (needs /Alt and has its own block), Note (PDF 1.7 reserves it for content REFERRED TO from the body, which is the footnote path), Caption, TOC. One of: aside, section, div, blockquote, none.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionExplicit box width (default "auto" = the full width): points, or "NN%" of the width the container is placed in -- its column, or the cell, card or box that holds it. The box takes that width, its blocks wrap inside its padding, and boxAlign places it; the room beside a narrower box stays empty (to run text beside a box, float it). A width wider than the space takes the full width, as does zero or less. A box keeps its width and boxAlign on every page it splits onto. On a floated container it sizes the float box instead -- points or a percentage of the column; unwritten, half the column -- and the box fills it.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The columns element

Type name: columns.

A row of side-by-side columns, each holding its own block. Use it for a two-up comparison, a label-and-value pair, or a picture beside its explanation. This is different from pageSetup.columns, which pours a single stream of text down multiple newspaper columns; columns places distinct content side by side.

Example

{
  "type": "columns",
  "gap": 18,
  "columns": [
    {
      "width": "50%",
      "block": {
        "type": "paragraph",
        "text": "Left."
      }
    },
    {
      "width": "50%",
      "block": {
        "type": "paragraph",
        "text": "Right."
      }
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
columnsarray of objectThe columns themselves - each holds its own blocks, and may set a width.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
gapnumberGutter between the columns, in points.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
valignstringHow the columns align to each other when they differ in height. "stretch" makes them equal. One of: top, center, bottom, stretch.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The cardGrid element

Type name: cardGrid. Accepted aliases: cardgrid.

A grid of cards that wraps: you supply the items, the engine decides how many fit per row and flows the rest onto the next. The signature layout of a dashboard or a product summary page.

Example

{
  "type": "cardGrid",
  "minColWidth": 150,
  "gap": 12,
  "items": [
    {
      "type": "container",
      "blocks": [
        {
          "type": "paragraph",
          "text": "One"
        }
      ]
    },
    {
      "type": "container",
      "blocks": [
        {
          "type": "paragraph",
          "text": "Two"
        }
      ]
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
gapnumberGap between cards, both directions. "gapCol" and "gapRow" override it per axis.
gapColnumberHorizontal gap between cards, overriding "gap".
gapRownumberVertical gap between cards, overriding "gap".
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
itemsarray of blockThe cards. Each is a block group rendered as one cell of the grid.
minColWidthnumberNarrowest a column may get, IN POINTS, before the grid drops to fewer columns. This is what makes the grid responsive to the measure.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The spacer element

Type name: spacer.

Blank vertical space, in points. The simplest way to open a gap.

Example

{
  "type": "spacer",
  "height": 12
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberBlank vertical space to insert, in points.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The rule element

Type name: rule.

A horizontal line, with a thickness, color and inset from the column edges.

Example

{
  "type": "rule",
  "thickness": 0.5,
  "color": "#cccccc",
  "inset": 0
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
colorcolorLine color.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
insetnumberInset from both column edges in points, so the rule is shorter than the full measure.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
thicknessnumberLine thickness in points.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The pagebreak element

Type name: pagebreak. Accepted aliases: page-break.

Forces the following content onto a fresh page. Skipped when the cursor is already at the top of a page, so it never leaves a blank sheet behind.

Example

{
  "type": "pagebreak"
}

Keys

KeyTypeDefaultDescription
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.

7. Tables

The tabular grid, its rows, cells and headers.

The table element

Type name: table.

A tabular grid with sized columns, header rows that repeat across page breaks, spanning cells, per-cell styling and full tagged-PDF structure. Cells hold blocks, so a cell can contain paragraphs, images, lists or a nested table. A long table splits across pages - and, when a single row is taller than a page, within a row.

Example

{
  "type": "table",
  "caption": "Quarterly totals.",
  "headerRows": 1,
  "headerRepeat": true,
  "columns": [
    {
      "width": "60%"
    },
    {
      "width": "40%",
      "align": "right"
    }
  ],
  "rows": [
    {
      "header": true,
      "cells": [
        "Quarter",
        "Units"
      ]
    },
    {
      "cells": [
        "Q1",
        "1,240"
      ]
    }
  ]
}

A spanning header row and one boxed total cell

{
  "type": "table",
  "headerRows": 1,
  "border": {
    "width": 0.5,
    "color": "#999999"
  },
  "columns": [
    {
      "width": "50%"
    },
    {
      "width": "25%",
      "align": "right"
    },
    {
      "width": "25%",
      "align": "right"
    }
  ],
  "rows": [
    {
      "header": true,
      "cells": [
        {
          "colspan": 3,
          "style": {
            "background": "#e8edf4"
          },
          "blocks": [
            {
              "type": "paragraph",
              "text": "Regional results"
            }
          ]
        }
      ]
    },
    {
      "cells": [
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "North"
            }
          ]
        },
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "112"
            }
          ]
        },
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "100"
            }
          ]
        }
      ]
    },
    {
      "cells": [
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "Total"
            }
          ]
        },
        {
          "style": {
            "border": {
              "width": 2,
              "color": "#1f4e79"
            }
          },
          "blocks": [
            {
              "type": "paragraph",
              "text": "314"
            }
          ]
        },
        {
          "blocks": [
            {
              "type": "paragraph",
              "text": "310"
            }
          ]
        }
      ]
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
borderborder{'width': 0.5, 'color': '#000000'}Border around the table as a whole.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
captionstring | runsA title drawn above the table and tagged <Caption>. Accepts a plain string, an ARRAY OF RUNS, or a SINGLE RUN OBJECT - the same run forms a paragraph's "runs" takes - so a caption can carry per-span styling, and above all a per-span "fontFamily": a caption that mixes scripts sets each span in the face that suits it instead of flattening to one. The flat text is always derived from the runs and stays the entry of record: it is what reaches the list of figures, the /Alt and the structure tree. A number ("Figure 3-2.") is prepended as the first run, unstyled, so it takes the block's own face.
cellPaddinginsetsPadding inside every cell. A single cell can override it with its own "padding".
cellVAlignstringDefault vertical alignment of content within each row's height. One of: top, middle, center, bottom.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
colRulesborder{'width': 0.5, 'color': '#000000'}Vertical rules drawn between columns.
columnsarray of dimension | objectColumn widths: dimensions, or { width } objects.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
footerRowsintegerThe last N rows form the footer (tagged <TFoot>), drawn at the table end.
frameborder{'width': 1.0, 'color': '#000000'}Outer box drawn around the table on each page it occupies. (On a pdf-contact-sheet "frame" is a boolean instead - a different key with the same name.)
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
headerColsintegerThe first N columns are row headers (tagged TH with Scope=Row). With rowGroups this counts the AUTHORED columns that are row headers -- the synthetic group columns are added in front of them and are always headers, so headerCols need not account for them.
headerRepeatbooleanRepeat the header rows after a page break (default true).
headerRowsintegerHow many rows from the top are header rows. They repeat at the top of every page the table splits across, and are tagged as header cells.
headerRuleborder{'width': 1.0, 'color': '#000000'}The rule drawn under the header rows, so it can be heavier than the body rules.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic caption numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
presentationalbooleanThis table is a LAYOUT grid, not data (default false). It keeps its geometry but emits no table structure at all - no Table, THead, TBody, TR, TH or TD - and each cell's content tags itself in reading order. Use it when the grid only positions things: a form whose cells hold bare fields with no text, or a row of independent captioned fields where none heads another. Declaring headerRows/headerCols on such a table invents a relationship that is not there, which misleads a screen reader worse than saying nothing; leaving them off trips WCAG 1.3.1 forever. This is the third answer, and it exempts the table from that check.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
rowGroupsobjectTag a HIERARCHICAL table the way a professional 508 remediator tags one. The ground truth is a remediation of this engine's own budget-schedule sample, and with this key the engine reproduces that tree: ONE <Table> across every page the table spans (no THead/TBody; the repeated column band on later pages is an artifact), each group row drawn where it is but tagged as a rowspanned <TH Scope=Row> at the head of its group's first <TR>, the first headerCols drawn columns as <TH Scope=Row>, a total row's label widened over the synthetic columns it still sits under, and a derived /Headers on every cell (the row header immediately to its left, its column header). Mark the rows with group and total. Absent = every table tags exactly as before.
rowRulesborder{'width': 0.5, 'color': '#000000'}Horizontal rules drawn between body rows. The header boundary is drawn by "headerRule" instead.
rowsarray of array of tableCell | objectThe table's rows. Each is a list of cells - a bare string for plain text, or a cell object for spans, styling or block content.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
stripeColorcolorZebra striping: fill alternating body rows with this color.
tableAlignstringHorizontal placement of the table box when width is narrower than the content. One of: left, center, right.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionExplicit table width (default Auto = the full content width).
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

8. Pictures and drawings

Raster images, vector art, and generated graphics.

The image element

Type name: image.

A raster picture - PNG, JPEG, GIF, TIFF or WebP, or HEIC/HEIF on macOS. Size it with width and height, cap it with maxHeight, or let it take its natural size; the file's own orientation and ICC profile are honored. Floating an image with float lets text wrap beside it. A caption is drawn beneath and collected into the list of figures; alt supplies the text an assistive reader announces, and is required for a document that must pass PDF/UA.

Example

{
  "type": "image",
  "src": "diagram.png",
  "width": "45%",
  "float": "right",
  "caption": "The airflow around the fin root.",
  "alt": "A fin with airflow lines curving around its root fillet."
}

A left-floated, captioned figure with alt text

{
  "type": "image",
  "src": "fin.png",
  "width": "40%",
  "float": "left",
  "caption": "The fin planform.",
  "alt": "A trapezoidal fin, its root chord longer than its tip."
}

A landscape scan turned upright (rotate 90) into a portrait box

{
  "type": "image",
  "src": "scan.png",
  "width": 120,
  "rotate": 90,
  "caption": "The fold-out, turned upright.",
  "alt": "A wide schematic rotated a quarter-turn clockwise to read upright."
}

Keys

KeyTypeDefaultDescription
altstringAlternate text (/Alt on the tagged Figure).
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
autoOrientbooleantrueHonor the file's own orientation (JPEG EXIF Orientation, TIFF tag 274, HEIF irot/imir) before rotate and flip, so a photo stored sideways is drawn and sized upright. false draws the pixels as stored -- for a document that already turned the picture with rotate.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
blurnumberGaussian blur applied to the image's PIXELS, as a standard deviation in POINTS. The same quantity SVG's feGaussianBlur calls stdDeviation and CSS's filter:blur() takes, so an author who knows either already knows this one. 0 (the default) is a sharp image and costs nothing.

STATED IN POINTS, NOT PIXELS, DELIBERATELY. The blur is applied to the raster that gets embedded, and how many pixels that raster has depends on the maxPpi downsample policy and on the box the picture was placed in. A radius in pixels would therefore soften by a visibly different amount when the same photograph moved into a smaller frame.

THE PICTURE STAYS INSIDE ITS BOX. Edge pixels are replicated outward rather than fading to transparent, so a blurred photograph keeps its rectangle instead of growing a vignette; the blur does not spread beyond the box the way a CSS filter does. A soft-edged PNG keeps its softness -- the colour is premultiplied by the alpha plane before blurring, so transparent pixels contribute nothing and no dark halo appears at the edge.
bordernumber | color | objectImage effect: a border stroked around the (rounded) edge - a width, a color, or { width, color }.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
captionstring | runsCaption printed under the image. A caption is also what enrolls the image in the Figure counter and gives a cross-reference something to resolve against. Accepts a plain string, an ARRAY OF RUNS, or a SINGLE RUN OBJECT - the same run forms a paragraph's "runs" takes - so a caption can carry per-span styling, and above all a per-span "fontFamily": a caption that mixes scripts sets each span in the face that suits it instead of flattening to one. The flat text is always derived from the runs and stays the entry of record: it is what reaches the list of figures, the /Alt and the structure tree. A number ("Figure 3-2.") is prepended as the first run, unstyled, so it takes the block's own face.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
cornerRadiusnumberImage effect: clip the image to a rounded rectangle of this radius (points).
fitstringHow the image fills its box: "contain" fits it whole, "cover" crops to fill, "fill" stretches, "none" draws it at its natural size - one pixel per point. "cover" and "none" are cropped to the box; the picture is centred in it unless "focus" aims the crop (and "zoom" magnifies a cover). One of: contain, cover, fill, none.
flipHbooleanMirror the image left-to-right within its box.
flipVbooleanMirror the image top-to-bottom within its box.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
focusstring | number | array of number | objectWhere a cropping fit aims: [x, y] (or {"x":, "y":}, or one number for both axes) as fractions of the photo AS IT STANDS UPRIGHT -- after the file's own EXIF / TIFF / HEIF orientation (see autoOrient), before rotate, flipH and flipV, which carry the point along with the picture -- 0 = left / top, 1 = right / bottom. The point is brought to the centre of the box as far as the picture allows; the crop never pulls away from an edge, so [0, 0] and [1, 1] reach the corners and the default [0.5, 0.5] is the centred crop. Only "cover" and "none" crop; on any other fit it is ignored with a warning. Values outside 0..1 are clamped. A STRING is a data token, so each record of a template aims its own crop: "{{cards.0.frontFocus}}" resolves to the whole value at that path -- a number, [x, y], {x, y}, or text of numbers such as "0.3 0.4" (a CSV field) -- and a field the record lacks leaves the centred crop, silently. A bound value that is not numbers, or outside 0..1, is reported with the block's pointer (and the -merge record). A string with no token is read as it stands.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightdimensionDrawn height - points, a percentage, or "auto" to keep the aspect ratio.
linkstringClickable image: a web URI.
linkFilestringClickable image: open another PDF (GoToR; a relative path resolves next to the produced document).
linkPageinteger1-based page in linkFile.
maxHeightnumberCap on the drawn height in points, applied after scaling, so a tall image cannot overrun the page.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic caption numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
radiusnumberAlias for cornerRadius.
renderingIntentrenderingIntentThis picture's own rendering intent, in place of the document's renderingIntent -- typically "perceptual" for a photograph on a page of relative-colorimetric brand color. In cmyk color mode its RGB is converted at this intent; and when it differs from the document's it is written as the image's /Intent, which a RIP or viewer converting the file uses for this image alone.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
rotateintegerRotate the image within its box: 0, 90, 180 or 270 degrees clockwise (other values snap to the nearest quarter turn). Applied after the file's own EXIF / TIFF / HEIF orientation, so it turns the picture as a viewer shows it upright. The box is sized to the displayed orientation, so a 90/270 rotation with a plain width auto-sizes to a portrait box and fills it undistorted.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
shadowshadow -
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
srcstringPNG, JPEG (incl. CMYK JPEG, passed through), GIF, TIFF, WebP, or HEIC/HEIF (incl. AVIF; macOS builds only, since macOS decodes them -- other builds draw a placeholder box and say so). Relative paths resolve against the spec file. The file's own orientation (JPEG EXIF Orientation, TIFF tag 274, HEIF irot/imir) is honored: a phone photo stored sideways is drawn upright and sized as it stands, with no re-encode (see autoOrient). An ICC profile embedded in the file (PNG iCCP, JPEG APP2, TIFF, WebP ICCP; for HEIC/HEIF, the color space macOS decodes it in, e.g. Display P3) is kept: the image is written in that ICCBased color space, and in colorMode "cmyk" (or pdfx) the profile is instead the source of the image's conversion to CMYK. A profile that cannot be used (damaged, not version 2 to 4, not Gray/RGB/CMYK, or not matching the image) is reported and ignored. See images.colorProfiles.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionDrawn width - points, a percentage of the column, or "auto".
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.
zoomnumber | string1"cover" only: magnify the picture past the size that just covers the box, keeping "focus" in view (1 = plain cover, 2 = twice as close). The box does not change; only more of the picture is cropped away, so the pixels are spread over more of the page. On any other fit it is ignored with a warning. A STRING is a data token, as for focus: "{{cards.0.frontZoom}}" takes the record's number (or text of one); a field the record lacks leaves plain cover, and a value that is not a number, or below 1, is reported.

The shape element

Type name: shape.

Vector geometry drawn directly: a rectangle, line, ellipse, polygon or an arbitrary bezier path. Fills accept flat color or a gradient, and strokes accept width, color and dash. Use shapes for rules, callout pointers, diagram primitives and decorative panels.

Example

{
  "type": "shape",
  "shape": "rect",
  "width": 200,
  "height": 40,
  "fill": {
    "type": "linear",
    "angle": 0,
    "stops": [
      {
        "offset": 0,
        "color": "#1f4e79"
      },
      {
        "offset": 1,
        "color": "#7ba44f"
      }
    ]
  }
}

Keys

KeyTypeDefaultDescription
altstringAlternate text. A shape draws as a decorative artifact by default; giving it alt makes it a tagged <Figure> carrying this text, the same rule svg and math follow. Added 2026-08-30 -- before that a shape could not be described at all.
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
arrowstringDraw a filled arrowhead at one or both ends. A LINE or a PATH only -- a closed shape has no ends, and naming it there is refused with a warning rather than silently ignored. The head is turned to the path's own direction where it lands (for a curve, its tangent) and takes the stroke color. One of: none, start, end, both.
arrowSizenumberArrowhead length in points. 0 (the default) derives it from "strokeWidth", which keeps the head in proportion when the line weight changes.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
dstring | array of objectPath data (shape: path): an SVG path string (M/L/H/V/C/Q/Z) or an op array (["M",x,y], ["C",x1,y1,x2,y2,x,y], ["Q",x1,y1,x,y], ["L",x,y], ["Z"]). Coordinates are in viewBox space.
dasharray of number | numberDash pattern: alternating on/off run lengths in points, repeating. A single number means equal on and off. Omit or [] for a solid line. A pattern whose lengths are all zero is refused with a warning and the line draws solid -- an all-zero array is a division by zero in most renderers and an invisible line in the rest.
dashPhasenumberHow far INTO the dash pattern the line starts, in points. Shifts the rhythm without changing it -- two parallel dashed rules with different phases read as a weave rather than as a grid.
fillcolor | gradientSolid color, or a gradient object. Ignored for a line.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberFlow box height in points (an absolute shape uses position.h).
lineCapstringThe shape of a stroke's free ends, and of each dash. "butt" (the default) stops square at the endpoint; "round" and "square" both extend half the stroke width past it. A dotted line is a dash of [0, gap] with a ROUND cap -- with butt ends the same pattern draws nothing at all. One of: butt, round, square.
lineJoinstringThe shape of a stroke's corners. "miter" (the default) extends both edges to a point, "round" arcs them, "bevel" cuts the point off. Only meaningful where one PATH turns a corner: a polygon, a polyline, a path, or a rectangle's border. One of: miter, round, bevel.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
miterLimitnumberHow far a mitered corner may spike before it is cut to a bevel, as a multiple of the stroke width. PDF's default is 10 (SVG's too). A sharp angle at a heavy weight can throw a spike many times the line width, which is what this bounds.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
pointsarray of array of numberLine / polygon vertices as [x, y] fractions 0..1 of the box. OUT OF RANGE IS ALMOST ALWAYS viewBox UNITS OR POINTS BY MISTAKE, and it fails silently: the value is MULTIPLIED by the box, so [[0, 0], [100, 0]] in a 300pt-wide box draws a line 30000pt long -- about forty-nine pages off the right edge of a Letter sheet -- while reading as an ordinary line block in the source. The range is bounded here so the schema check names the block instead.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
shadowshadow -
shapestringShape kind (default rect). One of: rect, line, ellipse, circle, polygon, path.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
strokecolorOutline color. Omit for no outline.
strokeWidthnumberOutline thickness in points.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
viewBoxarray of numberPath coordinate space [x, y, w, h] (or [w, h]) mapped to the box; default 0 0 100 100.
widthdimensionDrawn width - points, a percentage of the column, or "auto".
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The svg element

Type name: svg.

An imported SVG drawing, rendered as real PDF vector art rather than a picture of one - so it stays sharp at any zoom and prints at full device resolution. A broad practical slice is supported: groups and nested transforms, every basic shape and the full path grammar, solid/gradient paint with per-stop opacity, a CSS <style> sheet, a <defs> library reused through <use>/<symbol>, multi-line <text>/<tspan>, and compositing - clip-path, <mask> (even-odd knockout), <pattern> tiling, a filter drop shadow, and the root viewport clip. Raster filters (a blur of content, color matrices) have no vector form and are skipped.

Example

{
  "type": "svg",
  "src": "logo.svg",
  "width": 120
}

Keys

KeyTypeDefaultDescription
altstringAlternate text; when set, the drawing is tagged as a Figure (else a decorative artifact).
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
captionstring | runsA caption makes this a FIGURE: the text is drawn under the graphic, tagged <Caption>, and collected for the list of figures ("lof"). It also tags the drawing as a Figure even without "alt" (a captioned graphic is content, not decoration), and supplies the /Alt when "alt" is absent. Numbered when the document enables "numbering": { "figures": ... }. Accepts a plain string, an ARRAY OF RUNS, or a SINGLE RUN OBJECT - the same run forms a paragraph's "runs" takes - so a caption can carry per-span styling, and above all a per-span "fontFamily": a caption that mixes scripts sets each span in the face that suits it instead of flattening to one. The flat text is always derived from the runs and stays the entry of record: it is what reaches the list of figures, the /Alt and the structure tree. A number ("Figure 3-2.") is prepended as the first run, unstyled, so it takes the block's own face.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
contentstringInline SVG markup (wins over src).
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberExplicit box height in points (else derived from the SVG viewBox aspect). With a height that does not match that aspect, the drawing keeps its viewBox's shape inside the box, as its root preserveAspectRatio says: by default xMidYMid meet (fitted whole and centred); "none" stretches it to the box, and "slice" covers the box and is clipped to it.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic caption numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
srcstringPath to an .svg file (relative to the spec, made absolute at load).
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionBox width (points or "NN%"); the height follows the SVG aspect unless given.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The chart element

Type name: chart.

A data chart drawn by the engine: bar, line, area, pie, scatter, radar, heatmap, gauge, box plot, candlestick, treemap, sankey and more, including combination charts with a secondary axis. You supply the series; the engine chooses tick intervals, draws the axes, grid, legend and labels. Give it an alt so the picture has a spoken description.

Example

{
  "type": "chart",
  "chartType": "bar",
  "title": "Units by quarter",
  "size": {
    "width": "100%",
    "height": 180
  },
  "yAxis": {
    "label": "Units",
    "grid": true
  },
  "series": [
    {
      "name": "2026",
      "points": [
        [
          0,
          12
        ],
        [
          1,
          19
        ],
        [
          2,
          15
        ]
      ]
    }
  ],
  "alt": "A bar chart of units for three quarters: 12, 19 and 15."
}

Reference lines and a shaded value band

{
  "type": "chart",
  "chartType": "line",
  "title": "API latency (p95)",
  "size": {
    "height": 220
  },
  "xAxis": {
    "label": "Hour",
    "min": 0,
    "max": 24,
    "majorTick": 6
  },
  "yAxis": {
    "label": "ms",
    "min": 0,
    "max": 300,
    "majorTick": 50,
    "grid": true
  },
  "bands": [
    {
      "yFrom": 0,
      "yTo": 100,
      "color": "#2e8b57",
      "label": "Healthy"
    }
  ],
  "referenceLines": [
    {
      "y": 200,
      "label": "SLA",
      "color": "#c0202a"
    },
    {
      "y": 120,
      "label": "Target",
      "color": "#2e6f2e",
      "dashed": true
    }
  ],
  "series": [
    {
      "name": "p95",
      "points": [
        [
          0,
          60
        ],
        [
          6,
          88
        ],
        [
          12,
          175
        ],
        [
          18,
          205
        ],
        [
          24,
          95
        ]
      ]
    }
  ],
  "alt": "A latency line that crosses a 200 ms SLA reference line."
}

A horizontal bar with a value threshold on the X axis

{
  "type": "chart",
  "chartType": "bar",
  "horizontal": true,
  "title": "Revenue vs target",
  "size": {
    "height": 180
  },
  "categories": [
    "North",
    "South",
    "East",
    "West"
  ],
  "yAxis": {
    "unit": "K",
    "min": 0,
    "max": 120,
    "majorTick": 20
  },
  "referenceLines": [
    {
      "x": 80,
      "label": "Target",
      "color": "#c0202a"
    }
  ],
  "series": [
    {
      "name": "Revenue",
      "points": [
        [
          0,
          55
        ],
        [
          1,
          72
        ],
        [
          2,
          95
        ],
        [
          3,
          110
        ]
      ]
    }
  ],
  "alt": "Four regional revenue bars against an 80K target line."
}

Error bars — a symmetric mean +/- SD whisker on every point

{
  "type": "chart",
  "chartType": "line",
  "title": "Reaction time",
  "size": {
    "height": 200
  },
  "markers": true,
  "xAxis": {
    "label": "Session",
    "min": 1,
    "max": 4,
    "majorTick": 1
  },
  "yAxis": {
    "label": "ms",
    "min": 0,
    "max": 500,
    "majorTick": 100,
    "grid": true
  },
  "series": [
    {
      "name": "RT",
      "error": 35,
      "points": [
        [
          1,
          420
        ],
        [
          2,
          395
        ],
        [
          3,
          360
        ],
        [
          4,
          350
        ]
      ]
    }
  ],
  "alt": "A reaction-time line with a plus-or-minus 35 ms error bar on every point."
}

Keys

KeyTypeDefaultDescription
altstringAlternative text describing the chart, emitted for accessibility. Required for a tagged or PDF/UA document, since the chart itself is just artwork.
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
angleClockwisebooleanPolar chart: sweep angles clockwise instead of anticlockwise.
angleStartnumberPolar: degrees where angle 0 sits (0 = right / 3 o'clock).
angleUnitstringPolar chart: whether angle values are degrees or radians. One of: deg, rad.
bandsarray of objectCartesian charts: shaded value-range bands painted behind the data (a control range, a target zone, an incident window). Rendered as an opaque tint so a tagged chart stays PDF/UA clean.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
captionstring | runsA caption makes this a FIGURE: the text is drawn under the plot, tagged <Caption>, and collected for the list of figures ("lof"). Distinct from "title", which is drawn INSIDE the plot frame as part of the graphic. Supplies the /Alt when "alt" is absent. Numbered when the document enables "numbering": { "figures": ... }. Accepts a plain string, an ARRAY OF RUNS, or a SINGLE RUN OBJECT - the same run forms a paragraph's "runs" takes - so a caption can carry per-span styling, and above all a per-span "fontFamily": a caption that mixes scripts sets each span in the face that suits it instead of flattening to one. The flat text is always derived from the runs and stays the entry of record: it is what reaches the list of figures, the /Alt and the structure tree. A number ("Figure 3-2.") is prepended as the first run, unstyled, so it takes the block's own face.
categoriesarray of stringLabels for the slots along the shared category axis. A point is matched to its label by the RANK of its x among the distinct x values across every series - not by the x value itself - so points at x 1,2,3,4 with four categories still read as the first four labels, and a series that skips a category leaves that slot empty instead of shifting the labels after it. Honored on bar (either orientation), line, area and combo, where it turns the x axis into a category axis: one tick per category, no "nice" numeric rounding, and names in place of numbers. Also honored by radar (one spoke each), waterfall, funnel, treemap and sankey. Ignored on scatter and bubble, whose x is a real measurement. The array must DESCRIBE the whole axis for the category axis to engage: a name for every slot, and slots evenly spaced. If either fails - a short array, or x values that are not an arithmetic progression - the x axis stays numeric, which is what keeps a name off the wrong bar. A combo chart whose trend line sits off the bar grid has more slots than the bars do, so it keeps its numbers. Spare names past the end of the data are ignored.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
chartTypestringWhich chart to draw. The rest of the block's keys are read according to this - categories+series for bar/line/area/combo, slices for pie, x/y points for scatter. One of: line, scatter, bar, polar, pie, doughnut, donut, radar, spider, area, bubble, heatmap, gauge, waterfall, ....
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
closedbooleanPolar: connect the last point back to the first.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
fillOpacitynumberRadar: translucent fill strength.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightdimensionShorthand for size.height.
highColorcolorHeatmap ramp end (default the first palette color).
holeRationumber0 = pie; > 0 = doughnut hole.
horizontalbooleanDraw the bars horizontally, so the category axis runs down the side and the value axis along the bottom. Worth it when the category names are long: the plot widens its left gutter to fit them, where a vertical bar has only its slot width and thins the labels to fit. Bar only.
legendbooleanDraw the series legend.
legendAtobjectPut the legend at a point of your own: its box's top-left corner, measured from the chart's top-left -- x across and y down, each in points or as a percentage of the chart's width (x) or height (y). The legend is drawn over the chart and kept inside it. Wins over legendPos.
legendPosstringWhere the legend sits. Inside the plot, in a corner: tl, tr (the default), bl or br; it is drawn over the plot, so a tall bar can run under it. Outside the plot, on one side of the chart: top, bottom, left or right; the plot gives up the room the legend needs, and a top or bottom legend lists its entries side by side. legendAt, when set, wins over this. One of: tl, tr, bl, br, top, bottom, left, right.
lowColorcolorHeatmap ramp start (default white).
markersbooleanDraw a point marker at each data value, not just the connecting line.
matrixarray of array of numberHeatmap: rows of values (row 0 at the top).
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic caption numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
referenceLinesarray of objectCartesian charts: reference / threshold rules drawn across the plot at a fixed data value (a target, a budget, a spec limit). Drawn over the data.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
seriesarray of objectThe data series. Each carries a name, a color and its points.
sizeobjectExplicit plot size, when the chart should not take the full column width.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
smoothbooleanCentripetal Catmull-Rom spline (line + polar).
stack100booleanStack the series AND scale each category to fill 100%, showing proportion rather than magnitude.
stackedbooleanStack the series on top of each other instead of drawing them side by side.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
subtitlestringSecondary line drawn under the title.
titlestringHeading drawn above the plot area.
valuenumberGauge: the value the needle points at.
valueLabelsbooleanPrint each value as text on the chart.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionShorthand for size.width.
xAxischartAxisX-axis configuration - title, range, tick spacing, gridlines.
xLabelsarray of stringColumn labels along the bottom of a matrix chart: heatmap, candlestick and box-plot. The cartesian types use categories instead.
yAxischartAxisY-axis configuration - title, range, tick spacing, gridlines.
yAxis2chartAxisCombo: the secondary (right) value axis for series with axis/secondaryAxis.
yLabelsarray of stringHeatmap row labels.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.
zonesarray of objectGauge: colored bands up to each threshold, in range order.

The barcode element

Type name: barcode.

A barcode or 2-D symbol - QR, Data Matrix, PDF417, Code 128, EAN, UPC and the rest of the zint symbology set. Supply the payload as data; the engine encodes and draws it at a size that scans.

Example

{
  "type": "barcode",
  "code": "qrcode",
  "data": "https://appligent.com",
  "width": 90,
  "alt": "QR code linking to appligent.com"
}

Keys

KeyTypeDefaultDescription
altstringTagged-PDF /Alt for the Figure (default: the encoded data).
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
codestringSymbology name - every symbology the engine draws, plus convenient aliases. A name the engine does not know draws a gray placeholder box and leaves the figure untagged (the engine says so), which is why a misspelling is refused here. The names below are the engine's own table. Like anchor.to, the ENGINE normalizes by lowercasing and dropping punctuation, so "GS1-128" renders and does NOT validate; the schema is the strict companion, and lists the normalized spelling. Write the normalized form and both agree. One of: 128, 39, 93, auspost, ausredirect, ausreply, ausroute, azrune, aztec, bc412, c25iata, c25ind, c25inter, c25logic, ....
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
datastringThe content to encode (alias: "text").
eccintegerAlias for "security".
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
securityintegerError correction: QR's ECC level 1-4, PDF417's security level 0-8 (alias: "ecc").
showTextbooleanHuman-readable text line (drawn with the document's fonts).
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
symbologystringAlias for "code".
textstringAlias for "data".
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthnumberDisplay width in points (default: 120 square symbols, 216 wide).
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The math element

Type name: math. Accepted aliases: equation.

A display equation. Supply the visual as pre-rendered SVG and the meaning as Presentation MathML: the engine draws the SVG and embeds the MathML as an associated file on the Formula structure element, so assistive technology reads the real mathematics rather than a picture. With equation numbering enabled the engine adds the number in the right gutter.

Example

{
  "type": "math",
  "align": "center",
  "mathml": "<math><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></math>",
  "src": "energy.svg",
  "alt": "E equals m c squared"
}

Keys

KeyTypeDefaultDescription
altstringA spoken reading of the equation (the <Formula> /Alt). Also what an "as": "title" cross-reference to this equation reads back.
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
contentstringPre-rendered inline SVG markup of the equation.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberDrawn height in points.
mathMLstringAlias for "mathml".
mathmlstringPresentation MathML source. Embedded on the <Formula> element as a /Supplement associated file.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic equation numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
srcstringPath to a pre-rendered SVG file.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
svgstringAlias for "content".
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionDrawn width - points, a percentage of the column, or "auto".
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The mermaid element

Type name: mermaid. Accepted aliases: diagram.

A diagram written as code. Supply the rendered SVG and the engine places it as vector art; without one it degrades to a captioned panel showing the diagram source, so the information is never lost.

Example

{
  "type": "mermaid",
  "src": "flow.svg",
  "source": "graph TD; A-->B;",
  "alt": "A two-node build pipeline."
}

Keys

KeyTypeDefaultDescription
altstringAlternate text (/Alt on the tagged Figure).
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
contentstringPre-rendered inline SVG markup of the diagram.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberDrawn height in points.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
sourcestringThe diagram source (e.g. mermaid text). Shown as a code panel when no rendered SVG is supplied.
srcstringPath to a pre-rendered SVG file.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
svgstringAlias for "content".
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionDrawn width - points, a percentage of the column, or "auto".
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

9. Placing existing PDFs

Importing pages from PDF files you already have.

The pdf element

Type name: pdf.

One or more pages lifted from an existing PDF and placed into the flow as real vector content - not a screenshot. Use it to fold a supplied datasheet, certificate or signed page into a generated document.

Example

{
  "type": "pdf",
  "src": "datasheet.pdf",
  "page": 1,
  "width": "100%",
  "caption": "Supplied datasheet, page 1."
}

Keys

KeyTypeDefaultDescription
altstringAlternate text (/Alt on the tagged Figure).
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
captionstring | runsCaption printed under the placed page. A caption also enrolls it in the Figure counter and gives a cross-reference something to resolve against. Accepts a plain string, an ARRAY OF RUNS, or a SINGLE RUN OBJECT - the same run forms a paragraph's "runs" takes - so a caption can carry per-span styling, and above all a per-span "fontFamily": a caption that mixes scripts sets each span in the face that suits it instead of flattening to one. The flat text is always derived from the runs and stays the entry of record: it is what reaches the list of figures, the /Alt and the structure tree. A number ("Figure 3-2.") is prepended as the first run, unstyled, so it takes the block's own face.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
fitstringHow the placed page fills its box: "contain" fits it whole, "cover" crops to fill, "fill" stretches, "none" draws it at its natural size - the source page's own CropBox. "cover" and "none" are cropped to the box; the page is centred in it unless "focus" aims the crop (and "zoom" magnifies a cover). One of: contain, cover, fill, none.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
focusstring | number | array of number | objectWhere a cropping fit aims, as an image's "focus": [x, y] (or {"x":, "y":}, or one number for both axes) as fractions of the placed page, 0 = left / top, 1 = right / bottom. The point is brought to the centre of the box as far as the page allows; the crop never pulls away from an edge, so [0, 0] and [1, 1] reach the corners -- [1, 0] keeps the top-right corner, which is how a whole-envelope postage page is placed at 100% ("fit": "none") with only its corner on a card -- and the default [0.5, 0.5] is the centred crop. Only "cover" and "none" crop; on any other fit it is ignored with a warning. Values outside 0..1 are clamped. A STRING is a data token, as an image's: "{{cards.0.postageFocus}}" resolves to the whole value at that path -- a number, [x, y], {x, y}, or text of numbers -- and a field the record lacks leaves the centred crop, silently. A bound value that is not numbers, or outside 0..1, is reported with the block's pointer (and the -merge record). A string with no token is read as it stands.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightdimensionDrawn height - points, a percentage, or "auto" to keep the aspect ratio.
maxHeightnumberCap on the drawn height in points, applied after scaling.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numberstringPin a literal number/label for this block. Does not advance the counter.
numberedbooleantrueTake part in automatic caption numbering when the document enables it. false leaves this one unnumbered and does not advance the counter.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
pageintegerWhich page of the source PDF to place, 1-based. Default 1.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
srcstringSource PDF; the page is imported as a vector Form XObject. For PDF/UA output its fonts must be embedded.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionDrawn width - points, a percentage of the column, or "auto".
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.
zoomnumber | string1"cover" only: magnify the placed page past the size that just covers the box, keeping "focus" in view (1 = plain cover, 2 = twice as close). The box does not change; only more of the page is cropped away. On any other fit it is ignored with a warning. A STRING is a data token, as for focus; a field the record lacks leaves plain cover, and a value that is not a number, or below 1, is reported.

The pdf-contact-sheet element

Type name: pdf-contact-sheet. Accepted aliases: contact-sheet.

A grid of thumbnails of every page in a PDF, with optional page numbers. A quick visual index of a large document.

Example

{
  "type": "pdf-contact-sheet",
  "src": "report.pdf",
  "columns": 4,
  "labels": true
}

Keys

KeyTypeDefaultDescription
altstringAlternate text (/Alt on the tagged Figure).
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
columnsintegerHow many thumbnails per row.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
framebooleanDraw a thin border around each thumbnail cell. (On a table "frame" is a border object instead - a different key with the same name.)
gapnumberGap between thumbnails, in points.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
itemsarray of string | objectExplicit cells; may mix source files.
labelsboolean"p.N" label under each thumbnail.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
pagesstring | integer | array of integer"all", "1-5", "1,3,5", a single number, or an array of page numbers.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
srcstringSource PDF whose pages are laid out as thumbnails.
titlestringHeading printed above the sheet.
uniformCellsbooleanGive every cell the same height, sized to the tallest page, instead of letting rows vary.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

10. Navigation and apparatus

Generated front and back matter.

The toc element

Type name: toc. Accepted aliases: lof, lot, lol, loc.

An automatic table of contents built from the document's headings, with dot leaders and exact page numbers, every row an internal link. The page numbers are exact because the engine iterates: the contents list changes the pagination, so it re-counts until the numbers stop moving.

Example

{
  "type": "toc",
  "title": "Contents",
  "maxLevel": 2
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
continuationIndentnumber0Points added to the level indent for lines 2..N of a wrapped row, so a continuation reads as part of its entry rather than as a new one. Only meaningful with maxLines. Note that every line of the row is laid out to the narrowed width, the first included, so the wrap point is the same on all of them.
countintegerList of Figures (type "lof"): number of figure entries to reserve before the auto-filled captions converge.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
leaderstring.Glyph repeated between an entry and its page number. Empty turns the leader off; a Chinese book may prefer a middle dot · or an em dash.
levelStylesobjectPer heading level, a style delta applied to that level's rows and resolved over the block's own style -- so a contents list can set its top level larger, bolder or coloured, and give it space above, without touching the rest. Keys are heading levels as strings ("1", "2", ...). A level with no entry renders exactly as it does today. This does NOT switch on inheritance from the heading itself: a bold heading still yields a non-bold row unless a level says otherwise. On a flat list (lof / lot / lol / loc) every row is level 1.
maxLevelintegerDeepest heading level to list. Level 3 lists H1-H3 and ignores anything deeper.
maxLinesinteger1Lines one generated row may occupy. The default of 1 is the historical behaviour exactly: an over-long entry is truncated with an ellipsis so it cannot run into the page number. Any other value WRAPS the entry instead, on word boundaries, through the same line breaker body text uses (so hyphenation and CJK rules apply); continuation lines carry no leader and no page number, which stay on the last line, and the whole row remains ONE entry -- one TOCI, one Link, one clickable target over every line. A row that still does not fit within maxLines has its final permitted line clamped with the ellipsis as before. 0 means unbounded. Wrapping is opt-in because turning it on changes where existing documents break their pages.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numbersbooleanPrefix each entry with its outline number (1, 1.1, 1.1.3) derived from the heading hierarchy.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
scopestringdocumentHow much of the document this block draws on. "document" (the default) lists every entry in the document. "group" lists only entries whose page falls in the same page-numbering group as the block itself - the run of sections since the last restartNumbering - so one volume can hold several complete sub-documents, each with its own contents, figures and index restarting alongside its own page numbers. One of: document, group, section.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
titlestringHeading printed above the list. Omit for no heading.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The toc element

Type name: toc. Accepted aliases: lof, lot, lol, loc.

An automatic list of figures, tables or listings - lof, lot and lol are the same element filtered to a different kind of caption. Each row is a linked "caption ...... page" line, built the same converging way as the table of contents.

Example

{
  "type": "lof",
  "title": "List of Figures"
}

A list of tables (the lot alias)

{
  "type": "lot",
  "title": "List of Tables"
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
continuationIndentnumber0Points added to the level indent for lines 2..N of a wrapped row, so a continuation reads as part of its entry rather than as a new one. Only meaningful with maxLines. Note that every line of the row is laid out to the narrowed width, the first included, so the wrap point is the same on all of them.
countintegerList of Figures (type "lof"): number of figure entries to reserve before the auto-filled captions converge.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
leaderstring.Glyph repeated between an entry and its page number. Empty turns the leader off; a Chinese book may prefer a middle dot · or an em dash.
levelStylesobjectPer heading level, a style delta applied to that level's rows and resolved over the block's own style -- so a contents list can set its top level larger, bolder or coloured, and give it space above, without touching the rest. Keys are heading levels as strings ("1", "2", ...). A level with no entry renders exactly as it does today. This does NOT switch on inheritance from the heading itself: a bold heading still yields a non-bold row unless a level says otherwise. On a flat list (lof / lot / lol / loc) every row is level 1.
maxLevelintegerDeepest heading level to list. Level 3 lists H1-H3 and ignores anything deeper.
maxLinesinteger1Lines one generated row may occupy. The default of 1 is the historical behaviour exactly: an over-long entry is truncated with an ellipsis so it cannot run into the page number. Any other value WRAPS the entry instead, on word boundaries, through the same line breaker body text uses (so hyphenation and CJK rules apply); continuation lines carry no leader and no page number, which stay on the last line, and the whole row remains ONE entry -- one TOCI, one Link, one clickable target over every line. A row that still does not fit within maxLines has its final permitted line clamped with the ellipsis as before. 0 means unbounded. Wrapping is opt-in because turning it on changes where existing documents break their pages.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
numbersbooleanPrefix each entry with its outline number (1, 1.1, 1.1.3) derived from the heading hierarchy.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
scopestringdocumentHow much of the document this block draws on. "document" (the default) lists every entry in the document. "group" lists only entries whose page falls in the same page-numbering group as the block itself - the run of sections since the last restartNumbering - so one volume can hold several complete sub-documents, each with its own contents, figures and index restarting alongside its own page numbers. One of: document, group, section.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
titlestringHeading printed above the list. Omit for no heading.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The index element

Type name: index.

A back-of-book index. You supply the terms; the engine scans the drawn text as it lays the document out, records every page a term actually appears on, collapses runs of consecutive pages into ranges, and renders the result in columns with letter headings - every page number a working link. Chinese indexes sort the way their readers expect: collation chooses pinyin (Simplified) or stroke-count-then-radical (Traditional) order from an embedded Unihan-derived table, and a per-entry sortAs overrides the reading where dictionaries disagree.

Example

{
  "type": "index",
  "columns": 2,
  "letterHeadings": true,
  "collapseRanges": true,
  "terms": [
    "stability",
    "recovery",
    "motor"
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
collapseRangesbooleanCollapse runs of 3+ consecutive pages to a range (5-7).
collationstringunicodeHow the entries are ordered. "unicode" (the default) is code-point order with ASCII case folded - fine for Latin, meaningless for Chinese, where it is Unicode block order. "pinyin" orders by the Hanyu Pinyin reading then the tone, so 北京 files under B; it is the dominant order for Simplified Chinese, and Latin entries interleave alphabetically. "strokes" orders by total stroke count, then Kangxi radical, then code point - the Traditional and reference-work order - and gathers Latin entries into their own run first. Readings and stroke counts come from the Unicode Unihan database, embedded for CJK Unified Ideographs and Extension A; anything outside that (Extension B and beyond) falls back to code-point order, as does any character Unihan gives no reading. One collation per document: like collapseRanges and letterHeadings, the last index block in the document sets it. One of: unicode, pinyin, strokes.
columnsintegerHow many columns the index entries are set in.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
letterHeadingsbooleanPrint an A / B / C letter heading before each alphabetical group.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
scopestringdocumentHow much of the document this block draws on. "document" (the default) lists every entry in the document. "group" lists only entries whose page falls in the same page-numbering group as the block itself - the run of sections since the last restartNumbering - so one volume can hold several complete sub-documents, each with its own contents, figures and index restarting alongside its own page numbers. One of: document, group, section.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
strokeLabelstringDrawn after a stroke-count group divider: "" (the default) gives a bare "12", "画" gives 12画 and "畫" the Traditional form. No effect unless collation is "strokes".
termsarray of indexTermTerms to index. Each collects the pages where it was found in the text.
titlestringHeading printed above the index.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
wordsarray of indexTermAlias for "terms".
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The glossary element

Type name: glossary.

A list of defined terms. With autoLink on, the first appearance of each term in the body becomes a link to its definition.

Example

{
  "type": "glossary",
  "autoLink": true,
  "entries": [
    {
      "term": "apogee",
      "definition": "The highest point of the flight."
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
autoLinkbooleanLink the first body use of each term to its definition.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
entriesarray of objectThe glossary entries - each a term and its definition.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
titlestringHeading printed above the glossary.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The bibliography element

Type name: bibliography. Accepted aliases: references.

A reference list built from structured entries (author, title, container, publisher, year, DOI...) or a pre-formatted text, cited from body runs with cite. The numeric style numbers entries in order of first citation (Vancouver) and renders markers as [1] or [1, 3]; the author-year style renders (Knuth 1984) and sorts the list alphabetically. Every citation links to its entry, and with backlink on each entry links back to its first citation. references is an accepted alias.

Keys

KeyTypeDefaultDescription
backlinkbooleantrueMake each entry's [n] label a link back to the first citation of it. Default true; numeric style only, since author-year draws no label.
breakBeforeboolean -
citedOnlybooleanDrop entries no cite run named. Defaults to TRUE under "numeric" (the list IS the citation order, so an uncited work has no number and no place in it) and FALSE under "author-year" (the marker carries the name, so uncited background reading belongs).
entriesarray of objectThe works. Every field is optional except key (which is what a cite run names); an entry with no key still prints but can never be cited.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
titlestringHeading printed above the reference list ("References", "Works Cited").

The endnotes element

Type name: endnotes.

Collects every note run in the document and renders them as a numbered end-matter list, each marker linking to its note and each note linking back - the alternative to page-bottom footnotes for journals and books that gather notes at the end.

Keys

KeyTypeDefaultDescription
backlinkbooleanTurn each note's number into a link back to its body marker (default true) -- the first place it is drawn, never a contents row's or a list-of row's copy of it, on every form of heading (run-in, curved and vertical draw their own marker). A contents row's copy stands in only for a marker a fixed-height box clips away while its heading is still listed. The forward link (marker to note) is always made.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
titlestringHeading printed above the gathered notes (e.g. "Notes").

11. Notes and asides

Set-off editorial material.

The callout element

Type name: callout. Accepted aliases: admonition, note.

A set-off note, tip or warning in a tinted, bordered box. Tagged as an Aside, so assistive technology announces it as an aside rather than as body text.

Example

{
  "type": "callout",
  "variant": "warning",
  "title": "Caution",
  "blocks": [
    {
      "type": "paragraph",
      "text": "Never approach a rocket that failed to launch."
    }
  ]
}

A tip

{
  "type": "callout",
  "variant": "tip",
  "blocks": [
    {
      "type": "paragraph",
      "text": "Render with -map to get the layout geometry back as JSON."
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
blocksblockGroupAlias for "body".
bodyblockGroupThe call-out's content blocks.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
iconstringOptional glyph prefixed to the label (renders only if the font has it).
keepTogetherbooleantrueKeep the box on one page when it fits (default true).
labelstringAlias for "title".
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
showLabelbooleantrueDraw the label line (default true).
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
textstringShorthand body: a single paragraph. Use "body" for richer content.
titlestringOverrides the default variant label (the bold colored heading of the box).
variantstringnoteSelects the accent color, tint and default label (default "note"). One of: note, info, tip, success, important, warning, caution, danger.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

12. Forms

Interactive AcroForm fields.

The field element

Type name: field.

An interactive AcroForm field: text box, checkbox, radio group, dropdown, list box, button or signature. The field is a real form widget in the output PDF - fillable in any viewer - with a label, tooltip, default value and tab order.

Example

{
  "type": "field",
  "field": "text",
  "name": "customer",
  "label": "Customer name",
  "width": "60%",
  "tooltip": "Full legal name"
}

A radio-button group

{
  "type": "field",
  "field": "radio",
  "name": "size",
  "label": "Size",
  "options": [
    "Small",
    "Medium",
    "Large"
  ],
  "value": "Medium"
}

A signature field

{
  "type": "field",
  "field": "signature",
  "name": "approval",
  "label": "Approved by",
  "width": "60%"
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberHeight of the field control in points.
labelstringVisible label text next to the widget.
labelPosstringWhere the field's label sits relative to the control: before it, after it, or not drawn. One of: before, after, none.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthdimensionWidget width (auto = fill the row after the label).
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

13. Data-driven documents

Elements that build content from a data context.

The repeat element

Type name: repeat.

Builds content from data: names an array in the data context and clones its template blocks once per element, binding {{...}} placeholders as it goes. One template, N rendered sections.

Example

{
  "type": "repeat",
  "for": "lineItems",
  "as": "item",
  "blocks": [
    {
      "type": "paragraph",
      "text": "{{item.sku}}  -  {{item.qty}}"
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
asstringLoop-variable name bound to each element (default "item"); a "loop" helper exposes index / number / count / first / last.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
blocksarray of blockTemplate blocks cloned and bound once per array element.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
forstringRequired. Data-driven repeat: path to the array to iterate (e.g. "items", "order.lines"). The "blocks" template is cloned once per element.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The if element

Type name: if.

Includes its blocks only when a data predicate holds. The same test is available on any block as the when and unless keys; the if element is the form to reach for when you want to gate a whole group at once.

Example

{
  "type": "if",
  "when": "total > 1000",
  "blocks": [
    {
      "type": "paragraph",
      "text": "Volume discount applied."
    }
  ]
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
blocksarray of blockEmitted when the test is true.
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
elsearray of blockEmitted when the test is false.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
teststringPredicate: a path (truthiness) or "path OP value" (== != < > <= >=), optional leading !.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The include element

Type name: include.

Pulls in another spec file as a reusable partial, so shared front matter, a standard cover or a boilerplate appendix lives in one place.

Example

{
  "type": "include",
  "src": "shared/cover.json"
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
srcstringRequired. Path (relative to the spec) to a partial: a block, an array of blocks, or {blocks:[...]}.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
withobjectFields added/overridden in the included scope. A value that is a single "{{path}}" token resolves to the whole value at that path.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

The marker element

Type name: marker.

Plants an invisible named text value at a point in the flow, for running headers and footers to pick up with the {{marker:name}} building block - the dictionary-header idiom, where each page's header shows the last marker seen. Draws nothing itself.

Keys

KeyTypeDefaultDescription
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
namestringThe marker class. A running band's {{marker:Name}} / {{firstMarker:Name}} / {{lastMarker:Name}} select on this name.
textstringThe value the running band prints for this marker.

14. Story threading

Text that flows between frames.

The frame element

Type name: frame.

A frame in a threaded story. Text poured into a story flows from frame to frame in the order you thread them, jumping pages if you like, with automatic "continued on page N" lines. The magazine and newsletter idiom.

Example

{
  "type": "frame",
  "story": "feature",
  "columns": 2,
  "columnGap": 18
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
columnGapnumberGutter between frame columns, in points.
columnsintegerText columns inside the frame.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
continuationbooleanDraw "(Continued from page N)" at the top (N = the previous frame in the thread).
continuedbooleanDraw "(Continued on page N)" at the bottom when the story overflows this frame.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
fromTextstringOverride the "Continued from" line; "{{page}}" is replaced by the page number.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
storystringId of the story (top-level "stories") this frame pours its text from. Overflow threads into the next frame referencing the same story.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
toTextstringOverride the "Continued on" line; "{{page}}" is replaced by the page number.
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

15. Meters

Small inline indicators.

The bar element

Type name: bar.

A small horizontal gauge - a rounded track with a proportional fill. Useful in dashboard tables and summary cards where a number wants a visual weight beside it.

Example

{
  "type": "bar",
  "value": 72,
  "max": 100,
  "width": "40%"
}

Keys

KeyTypeDefaultDescription
anchoranchor -
anglenumber | stringTurn the block, in degrees counter-clockwise, about the CENTRE of its "position" box. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. The block lays out upright inside its box and the finished result is turned, so a "position.height" that truncates content truncates the same text it would unrotated, and the turned result is NOT clipped to its box: it may extend past it, exactly as an unrotated positioned block may. Not to be confused with the image block's "rotate", which is an integer QUARTER TURN CLOCKWISE applied to the picture INSIDE its box (and re-sizes that box to the turned orientation). The two compose -- an image can stand a sideways scan upright with "rotate" and tilt the whole frame with "angle". Same meaning as the watermark's "angle". A STRING is a data token, so each record of a template turns its own sticker: "{{s.angle}}" takes the record's number (or text of one, such as a CSV field); a record without it leaves the block upright, silently, and a value that is not a number is reported with the block's pointer (and the -merge record) and drawn upright. A string with no token is read as it stands.
bleednumberStep and Repeat: how far this placed block's box reaches past its TRIM on every side, in points -- the box IS the bleed box, and the trim is the box inset by this much. A design tool sizes a card by it (the trim plus twice the bleed) and draws the trim line inside each copy; the engine draws nothing from it, and keeps the bleed only through the block's own clip. Only a block with position has a trim: on any other block the key is refused with a notice naming the block.
blendstringHow this block's OWN ink combines with the page under it -- the sixteen PDF blend modes (ISO 32000-1 11.3.5), written as /BM. On a container that means its background, border and stripe, never its children; for the subtree as one object see "groupBlend". Case and separators are folded, so "colorDodge", "color-dodge" and "COLORDODGE" are one mode. An unrecognized name is refused with a warning and the block paints normally. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
breakBeforebooleanPagination: open a new page before this block - a part divider, an appendix. Ignored when the page is already empty, so it never leaves a blank page behind.
changedbooleanRevision / change bar: draw a vertical bar in the page margin beside this block's content to flag it as changed. A document-level changeBars object customizes the bar (width / color / side / distance); a condition tag declared with changeBar:true sets this automatically for tagged content.
clipbooleanClip this block's children (on a container) or its own ink to its box, with the style's corner "radius". Only bites where content is BIGGER than the box: a flow container grows to fit its children vertically, so give it "position" with a height, or a "minHeight", before expecting a vertical crop. Container-scoped, like "groupOpacity": on any other flow block it is refused with a warning. For a crop to a SHAPE rather than to the box, see "clipPath"; giving both INTERSECTS them.
clipPathobjectCrop this block to an arbitrary SHAPE stated inside its box -- a design tool's FRAME. Same vocabulary as the "shape" block and read by the same outline builder, so a shape and a clipPath given identical parameters describe the identical outline. Scoped exactly as "clip" is: a positioned block of any type (it needs a "position.height", because the shape is stated inside the box) or a container in the flow; anywhere else it is refused with a warning. Naming "clip" as well INTERSECTS the two. The shape turns, scales and skews with the block that carries it.
conditionstringConditional text: render this block only when its condition-tag expression is shown under the document's conditions map. Whitespace/comma tag terms, each optionally '!'-negated, AND-combined; an unlisted tag defaults to shown. Empty means always shown.
fillcolor | objectColor of the filled portion of the bar.
floatstringFloat this block out of the stacked flow to one side of its column; content that FOLLOWS it wraps into the space beside it. Works on ANY block type -- a table, a container, a chart, a code listing, a figure. "center" straddles the page content width, so text in every column wraps around it. The float box takes the block's own "width"; a block that declares none takes HALF the column, so a float always leaves room beside it. Ignored on a block that also sets "position" or "anchor" -- those have already chosen their placement. Not to be confused with "anchor": { "float": true }, a BOOLEAN about migrating an anchored frame to the next column that can hold it. One of: left, right, center.
groupBlendstringComposite this block AND its children once, then blend the RESULT with the page. NOT the same key as "blend", and the difference is larger than it is for opacity: two stacked children that each multiply the backdrop multiply it TWICE, which is darker than the group multiplying once, and no per-child setting reproduces it. That is what a design tool's blend-mode control on a group means. Container-scoped: on any other flow block it is refused with a warning. Also the way to blend an element together with its drop shadow, which is drawn as stacked alpha layers and so keeps its own normal compositing. See the blend-modes sample. One of: normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, ....
groupOpacitynumberComposite this block AND its children once, then fade the result -- true GROUP opacity. NOT the same key as "opacity", which fades each element as it paints: two overlapping opaque children at opacity 0.5 each show a seam where they cross, and under groupOpacity 0.5 they do not. The two compose. Container-scoped: on any other flow block it is refused with a warning. See the clipped-groups sample.
heightnumberThickness of the bar, in points.
maxnumberValue that corresponds to a completely full bar. Default 1, so a 0-1 value reads as a fraction.
maxValuenumberAlias for "max".
mirrorHboolean | stringMirror the whole block left-to-right about the centre of its "position" box, under the same restriction as "angle". Identical to scale x -1, and the two compose. NOT "flipH": that key belongs to the image block, where it mirrors the PICTURE INSIDE its frame. The two are separate words so an image can do both. A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
mirrorVboolean | stringMirror the whole block top-to-bottom about the centre of its "position" box, under the same restriction as "angle". Identical to scale y -1. NOT "flipV" -- see "mirrorH". A STRING is a data token, so each record of a template mirrors its own sticker: "{{s.flip}}" takes the record's boolean, or text "true" / "false" in any case, or 1 / 0 (a number or text); a record without it draws the block unmirrored, silently, and any other value ("yes" included) is reported with the block's pointer (and the -merge record) and drawn unmirrored. A string with no token is read as it stands.
opacitynumberConstant opacity for this block: 0 (invisible) to 1 (opaque, the default). REAL alpha -- a /CA /ca ExtGState in the PDF -- so a translucent panel over a photograph shows the photograph through it. Not to be confused with "style.background" {color, alpha}, which composites the tint toward WHITE at parse time and so only looks right over a white page. PER-ELEMENT, NOT GROUP: on a container it paints that container's own decoration -- background, border, accent bar, stripe -- and never its children, which draw at their own opacity (1 unless they set one). Two translucent shapes that overlap therefore composite twice, which is what per-element opacity means; group opacity, where a whole subtree is composited once and then faded, is a separate feature. Where a draw already carries its own alpha (an SVG fill-opacity, a gradient's alpha) the two MULTIPLY.
radiusnumberCorner radius of the bar and its track, in points. Use half the height for a fully rounded capsule.
repeatAtarray of objectImposition: draw this block ONCE and place the drawing again at each of these offsets, in points from the block's own position, y DOWNWARD as every position is. One entry = one extra copy, so a list of 7 puts 8 cards on a sheet. The drawing is captured into a Form XObject and each offset is a cm + Do of that one stream, so n copies cost one copy of the content. Translation only -- angle / scale / skew are not applied per placement. A REPEATED FORM MAY NOT CARRY TAGGED CONTENT: ISO 14289-1 7.20 allows a form's content ONE semantic parent, so repeating tagged text (or a shape with alt) fails PDF/UA -- veraPDF catches it, ap_addstruct's audit does not. The engine warns and draws it anyway. Repeat decoration: a shape or svg with no alt, or put the block in a master's frames, which is drawn as a page artifact. Only a block with position is drawn again: on a block without one (in the flow, or an anchored frame), or on a repeat / if / include block, the key is refused with a notice naming the block. Each copy takes the block's groupOpacity and groupBlend. The layout map (-map) reports the copies as repeats on the block's fragment, so a design tool can select and outline them.
scalenumber | array of number | object | stringScale the block about the same pivot as "angle" (the centre of its "position" box), and under the same restriction. A CONTAINER in the flow may also carry this: it becomes a GROUP that turns about its own measured box and reserves the bounding box of the turned result, and its children inherit the transform. Any other flow block ignores it with a warning -- wrap it in a container. A number scales both axes; [x, y] or {"x":, "y":} scales them separately. 1 = natural size. Applied AFTER layout, so a scaled block does NOT re-wrap its text at the scaled measure -- this is a design tool's scale handle, not a change of font size. A STRING is a data token, so each record of a template scales its own sticker: "{{s.scale}}" takes the record's number, [x, y] or {"x":, "y":} (members may be text of numbers), or text of one or two plain numbers such as a CSV field ("1.5", "1.5 0.8", "[1.5, 0.8]"); a record without it draws the block at its natural size, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn at natural size. A string with no token is read as it stands.
skewnumber | array of number | object | stringShear the block, in DEGREES, about the same pivot as "angle", under the same restriction. [x, y] or {"x":, "y":}; a bare number skews x only. Positive x leans the top to the right and positive y lifts the right-hand side -- both counter-clockwise, matching "angle". Clamped to +/-89.9: a 90-degree skew is a singular matrix and draws nothing. A STRING is a data token, so each record of a template skews its own sticker: "{{s.skew}}" takes the record's number (x only), [x, y] or {"x":, "y":}, or text of one or two plain numbers; clamped as the written one is. A record without it draws the block unskewed, silently, and a value that is not a number or a pair is reported with the block's pointer (and the -merge record) and drawn unskewed. A string with no token is read as it stands.
straddlebooleanStraddle across columns: when true and the body is multi-column, this block spans the FULL content width and text in every column wraps around it (above and below) - a spanning float generalized from center-floated images to any block. No effect in a single-column body, on a positioned / anchored block, or on an image/pdf already using float:"center" (the same mechanism). Does not balance the columns above it or act as a section divider.
trackcolorColor of the unfilled groove behind the bar.
valuenumberThe value to show, measured against "max".
verticalAlignstringWhere the content sits in its "position" box when it is shorter than the box's height: "top" (the default), "middle" ("center" is the same) or "bottom". A paragraph, heading or pre moves as a whole (its own box with it); a container keeps its box where it is and moves its children inside it (a child with its own "position" stays put). The offset is the box height less the content's height as it is actually placed -- the same lines, shrinkToFit size and padding that are then drawn -- halved for middle. Content as tall as the box or taller does not move: it is clipped and reported exactly as without the key. Needs a position with a height: on a block without one, on any other kind of block, on text set along a textPath "d" curve (stated in fractions of the whole box, so drawn across all of it; the "arc" form aligns), and on vertical text whose columns fill the box (columns run the full height unless a paragraph's or a heading's boxHeight is shorter), it is refused with a notice and ignored. The layout map keeps reporting the box. One of: top, middle, bottom, center.
widthnumber | stringDrawn track width - points, a percentage of the column, or "auto"/absent for the full content width. The colored fill inside the track is value/max of this.
znumberCANVAS MODE ONLY (see the top-level "pages"): where this element sits in the PAINT order of its page. Lower draws first, further back; unset means "wherever I am in the list", and ties keep author order. It moves the PAINT and not the READING: the structure tree keeps the document order, so a caption written first because it is read first can still be painted last because it sits on top. That separation is what a flattened scene graph cannot express. Ignored outside canvas mode, where the flow already fixes both orders.

16. Shared definitions

These are the building blocks referred to from many elements. Where an element's key table gives a type such as style or color, this is where that type is defined.

Style

Schema definition: style. Type and paragraph properties every block accepts.

A style: text properties (face, size, color, tracking) cascade to nested content; box properties (padding, border, background, radius, spacing) apply only to the block that sets them.

A justified body style

{
  "fontFamily": "Helvetica",
  "fontSize": 11,
  "color": "#222222",
  "align": "justify",
  "lineHeight": 1.4,
  "firstLineIndent": 24,
  "spaceAfter": 6
}

Decimal tab stops with a dot leader

{
  "tabs": [
    {
      "at": 60,
      "align": "left"
    },
    {
      "at": 470,
      "align": "decimal",
      "leader": "dot"
    }
  ]
}
KeyTypeDefaultDescription
accentBarborderA solid colored bar drawn on the LEFT edge of a box (a textbook call-out's spine). Uses the border width as the bar thickness. Painted by container, frame, table cell and, since the box became a text-block property, paragraph and heading; a list or pre still ignores it.
alignstringHorizontal alignment of the text within its column. One of: left, center, right, justify, start, end.
backgroundcolor | objectA solid fill color, or { color, alpha } composited over white to a solid tint.
basedOnstringStart from this named "styles" entry; the object's own keys override it.
blockAlignstringSet the paragraph's lines as ONE BLOCK: every line keeps its own "align", but against a box as wide as the paragraph's longest line, and that box sits at the left, centre or right of the content width -- so an address typed flush left stays flush left line to line while the group sits centred in its box. The line breaks never change (the block is measured from the lines already broken), a line's indent counts toward its width, and each line moves by (content width - longest line) x (block fraction - align fraction), with fractions 0 / 0.5 / 1 for left / center / right: so centre-aligned text under "center", and a justified paragraph (whose lines fill the width), do not move. Absent, each line aligns across the full width as always -- for left-aligned text the same as "left", which is a real request on right-aligned text (it pulls the block to the left edge). Applied at the size drawn (after shrinkToFit), with keepIndent, tabs (a line's tab stops move with it) and in columns, on paragraphs, headings and pre blocks, in the flow or in a positioned box. Not applied where text flows around floats, through threaded frames, beside a run-in heading or a drop cap: those lines align across the width as before. Vertical text and text on a textPath curve have no lines to set as a block and are left as they are. A BLOCK property: read from the block being laid out, so setting it on a RUN changes nothing. Inherited by nested blocks. One of: left, center, right.
boldbooleanShorthand for weight: "bold".
borderborderRule drawn around the block's box. A box property - not inherited.
colorcolorText color. Inherited by nested content. The special value "none" fills nothing, leaving hollow letterforms that show only the "strokeColor" outline - with no outline to fall back on it is ignored, so text is never silently made invisible.
directionstringWhich way this block's text RUNS, for bidirectional scripts (Hebrew, Arabic). "auto" (the default) applies the Unicode bidirectional algorithm's own rule - the first strong character in the paragraph decides - so a Hebrew paragraph inside an English report resolves right-to-left on its own and an English one inside a Hebrew report resolves left-to-right, with nothing declared. Set it explicitly only to override that: a paragraph of digits or punctuation has no strong character to read, and an interface label may need to run right-to-left regardless of its content. Numbers inside right-to-left text still run left-to-right, and brackets take the direction of what they enclose, because the algorithm says so. NOT the same key as pageSetup.binding, which is the edge the BOOK is bound on: a Hebrew book wants both, an English book quoting one Hebrew sentence wants neither. WHAT IT MOVES: text alignment (align "start"/"end"), list bullets and numbers, table columns (the first column sits at the right edge, spans and rules follow), footnote and endnote markers, bibliography labels, and the titles of those blocks. Direction is INHERITED, so setting it on a container or on the document carries to everything inside - including a table cell of nothing but digits, which has no strong character of its own to detect from. WHAT IT DOES NOT MOVE, deliberately: float, imageAlign, tableAlign and a pdfContactSheet grid are PHYSICAL placements - left means the left of the page, the way a printer means it - and the left/center/right slots of a running header or footer are named for the page edges they sit at (pageSetup mirroring already swaps those on verso pages). A pre block stays left-aligned because code is read left-to-right whatever surrounds it. Table of contents leaders and index columns do not mirror YET. One of: auto, ltr, rtl, l2r, r2l, left-to-right, right-to-left.
featuresarray of stringOpenType features to set this text with, on an EMBEDDED face that defines them: liga standard ligatures (fi, ffi), dlig discretionary ligatures (ct, st), onum oldstyle figures / lnum lining figures, tnum tabular figures / pnum proportional figures, smcp true small capitals. Inherited, and the list REPLACES the inherited one rather than merging with it; an EMPTY list means none, which is how a table turns figures back to lining inside a document that set them oldstyle. Applied in a fixed order (smcp, liga, dlig, then the figure sets) rather than the order listed, because each lookup sees the glyphs the last one left. A feature the face does not define substitutes nothing and is not an error. Measured and drawn from the same glyphs, so line breaking accounts for the substituted widths. Base-14 faces carry no GSUB and are unaffected. A substituted glyph is placed by glyph id, and the engine writes the glyph-to-text map itself rather than leaving the PDF library to derive it from glyph names - so the characters survive for every feature listed here, including pnum, whose one.fitted figures used to come back as Private Use characters and could not be copied at all.
firstLineIndentnumberPoints the paragraph's FIRST line is indented; negative = hanging (first line pulled back left of the rest). Not inherited.
fitWidthbooleanHeading: solve the font size so the (single-line) text fills the column.
fontFamilystringFont family name, resolved and embedded by name. Inherited by nested content.
fontSizenumberType size in points. Inherited by nested content.
italicbooleanUse the italic face of the family. Inherited by nested content.
keepIndentbooleanKeep the spaces a line starts with -- at the start of the text and after each line break -- as that line's indent, the width of that many of the paragraph's spaces (wordSpacing included), instead of collapsing them. Only leading spaces: every other run of spaces still collapses to one, a line of nothing but spaces stays empty, and tabs keep their own meaning (they align to "tabs" stops when those are set). The indent is part of the line, like firstLineIndent, so justification never stretches it and it is never underlined; it gives way rather than push the line's first word out of the column, and tab stops stay measured from the paragraph's edge. Like firstLineIndent, it is applied at the left edge in right-to-left text too. Not applied where text flows around floats, through threaded frames, beside a run-in heading or a drop cap. A BLOCK property: it is read from the block being laid out, so setting it on a RUN changes nothing. Inherited by nested blocks.
letterSpacingnumberExtra points between glyphs (inherited).
lineHeightnumberMultiple of font size (1.0 = single).
minFontSizenumber6shrinkToFit's floor, in points: shrinking never sets any run of the text below this, and text written smaller keeps its own size. Not inherited.
minHeightnumberThe box grows to at least this height in points (uniform tiles in a grid, a deep chapter banner on a heading). Honored by container, frame, table cell, paragraph and heading.
overlinebooleanDraw a rule above the text, clear of the ascender (in the text color, same weight as the underline). Inherited; consecutive decorated words bridge into one continuous rule.
paddinginsetsPadding between the block's box edge and its content. A box property - not inherited. Honored by container, frame, table cell, paragraph and heading; on a paragraph or a heading it insets the text on all four sides, so a negative firstLineIndent hangs against it. A list or pre ignores it.
radiusnumberRounded-corner radius in points for a container / card box (fill, border, and edge stripe follow it).
shadowshadow -
shrinkToFitbooleanParagraph or heading with a position height: shrink the text until its lines fit that box, in 0.25 pt steps of its largest size -- every size scaled by one ratio, but no run set below minFontSize (a run written smaller keeps its own size). Only a positioned block with a height, set in horizontal lines, is shrunk -- not a dropCap or textPath paragraph, one a run-in heading is drawn into, or a side head; anywhere else the key does nothing. Text that still does not fit at minFontSize, or that its padding leaves no room for, is clipped and reported. Not inherited.
smallCapsbooleanRender lowercase letters as smaller uppercase (two-tier small caps): capitals/digits stay full size, lowercase becomes uppercase at about 0.75x the size. Synthetic from the regular font; the original text is preserved via /ActualText so search and assistive tech still read the real words. Inherited. When the same style ALSO lists smcp in features and the embedded face actually defines that feature, the drawn small capitals win and this synthesis is skipped - never both, because the synthetic pass uppercases the text and smcp maps from the lowercase, so running both would leave the feature nothing to substitute. A face without smcp keeps these synthetic capitals.
spaceAfternumberBlank space below the block, in points. A box property: it applies to this block only and is NOT inherited by nested content.
spaceBeforenumberBlank space above the block, in points. A box property: it applies to this block only and is NOT inherited by nested content.
stretchnumber100Horizontal glyph scale as a PERCENT: 100 is normal, below 100 condenses, above 100 extends (FrameMaker's FP_Stretch, the PDF Tz operator). Scales the letterforms and their advances but NOT the font size, so the baseline and line height are unchanged and line breaking accounts for the new widths. Inherited.
strikebooleanStrike through the text.
strikethroughbooleanAlias for "strike".
stripeobjectA colored bar along one edge of a box (generalizes accentBar; rounds only the two corners on its own edge). Painted by container, frame, table cell and, since the box became a text-block property, paragraph and heading; a list or pre still ignores it.
strokeColorcolorColor of the glyph OUTLINE, drawn in addition to (or instead of) the fill. Inherited by nested content, so a run inside a stroked paragraph keeps the outline unless it overrides it. Needs "strokeWidth" above 0 to show; on its own it does nothing.
strokeWidthnumberThickness of the glyph outline, in POINTS. Needs "strokeColor" to show. A PDF stroke straddles the outline - half inside the letterform, half outside - so stroking always thickens the type somewhat; a value tuned for display sizes is far too heavy at body sizes. Set 0 to drop an outline inherited from a parent.
tabsarray of objectParagraph tab stops. A Tab (\t) in the text advances to the next stop and fills the gap with its leader (Name ......... 42).
textEffecttextEffect -
textFilltextFill -
trackingnumberAlias for letterSpacing.
underlineboolean | stringUnderline the text, drawn in the text color. true is a single rule and false is none - the spelling every existing document uses, and it keeps working. The named values add FrameMaker's other two: "double" draws a second rule below the first; "numeric" is carried distinctly but DRAWS AS SINGLE today - the FDK defines the constant (FV_CB_NUMERIC_UNDERLINE) and neither of its manuals says what it looks like, so the value is preserved rather than guessed at. One of: none, single, double, numeric.
weightstringFont weight - "bold" for the bold face of the family. Inherited by nested content. One of: normal, regular, bold.
wordSpacingnumberExtra points between words. A BLOCK property: it is read from the block being laid out, so setting it on a RUN parses and resolves but changes nothing. Inherited by nested blocks.

Inline runs

Schema definition: runObject. The pieces a paragraph or heading is built from.

An inline run object: text + inline properties (style, link, lang, footnote, or an inline form field).

A styled run

{
  "text": "18 N·m",
  "style": {
    "weight": "bold"
  }
}

A run that links out

{
  "text": "the engine",
  "link": "https://appligent.com"
}

A run with a footnote

{
  "text": "apogee",
  "footnote": "The highest point of the flight."
}

A run with an endnote (gathered at the next endnotes block)

{
  "text": "apogee",
  "endnote": "The highest point of the flight."
}

A superscript run (ordinals, ® / ™, exponents)

{
  "text": "nd",
  "superscript": true
}

A cross-reference to a block id

{
  "ref": "assembly"
}
KeyTypeDefaultDescription
altstringWhat a screen reader is given in place of the graphic or equation this run draws: the /Alt of the inline <Figure>, or of the inline <Formula> (there an alias for "mathAlt"). Falls back to the run's own text. REQUIRED on a graphic, which has no other accessible text - without it the engine warns and tags the placeholder /Alt "graphic".
asstringpageWhat a "ref" run renders: "page" the page number, "number" the bare number ("3-2"), "label" the label word + number ("Figure 3-2"), "title" the target's heading / caption text without its number, "full" the target named and located ("Figure 3-2 on page 47", or "Centre of Pressure on page 47" where nothing is numbered). "number" and "label" need a numbering scheme and degrade to the page label without one; "title" and "full" do not, and only a target with neither a number nor a title falls back to the page. Ignored without "ref". One of: page, number, label, title, full.
badgestringAn inline badge / pill: this text is drawn inside a rounded, padded, filled label. style colors the label text; badgeStyle carries the box.
badgeStyleobjectThe badge box: fill / radius / padding (radius >= 999 renders a full pill).
baselineShiftnumberPoints to move this run off its baseline: positive raises it, negative lowers it. It is added to the superscript or subscript position, so a small superscript ™ can be lifted to the cap height or a subscript set lower; on a plain run it moves that run alone. Only the ink moves: the line keeps its height, so a large shift can reach the line above or below.
citestringCite one or more works from the document's bibliography block, by key. Several keys are separated by a comma or a space ("knuth1984, lamport1986"). The run's own text is IGNORED - the engine composes the marker from the bibliography's style ([3] or (Knuth 1984)), numbering entries by order of first citation, and links the marker forward to its entry (the entry's own label links back to the first citation of it). In a run-form caption too, numbered where the caption stands in reading order -- when the caption is drawn.
citePagesstringA locator inside the cited work, drawn inside the marker: "pp. 12-14" gives [3, pp. 12-14]. Ignored without cite.
conditionstringConditional text: render this run only when its condition-tag expression is shown under the document's conditions map. Same grammar as a block condition.
contentstringInline SVG source, drawn baseline-aligned in place of the text. Alias for "svg".
endnotestringEndnote text: a superscript marker here + a note gathered into the next endnotes block (chapter / document end). The marker links to its note and back (to the first place the marker is drawn, never to a contents row's copy of it). Independent of the footnote stream. In a run-form caption too, numbered where the caption stands in reading order (a table's caption before its rows) -- when the caption is drawn: one with no text is not, and its endnote takes no number (the engine says so).
footnotestringFootnote text: a superscript marker here + an auto-numbered note at the page bottom. Not drawn in a caption: a caption reserves no room at the page foot, so the note is dropped, takes no number from the footnotes after it, and the engine says so, naming the block (use an endnote there). Not drawn in vertical (直排) text either -- a column has no page foot along it -- with the same result: no number, and the engine says so. Drawn on every other form of heading, run-in and curved included.
heightnumberInline box height in points: a field widget (with "field"), or a graphic (with "image" / "svg"). Absent on a graphic, the height is the run's own font size - an unsized icon is one em tall, so it sits on the line like a letter.
imagestringAn inline GRAPHIC: an image file drawn baseline-aligned in place of the text, tagged <Figure> carrying "alt". The inline analogue of the "image" block - an icon, a logo, a signature, a symbol no font carries. Sized by "width" / "height"; unsized it is one em tall and takes its width from the image's own aspect, so it sits on the line like a letter and follows the text when the style changes.
labelstringInline field tooltip fallback (with "field").
langstringRun language -> a <Span> with /Lang in tagged output, and the language whose rules divide this run's words when hyphenate is on. A run beats its block, which beats the document.
linkstringURL (external) or #anchor (internal) link target.
mathAltstringAn inline equation: a spoken reading (the inline <Formula> /Alt).
mathMLstringAlias for "mathml".
mathSvgstringAn inline equation: its pre-rendered SVG (MathJax, fontCache:'none'), drawn baseline-aligned in place of text.
mathmlstringAn inline equation: its Presentation MathML source, embedded as a /Supplement associated file on the inline <Formula>. Pair with "mathSvg".
refstringCross-reference: renders a live property of the block whose "id" matches, as an internal link. The run's own text is ignored; "as" picks which property (default the page number).
refTemplatestringCustom "ref" text over the placeholders {number} {word} {label} {title} {page} - e.g. "see {label} ({title}) on p.{page}". Overrides "as". Ignored without "ref".
sidenotestringMargin-note text drawn in the reserved side band, top-aligned with the line this run STARTS on. Unlike a footnote or endnote it leaves NO marker in the text - a margin note refers to its text by sitting beside it. Requires the band: set pageSetup.sidenoteWidth (alias sideHeadWidth) and a single-column body. Notes stack in the band rather than overlapping, sharing the cursor with side heads. Tagged as a Note element. Not drawn in a caption, which has no margin band, nor beside vertical (直排) text, which has none along its columns: the engine says so, naming the block.
stylestyleStyle for this run only. Inherited text properties still apply underneath it. TEXT properties only: a box property written here (background, padding, border, radius, stripe, shadow, minHeight, align, lineHeight, firstLineIndent, tabs, spaceBefore/After, fitWidth) is accepted and ignored, because a run is not a box -- to tint words behind the type use "textEffect": "background".
subscriptbooleanDrop this run to subscript position - for chemical formulae (H2O) and mathematical indices. The run is DROPPED but not resized; set a smaller "style.fontSize" for the conventional look. Ignored when "superscript" is also set.
superscriptbooleanRaise this run to superscript position - for ordinals (1st), trademark marks (® / ™) and exponents. The run is RAISED but not resized; set a smaller "style.fontSize" for the conventional look.
svgstringInline SVG source, drawn baseline-aligned in place of the text. Beside "mathml" or "mathSvg" this is the EQUATION's SVG (tagged <Formula>, with the MathML as a /Supplement associated file). On its own it is a GRAPHIC (tagged <Figure>, described by "alt"), sized by "width" / "height" or, unsized, one em tall with its width from the viewBox aspect.
textstringThe run's text - the characters this run contributes to the line.
widthnumberInline box width in points: a field widget (with "field"), or a graphic (with "image" / "svg"). Absent on a graphic, the width is derived from the height and the graphic's intrinsic aspect. Giving BOTH width and height sets the box: an image is stretched to it; an svg keeps its viewBox's shape inside it, as its root preserveAspectRatio says (default xMidYMid meet: fitted whole and centred), and is stretched only when that says "none".

Color

Schema definition: color. How a color is written.

A color in one of four color spaces. The PDF backend emits the true operator for each (rg / g / k / scn).

Hex, the everyday form

#1f4e79

DeviceCMYK for print

{
  "cmyk": [
    0.75,
    0.0,
    0.62,
    0.06
  ]
}

A spot ink with a tint and a process alternate

{
  "spot": "PANTONE 185 C",
  "tint": 0.6,
  "cmyk": [
    0,
    0.91,
    0.76,
    0
  ]
}
KeyTypeDefaultDescription
cmykarray of numberThe alternate -- what the ink approximates at full tint.
colorstringRGB alternate when no "cmyk" given (display only).
graynumberSingle gray component: 0 is black, 1 is white. A value above 1 is read as a 0-100 percentage.
rgbarray of number[red, green, blue]. Components run 0-1, but if any value exceeds 1 the whole array is read as 0-100 percentages.
spotstringThe colorant name written into the PDF (e.g. "PANTONE 185 C").
tintnumberInk strength 0..1 (or 0..100 percent). Default 1.

Dimensions

Schema definition: dimension. How a length is written.

A length: points (number), "NN%" of the available width, "auto", or "NN".

A percentage of the column

50%

Absolute points

180

Inches

3in

Absolute position

Schema definition: position. Taking a block out of the flow.

Absolute placement: fixed coordinates (points, top-left origin), measured from the trim page's top-left -- or, when the nearest enclosing container that writes "childOrigin" says "container", from that container's border box's top-left, wherever it lands. Either way everything that reports geometry (the layout map, notices) reports PAGE coordinates. The block leaves the flow and consumes no flow height; content past an explicit height is not drawn, and the engine says so -- an "aplayout:" notice naming the block, and "overset" on its layout-map entry. A paragraph or heading can set "shrinkToFit" to shrink its text into the box instead. Pairs with pageSetup.template for form overlays onto existing PDFs. Bindable from data, so each record of a template places its own sticker: any of x, y, width, height may be a "{{path}}" string, and so may the whole position ("{{s.box}}", a record's {x, y, width, height}). A value that is exactly one token takes the whole value at that path -- a number, or text of one such as a CSV field (points; there are no units). A record without a width or height sizes the box to its content, silently, as an unwritten one does; a record without a usable x or y is reported with the block's pointer (and the -merge record) and the block is NOT drawn, because there is nowhere it was meant to go. Text that is not a number, and a negative width or height, are reported. A string with no token is read as it stands, in a document with no data. The whole position from data: "{{s.box}}" resolves to the record's {x, y, width, height} object ("w" / "h" accepted), each member a number or text of one.

Example

{
  "x": 320,
  "y": 60,
  "width": 180,
  "height": 90
}
KeyTypeDefaultDescription
hnumber | stringAlias for "height", used only when "height" is absent.
heightnumber | stringHeight of the block's box, in points. Optional - most blocks size to their content; vertical text with no boxHeight, which has no size of its own, takes the room from where it begins down to the flow's foot, above any footnote band (the page's foot, where it begins in the bottom margin or within a character of the flow's), and at most the text area's full height - in a positioned paragraph or heading, or in the flow of a container with no height, where what precedes the text is drawn as before and what follows it is not drawn, and said; in a cell of a positioned columns block, table or cardGrid, every such text shares the room: each is given the same box, the tallest with which the whole block ends at that foot (or, where the block's horizontal content runs past the foot anyway, with which the text does), every row still drawn (since 2026-10-15). With a height, a table drops its rows past it (said) and a columns block's or cardGrid's vertical text with no boxHeight stands a text area tall past it; and in a vertical section positioned vertical text is not bounded - give it a boxHeight. A STRING is a data token (or text holding one, or a number as text), resolved per record: a record without it sizes the box to its content.
wnumber | stringAlias for "width", used only when "width" is absent.
widthnumber | stringWidth of the block's box, in points. A positioned block is taken out of the normal flow and reserves no vertical space unless it also sets "textWrap". A STRING is a data token (or text holding one, or a number as text), resolved per record: a record without it sizes the box as if no width were written.
xnumber | stringRequired. Distance from the origin's left edge to the block's left edge, in points: the trim page's left edge, or -- when the nearest enclosing container that writes "childOrigin" says "container" -- that container's box's left edge. A STRING is a data token (or text holding one, or a number as text), resolved per record: a record without it is reported and the block is not drawn.
ynumber | stringRequired. Distance from the origin's TOP edge to the block's top edge, in points: the trim page's top edge, or -- when the nearest enclosing container that writes "childOrigin" says "container" -- that container's box's top edge. A STRING is a data token (or text holding one, or a number as text), resolved per record: a record without it is reported and the block is not drawn.

Borders

Schema definition: border. Stroke width and color.

A border: {width, color}, or a color (hairline) / number (black) shorthand. The color may be an ink object such as {"cmyk": [0, 1, 1, 0]}; "none" (or "transparent") draws no border.

Example

{
  "width": 0.75,
  "color": "#2a4d7a"
}
KeyTypeDefaultDescription
colorcolorLine color. "none" (or "transparent") draws no line at any width.
dasharray of number | numberDash pattern: alternating on/off run lengths in points, repeating. A single number means equal on and off. Omit or [] for a solid line. A pattern whose lengths are all zero is refused with a warning and the line draws solid -- an all-zero array is a division by zero in most renderers and an invisible line in the rest.
dashPhasenumberHow far INTO the dash pattern the line starts, in points. Shifts the rhythm without changing it -- two parallel dashed rules with different phases read as a weave rather than as a grid.
lineCapstringThe shape of a stroke's free ends, and of each dash. "butt" (the default) stops square at the endpoint; "round" and "square" both extend half the stroke width past it. A dotted line is a dash of [0, gap] with a ROUND cap -- with butt ends the same pattern draws nothing at all. One of: butt, round, square.
lineJoinstringThe shape of a stroke's corners. "miter" (the default) extends both edges to a point, "round" arcs them, "bevel" cuts the point off. Only meaningful where one PATH turns a corner: a polygon, a polyline, a path, or a rectangle's border. One of: miter, round, bevel.
miterLimitnumberHow far a mitered corner may spike before it is cut to a bevel, as a multiple of the stroke width. PDF's default is 10 (SVG's too). A sharp angle at a heavy weight can throw a spike many times the line width, which is what this bounds.
widthnumberLine thickness, in points. Leave it out and give a color, and the line is a 0.75pt hairline, as the color shorthand draws.

Insets and padding

Schema definition: insets. Four-edge measurements.

Edge insets: one number for all four edges, [top, right, bottom, left], or per-edge keys.

One value for all four edges

10

Per edge

{
  "top": 6,
  "right": 10,
  "bottom": 6,
  "left": 10
}
KeyTypeDefaultDescription
bottomnumberInset on the bottom edge, in points.
leftnumberInset on the left edge, in points.
rightnumberInset on the right edge, in points.
topnumberInset on the top edge, in points.

Gradients

Schema definition: gradient. Axial and radial fills.

A gradient fill: a color ramp along an axis (linear / axial) or out from a center (radial). Rendered as a true PDF axial (Type 2) or radial (Type 3) shading.

A diagonal linear ramp

{
  "type": "linear",
  "angle": 45,
  "stops": [
    {
      "offset": 0,
      "color": "#1f4e79"
    },
    {
      "offset": 1,
      "color": "#7ba44f"
    }
  ]
}

A radial badge

{
  "type": "radial",
  "center": [
    0.4,
    0.4
  ],
  "radius": 0.7,
  "stops": [
    {
      "offset": 0,
      "color": "#ffffff"
    },
    {
      "offset": 1,
      "color": "#1a4fa0"
    }
  ]
}
KeyTypeDefaultDescription
anglenumberLinear gradient direction in degrees: 0 = left->right, 90 = top->bottom. Shorthand for from/to.
centerarray of numberRadial center [x, y] as box fractions 0..1 (default [0.5, 0.5]).
fromarray of numberLinear axis start [x, y] as box fractions 0..1 (default [0, 0.5]).
radiusnumberRadial outer radius as a fraction of the box's longer side (default 0.5).
stopsarray of objectRequired. Two or more color stops, each { offset: 0..1, color: <color> }. Each stop sits at its offset: before the first stop the ramp is that stop's color, after the last stop the last stop's color, linear between consecutive stops (stops at 0.3 and 0.7 are solid to 30% and from 70%). Sorted by offset, keeping the written order of stops at the same offset, so two stops at one offset make a hard edge.
toarray of numberLinear axis end [x, y] as box fractions 0..1 (default [1, 0.5]).
typestringlinear (axial, default) or radial. One of: linear, radial.

Form-field spec

Schema definition: fieldSpec. Keys shared by every interactive field.

Interactive AcroForm field keys, shared by the "field" block and inline run fields.

Example

{
  "field": "text",
  "name": "email",
  "caption": "Email address",
  "tooltip": "We never share it",
  "required": true
}
KeyTypeDefaultDescription
captionstringPush-button caption.
checkedbooleanInitial state of a checkbox or radio button.
combobooleantrue = a dropdown combo (choice fields).
fieldstringThe field kind. One of: text, checkbox, check, radio, choice, combo, list, button, signature, sig.
fontSizenumberPoint size for the field's own text. 0 means auto-size to the field height.
listbooleantrue = a list box rather than a dropdown (choice fields).
multilinebooleanLet a text field hold more than one line.
namestringFully-qualified field name (radios sharing a name form one group).
optionsarray of string | objectChoice options: strings, or { value, label } pairs.
passwordbooleanMask typed characters, for a password entry field.
readOnlybooleanMake the field read-only: it renders and prints but cannot be edited.
readonlybooleanAll-lowercase alias for "readOnly".
requiredbooleanMark the field required in the PDF form.
tooltipstring/TU alternate description (also the PDF/UA accessible name).
valuestringInitial value (text/choice), or export value (checkbox/radio).

Page setup

Schema definition: pageSetup. Page size, margins, columns, running bands.

Page geometry and print setup: size, orientation, margins, columns, running heads, page numbering, and the print-production extras (bleed, marks, color bars, letterhead).

Example

{
  "size": "Letter",
  "margins": 64,
  "columns": 2,
  "header": {
    "center": "{{title}}"
  },
  "footer": {
    "right": "{{page}} / {{pages}}"
  }
}
KeyTypeDefaultDescription
backgroundcolorPage background color, painted to the full bleed extent as an artifact.
bleedinsetsBleed allowance in points: one number for all edges or per-edge insets. The MediaBox grows by it; TrimBox stays the trim page and BleedBox covers the bleed.
colorBarsbooleanProcess color bars (K tints + C/M/Y solids) in the bottom slug area.
columnGapnumberGutter between columns, in points.
columnsintegerNumber of text columns the content box is divided into. Default 1.
embedFontstringSystem font to embed (subset) instead of the Base-14 path. Forced to "Helvetica" for tagged / PDF/A docs with no value. If the family cannot be embedded on the rendering machine the document is REFUSED, not substituted and not written -- unembedded text is invisible off the machine that made it and cannot honestly carry a PDF/UA or PDF/A claim. Preflight with aplayoutapp -auditfonts <spec>; -allowunembeddedfonts downgrades the refusal to a warning for diagnosis.
footerrunningHFRunning footer drawn at the bottom of every page in range.
footerMarginnumberDistance from the page edge to the footer band (< 0 => sit on the bottom margin).
headerrunningHFRunning header drawn at the top of every page in range.
headerMarginnumberDistance from the page edge to the header band (< 0 => sit on the top margin).
margininsetsAlias for "margins".
marginsinsetsPage margins. The area inside them is the content box that text flows through.
marksbooleanDraw printer's marks (corner crop marks + registration targets) outside the bleed. Registration marks use the /All separation so they appear on every plate.
mirrorMarginsbooleanSwap left/right margins on verso (even) pages - the book gutter convention. Content width is unchanged.
numberingnumberingHow page numbers are formatted and where they restart. (This is PAGE numbering - folios. The document-level "numbering" is a different key: it counts headings, figures, tables, listings and equations.)
orientationstringPage orientation. Landscape swaps the width and height of the chosen size. One of: portrait, landscape.
pageSizepageSizeAlias for "size".
rotateintegerPage rotation: a display /Rotate on the page (a multiple of 90; normalized). Turns the whole page for viewing/printing WITHOUT changing layout coordinates - a landscape fold-out in a portrait book is a wide (landscape) page plus rotate:90. Distinct from orientation, which swaps the MediaBox.
sideHeadGapnumberSide-head area: gap in points between the side-head band and the body column (default 18).
sideHeadSidestringSide-head area: which side the band sits on. left/right are absolute physical sides; outside/inside are binding-relative and flip per recto/verso (outside = outer edge, inside = spine edge), independent of mirrorMargins. One of: left, right, outside, inside.
sideHeadWidthnumberSide-head area: reserve a band this many points wide beside the body column for margin headings. When > 0 the body column is narrowed by (sideHeadWidth + sideHeadGap) document-wide, and a heading with "sidehead": true is drawn in the band with its top aligned to the body that follows (which flows beside it). 0 (default) = no side-head area.
sidenoteGapnumberAlias for sideHeadGap - the gap between the band and the body column.
sidenoteSidestringAlias for sideHeadSide. One of: left, right, outside, inside.
sidenoteWidthnumberAlias for sideHeadWidth. Side heads and margin notes (run key "sidenote") share ONE reserved band; use whichever spelling matches what the band is for.
sizepageSizePage size - a named size such as "Letter" or "A4", or explicit dimensions.
slugboolean | stringSlug line in the top slug area: true for "{{title}} - {{date}} - p. {{page}}", or a custom template using those variables.
templatepageTemplatePage template - repeating furniture such as a letterhead PDF drawn under each page.
verticalAlignstringVertical justification: position this section's body content in the page. "top" (default). "center" / "bottom" ("middle" = center) shift the whole content block, applied only when the section fits on ONE single-column page (a title page / opener / colophon), so nothing splits and the shift is exact. "justify" feathers a MULTI-page single-column flow - spreading each page's leftover slack across its inter-paragraph gaps so the bottoms line up flush (every page but the last). All modes fall back to top on a multi-column section, a baseline grid, or (for justify) any float / side head / straddle / anchored frame / footnote / tall inline atom. One of: top, center, bottom, middle, justify.
watermarkwatermarkWatermark drawn behind the content of every page in range.
writingModestringDefault writing mode for the document's blocks: a block that does not set its own takes this, and a section can override it. "vertical-rl" sets the prose in 直排 (tategaki) columns; "horizontal-tb" is the default. The page's own geometry - margins, columns, running bands - is unaffected. One of: horizontal-tb, horizontal, lr-tb, yokogaki, 横排, vertical-rl, vertical, tb-rl, tategaki, 直排, 縦書き, vertical-lr, tb-lr.

Running bands

Schema definition: runningBand. Headers and footers.

One running band. Slots may use the {{page}}, {{pages}}, {{title}}, {{date}} variables.

Example

{
  "height": 24,
  "style": {
    "fontFamily": "Helvetica",
    "fontSize": 9
  },
  "left": [
    {
      "text": "Appligent, Inc."
    }
  ],
  "right": [
    {
      "text": "Page {{page}}"
    }
  ],
  "rule": {
    "width": 0.5,
    "color": "#cdd4de"
  }
}
KeyTypeDefaultDescription
centerslotRunsContent for the center slot.
heightnumberHeight reserved for the band, in points.
imageobjectA logo / image drawn in the band (e.g. a letterhead mark): placed horizontally by align, vertically centered; the band grows to fit it.
leftslotRunsContent for the left-hand slot.
rightslotRunsContent for the right-hand slot.
stylestyleDefault style for every slot in the band.

Page numbering

Schema definition: numbering. Folio start value and numeral style.

PAGE numbering for a section - the folio sequence. Not to be confused with the document-level "numbering", which counts headings, figures, tables, listings and equations.

Lower-roman front matter

{
  "startAt": 1,
  "format": "lower-roman"
}

Zero-padded Bates numbers

{
  "format": "arabic",
  "startAt": 1,
  "pad": 6
}
KeyTypeDefaultDescription
formatstringNumeral style for the page number - arabic, roman or alphabetic in either case, or Chinese numerals ("hanzi", or "hanzi-traditional" for 萬 rather than 万). PDF has no page-label style for Chinese numerals, so such a page carries its whole rendered label as the /PageLabels /P prefix. One of: arabic, decimal, 1, roman, lower-roman, i, ROMAN, upper-roman, I, alpha, lower-alpha, a, ALPHA, upper-alpha, ....
padintegerZero-pad arabic page numbers to N digits (the Bates idiom); set startAt to continue across documents.
startAtintegerFirst page number in the sequence. (This is PAGE numbering - folios. The document-level "numbering" is a different key: it counts headings, figures, tables, listings and equations.)

Automatic numbering

Schema definition: captionNumbering. Numbering headings, captions and equations.

Automatic numbering for one counter (figures, tables, listings or equations). true takes the defaults; an object tunes them. The label is composed as labelWord + " " + prefix + [chapter + prefixSep] + number + suffix - prepended to the caption for a captioned block, or drawn in the right gutter for an equation.

Example

{
  "enabled": true,
  "labelWord": "Figure",
  "prefixWithHeading": true,
  "prefixLevel": 1,
  "prefixSep": "."
}
KeyTypeDefaultDescription
enabledbooleantrueTurn this counter off. Supplying the object at all implies on, so this is only needed to disable a counter a template switched on.
formatstringarabicNumeral style for the number itself. One of: arabic, decimal, 1, roman, lower-roman, i, ROMAN, upper-roman, I, alpha, lower-alpha, a, ALPHA, upper-alpha, ....
frominteger1Value given to the first caption.
labelWordstring"Figure" / "Table" / "Listing"; empty string gives a bare number. Equations default to empty - their label IS the wrapped number. A "{}" in the word says where the number goes instead of the default trailing space, which is how a Chinese caption reads 图1-1: "图{}".
prefixstring""Wraps the number's left side. Empty for captions; "(" for equations, whose label is "(3.2)".
prefixLevelinteger1Heading level that supplies the chapter number.
prefixSepstring-Between chapter and ordinal.
prefixWithHeadingbooleanfalseChapter-relative numbering: "Figure 3-2". The counter restarts at each heading of prefixLevel.
suffixstring. Between the number and the caption text.

Watermarks

Schema definition: watermark. Stamps drawn on every page.

A page watermark / stamp repeated on the section's pages: text (or an image) drawn diagonally / at an angle / centered / top / bottom, solid or outline, under or over the body, with constant alpha. Always a Pagination/Watermark artifact.

Example

{
  "text": "DRAFT",
  "angle": 45,
  "mode": "dual",
  "opacity": 0.12,
  "color": "#c0202a"
}
KeyTypeDefaultDescription
anglenumberDegrees counter-clockwise, with position "angle" (setting it implies the position).
autoScalenumberFont size as a percentage of the page height (omitted = fit to the extent).
colorcolorWatermark color.
imagestringImage file stamped instead of text (PNG/JPEG/GIF/TIFF/WebP, or HEIC/HEIF on macOS).
justifystringMulti-line alignment: left / center (default) / right. For top/bottom it also anchors the block on the page; for diagonal / diagonal-down / center / angle it justifies each line within the rotated text block. One of: left, center, right.
layerstringUnder or over the body content (dual draws both). One of: under, over.
modestringsolid fill, hollow outline, or dual (solid under + outline over - reads on text and image pages alike). One of: solid, outline, dual.
offsetXnumberHorizontal nudge from the chosen position, in points.
offsetYnumberVertical nudge from the chosen position, in points.
opacitynumberOpacity from 0 (invisible) to 1 (solid). A watermark is drawn as a page artifact, so it stays out of the reading order.
pagesstringWhich pages to stamp (first/rest are relative to the section). One of: all, even, odd, first, rest.
positionstringWhere the watermark sits and how it is angled. "angle" uses the explicit "angle" property instead of a preset. One of: diagonal, diagonal-down, center, top, bottom, angle.
scalenumberImage width as a percentage of the page width (default 50).
sizenumberExplicit font size in points (0/omitted = automatic).
srcstringAlias for "image".
textstring | array of stringWatermark text; an array gives one line per entry (or use \n).

Page templates

Schema definition: pageTemplate. Reusable page furniture.

Letterhead / stationery: one page of an external PDF drawn to the full trim page under the body (a Page artifact). For PDF/UA the source's fonts must be embedded.

Example

{
  "src": "letterhead.pdf",
  "page": 1,
  "on": "first"
}
KeyTypeDefaultDescription
onstringWhich pages get the underlay: every page, only the first, or every page after the first. One of: all, first, rest.
pageintegerWhich page of "src" to draw, 1-based. Default 1.
srcstringExternal PDF whose page is drawn as a full-page underlay (vector Form XObject).

Table cells

Schema definition: tableCell. One cell of a table row.

One table cell: a bare array of blocks, or an object with span/padding/valign/style + blocks.

Example

{
  "colspan": 2,
  "valign": "middle",
  "style": {
    "background": "#eef2f8"
  },
  "blocks": [
    {
      "type": "paragraph",
      "text": "A merged, tinted header cell"
    }
  ]
}
KeyTypeDefaultDescription
blocksarray of blockFull block content for the cell when a plain string is not enough: a paragraph with runs, a list, a nested table, an image.
colspanintegerHow many columns this cell spans. Default 1.
headerbooleanTag THIS ONE cell as a header (TH), whatever its row and column are. A row's "header" makes every cell in the row a header and "headerCols" makes the leading N columns headers in every row; neither can say "this label cell heads its row and the values beside it are data", which is the shape of a subtotal row. Scopes as a row header, or Both inside a header row.
headersstring | array of stringPDF/UA: id(s) of the header cells that govern this data cell (/Headers). With rowGroups the engine DERIVES this for every cell -- the row header immediately to its left plus its column header -- so an authored list is only needed for a table that does not use row groups.
idstringPDF/UA cell identifier, referenced by other cells' "headers".
paddinginsetsPadding inside this one cell, overriding the table's "cellPadding".
rotatestringDraw this cell's text label VERTICALLY (a narrow-column header). 90 = read bottom-to-top, 270 = read top-to-bottom; any other value = upright. The cell's height grows to the label's length and its column stays narrow. The rotation rides the text matrix, so the TH tag and PDF/UA stay intact. Scope: a text label (rich block content in a rotated cell is not laid out rotated). One of: 0, 90, 270.
rowspanintegerHow many rows this cell spans. Default 1.
stylestyleStyle for this cell - including "background", which is how a single cell is tinted.
valignstringVertical alignment of the cell's content within the row height. One of: top, middle, center, bottom.

Chart axes

Schema definition: chartAxis. Axis labels, ticks and gridlines.

One chart axis: its title, bounds, tick spacing, gridlines and scale. These all describe a NUMERIC axis. When the chart supplies categories, its x axis becomes a category axis instead - one tick per category, no numeric rounding - and the keys below behave as each notes.

Example

{
  "label": "Revenue",
  "unit": "K",
  "min": 0,
  "max": 120,
  "majorTick": 20,
  "grid": true
}
KeyTypeDefaultDescription
gridbooleanHairline gridlines at the major ticks. On a category axis, one per LABELED category, on the slot center with its tick - rule, tick and name are one decision, so the axis never carries a mark it cannot explain.
labelstringAxis title, drawn alongside the axis.
logScalebooleanPlot this axis logarithmically (base 10). Major ticks land on decades with minor ticks at 2x..9x, and labels read as plain decimals while they stay short (0.001 to 10000) and as 1e5 / 1e-6 beyond. The axis never starts at zero, so min is taken from the smallest positive value in the data unless you set it; values at or below that floor are drawn on it rather than dropped. Honored on the cartesian chart types - line, bar, scatter, area and combo - on both xAxis and yAxis. Not honored on waterfall, candlestick, box-plot, gauge or radar, whose value axes are linear by construction, nor on yAxis2. Not honored on an x axis carrying categories either: a category index is ordinal, not a magnitude, and a log scale would map two adjacent slots onto the same pixel.
majorTicknumberExplicit major-tick spacing; omit for automatic nice 1/2/5x10^n. On a category axis this reads as a whole-number stride - label every Nth category - since a fractional step would put a tick where no category exists. Omit it there and the stride is measured from the widest label so the names cannot collide.
maxnumberFixed upper bound. Omit to take it from the data. On a category axis, see min.
minnumberFixed lower bound. Omit to take it from the data. On a category axis, omitting it is normal: the range is pinned to the categories with half a slot of air at each end, so the outermost bar sits inside the frame. Setting it pins that edge instead, and any category falling outside the plot is not drawn.
minorTicksintegerMinor subdivisions per major interval. Ignored on a category axis (there is nothing between two adjacent slots to subdivide) and on a log axis, which always draws its own 2x..9x minors.
unitstringSuffix appended to every tick label ("K", "%", "ms", ...). Ignored on a category axis, where the label is the category name.

Index terms

Schema definition: indexTerm. What the index scanner looks for.

One index term: a bare word/phrase, or an object with an optional match pattern and one level of sub-entries.

Example

{
  "term": "stability",
  "subentries": [
    {
      "term": "static margin"
    },
    {
      "term": "dynamic"
    }
  ]
}
KeyTypeDefaultDescription
labelstringAlias for "term".
langstringBCP-47 language tag of this entry's label (e.g. "de-DE" for {"term": "Weltanschauung", "lang": "de-DE"}), emitted as /Lang on the entry so a screen reader pronounces it in that language. Without it an entry whose label is the body text it matches reads in the language of the term's first occurrence, and one whose label is your own wording (a "match" of other words, or a label-only group header) reads as the index; this wins over both. It names the label, not the index: the page numbers ride inside the entry as they always do, and a Hanzi page number or the engine's "(continued)" still reads as the index. A sub-entry takes its own. "de_DE" is written de-DE; a value that is no language tag ("x!!bad") is ignored, and the entry reads as it would without it; either is reported once per spelling, naming the term. Tagged documents only.
matchstringThe text matched in the body when it differs from the label. An explicit empty string makes the entry label-only: never scanned, rendered as a group header when its subentries have hits.
sortAsstringThe key this entry sorts and files under, replacing the label. Wins in every collation, and is the escape hatch a Chinese index needs: no reading table can be right for every proper noun or 多音字 - a character whose reading depends on the word - so 长江 takes "changjiang" (Chángjing, not zhng) and 重庆 takes "chongqing". It is also how a Latin-ordered index files a Chinese term. InDesign spells this "Yomi", makeindex spells it key@display.
subentriesarray of indexTermSub-entries nested one level under this term.
termstringThe displayed entry label (also the match text when no "match").

17. Document-level keys

The keys of the top-level object: metadata that lands in the PDF's /Info dictionary and XMP, the standards switches, the typography defaults, and the two content keys.

KeyTypeDefaultDescription
attachmentsarray of objectAssociated files embedded as PDF/A-3 attachments (catalog /AF + /Names /EmbeddedFiles). Any attachment promotes a PDF/A-2 conformance level to the matching PDF/A-3 level.
authorstring/Info Author + XMP dc:creator.
baselineGridnumberBaseline grid pitch in points (0 = off): text-line baselines snap to a page-absolute grid (measured from the content top) so columns and facing pages align. Set it to your body leading (fontSize x lineHeight).
bindingstringWhich edge the book is bound on. "left" (default) is the Western binding: the spine is on the left of a recto and the pages progress left to right. "right" is the 右綴じ binding of a vertical Chinese or Japanese book - the spine on the right, the pages read back to front. A right binding writes /ViewerPreferences /Direction /R2L, so a two-up viewer lays the spreads out in reading order, and it mirrors every binding-relative side: with mirrorMargins the wider spine margin moves to the other edge, and inside/outside side heads and margin frames follow. Independent of writingMode - a right-bound book of horizontal text is a real thing, and one vertical block never re-binds the document. One of: left, right, ltr, rtl, l2r, r2l.
blocksarray of blockThe document body as a single implicit section. Use "sections" instead for multi-section documents.
canvasbooleanRead-only echo of canvas mode, which is turned on by the presence of "pages". Setting it without "pages" does nothing -- there would be no designed page to apply it to.
changeBarsobjectRevision / change bars: the appearance of the vertical bar drawn in the page margin beside any content marked changed (a block's changed:true, or a block carrying a condition tag declared with changeBar:true). This object only styles the bar; it is not an on/off switch - a bar draws wherever changed content exists. The bar is a PDF artifact, out of the tagged reading order.
chaptersarray of string | objectNative book model: assemble N chapter spec files into ONE PDF. Each entry is a chapter file path (relative to this book spec), or an object {src, restart}. Chapters are loaded through the full per-chapter pipeline (their images/includes/data resolve relative to the chapter's own directory) and merged as sections, so the TOC/index/list-of-figures, cross-file cross-references and the tagged-PDF structure tree span every chapter. This document's own top-level blocks become front matter (a cover, a master TOC) before chapter 1, and its pageSetup / running header+footer / numbering / defaultStyle are the book defaults each chapter inherits (and may override).
colorModestring"cmyk" renders the whole document in DeviceCMYK (RGB inputs are converted, so the file carries no DeviceRGB) and a PDF/A document then gets a CMYK OutputIntent (the bundled U.S. Web Coated SWOP v2, or outputProfile) instead of sRGB. Default "rgb". One of: rgb, cmyk, RGB, CMYK.
conditionIndicatorsbooleanConditional text author view: when true, conditional content is NOT dropped - every conditional block/run is tinted in its tag's color and the hidden ones are struck through, so an editor can see what is conditional. Off (default) = final build.
conditionsobjectConditional text (FrameMaker condition tags): maps a condition-tag name to its state - a bare boolean (true = shown), or an object { show, color }. A block / run / table-row "condition" expression (a boolean grammar over tag names: AND / OR / NOT / parentheses, with juxtaposition = AND) is evaluated against this map; an unlisted tag defaults to shown. In a final build hidden content is removed in a pre-pass before layout; see conditionIndicators for the author view.
creatorstring/Info Creator + XMP xmp:CreatorTool (default "PageWright").
dataobject | arrayData-driven binding context: a record (object) whose fields are substituted into every {{path}} token in the document before layout, e.g. {{customer.name}} or {{items.0.sku}}. Reserved page variables ({{page}} {{pages}} {{date}} {{time}} {{datetime}} {{title}}) are not consumed here. Mutually exclusive with dataFile.
dataFilestringPath (relative to the spec) to an external record used as the data binding context when no inline data is given. JSON, or CSV when the path ends in .csv - a CSV's first line names the fields, one data row binds as the record itself and two or more bind as an array.
declarationGatestringHow the WCAG gate reacts when this document contradicts a WCAG level it declares in declarations. "warn" (the default whenever a WCAG declaration is present) reports every finding on stderr and still writes the file - the claim is the author's. "strict" refuses to write the file at all, so no PDF can ship carrying a claim the engine just contradicted. "off" skips the checking. A document that declares no WCAG level is never gated. The gate decides only what a layout model can decide - alt-text presence (not accuracy), structure and table headers, text contrast, title, link purpose, heading order and language; a clean run means nothing checkable contradicts the claim, NOT that the document is WCAG conformant. One of: off, warn, strict.
declarationsarray of object | stringPDF Declarations (PDF Association): machine-readable claims, written to the XMP pdfd:declarations bag, that this document conforms to a standard PDF has no identifier of its own for - WCAG 2.1/2.2 at level A/AA/AAA, Well-Tagged PDF, ISO/TS 32005, or a profile you define. PDF/A, PDF/UA and PDF/X have their own identifiers and are declared with pdfa, pdfua and pdfx instead. A declaration is a CLAIM, not a validation result, and it is yours: the engine writes exactly what you list here, and never adds a declaration or fills in a claimant on your behalf. An entry may be a bare alias string as shorthand for { "conformsTo": ... }.
defaultStylestyleDocument-wide default style - the base every block inherits before its own style applies.
fallbackFontstringCJK fallback font family for the engine's auto-generated blocks (toc / lof / glossary / index). Those blocks flatten source text (headings, captions, terms) into one block font; when it is Latin, CJK characters render as blank boxes. Set a CJK-capable family here and each wide/CJK segment is drawn in it while the rest stays in the block font. It also reaches body text: any character the run's own face does not have (an emoji, a script the face lacks) is drawn in this family, so an emoji face such as NotoColorEmoji here makes emoji typed in ordinary text print in colour. A character neither face has still prints as .notdef, and one the fallback face lacks is reported. Empty = off.
featuresarray of stringThe document-wide OpenType feature set - what every run is set with unless its own style names a different one. The same vocabulary as the per-style features key, and the general form of ligatures above (which is folded in as liga). Use this for a decision that really is document-wide, such as oldstyle figures throughout a book, and the style key for the exceptions.
fontDirsarray of stringExtra font FOLDERS this document may name faces from, searched before the installed ones, so a spec can travel with a face that is not installed on the rendering machine. Relative paths resolve against the spec file's own directory. Folders, not files: the same list is given to the engine's font index and to the PDF library that embeds the face, and the library takes directories only. Under -batch the union over every spec in the job list is used, and a book's chapters contribute theirs; on the apDocLib server a document's folders reach the resolver but not the embedder, which is fixed at startup by the server's own -fontdir.
hangingPunctuationbooleanCJK hanging punctuation (ぶら下げ): a trailing ideographic comma/full stop (、。) or its full-width Latin form (,.) hangs past the line edge into the margin instead of wrapping to the next line.
hyphenatebooleanHyphenate Latin words at line ends (Knuth/Liang patterns; en, fr, de and es are embedded). Each word divides by the rules of ITS OWN language -- the nearest lang on the run, the block or the document -- so a quoted German passage inside an English report breaks as German. A language with no embedded pattern set is left undivided rather than guessed at in English.
imagesobjectOutput policy for placed raster images, and a low-resolution report (minPpi).
imposeobjectHow a print copy of this document is laid out on sheets: a saddle-stitched booklet, two pages to a sheet side, for sheets printed on both sides, folded once and stapled on the fold. The key changes nothing in the document itself. The print copy is a second PDF, made from the first one's pages only when one is asked for (pagewrightapp spec.json out.pdf -impose booklet.pdf, or Export Booklet PDF in PageWright Studio's File menu). It is untagged and claims no conformance, because a sheet has no reading order: the reading PDF stays the accessible one. Its /Subject says how to print it, and its viewer preferences preset two-sided, actual-size printing on the sheet it was laid out for. A right binding mirrors every sheet. Every render of a document that carries the key says what its page count costs the booklet: a count that is not a multiple of four is padded with blank pages.
kashidabooleanfalseKashida (tatweel) justification for Arabic: a JUSTIFIED Arabic line spends its slack elongating the connections between letters - inserting the face's own tatweel glyph (U+0640) at the joints where a letter joins onward - and only what the tatweels cannot absorb goes to the word gaps. That is what Arabic typography does; Latin justification applied to Arabic leaves the words floating in holes. Off by default, like every other typographic switch here, because turning it on changes the ink on every justified Arabic line. Needs an EMBEDDED face that shaped through its GSUB (see the Arabic samples) - a run drawn from Unicode presentation forms has no joints this can reach - and has no effect on any other script, or on Arabic that is ragged rather than justified. The quantum is coarse (a tatweel is about 0.195 em), so the line still ends exactly on the margin by handing the remainder back to the word gaps. The engine spreads the elongation evenly across the line; it does not implement the priority order classical Arabic typography uses among candidate joints.
kerningbooleanApply pair kerning: tightens pairs such as AV, To, Wa, F. and P. The built-in Helvetica kerns from the canonical Adobe AFM tables; any other embedded font kerns from its own OpenType kern and GPOS pair tables, located by name in the system font folders - including faces housed inside TrueType collections (.ttc). Measured and drawn alike, so justification and line breaks stay exact. A font whose file cannot be found simply renders unkerned.
keywordsstring/Info Keywords + XMP pdf:Keywords.
langstringDocument language, BCP-47 (e.g. "en-US"). Default when tagged, and the hyphenation language for text that declares none of its own.
ligaturesbooleanfalseApply the embedded face's OpenType standard ligatures (the liga feature): fi, fl, ff, ffi and ffl are drawn as the single glyph the type designer cut for them instead of as colliding letters. Off by default, because turning it on reflows every line that gains a ligature and that is the author's call. Measured and drawn from the same glyphs, so justification and line breaks stay exact - a ligature is narrower than the letters it replaces. Base-14 faces carry no GSUB and are unaffected, as is any embedded face whose liga is absent, so a document with mixed faces is safe. The letters remain the stored text: a ligature glyph declares the characters it stands for through /ToUnicode, so copy, search and a screen reader all still see "office". Shorthand for "features": ["liga"]; setting both is safe, since this key can only add.
linkStyleboolean | objectMake links LOOK clickable. A PDF link annotation is invisible by default, so without this nothing distinguishes linked text and a reader can only find it by hovering. true uses the defaults (a conventional link blue, underlined); an object overrides them. Only fills what a run left unset - a run that sets its own color or underline keeps it. Off by default: enabling it would restyle every existing document.
masterstringThe document-wide DEFAULT master page (a key of "masters"). Applied to every section - and the implicit top-level section - that names no master of its own.
mastersobjectMaster pages: named, reusable page templates. Each master is an ordinary pageSetup object (size/margins/columns/header/footer/numbering/background/watermark/template/bleed+marks) PLUS an optional "frames" array of positioned background blocks stamped on every page that uses the master, as page artifacts. A master's decoration is merged into each using section's pageSetup (doc default -> master -> section overrides).
metaobjectOptional metadata object; its fields override the top-level ones. One field lives only here: rights, a copyright or licence notice written to the XMP as dc:rights.
namedDestinationsbooleantruePublish every block "id" as a PDF named destination (catalog /Names /Dests), so guide.pdf#nameddest=chapter3 opens at that block and other tools can deep-link into the document. Additive and invisible, so it is on by default; set false to omit the name tree.
notesobjectFootnote / endnote streams: the marker glyph style and, for footnotes, the counter-restart scope. Absent => numeric markers with footnotes counted continuously through the document (unchanged from before).
numberingobjectAutomatic content numbering. Absent => nothing is numbered.
opticalMarginsbooleanLatin optical margins: trailing punctuation (. , ; : - - - and quotes) hangs slightly past the line edge so the body's optical right edge stays flush (most visible in justified text).
optimalBreaksbooleanKnuth-Plass optimal line breaking for justified paragraphs: choose breakpoints that minimize the total badness across the whole paragraph (more even spacing, fewer rivers) instead of the default greedy line-by-line fit.
outputProfilestring | objectThe CMYK press this document is printed on, as an ICC output profile file - in place of the built-in U.S. Web Coated (SWOP) v2. In cmyk color mode (colorMode "cmyk", or pdfx) every RGB and gray color, raster image, gradient stop and emoji is converted to it, and a PDF/X or PDF/A file embeds it as the OutputIntent's DestOutputProfile, identified as "Custom" with the profile's own description as Info unless you give a registered identifier. The path resolves against the spec file's directory. The profile must be a CMYK printer ('prtr') profile, ICC version 2 to 4, that the color engine can convert to; anything else is refused with one notice and the document converts to and declares the built-in SWOP. Ignored, with a notice, in rgb mode. Colors already in CMYK (cmyk colors, spot alternates, CMYK images) are not converted. Either a path string or an object.
overprintbooleanSpot-ink (separation) fills and strokes overprint instead of knocking out (/OP /op true, /OPM 1). For print workflows where a spot accent must not knock a hole in the underlying process color.
pageSetuppageSetupPage geometry and print setup for the document: size, orientation, margins, columns, running heads and the print-production extras. A section can override it.
pagesarray of objectCANVAS MODE: the document is a stack of DESIGNED pages rather than a flow. Each entry is one page and the elements on it, and every element needs a "position". A canvas page IS a section that makes exactly one page, so page size, margins, masters, running bands, page labels, links and tagging all work here unchanged. Present => canvas mode is on, which adds the two things a design needs and a flow does not: elements paint in "z" order and each occupies ONE z slot, so a shape declared after a text element draws OVER it (in a flow it cannot -- all text is written after all graphics); and the structure tree keeps the DOCUMENT order, so the reading order is the one you wrote while the paint order is the one you designed. Use "blocks" or "sections" for a flowing document.
paletteobjectDocument color palette. The one edit that re-skins every chart, bar and table zebra in the document. Every field is optional and every consumer keeps the constant it has always used when a field is absent, so a document with no palette renders exactly as it did before this existed. The order is always: a color written on the block wins, then the palette, then the engine default.
pdfastring | booleanPDF/A archival conformance: "2b"|"2u"|"2a" (PDF/A-2), "3b"|"3u"|"3a" (PDF/A-3, which also permits embedded files), or "4"|"4e"|"4f" (PDF/A-4, the PDF 2.0 revision; "4f" permits embedded files), case-insensitive; or true for a smart default (2a when tagged, else 2u). An "a" level ("2a", "3a") forces tagged. Adding attachments promotes a PDF/A-2 level to the PDF/A-3 level with the same letter ("2u" becomes "3u"), and "4" or "4e" to "4f".
pdfuaboolean | stringPDF/UA target. true (or 1) = PDF/UA-1 (same as "tagged"); 2 = PDF/UA-2 (ISO 14289-2 on PDF 2.0: namespaced structure, structure destinations, pdfuaid:part 2 + rev 2024). Any positive value forces tagging. FALSE IS NOT THE SAME AS ABSENT: it keeps the structure tree but writes no pdfuaid claim at all, for a document that is tagged without having been verified conformant. A structure tree is necessary for PDF/UA and nowhere near sufficient -- the fonts must all be embedded, no tagged content may sit inside an artifact -- so use this rather than ship a claim nothing has checked. One of: 1, 2.
pdfxstring | booleanPDF/X print-production conformance. "x-4" (case-insensitive; any spelling containing 4) or true badges the file as PDF/X-4 (ISO 15930-7): it forces cmyk color mode, embeds all fonts, writes a GTS_PDFX output intent (U.S. Web Coated SWOP v2, or the document's outputProfile) with the pdfxid metadata, gives each page a TrimBox, sets PDF 1.6, and skips encryption. Only X-4 is supported - it permits live transparency and layers, so nothing is flattened.
producerstring/Info Producer + XMP pdf:Producer (default "Appligent PageWright").
recordIndexinteger1Which record of "records" this document binds (1-based). Clamped to the record count. Ignored when the CLI supplies -data.
recordsstring | arrayA record SET for mail merge: a path (relative to the spec) to a JSON array or a CSV whose header row names the fields, or an inline array of record objects. The document renders bound to one record (see recordIndex); the CLI's -data renders one document per record and overrides this.
renderingIntentrenderingIntentHow colors outside the destination's gamut are mapped, for the whole document. "relative" (relative colorimetric, the default): colors the press can print come through exactly and the rest are clipped to the nearest printable one -- right for flat brand colors, rules and charts. "perceptual": the whole gamut is compressed so a photograph's gradations survive, at the cost of shifting colors that were already printable. "saturation": vividness over accuracy (business graphics). "absolute": relative colorimetric that also simulates the paper's white (proofing). In cmyk color mode (colorMode "cmyk", or pdfx) every RGB-to-CMYK conversion runs at it -- vector colors, images, gradients, emoji, placed PDF pages -- into SWOP or outputProfile. In every mode a value other than "relative" is also written into each page (the PDF ri operator), so a RIP or viewer converting the file onward uses it too. An image block may name its own. The PDF names ("Perceptual", "RelativeColorimetric", "Saturation", "AbsoluteColorimetric") are accepted too.
restartbooleanBook model: restart page numbering at 1 for every chapter (per-chapter folios). Default false = continuous pagination across the whole book. A chapter's own restart overrides this.
sectionTagsstringautoHow much <Sect> GROUPING the structure tree carries. "auto" (default, and what this engine has always emitted) opens a nested <Sect> for every heading and wraps each document section in one more. "pages" keeps only the per-section wrapper. "none" emits no <Sect> at all, so headings and content sit directly under <Document>.

A FLATTER TREE IS OFTEN THE BETTER ONE. ISO 32000-1 14.8.4.3.5 calls a Sect-grouped tree "strongly structured" and PDF/UA permits either, so no validator has an opinion -- all three modes are veraPDF UA-1 clean. What decides it is how assistive technology reads the result, and professional remediators commonly strip <Sect> for exactly that reason: every level is one more container between the reader and the heading. Measured on this engine's marketing brochure: "auto" 63 Sect elements and 12 levels at its deepest, "none" 0 and 10. The ink is identical in every mode -- this changes structure only. An unknown value refuses the document. One of: auto, pages, none.
sectionsarray of objectExplicit sections. Each inherits the document pageSetup, then applies its own overrides.
securityobjectStandard PDF security (encryption + permissions) applied via aplib APEncrypt. Requires an owner (permissions) password; an empty/absent user password lets the file open with no password. Mutually exclusive with PDF/A.
storiesarray of object | objectFree-standing block lists poured into the thread of "frame" blocks that reference them by id (article threading). Either an array of {id, blocks:[...]} or an object { id: {blocks:[...]} | [...] }. Frames in one thread should share a column width.
stylesobjectNamed stylesheet: each entry is a style object (optionally "basedOn" another entry). Any "style" below may reference an entry by name.
subjectstring/Info Subject + XMP dc:description.
taggedbooleanEmit a tagged PDF (logical structure tree). Also writes a PDF/UA-1 claim into the XMP unless "pdfua" is false. How much <Sect> GROUPING that tree carries is a separate choice -- see sectionTags.
titlestringDocument title: /Info Title, XMP dc:title, and the {{title}} variable.
variablesobjectVariable catalog: named values referenced with {{var.name}} anywhere the data binding reaches (text runs, links, image/pdf src, running-band templates, metadata). Defined once, edited in one place; static and page-independent, unlike the render-time {{page}}/{{section}} tokens. Folded into the binding context under var, so a real data record still wins on its own keys. Nested objects are allowed ({{var.company.name}}).
viewerobjectInitial view: catalog /PageLayout, /PageMode and the /OpenAction destination.

Worked document examples

Complete top-level objects, each one turning on a single document-level capability. Paste any of them into a file and render it as it stands.

Sections, each with its own page setup and folio style

{
  "title": "Report",
  "tagged": true,
  "sections": [
    {
      "size": "Letter",
      "numbering": {
        "format": "lower-roman"
      },
      "blocks": [
        {
          "type": "heading",
          "level": 1,
          "text": "Preface"
        }
      ]
    },
    {
      "size": "Letter",
      "columns": 2,
      "restartNumbering": true,
      "numbering": {
        "format": "arabic"
      },
      "blocks": [
        {
          "type": "heading",
          "level": 1,
          "text": "Body"
        }
      ]
    }
  ]
}

A tagged, accessible document (PDF/UA-1)

{
  "title": "Accessible report",
  "tagged": true,
  "lang": "en-US",
  "blocks": [
    {
      "type": "heading",
      "level": 1,
      "text": "Summary"
    },
    {
      "type": "image",
      "src": "chart.png",
      "alt": "Revenue rose nine per cent."
    }
  ]
}

Archival PDF/A-2a with an sRGB output intent

{
  "title": "Archive",
  "tagged": true,
  "pdfa": "2A",
  "lang": "en-US",
  "blocks": [
    {
      "type": "paragraph",
      "text": "Self-contained by construction."
    }
  ]
}

Press-ready PDF/X-4 in DeviceCMYK

{
  "title": "Press",
  "pdfx": "x-4",
  "pageSetup": {
    "size": "Letter",
    "bleed": 9,
    "marks": true
  },
  "blocks": [
    {
      "type": "paragraph",
      "text": "Every color converts to CMYK; fonts embed; transparency stays live."
    }
  ]
}

PDF/UA-2 with deep-linkable named destinations

{
  "title": "UA-2",
  "pdfua": 2,
  "tagged": true,
  "lang": "en-US",
  "namedDestinations": true,
  "blocks": [
    {
      "type": "heading",
      "level": 1,
      "id": "intro",
      "text": "Introduction"
    }
  ]
}

Viewer folios from section numbering (a /PageLabels tree)

{
  "title": "Folios",
  "sections": [
    {
      "numbering": {
        "format": "lower-roman"
      },
      "blocks": [
        {
          "type": "heading",
          "level": 1,
          "text": "Preface"
        }
      ]
    },
    {
      "restartNumbering": true,
      "numbering": {
        "format": "arabic"
      },
      "blocks": [
        {
          "type": "heading",
          "level": 1,
          "text": "Chapter 1"
        }
      ]
    }
  ]
}

Viewer preferences: open on the outline, fit the width

{
  "title": "Prefs",
  "viewer": {
    "layout": "single",
    "mode": "outlines",
    "fit": "fitWidth"
  },
  "blocks": [
    {
      "type": "paragraph",
      "text": "Honored by every conforming reader."
    }
  ]
}

Endnotes gathered at the document end, with symbol footnote markers

{
  "title": "Notes",
  "tagged": true,
  "lang": "en-US",
  "notes": {
    "footnoteMarker": "symbols",
    "footnoteRestart": "section"
  },
  "blocks": [
    {
      "type": "paragraph",
      "runs": [
        {
          "text": "The rocket equation",
          "endnote": "Tsiolkovsky, 1903."
        },
        {
          "text": " sets the ceiling on performance."
        }
      ]
    },
    {
      "type": "endnotes",
      "title": "Notes"
    }
  ]
}

A. Every element at a glance

Every type name the engine accepts, and where it is documented.

ElementAliasesGroupSize
paragraph - Text32 keys
heading - Text36 keys
list - Text29 keys
precodeText33 keys
container - Structure and spacing27 keys
columns - Structure and spacing24 keys
cardGridcardgridStructure and spacing26 keys
spacer - Structure and spacing22 keys
rule - Structure and spacing24 keys
pagebreakpage-breakStructure and spacing2 keys
table - Tables42 keys
image - Pictures and drawings45 keys
shape - Pictures and drawings39 keys
svg - Pictures and drawings29 keys
chart - Pictures and drawings60 keys
barcode - Pictures and drawings30 keys
mathequationPictures and drawings31 keys
mermaiddiagramPictures and drawings28 keys
pdf - Placing existing PDFs33 keys
pdf-contact-sheetcontact-sheetPlacing existing PDFs29 keys
toclof, lot, lol, locNavigation and apparatus30 keys
toclof, lot, lol, locNavigation and apparatus30 keys
index - Navigation and apparatus30 keys
glossary - Navigation and apparatus24 keys
bibliographyreferencesNavigation and apparatus6 keys
endnotes - Navigation and apparatus5 keys
calloutadmonition, noteNotes and asides30 keys
field - Forms25 keys
repeat - Data-driven documents24 keys
if - Data-driven documents24 keys
include - Data-driven documents23 keys
marker - Data-driven documents4 keys
frame - Story threading28 keys
bar - Meters29 keys

B. Enumerations

Every fixed set of values in the language, gathered in one place.

KeyAccepted values
aligncenter, decimal, left, right
aligncenter, end, justify, left, right, start
aligncenter, end, start
aligncenter, left, right
angleUnitdeg, rad
appendixFormat1, A, ALPHA, I, ROMAN, a, alpha, arabic, chinese, chinese-traditional, cjk-decimal, decimal, hanzi, hanzi-traditional, hanzi-tw, i, lower-alpha, lower-roman, roman, upper-alpha, upper-roman
arrowboth, end, none, start
asfull, label, number, page, title
axisleft, right, secondary, y2
bindingl2r, left, ltr, r2l, right, rtl
blanksbeforeLast, end
blendcolor, colorBurn, colorDodge, darken, difference, exclusion, hardLight, hue, lighten, luminosity, multiply, normal, overlay, saturation, screen, softLight
blockAligncenter, left, right
boxAligncenter, left, right
cellVAlignbottom, center, middle, top
chainFormat1, A, ALPHA, I, ROMAN, a, alpha, arabic, chinese, chinese-traditional, cjk-decimal, decimal, hanzi, hanzi-traditional, hanzi-tw, i, lower-alpha, lower-roman, roman, upper-alpha, upper-roman
chartTypearea, bar, box, boxplot, bubble, candlestick, combo, donut, doughnut, funnel, gauge, heatmap, line, ohlc, pie, polar, radar, sankey, scatter, spider, treemap, waterfall
childOrigincontainer, page
cipheraes, rc4
code128, 39, 93, auspost, ausredirect, ausreply, ausroute, azrune, aztec, bc412, c25iata, c25ind, c25inter, c25logic, c25matrix, c25standard, cepnet, channel, codabar, codablockf, code11, code128, code128ab, code128b, code16k, code32, code39, code39ext, code3of9, code49, code93, codeone, daft, databar, databarexpanded, databarlimited, datamatrix, dbarexp, dbarexpcc, dbarexpstk, dbarexpstkcc, dbarltd, dbarltdcc, dbaromn, dbaromncc, dbaromnstk, dbaromnstkcc, dbarstk, dbarstkcc, dotcode, dpd, dpident, dpleit, dxfilmedge, ean, ean128, ean13, ean14, ean8, eanx, eanxcc, eanxchk, excode39, fim, flat, gridmatrix, gs1128, gs1128cc, hanxin, hibc128, hibc39, hibcaztec, hibcblockf, hibcdm, hibcmicpdf, hibcpdf, hibcqr, i2of5, intelligentmail, interleaved2of5, isbn, isbnx, itf14, japanpost, kix, koreapost, logmars, mailmark, mailmark2d, mailmark4s, maxicode, micropdf417, microqr, msi, msiplessey, nve18, onecode, pdf417, pdf417comp, pharma, pharmatwo, planet, plessey, postnet, pzn, qr, qrcode, rm4scc, rmqr, royalmail, rss14, telepen, telepennum, ultra, ultracode, upca, upcacc, upcachk, upce, upcecc, upcechk, upnqr, upus10, uspsimail, vin
collationpinyin, strokes, unicode
colorModeCMYK, RGB, cmyk, rgb
colorProfilesembed, ignore
declarationGateoff, strict, warn
directionauto, l2r, left-to-right, ltr, r2l, right-to-left, rtl
duplexlongEdge, shortEdge
edgebottom, left, right, top
endnoteMarkernumeric, symbols
extentblock, line
fieldbutton, check, checkbox, choice, combo, list, radio, sig, signature, text
fitactual, fit, fitHeight, fitWidth
fitcontain, cover, fill, none
fitcontain, cover, stretch
floatcenter, left, right
footnoteMarkernumeric, symbols
footnoteRestartdocument, section
format1, A, ALPHA, I, ROMAN, a, alpha, arabic, chinese, chinese-traditional, cjk-decimal, decimal, hanzi, hanzi-traditional, hanzi-tw, i, lower-alpha, lower-roman, roman, upper-alpha, upper-roman
formatauto, flate, jpeg, png
groupBlendcolor, colorBurn, colorDodge, darken, difference, exclusion, hardLight, hue, lighten, luminosity, multiply, normal, overlay, saturation, screen, softLight
justifycenter, left, right
keyLength128, 256, 40
labelPosafter, before, none
layerover, under
layoutoneColumn, single, twoColumnLeft, twoColumnRight, twoPageLeft, twoPageRight
leaderdash, dot, dots, hyphen, none, underline, underscore
legendPosbl, bottom, br, left, right, tl, top, tr
lineCapbutt, round, square
lineJoinbevel, miter, round
marker1, A, I, a, bullet, dash, decimal, i, lower-alpha, lower-roman, upper-alpha, upper-roman
modeattachments, fullscreen, none, outlines, thumbs
modedual, outline, solid
onall, first, rest
orientationlandscape, portrait
pagesall, even, first, odd, rest
pdfua1, 2
positionangle, bottom, center, diagonal, diagonal-down, top
relationshipAlternative, Data, EncryptedPayload, FormData, Schema, Source, Supplement, Unspecified
roleaside, blockquote, div, none, section
rotate0, 270, 90
scaleactual, fit
scopedocument, group, section
sectionTagsauto, none, pages
shapecircle, ellipse, line, path, polygon, rect
shapecircle, ellipse, path, polygon, rect
sheet11x17, 12x18, A3, A4, Ledger, Letter, Tabloid, auto
side"", both, recto, verso
sideinside, left, outside, right
sideHeadSideinside, left, outside, right
sidenoteSideinside, left, outside, right
startOnfromFile, nextAvailable, recto, verso
styleauthor-year, numeric
tableAligncenter, left, right
textWraparound, none, topAndBottom
tobelow, below-line, bottom-of-column, bottom-of-page, col-bottom, col-top, column-bottom, column-top, in-flow, margin, outside, outside-column, outside-text-frame, page-bottom, page-top, top-of-column, top-of-page
typeadmonition, callout, note
typearea, bar, line, scatter
typebackground, echo, glow, lift, neon, none, shadow
typebibliography, references
typecardGrid, cardgrid
typecode, pre
typecontact-sheet, pdf-contact-sheet
typediagram, mermaid
typeequation, math
typegradient, image, none
typelinear, radial
typeloc, lof, lol, lot, toc
typepage-break, pagebreak
underlinedouble, none, numeric, single
valignbottom, center, middle, top
valignbottom, center, stretch, top
variantcaution, danger, important, info, note, success, tip, warning
verticalAlignbottom, center, justify, middle, top
verticalAlignbottom, center, middle, top
weightbold, normal, regular
writingModehorizontal, horizontal-tb, inherit, lr-tb, tategaki, tb-lr, tb-rl, vertical, vertical-lr, vertical-rl, yokogaki, 横排, 直排, 縦書き
writingModehorizontal, horizontal-tb, lr-tb, tategaki, tb-lr, tb-rl, vertical, vertical-lr, vertical-rl, yokogaki, 横排, 直排, 縦書き

Glossary

block
One element in the document flow — a paragraph, table, image and so on. The unit the engine lays out and paginates.

run
A styled piece of a paragraph or heading. One paragraph is built from any number of runs, each with its own style, link or footnote.

flow
The sequence in which blocks are placed down the page, and onward to the next page or column when the current one fills.

section
A stretch of the document with its own page setup — size, orientation, margins, columns and folio style.

spec
The JSON file describing a document.

tagged PDF
A PDF carrying a logical structure tree, so assistive technology can read it in a meaningful order.

counting pass
A layout pass that measures without drawing, so the engine can resolve page numbers that depend on the pagination they change.

Back to top