JSON PressBack to site

Template format (v2)

A template is a single JSON object, the ReportDefinition. The format is snake_case and versioned; the only supported version is 2. Unknown keys are ignored on input, and new keys are additive and optional so older templates keep loading.

Coordinates and sizes are millimetres unless the key ends in _pt (points). The origin is the top-left of the page, with y growing downwards. The renderer uses the page's top origin, not PDF's bottom-left origin.

Complete example

This is the shape of a full document. The checked-in neutral fixture crates/jsonpress-core/testdata/invoice.json is a smaller working version of the same idea.

{
  "version": 2,
  "name": "Invoice",
  "source": "invoice.json",
  "font_family": "Helvetica",
  "fields": [
    { "name": "CUSTOMER", "label": "Customer", "sample": "Acme Ltd" },
    { "name": "AMOUNT", "label": "Amount", "sample": "1250.00" }
  ],
  "sample_rows": [
    { "CUSTOMER": "Acme Ltd", "AMOUNT": "1250.00" },
    { "CUSTOMER": "Beta Trading", "AMOUNT": "89.50" }
  ],
  "parameters": [
    {
      "name": "month",
      "label": "Month",
      "kind": "integer",
      "default": 11,
      "options": []
    }
  ],
  "page": { "width_mm": 210, "height_mm": 297, "bottom_margin_mm": 15 },
  "title": [
    {
      "kind": "text", "x_mm": 20, "y_mm": 15, "width_mm": 170,
      "size_pt": 16, "bold": true, "align": "left", "color": "title",
      "expr": "\"Invoice\""
    },
    {
      "kind": "text", "x_mm": 20, "y_mm": 40, "width_mm": 90,
      "size_pt": 9, "expr": "\"CUSTOMER\""
    },
    {
      "kind": "text", "x_mm": 120, "y_mm": 40, "width_mm": 40,
      "size_pt": 9, "align": "right", "expr": "\"AMOUNT\""
    }
  ],
  "detail": {
    "row_height_mm": 6,
    "first_row_y_mm": 50,
    "columns": [
      { "x_mm": 20, "width_mm": 90, "size_pt": 9, "expr": "CUSTOMER" },
      {
        "x_mm": 120, "width_mm": 40, "size_pt": 9,
        "align": "right", "expr": "AMOUNT"
      }
    ]
  },
  "note": {
    "x_mm": 20, "width_mm": 170, "size_pt": 8,
    "offset_mm": 5, "extra_mm": 4, "expr": "\"Thank you\""
  },
  "summary": [
    {
      "kind": "text", "x_mm": 20, "y_mm": 10, "width_mm": 90,
      "size_pt": 9, "bold": true, "expr": "\"Total\""
    },
    {
      "kind": "text", "x_mm": 120, "y_mm": 10, "width_mm": 40,
      "size_pt": 9, "bold": true, "align": "right",
      "expr": "AMOUNT", "total_type": 2
    }
  ]
}

Top-level keys

KeyTypeRequiredMeaning
versionintegeryesmust be 2
namestringyestemplate name; PDF document title and download file stem
sourcestringnooptional audit trail (original file name), default ""
font_familystring or nullnoPDF font family from the server catalog; DOCX names the family for Word; unset uses the default
pageobjectyeswidth_mm, height_mm, bottom_margin_mm
titlearray of elementsyesprinted on every page
detailobjectyesthe repeating row band
noteobjectnooptional line below each detail row
summaryarray of elementsyes (may be [])printed after the last detail row
fieldsarraynodesigner field catalog
sample_rowsarray of objectsnodesigner preview rows
parametersarraynodesigner parameter form

Earlier templates may contain font_family as metadata even though it was previously ignored. Remove that property to use the server default, or configure the named family in JSONPRESS_FONTS before rendering the template as PDF.

Text elements and detail columns may override the report's font_family. An omitted, null, empty or whitespace-only override inherits the report font; an unset or blank report font uses the server default (Helvetica if none is configured). Names are trimmed. PDF requires each selected family to be in the server's font catalog and fails for unknown families. DOCX names the selected family for Word; XLSX and CSV are unaffected. Table cells and note lines use the report font and do not have per-object overrides.

PDF measures text using the selected family and regular or bold face for wrapping, clipping and alignment. Text that does not fit its field is truncated with an ellipsis when the face contains that glyph and it fits; otherwise it is truncated without one. A stretched column wraps at word boundaries, splitting long words (including unspaced Chinese text) between characters. A field too narrow for even one glyph cannot display that glyph.

fields, sample_rows, parameters

These three keys are additive v2 metadata. The render path ignores all three; the designer uses them to populate its field rail, sample data and parameter form.

"fields": [
  { "name": "CUSTOMER", "label": "Customer", "sample": "Acme Ltd" }
],
"sample_rows": [
  { "CUSTOMER": "Acme Ltd" }
],
"parameters": [
  { "name": "month", "label": "Month", "kind": "integer",
    "default": 11, "options": [] }
]

Page geometry

"page": {
  "width_mm": 210, "height_mm": 297,
  "top_margin_mm": 0, "right_margin_mm": 0, "bottom_margin_mm": 15, "left_margin_mm": 0
}

bottom_margin_mm is the bottom print limit. The flow engine stops adding rows past height_mm - bottom_margin_mm. A4 portrait is 210 × 297; A4 landscape is 297 × 210.

top_margin_mm, left_margin_mm and right_margin_mm are optional and default to 0. They define a print area for validation: when a side margin is set, an element whose box crosses it fails the render with an error naming the band and index, for example title[3] ends at 290.0 mm, right of the right margin (287.0 mm). When any of these margins is nonzero, vertical positions of title objects, rendered detail and note lines, and paginated summaries are also checked against the top and bottom limits. Text bounds use its baseline and font size. Margins never move, clip or rescale objects; templates with all three new margins zero keep their previous behavior.

