soli-pdf

The complete tour

A twelve-page document that is its own reference: every element and option, each shown with its JSON.

Text and flow, graphics and codes, tables, charts and layout options, with a clickable table of contents whose page numbers come from the renderer.

Every element Clickable TOC #PAGE_OF tokens 12 pages
Page 1
Page 1 of 12
Page 2
Page 2 of 12
Page 3
Page 3 of 12
Page 4
Page 4 of 12
Page 5
Page 5 of 12
Page 6
Page 6 of 12
Page 7
Page 7 of 12
Page 8
Page 8 of 12
Page 9
Page 9 of 12
Page 10
Page 10 of 12
Page 11
Page 11 of 12
Page 12
Page 12 of 12
render_features.rb ruby
require 'json'
require 'soli/pdf'

template = JSON.parse(File.read('features.template.json'))
data     = JSON.parse(File.read('features.data.json'))

pdf = Soli::PDF.render(
  template: template,
  data: data,
  title: 'The soli-pdf template reference'
)
File.binwrite('features.pdf', pdf)

Run it yourself

  1. Add the gem to your Gemfile — see Installation.
  2. Download features.template.json and features.data.json.
  3. Run ruby render_features.rb.
features.template.json json
{
  "fonts": [
    "TitilliumWeb"
  ],
  "options": {
    "page": "a4",
    "margins": {
      "top": 42,
      "right": 50,
      "bottom": 46,
      "left": 50
    },
    "header_height": 34,
    "backgroundImage": {
      "src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 595 842'><defs><linearGradient id='g' x1='0' y1='0' x2='0' y2='1'><stop offset='0' stop-color='%23ecfeff'/><stop offset='0.4' stop-color='%23ffffff'/><stop offset='1' stop-color='%23f8fafc'/></linearGradient></defs><rect width='595' height='842' fill='url(%23g)'/><g opacity='0.05' fill='none' stroke='%230F766E' stroke-width='10'><circle cx='560' cy='788' r='150'/><circle cx='560' cy='788' r='96'/><circle cx='560' cy='788' r='44'/></g></svg>",
      "pages": "first"
    }
  },
  "header": [
    {
      "type": "image",
      "width": 15,
      "value": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'><circle cx='12' cy='12' r='10' fill='%230F766E'/><path d='M7 13l3 3 7-8' stroke='white' stroke-width='2.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>"
    },
    {
      "type": "move",
      "x": 0,
      "y": 1
    },
    {
      "type": "paragraph",
      "value": "the soli-pdf template reference",
      "options": {
        "alignment": "right",
        "fontSize": 8.5,
        "color": "94a3b8"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.6
    }
  ],
  "footer": [
    {
      "type": "paragraph",
      "value": "soli-pdf  ·  page #PAGE# of #TOTAL_PAGE#",
      "options": {
        "alignment": "center",
        "fontSize": 8,
        "color": "94a3b8"
      }
    }
  ],
  "content": [
    {
      "type": "rect",
      "width": 495,
      "height": 6,
      "fill": "f59e0b",
      "radius": 3
    },
    {
      "type": "move",
      "x": 0,
      "y": 6
    },
    {
      "type": "rect",
      "width": 495,
      "height": 150,
      "fill": "0F766E",
      "radius": 14
    },
    {
      "type": "move",
      "x": 30,
      "y": 30
    },
    {
      "type": "image",
      "width": 52,
      "value": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'><circle cx='12' cy='12' r='10' fill='white'/><path d='M7 13l3 3 7-8' stroke='%230F766E' stroke-width='2.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>"
    },
    {
      "type": "move",
      "x": 72,
      "y": 6
    },
    {
      "type": "paragraph",
      "value": "The soli-pdf template",
      "options": {
        "fontSize": 25,
        "fontWeight": "bold",
        "color": "ffffff",
        "bookmark": "Cover",
        "anchor": "sec-cover"
      }
    },
    {
      "type": "paragraph",
      "value": "A complete, worked reference — rendered by the engine it documents.",
      "options": {
        "fontSize": 10.5,
        "color": "ccfbf1",
        "italic": true
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 20
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "•  ",
          "color": "f59e0b"
        },
        {
          "text": "every element & option",
          "color": "ffffff",
          "fontSize": 9.5
        },
        {
          "text": "      •  ",
          "color": "f59e0b"
        },
        {
          "text": "each shown with its JSON",
          "color": "ffffff",
          "fontSize": 9.5
        },
        {
          "text": "      •  ",
          "color": "f59e0b"
        },
        {
          "text": "one template, one call",
          "color": "ffffff",
          "fontSize": 9.5
        }
      ],
      "options": {
        "fontSize": 9.5
      }
    },
    {
      "type": "move",
      "x": -102,
      "y": 56
    },
    {
      "type": "paragraph",
      "value": "This document is itself a soli-pdf template. Everything you see — the cover band, the tables, the charts, the QR code — was produced by a single JSON template passed to one pdf_render() call, with no browser and no HTML. Each feature below is shown twice: the JSON that declares it, and the result it renders to.",
      "options": {
        "fontSize": 10.5,
        "color": "334155",
        "lineHeight": 1.5,
        "alignment": "justify"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 26
    },
    {
      "type": "paragraph",
      "value": "CONTENTS",
      "options": {
        "fontSize": 10,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 6
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 12
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "1   ",
          "fontWeight": "bold",
          "color": "0F766E",
          "mono": true
        },
        {
          "text": "Text & flow",
          "fontWeight": "bold",
          "color": "0f172a"
        },
        {
          "text": "   —   paragraphs, spans, lists",
          "color": "64748b",
          "fontSize": 9
        }
      ],
      "options": {
        "fontSize": 11.5,
        "linkTo": "sec-text"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": -13.8
    },
    {
      "type": "paragraph",
      "value": "p. #PAGE_OF:sec-text#",
      "options": {
        "fontSize": 9.5,
        "color": "2563eb",
        "alignment": "right",
        "linkTo": "sec-text"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 13
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "2   ",
          "fontWeight": "bold",
          "color": "0F766E",
          "mono": true
        },
        {
          "text": "Graphics & codes",
          "fontWeight": "bold",
          "color": "0f172a"
        },
        {
          "text": "   —   rects, strokes, images, QR, barcodes",
          "color": "64748b",
          "fontSize": 9
        }
      ],
      "options": {
        "fontSize": 11.5,
        "linkTo": "sec-graphics"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": -13.8
    },
    {
      "type": "paragraph",
      "value": "p. #PAGE_OF:sec-graphics#",
      "options": {
        "fontSize": 9.5,
        "color": "2563eb",
        "alignment": "right",
        "linkTo": "sec-graphics"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 13
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "3   ",
          "fontWeight": "bold",
          "color": "0F766E",
          "mono": true
        },
        {
          "text": "Tables",
          "fontWeight": "bold",
          "color": "0f172a"
        },
        {
          "text": "   —   data binding, merges, rich cells",
          "color": "64748b",
          "fontSize": 9
        }
      ],
      "options": {
        "fontSize": 11.5,
        "linkTo": "sec-tables"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": -13.8
    },
    {
      "type": "paragraph",
      "value": "p. #PAGE_OF:sec-tables#",
      "options": {
        "fontSize": 9.5,
        "color": "2563eb",
        "alignment": "right",
        "linkTo": "sec-tables"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 13
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "4   ",
          "fontWeight": "bold",
          "color": "0F766E",
          "mono": true
        },
        {
          "text": "Charts",
          "fontWeight": "bold",
          "color": "0f172a"
        },
        {
          "text": "   —   bar, line, pie, donut",
          "color": "64748b",
          "fontSize": 9
        }
      ],
      "options": {
        "fontSize": 11.5,
        "linkTo": "sec-charts"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": -13.8
    },
    {
      "type": "paragraph",
      "value": "p. #PAGE_OF:sec-charts#",
      "options": {
        "fontSize": 9.5,
        "color": "2563eb",
        "alignment": "right",
        "linkTo": "sec-charts"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 13
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "5   ",
          "fontWeight": "bold",
          "color": "0F766E",
          "mono": true
        },
        {
          "text": "Layout & render options",
          "fontWeight": "bold",
          "color": "0f172a"
        },
        {
          "text": "   —   columns, branches, the render call",
          "color": "64748b",
          "fontSize": 9
        }
      ],
      "options": {
        "fontSize": 11.5,
        "linkTo": "sec-layout"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": -13.8
    },
    {
      "type": "paragraph",
      "value": "p. #PAGE_OF:sec-layout#",
      "options": {
        "fontSize": 9.5,
        "color": "2563eb",
        "alignment": "right",
        "linkTo": "sec-layout"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 13
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "solilang.com/docs/builtins/pdf  ·  edit this template live in the playground",
      "options": {
        "fontSize": 8.5,
        "color": "94a3b8",
        "italic": true,
        "link": "https://solilang.com/docs/builtins/pdf"
      }
    },
    {
      "type": "page_break"
    },
    {
      "type": "rect",
      "width": 30,
      "height": 30,
      "fill": "0F766E",
      "radius": 8
    },
    {
      "type": "move",
      "x": 11,
      "y": 6
    },
    {
      "type": "paragraph",
      "value": "1",
      "options": {
        "fontSize": 13,
        "fontWeight": "bold",
        "color": "ffffff"
      }
    },
    {
      "type": "move",
      "x": 30,
      "y": -19
    },
    {
      "type": "paragraph",
      "value": "Text & flow",
      "options": {
        "fontSize": 20,
        "fontWeight": "bold",
        "color": "0f172a",
        "bookmark": "Text & flow",
        "anchor": "sec-text"
      }
    },
    {
      "type": "move",
      "x": -41,
      "y": 12
    },
    {
      "type": "paragraph",
      "value": "Paragraphs, inline rich text, and lists — the words on the page.",
      "options": {
        "fontSize": 10,
        "color": "64748b",
        "italic": true
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 10
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 16
    },
    {
      "type": "paragraph",
      "value": "paragraph",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 167.35000000000002,
        "bookmark": "paragraph",
        "bookmarkLevel": 2,
        "anchor": "sec-para"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "The workhorse: a wrapped block of text. Options set size, weight, colour and alignment.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"paragraph\",\n  \"value\": \"Invoice $${invoice.number}\",\n  \"options\": { \"fontSize\": 16, \"fontWeight\": \"bold\", \"color\": \"0F766E\" } }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "paragraph",
      "value": "Invoice INV-2026-0042",
      "options": {
        "fontSize": 16,
        "fontWeight": "bold",
        "color": "0F766E"
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "alignment & justify",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 167.0,
        "bookmark": "alignment & justify",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "alignment is left · center · right · justify. justify spreads each full line to both margins by widening word gaps; the last line stays left.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 45.35,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 45.35,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"paragraph\", \"options\": { \"alignment\": \"justify\" },\n  \"value\": \"soli-pdf typesets text itself...\" }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "paragraph",
      "value": "soli-pdf typesets text itself — measuring, wrapping and justifying every line in-process, with no headless browser in sight. This paragraph is set justify, so both edges line up while the final line stays left.",
      "options": {
        "fontSize": 10,
        "color": "334155",
        "alignment": "justify",
        "lineHeight": 1.4
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "lineHeight & spacing",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 180.175,
        "bookmark": "lineHeight & spacing",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "lineHeight multiplies the leading (engine default 1.2); spacing adds points below the block, replacing a trailing move.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"paragraph\",\n  \"options\": { \"lineHeight\": 1.7 },\n  \"value\": \"Loose leading lets dense reference text breathe.\" }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "paragraph",
      "value": "Loose leading lets dense reference text breathe — this block is set at lineHeight 1.7, so the wrapped lines sit further apart than the 1.2 default.",
      "options": {
        "fontSize": 10,
        "color": "334155",
        "lineHeight": 1.7
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "spans — inline rich text",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 232.875,
        "bookmark": "spans — inline rich text",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "spans mix weight, size, colour, links, underline and strike within one wrapped flow; each span inherits the paragraph options it omits.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 111.22500000000001,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 111.22500000000001,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"paragraph\", \"spans\": [\n  { \"text\": \"Mix \" }, { \"text\": \"bold\", \"fontWeight\": \"bold\" },\n  { \"text\": \", a \", }, { \"text\": \"link\", \"link\": \"https://solilang.com\",\n    \"color\": \"2563eb\", \"underline\": true },\n  { \"text\": \", and a redline was \" },\n  { \"text\": \"99.00\", \"strike\": true, \"color\": \"b91c1c\" },\n  { \"text\": \" now 79.00\", \"fontWeight\": \"bold\", \"color\": \"0F766E\" } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "Mix "
        },
        {
          "text": "bold",
          "fontWeight": "bold"
        },
        {
          "text": ", "
        },
        {
          "text": "italic",
          "italic": true
        },
        {
          "text": ", "
        },
        {
          "text": "mono",
          "mono": true
        },
        {
          "text": ", a "
        },
        {
          "text": "link",
          "link": "https://solilang.com",
          "color": "2563eb",
          "underline": true
        },
        {
          "text": ", and a redline was "
        },
        {
          "text": "99.00",
          "strike": true,
          "color": "b91c1c"
        },
        {
          "text": " now "
        },
        {
          "text": "79.00 EUR",
          "fontWeight": "bold",
          "color": "0F766E"
        }
      ],
      "options": {
        "fontSize": 10.5
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "list — ordered · nested · custom marker",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 219.7,
        "bookmark": "list — ordered · nested · custom marker",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "ordered numbers from start; items may nest a list or carry spans; marker and indent tune the look.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 84.875,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 84.875,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"list\", \"ordered\": true, \"items\": [\n  \"Grind the beans\",\n  { \"text\": \"Brew\", \"list\": { \"items\": [\"Bloom 30 s\", \"Pour to 250 g\"] } },\n  { \"spans\": [ { \"text\": \"Enjoy \" },\n               { \"text\": \"responsibly\", \"italic\": true } ] } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "list",
      "ordered": true,
      "options": {
        "fontSize": 10,
        "color": "334155"
      },
      "spacing": 3,
      "items": [
        "Grind the beans",
        {
          "text": "Brew",
          "list": {
            "items": [
              "Bloom 30 s",
              "Pour to 250 g"
            ]
          }
        },
        {
          "spans": [
            {
              "text": "Enjoy "
            },
            {
              "text": "responsibly",
              "italic": true
            }
          ]
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "page_break"
    },
    {
      "type": "rect",
      "width": 30,
      "height": 30,
      "fill": "0F766E",
      "radius": 8
    },
    {
      "type": "move",
      "x": 11,
      "y": 6
    },
    {
      "type": "paragraph",
      "value": "2",
      "options": {
        "fontSize": 13,
        "fontWeight": "bold",
        "color": "ffffff"
      }
    },
    {
      "type": "move",
      "x": 30,
      "y": -19
    },
    {
      "type": "paragraph",
      "value": "Graphics & codes",
      "options": {
        "fontSize": 20,
        "fontWeight": "bold",
        "color": "0f172a",
        "bookmark": "Graphics & codes",
        "anchor": "sec-graphics"
      }
    },
    {
      "type": "move",
      "x": -41,
      "y": 12
    },
    {
      "type": "paragraph",
      "value": "Boxes, strokes, images, and machine-readable codes.",
      "options": {
        "fontSize": 10,
        "color": "64748b",
        "italic": true
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 10
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 16
    },
    {
      "type": "paragraph",
      "value": "move — the cursor primitive",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 97.825,
        "bookmark": "move — the cursor primitive",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "The layout primitive. +y is down, −y up, +x right. rect, line, ellipse, image, qr and barcode draw at the cursor without advancing it — move positions them (that is how these result rows are placed).",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 32.175,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 32.175,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"move\", \"x\": 20, \"y\": -8 }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "rect — panels & boxes",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 177.0,
        "bookmark": "rect — panels & boxes",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "A filled and/or stroked box. radius rounds the corners; dash strokes the border. Drawn at the cursor, never advancing it.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 45.35,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 45.35,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"rect\", \"width\": 495, \"height\": 44,\n  \"fill\": \"f0fdfa\", \"border\": \"99f6e4\", \"radius\": 10 }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "rect",
      "width": 240,
      "height": 44,
      "fill": "f0fdfa",
      "border": "99f6e4",
      "borderWidth": 0.9,
      "radius": 10
    },
    {
      "type": "move",
      "x": 14,
      "y": 15.0
    },
    {
      "type": "paragraph",
      "value": "rounded · filled · bordered",
      "options": {
        "fontSize": 9.5,
        "fontWeight": "bold",
        "color": "0F766E"
      }
    },
    {
      "type": "move",
      "x": -14,
      "y": 17.6
    },
    {
      "type": "move",
      "x": 255,
      "y": -44
    },
    {
      "type": "rect",
      "width": 240,
      "height": 44,
      "border": "94a3b8",
      "borderWidth": 1,
      "radius": 6,
      "dash": [
        5,
        3
      ]
    },
    {
      "type": "move",
      "x": 14,
      "y": 15.0
    },
    {
      "type": "paragraph",
      "value": "\"dash\": [5, 3]",
      "options": {
        "fontSize": 9.5,
        "fontWeight": "bold",
        "color": "64748b"
      }
    },
    {
      "type": "move",
      "x": -14,
      "y": 17.6
    },
    {
      "type": "move",
      "x": -255,
      "y": 0
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "ellipse · line · hr — strokes & dots",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 180.175,
        "bookmark": "ellipse · line · hr — strokes & dots",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "ellipse is a circle when rx == ry; line runs from the cursor to +(dx, dy); hr rules across the width (or an explicit width). line & hr take a dash too.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"ellipse\", \"rx\": 6, \"ry\": 6, \"fill\": \"0F766E\" }\n{ \"type\": \"line\", \"dx\": 170, \"dy\": 0, \"dash\": [5, 3] }\n{ \"type\": \"hr\", \"color\": \"f59e0b\", \"thickness\": 1.4, \"width\": 220 }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "ellipse",
      "rx": 6,
      "ry": 6,
      "fill": "16a34a"
    },
    {
      "type": "move",
      "x": 20,
      "y": 0
    },
    {
      "type": "ellipse",
      "rx": 6,
      "ry": 6,
      "fill": "eab308"
    },
    {
      "type": "move",
      "x": 20,
      "y": 0
    },
    {
      "type": "ellipse",
      "rx": 6,
      "ry": 6,
      "fill": "dc2626"
    },
    {
      "type": "move",
      "x": 28,
      "y": -1
    },
    {
      "type": "ellipse",
      "rx": 24,
      "ry": 8,
      "fill": "e0f2fe",
      "border": "0284c7",
      "borderWidth": 0.8
    },
    {
      "type": "move",
      "x": 70,
      "y": 6
    },
    {
      "type": "line",
      "dx": 170,
      "dy": 0,
      "color": "94a3b8",
      "width": 1,
      "dash": [
        5,
        3
      ]
    },
    {
      "type": "move",
      "x": -138,
      "y": 16
    },
    {
      "type": "hr",
      "color": "f59e0b",
      "thickness": 1.4,
      "width": 220
    },
    {
      "type": "move",
      "x": 0,
      "y": 6
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "image — raster & SVG",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 183.0,
        "bookmark": "image — raster & SVG",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "PNG · JPEG · WebP · GIF and SVG (auto-rasterised, so vector logos stay crisp). width alone derives the height from the aspect ratio.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 45.35,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 45.35,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"image\", \"width\": 150,\n  \"value\": \"data:image/svg+xml,<svg ...>soli-pdf</svg>\" }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "image",
      "width": 150,
      "value": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' width='120' height='40'><rect width='120' height='40' rx='8' fill='%230F766E'/><circle cx='22' cy='20' r='11' fill='%2314b8a6'/><path d='M16 21l4 4 8-9' stroke='white' stroke-width='2.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/><text x='44' y='26' font-family='Titillium Web' font-size='16' fill='white' font-weight='bold'>soli-pdf</text></svg>"
    },
    {
      "type": "move",
      "x": 0,
      "y": 55
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "qr — scan-to-pay & text",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 261.35,
        "bookmark": "qr — scan-to-pay & text",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "kind \"epc\" builds a SEPA GiroCode from the payment data — scan it in a banking app to pre-fill the transfer; kind \"text\" encodes any string. Both rasterised locally, no network.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 71.7,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 71.7,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"qr\", \"kind\": \"epc\", \"width\": 88,\n  \"name\": \"$${payment.name}\", \"iban\": \"$${payment.iban}\",\n  \"amount\": \"$${payment.amount}\", \"currency\": \"EUR\",\n  \"remittance\": \"$${doc.ref}\" }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "qr",
      "kind": "epc",
      "name": "${payment.name}",
      "iban": "${payment.iban}",
      "amount": "${payment.amount}",
      "currency": "EUR",
      "remittance": "${doc.ref}",
      "width": 88
    },
    {
      "type": "move",
      "x": 116,
      "y": 0
    },
    {
      "type": "qr",
      "kind": "text",
      "value": "https://solilang.com/docs/builtins/pdf",
      "width": 88
    },
    {
      "type": "move",
      "x": -116,
      "y": 94
    },
    {
      "type": "paragraph",
      "value": "left: EPC scan-to-pay (79.00 EUR to Soli Demo SARL)   ·   right: a URL",
      "options": {
        "fontSize": 8.5,
        "color": "94a3b8",
        "italic": true,
        "spacing": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "barcode — code128 · ean13",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 191.0,
        "bookmark": "barcode — code128 · ean13",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "1-D barcodes. code128 takes any printable ASCII; ean13 takes 12 digits and computes the check digit. humanReadable prints the caption under the bars.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 45.35,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 45.35,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"barcode\", \"symbology\": \"code128\", \"value\": \"$${doc.ref}\",\n  \"width\": 210, \"height\": 46, \"humanReadable\": true }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "barcode",
      "symbology": "code128",
      "value": "${doc.ref}",
      "width": 210,
      "height": 46,
      "humanReadable": true
    },
    {
      "type": "move",
      "x": 250,
      "y": 0
    },
    {
      "type": "barcode",
      "symbology": "ean13",
      "value": "400638133393",
      "width": 185,
      "height": 46,
      "humanReadable": true
    },
    {
      "type": "move",
      "x": -250,
      "y": 62
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "page_break"
    },
    {
      "type": "rect",
      "width": 30,
      "height": 30,
      "fill": "0F766E",
      "radius": 8
    },
    {
      "type": "move",
      "x": 11,
      "y": 6
    },
    {
      "type": "paragraph",
      "value": "3",
      "options": {
        "fontSize": 13,
        "fontWeight": "bold",
        "color": "ffffff"
      }
    },
    {
      "type": "move",
      "x": 30,
      "y": -19
    },
    {
      "type": "paragraph",
      "value": "Tables",
      "options": {
        "fontSize": 20,
        "fontWeight": "bold",
        "color": "0f172a",
        "bookmark": "Tables",
        "anchor": "sec-tables"
      }
    },
    {
      "type": "move",
      "x": -41,
      "y": 12
    },
    {
      "type": "paragraph",
      "value": "Grids of cells — data-bound rows, merges, rich content, and per-table stamps.",
      "options": {
        "fontSize": 10,
        "color": "64748b",
        "italic": true
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 10
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 16
    },
    {
      "type": "paragraph",
      "value": "table — data binding · header · stripe",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 292.525,
        "bookmark": "table — data binding · header · stripe",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "data binds the template row to an array, repeating it per item. The header_columns band repeats on every page; options.stripe zebra-fills every second body row.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 84.875,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 84.875,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"table\", \"data\": \"items\",\n  \"header_columns\": [ { \"text\": \"SERVICE\", \"width\": 250 }, ... ],\n  \"rows\": [ [ { \"text\": \"$${name}\", \"width\": 250 }, ... ] ],\n  \"options\": { \"header\": { \"fillColor\": \"0F766E\", \"textColor\": \"FFFFFF\" },\n               \"stripe\": \"f1f5f9\" } }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "table",
      "data": "items",
      "header_columns": [
        {
          "text": "SERVICE",
          "width": 250,
          "fontWeight": "bold",
          "fontSize": 9.5
        },
        {
          "text": "QTY",
          "width": 60,
          "fontWeight": "bold",
          "fontSize": 9.5,
          "alignment": "right"
        },
        {
          "text": "AMOUNT",
          "width": 185,
          "fontWeight": "bold",
          "fontSize": 9.5,
          "alignment": "right"
        }
      ],
      "rows": [
        [
          {
            "text": "${name}",
            "width": 250,
            "fontSize": 10
          },
          {
            "text": "${qty}",
            "width": 60,
            "fontSize": 10,
            "alignment": "right"
          },
          {
            "text": "${amount}",
            "width": 185,
            "fontSize": 10,
            "alignment": "right"
          }
        ]
      ],
      "options": {
        "header": {
          "fillColor": "0F766E",
          "textColor": "FFFFFF",
          "borderColor": "0F766E"
        },
        "stripe": "f1f5f9",
        "padding_x": 8,
        "padding_y": 6
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "colspan & per-cell fill — a totals row",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 193.35000000000002,
        "bookmark": "colspan & per-cell fill — a totals row",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "colspan merges a cell across column slots; a cell's own fill paints its background over the stripe — the recipe for summary and total rows.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 71.7,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 71.7,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"rows\": [ [\n  { \"text\": \"TOTAL DUE\", \"colspan\": 2, \"alignment\": \"right\",\n    \"fontWeight\": \"bold\" },\n  { \"text\": \"$${doc.total}\", \"alignment\": \"right\", \"fill\": \"fef3c7\" } ] ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "table",
      "header_columns": [
        {
          "text": "",
          "width": 250
        },
        {
          "text": "",
          "width": 60
        },
        {
          "text": "",
          "width": 185
        }
      ],
      "rows": [
        [
          {
            "text": "TOTAL DUE",
            "colspan": 2,
            "alignment": "right",
            "fontWeight": "bold",
            "fontSize": 10.5,
            "borderSides": {
              "top": "true",
              "bottom": "true",
              "left": "true",
              "right": "true"
            },
            "borderColor": "0F766E"
          },
          {
            "text": "${doc.total}",
            "alignment": "right",
            "fontWeight": "bold",
            "fontSize": 10.5,
            "fill": "fef3c7",
            "borderSides": {
              "top": "true",
              "bottom": "true",
              "left": "true",
              "right": "true"
            },
            "borderColor": "0F766E"
          }
        ]
      ],
      "options": {
        "padding_x": 8,
        "padding_y": 6
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "rich cells · borderSides · per-table watermark",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 289.35,
        "bookmark": "rich cells · borderSides · per-table watermark",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "A rich cell stacks text lines and an image in one cell; borderSides sets each edge with a borderColor; the table carries its own centred watermark.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 71.7,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 71.7,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"table\",\n  \"watermark\": { \"text\": \"SAMPLE\", \"fontSize\": 44, \"color\": \"fecaca\" },\n  \"rows\": [ [ { \"content\": [ { \"type\": \"text\", ... },\n                             { \"type\": \"image\", ... } ] }, ... ] ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 14
    },
    {
      "type": "table",
      "watermark": {
        "text": "SAMPLE",
        "fontSize": 44,
        "color": "fecaca"
      },
      "rows": [
        [
          {
            "width": 300,
            "borderSides": {
              "top": "true",
              "bottom": "true",
              "left": "true",
              "right": "true"
            },
            "borderColor": "0F766E",
            "content": [
              {
                "type": "text",
                "value": "${payment.name}",
                "fontSize": 11,
                "fontWeight": "bold"
              },
              {
                "type": "text",
                "value": "IBAN ${payment.iban}",
                "fontSize": 9
              },
              {
                "type": "image",
                "width": 74,
                "value": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' width='120' height='40'><rect width='120' height='40' rx='8' fill='%230F766E'/><text x='12' y='26' font-family='Titillium Web' font-size='16' fill='white' font-weight='bold'>logo</text></svg>"
              }
            ]
          },
          {
            "text": "valign \"bottom\"",
            "fontSize": 10,
            "alignment": "right",
            "valign": "bottom"
          }
        ]
      ],
      "options": {
        "padding_x": 10,
        "padding_y": 9
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 20
    },
    {
      "type": "move",
      "x": 0,
      "y": 30
    },
    {
      "type": "paragraph",
      "value": "repeat — block-level data binding",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 228.525,
        "bookmark": "repeat — block-level data binding",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "The block-level analogue of a data-bound row: lay out any elements once per array item, with $${field} scoped to each item.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 84.875,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 84.875,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"repeat\", \"data\": \"milestones\", \"content\": [\n  { \"type\": \"paragraph\", \"spans\": [\n    { \"text\": \"$${date}  \", \"mono\": true, \"color\": \"64748b\" },\n    { \"text\": \"$${title}\", \"fontWeight\": \"bold\" },\n    { \"text\": \" — $${note}\" } ] } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "repeat",
      "data": "milestones",
      "content": [
        {
          "type": "paragraph",
          "spans": [
            {
              "text": "${date}   ",
              "mono": true,
              "fontSize": 9,
              "color": "64748b"
            },
            {
              "text": "${title}",
              "fontWeight": "bold",
              "color": "0f172a"
            },
            {
              "text": "  —  ${note}",
              "color": "475569"
            }
          ],
          "options": {
            "fontSize": 10
          }
        },
        {
          "type": "move",
          "x": 0,
          "y": 5
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "page_break"
    },
    {
      "type": "rect",
      "width": 30,
      "height": 30,
      "fill": "0F766E",
      "radius": 8
    },
    {
      "type": "move",
      "x": 11,
      "y": 6
    },
    {
      "type": "paragraph",
      "value": "4",
      "options": {
        "fontSize": 13,
        "fontWeight": "bold",
        "color": "ffffff"
      }
    },
    {
      "type": "move",
      "x": 30,
      "y": -19
    },
    {
      "type": "paragraph",
      "value": "Charts",
      "options": {
        "fontSize": 20,
        "fontWeight": "bold",
        "color": "0f172a",
        "bookmark": "Charts",
        "anchor": "sec-charts"
      }
    },
    {
      "type": "move",
      "x": -41,
      "y": 12
    },
    {
      "type": "paragraph",
      "value": "Bar, line, pie and donut — drawn from the data, no plotting library.",
      "options": {
        "fontSize": 10,
        "color": "64748b",
        "italic": true
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 10
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 16
    },
    {
      "type": "paragraph",
      "value": "chart — grouped bars · multi-series · gridlines",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 327.35,
        "bookmark": "chart — grouped bars · multi-series · gridlines",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "values is an array of { field, name, color }: each series reads its own field, bars group per category, and the legend lists the names. gridlines adds the value axis.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 71.7,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 71.7,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"chart\", \"kind\": \"bar\", \"data\": \"quarters\", \"label\": \"q\",\n  \"gridlines\": true, \"width\": 495, \"height\": 150,\n  \"values\": [ { \"field\": \"fy25\", \"name\": \"FY 2025\", \"color\": \"94a3b8\" },\n              { \"field\": \"fy26\", \"name\": \"FY 2026\", \"color\": \"0F766E\" } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "chart",
      "kind": "bar",
      "data": "quarters",
      "label": "q",
      "gridlines": true,
      "width": 495,
      "height": 150,
      "title": "Grouped — one bar per series",
      "values": [
        {
          "field": "fy25",
          "name": "FY 2025",
          "color": "94a3b8"
        },
        {
          "field": "fy26",
          "name": "FY 2026",
          "color": "0F766E"
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "chart — stacked bars",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 327.35,
        "bookmark": "chart — stacked bars",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "mode \"stacked\" piles the series per category instead of grouping them; a colors palette is cycled when series set no colour of their own.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 71.7,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 71.7,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"chart\", \"kind\": \"bar\", \"mode\": \"stacked\",\n  \"data\": \"quarters\", \"label\": \"q\", \"gridlines\": true,\n  \"colors\": [ \"0F766E\", \"f59e0b\" ],\n  \"values\": [ { \"field\": \"fy25\" }, { \"field\": \"fy26\" } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "chart",
      "kind": "bar",
      "data": "quarters",
      "label": "q",
      "gridlines": true,
      "width": 495,
      "height": 150,
      "title": "Stacked — series pile per category",
      "mode": "stacked",
      "colors": [
        "0F766E",
        "f59e0b"
      ],
      "values": [
        {
          "field": "fy25",
          "name": "FY 2025"
        },
        {
          "field": "fy26",
          "name": "FY 2026"
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "chart — line",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 271.35,
        "bookmark": "chart — line",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "One line per series over the category axis; share the same data binding as the bar charts.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"chart\", \"kind\": \"line\", \"data\": \"quarters\", \"label\": \"q\",\n  \"width\": 495, \"height\": 120,\n  \"values\": [ { \"field\": \"fy26\", \"name\": \"FY 2026\", \"color\": \"0F766E\" } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "chart",
      "kind": "line",
      "title": "FY 2026 by quarter",
      "data": "quarters",
      "label": "q",
      "values": [
        {
          "field": "fy26",
          "name": "FY 2026",
          "color": "0F766E"
        }
      ],
      "width": 495,
      "height": 120
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "chart — pie & donut",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 448.175,
        "bookmark": "chart — pie & donut",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "points may be inline instead of data-bound. pie draws a swatch + percentage legend; donut is a pie with a ring cutout (\"kind\": \"donut\").",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"chart\", \"kind\": \"pie\", \"width\": 240, \"height\": 130,\n  \"points\": [ { \"label\": \"Templates\", \"value\": 45 },\n              { \"label\": \"Charts\", \"value\": 25 }, ... ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "chart",
      "kind": "pie",
      "title": "Pie",
      "legend": true,
      "width": 495,
      "height": 128,
      "points": [
        {
          "label": "Templates",
          "value": 45
        },
        {
          "label": "Charts",
          "value": 25
        },
        {
          "label": "Codes",
          "value": 18
        },
        {
          "label": "Everything else",
          "value": 12
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 6
    },
    {
      "type": "chart",
      "kind": "donut",
      "title": "Donut",
      "legend": true,
      "width": 495,
      "height": 128,
      "points": [
        {
          "label": "Templates",
          "value": 45
        },
        {
          "label": "Charts",
          "value": 25
        },
        {
          "label": "Codes",
          "value": 18
        },
        {
          "label": "Everything else",
          "value": 12
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "page_break"
    },
    {
      "type": "rect",
      "width": 30,
      "height": 30,
      "fill": "0F766E",
      "radius": 8
    },
    {
      "type": "move",
      "x": 11,
      "y": 6
    },
    {
      "type": "paragraph",
      "value": "5",
      "options": {
        "fontSize": 13,
        "fontWeight": "bold",
        "color": "ffffff"
      }
    },
    {
      "type": "move",
      "x": 30,
      "y": -19
    },
    {
      "type": "paragraph",
      "value": "Layout & render options",
      "options": {
        "fontSize": 20,
        "fontWeight": "bold",
        "color": "0f172a",
        "bookmark": "Layout & render options",
        "anchor": "sec-layout"
      }
    },
    {
      "type": "move",
      "x": -41,
      "y": 12
    },
    {
      "type": "paragraph",
      "value": "Multi-column flow, data-driven branches, and the options that live in the render call rather than the template.",
      "options": {
        "fontSize": 10,
        "color": "64748b",
        "italic": true
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 10
    },
    {
      "type": "hr",
      "color": "e2e8f0",
      "thickness": 0.8
    },
    {
      "type": "move",
      "x": 0,
      "y": 16
    },
    {
      "type": "paragraph",
      "value": "columns — multi-column flow",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 259.0,
        "bookmark": "columns — multi-column flow",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "Children fill column 1 to the bottom, then column 2 (sequential fill); a page_break inside is a column break. Everything flows — paragraphs, lists, images, tables and charts. A table that overflows a column continues in the next one with its header repeated.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"columns\", \"count\": 2, \"gap\": 22, \"content\": [\n  { \"type\": \"paragraph\", \"value\": \"flows down column 1...\" },\n  { \"type\": \"table\", \"header_columns\": [...], \"rows\": [...] } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "columns",
      "count": 2,
      "gap": 22,
      "content": [
        {
          "type": "paragraph",
          "value": "Column one fills top to bottom, then the flow hops into column two automatically — sequential fill. A table renders inline too:",
          "options": {
            "fontSize": 9,
            "alignment": "justify",
            "color": "475569",
            "spacing": 6
          }
        },
        {
          "type": "table",
          "options": {
            "padding_x": 4,
            "padding_y": 2,
            "stripe": "f1f5f9",
            "header": {
              "fillColor": "0F766E",
              "textColor": "ffffff"
            }
          },
          "header_columns": [
            {
              "text": "Item",
              "width": 92,
              "fontWeight": "bold"
            },
            {
              "text": "Qty",
              "width": 48,
              "fontWeight": "bold"
            }
          ],
          "rows": [
            [
              {
                "text": "Widgets",
                "width": 92
              },
              {
                "text": "12",
                "width": 48
              }
            ],
            [
              {
                "text": "Gadgets",
                "width": 92
              },
              {
                "text": "5",
                "width": 48
              }
            ],
            [
              {
                "text": "Sprockets",
                "width": 92
              },
              {
                "text": "9",
                "width": 48
              }
            ]
          ]
        },
        {
          "type": "paragraph",
          "value": "…and the paragraph after the table keeps flowing, spilling into the second column to show the block is genuinely two columns.",
          "options": {
            "fontSize": 9,
            "alignment": "justify",
            "color": "0F766E",
            "fontWeight": "bold",
            "spacing": 6
          }
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 6
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "if / unless — data-driven branches",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 180.175,
        "bookmark": "if / unless — data-driven branches",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "Render content only when a condition holds (if) or fails (unless); an optional else is the other branch. With equals it is a string test, otherwise a truthiness test.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "{ \"type\": \"if\", \"when\": \"doc.paid\", \"equals\": \"true\",\n  \"content\": [ { \"type\": \"paragraph\", \"value\": \"PAID IN FULL\" } ],\n  \"else\":    [ { \"type\": \"paragraph\", \"value\": \"Balance due\" } ] }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "if",
      "when": "doc.paid",
      "equals": "true",
      "content": [
        {
          "type": "paragraph",
          "spans": [
            {
              "text": "PAID IN FULL",
              "fontWeight": "bold",
              "color": "16a34a"
            },
            {
              "text": "   —   doc.paid is \"true\", so the content branch renders (the else branch is skipped).",
              "color": "64748b",
              "fontSize": 9
            }
          ],
          "options": {
            "fontSize": 10.5
          }
        }
      ],
      "else": [
        {
          "type": "paragraph",
          "value": "Balance due",
          "options": {
            "color": "b91c1c"
          }
        }
      ]
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "interpolation & page tokens",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 170,
        "bookmark": "interpolation & page tokens",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "A dotted path pulls from the data; page tokens resolve after pagination and turn a contents list into real page numbers. Print a literal brace token by doubling the dollar.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58.525,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 58.525,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "\"Invoice "
        },
        {
          "text": "$${invoice.number}",
          "color": "0F766E"
        },
        {
          "text": " — page "
        },
        {
          "text": "#PAGE#",
          "color": "0F766E"
        },
        {
          "text": " of "
        },
        {
          "text": "#TOTAL_PAGE#",
          "color": "0F766E"
        },
        {
          "text": "\""
        }
      ],
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "\"See charts on p. "
        },
        {
          "text": "#PAGE_OF:sec-charts#",
          "color": "0F766E"
        },
        {
          "text": "\"   with "
        },
        {
          "text": "\"linkTo\": \"sec-charts\"",
          "color": "64748b"
        }
      ],
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "\""
        },
        {
          "text": "$${",
          "color": "b45309"
        },
        {
          "text": " prints a literal "
        },
        {
          "text": "${",
          "color": "b45309"
        },
        {
          "text": "\"   — this document's code boxes rely on it",
          "color": "64748b"
        }
      ],
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "paragraph",
      "value": "renders",
      "options": {
        "fontSize": 7.5,
        "mono": true,
        "color": "94a3b8",
        "spacing": 3
      }
    },
    {
      "type": "paragraph",
      "spans": [
        {
          "text": "The running header and footer of this page both resolve "
        },
        {
          "text": "#PAGE#",
          "mono": true,
          "color": "0F766E"
        },
        {
          "text": " / "
        },
        {
          "text": "#TOTAL_PAGE#",
          "mono": true,
          "color": "0F766E"
        },
        {
          "text": ", and the cover's contents used "
        },
        {
          "text": "#PAGE_OF:…#",
          "mono": true,
          "color": "0F766E"
        },
        {
          "text": " for its page numbers."
        }
      ],
      "options": {
        "fontSize": 10,
        "color": "334155",
        "lineHeight": 1.4
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "document options",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 176.52499999999998,
        "bookmark": "document options",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "The options block sets page size, margins, the reserved header_height, a page background or backgroundImage (this cover's wash — opacity fades it to a soft tint), a document watermark, and tagged for accessible output.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 98.05,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 98.05,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "\"options\": {\n  \"page\": \"a4\", \"margins\": { \"top\": 42, \"left\": 50 },\n  \"header_height\": 34,\n  \"watermark\": { \"text\": \"DRAFT\", \"front\": true, \"pages\": \"first\" },\n  \"backgroundImage\": { \"src\": \"...\", \"pages\": \"first\",\n                       \"opacity\": 0.15 } }",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "paragraph",
      "value": "the render call — pdf_render(tpl, data, options)",
      "options": {
        "fontSize": 12,
        "fontWeight": "bold",
        "mono": true,
        "color": "0F766E",
        "minSpaceBelow": 150.525,
        "bookmark": "the render call — pdf_render(tpl, data, options)",
        "bookmarkLevel": 2
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "paragraph",
      "value": "Some features live in the call, not the template JSON: a letterhead to draw on, files to embed, Info-dictionary metadata, a password, or PDF/A archival output.",
      "options": {
        "fontSize": 9.5,
        "color": "64748b",
        "lineHeight": 1.35
      }
    },
    {
      "type": "move",
      "x": 0,
      "y": 7
    },
    {
      "type": "rect",
      "width": 495,
      "height": 84.875,
      "fill": "f8fafc",
      "border": "e2e8f0",
      "borderWidth": 0.8,
      "radius": 6
    },
    {
      "type": "rect",
      "width": 3,
      "height": 84.875,
      "fill": "0F766E",
      "radius": 1.5
    },
    {
      "type": "move",
      "x": 15,
      "y": 10
    },
    {
      "type": "paragraph",
      "value": "pdf_render(tpl, data, {\n  \"stationery\": \"pdf/letterhead.pdf\",   # draw on a letterhead PDF\n  \"attachments\": [{ \"path\": \"data.csv\" }],\n  \"title\": \"Invoice 42\", \"author\": \"Soli\",\n  \"font_dirs\": [\"font\"], \"fetch_images\": false })",
      "options": {
        "fontSize": 8.5,
        "mono": true,
        "color": "334155",
        "lineHeight": 1.55
      }
    },
    {
      "type": "move",
      "x": -15,
      "y": 22
    },
    {
      "type": "move",
      "x": 0,
      "y": 24
    },
    {
      "type": "move",
      "x": 0,
      "y": 4
    },
    {
      "type": "rect",
      "width": 495,
      "height": 58,
      "fill": "0F766E",
      "radius": 12
    },
    {
      "type": "rect",
      "width": 495,
      "height": 4,
      "fill": "f59e0b",
      "radius": 2
    },
    {
      "type": "move",
      "x": 22,
      "y": 13
    },
    {
      "type": "paragraph",
      "value": "Built with Soli — one JSON template, one pdf_render() call.",
      "options": {
        "fontSize": 13,
        "fontWeight": "bold",
        "color": "ffffff"
      }
    },
    {
      "type": "paragraph",
      "value": "solilang.com/docs/builtins/pdf  ·  try this very template live in the playground",
      "options": {
        "fontSize": 9,
        "color": "ccfbf1",
        "link": "https://solilang.com/docs/builtins/pdf"
      }
    },
    {
      "type": "move",
      "x": -22,
      "y": 20
    }
  ]
}
features.data.json json
{
  "data": {
    "invoice": {
      "number": "INV-2026-0042"
    },
    "doc": {
      "ref": "TOUR-2026-001",
      "paid": "true",
      "total": "1,940.00 EUR"
    },
    "payment": {
      "name": "Soli Demo SARL",
      "iban": "FR7630006000011234567890189",
      "amount": "79.00"
    },
    "items": [
      {
        "name": "Template engine",
        "qty": "1",
        "amount": "890.00 EUR"
      },
      {
        "name": "Chart pack (bar · line · pie · donut)",
        "qty": "4",
        "amount": "450.00 EUR"
      },
      {
        "name": "QR & barcode primitives",
        "qty": "2",
        "amount": "300.00 EUR"
      },
      {
        "name": "Factur-X / PDF-A-3b embedding",
        "qty": "1",
        "amount": "300.00 EUR"
      }
    ],
    "milestones": [
      {
        "date": "2026-03",
        "title": "Charts & lists",
        "note": "bar, line, pie, nested lists"
      },
      {
        "date": "2026-06",
        "title": "SVG & barcodes",
        "note": "crisp vector logos, Code 128 / EAN"
      },
      {
        "date": "2026-07",
        "title": "Flow & polish",
        "note": "columns, tokens, the ${ escape"
      }
    ],
    "quarters": [
      {
        "q": "Q1",
        "fy25": 120,
        "fy26": 180
      },
      {
        "q": "Q2",
        "fy25": 150,
        "fy26": 210
      },
      {
        "q": "Q3",
        "fy25": 170,
        "fy26": 260
      },
      {
        "q": "Q4",
        "fy25": 160,
        "fy26": 300
      }
    ]
  }
}