Band model

A report is four vertical bands:

  1. Title — drawn at the top of every page. It usually holds the report title, column headers and decorative lines/rectangles.
  2. Detail — repeats one row per data row. first_row_y_mm is the first row's baseline; row_height_mm is the band height and the pitch between rows. When a page fills, the flow continues on the next page. Optional detail.elements repeats text, lines, rectangles, tables and images once per row; their y_mm values are relative to the row's first baseline.
  3. Note — optional. When the note expression is non-empty, a line is drawn below the detail row; extra_mm is the extra flow height it consumes.
  4. Summary — drawn after the last detail row, on the same page when it fits and otherwise on a new one. Element y_mm values are relative to the top of the summary block, not to the page.

Pagination is planned before drawing, because stretched detail cells and note lines change row heights.

Elements

Every element is tagged by kind.

Text

{
  "kind": "text", "x_mm": 20, "y_mm": 40, "width_mm": 90,
  "size_pt": 9, "bold": false, "align": "left", "color": "default",
  "expr": "CUSTOMER"
}
KeyRequiredDefaultMeaning
x_mm, y_mm, width_mm, size_ptyes—position and box width; y_mm is the baseline
expryes—expression, see expressions.md
boldnofalsebold weight
alignnoleftleft or right
colornodefaultdefault, title or muted
font_familynoreport fontserver-catalog family override for PDF; family name for DOCX
total_typeno0in a summary band: 0 plain, 1 count, 2 sum

A quoted literal such as "\"Invoice\"" prints fixed text; a bare identifier such as "CUSTOMER" reads the row field.

Line

{
  "kind": "line", "x0_mm": 20, "y0_mm": 38,
  "x1_mm": 190, "y1_mm": 38, "thickness_mm": 0.2
}

All keys are required.

Rectangle

{
  "kind": "rect", "x0_mm": 20, "y0_mm": 12,
  "width_mm": 170, "height_mm": 10, "filled": false
}

x0_mm, y0_mm, width_mm and height_mm are required; filled defaults to false.

Image

{
  "kind": "image", "x0_mm": 20, "y0_mm": 12,
  "width_mm": 40, "height_mm": 16,
  "src": "data:image/png;base64,iVBORw0KGgo..."
}

An image is a fixed PNG or JPEG embedded in the template, useful for a logo or letterhead. src must be a base64 data:image/png or data:image/jpeg URL, so a template has no external references and renders offline. All keys are required. In the designer, choose Image in the toolbox and pick a file. PDF and DOCX scale images to width_mm × height_mm; XLSX and CSV omit images. Each encoded image is limited to 8 MiB; unique images in one report may total at most 8 million decoded pixels. Images follow element order in each band.

Table

{
  "kind": "table", "x0_mm": 20, "y0_mm": 15,
  "width_mm": 100, "height_mm": 20, "size_pt": 9,
  "border": "solid",
  "cells": [
    ["\"Item\"", { "expr": "\"Amount\"", "align": "right" }],
    ["\"Example\"", { "expr": "report_total", "align": "right" }]
  ]
}

Tables are fixed grids in the title or summary band. Columns and rows have equal sizes by default. Optional column_weights and row_weights arrays set their relative sizes; each must contain one positive number per column or row. Sizes are proportional to the weights and the table's total width or height. In XLSX, column widths apply to the entire worksheet, including other tables and detail rows; the last table with custom column widths determines the shared widths. Each cell contains an expression; quote literal text as shown. Cells use the same parameter and page context as other text in their band. Rows and columns must be rectangular and nonempty. Title tables repeat on each page; summary tables appear in the summary. XLSX exports each table as spreadsheet cells; CSV omits tables and the HTTP response includes an X-JSONPress-Warning header. The optional border is solid (default), dashed, dotted, or none in PDF and XLSX. A cell is either an expression string, left-aligned, or an object { "expr": "...", "align": "left" | "center" | "right" }; alignment applies to PDF and XLSX. In PDF, cell text is fitted and aligned using the configured font's glyph advances, including the width of spaces. Ordinary text and detail columns retain their existing left/right alignment options.

Detail columns

{
  "x_mm": 20, "width_mm": 90, "size_pt": 9,
  "bold": false, "align": "left", "stretch": false,
  "label": "Customer", "expr": "CUSTOMER"
}

x_mm, width_mm, size_pt and expr are required.

Detail objects

detail.elements is an optional array of the same element objects used by the title and summary bands — text, line, rect, table and image. Each object is drawn once per data row, with y_mm measured from that row's first baseline. Expressions use the row's values and the current page. An object that extends below row_height_mm increases that row's height and therefore pagination. XLSX and CSV omit detail objects. DOCX includes detail objects in an editable flow layout; positions are approximated and Word determines pagination. In DOCX, rectangles and non-horizontal lines become editable Word shapes. Horizontal positions are retained where the flow layout permits; overlapping detail columns may be narrowed. Word determines vertical placement.

Notes

{
  "x_mm": 20, "width_mm": 170, "size_pt": 8,
  "offset_mm": 5, "extra_mm": 4, "expr": "NOTE"
}

All keys are required. The note prints only when the expression is non-empty after trimming. offset_mm is the distance from the row baseline to the note baseline; extra_mm is the extra flow height consumed. In XLSX/CSV the note is added as a final Notes column when options.includeNotes is true.

Rows

Rows are supplied in the request, not in the template:

{ "data": { "rows": [ { "customer": "Acme", "amount": 1250, "active": true } ] } }

Keys are matched case-insensitively and normalised to uppercase. Values may be string, number, boolean or null; objects and arrays are rejected. See api.md for the full conversion and sparse-row rules.