The complete set of functions available inside process.js - plain JavaScript, running once per submission after validate.js passes. See process.js for the bigger picture (inputs, when it runs, handing computed data to blocks); this page is the exhaustive function-by-function reference.

Console

console.log(), console.warn(), and console.error() all work inside process.js (and validate.js) - but there's no browser or terminal for their output to appear in. Instead, every call is redirected into that submission's processing trace - the same in-app Processing Log / debug.log file covered on the Debugging page. console.warn()/ console.error() calls specifically are what turn the Processing Log button amber/red.

console.log("line items:", inputs.line_items.length);
if (inputs.line_items.length > 50) {
  console.warn("more than 50 line items - page may run long");
}

console.error(msg) is also a clean way to bail out of your own script with an explanation: it records the ERROR: line same as console.warn/ console.log do, then halts the rest of the script - same as any other uncaught error already does. There's no exception for "an error a script raises on purpose" - any error halts processing.

Multiple arguments are joined with a space; a plain string passes through as-is, anything else (a number, object, array) is JSON.stringify()'d first - so console.log("bounds:", getBlockBounds(1)) prints something like bounds: {"x":10,"y":20,"width":30,"height":40}.

Pages

addPage(properties)

Declares one output page. Page number = position in the call sequence - the first call produces page 1, the second page 2, and so on. Two shapes, discriminated by whether pdf is present:

addPage({ width: 80, height: 200, backgroundColor: "#FFFFFF" });
// blank, synthesized page - width/height required (mm), backgroundColor
// optional hex, default "#FFFFFF"

addPage({ pdf: "base.pdf", pdfPage: 1 });
// backed by page 1 (default) of a project-relative PDF resource file - an
// ordinary named asset, not one blessed "base.pdf" filename. width/height
// are optional overrides that stretch-fit the rasterized page; omitted,
// the page's own natural size is used.

A document with no addPage() calls at all fails to generate - required, no fallback. A malformed call (no usable width/height or pdf resource, an unreadable pdf resource, an out-of-range pdfPage) halts processing immediately, the same as any other host function error below.

overlay(filename[, page[, transform]])

Loads a layout partial and merges its blocks into the page named by page (default 1, an addPage()-declared page number - must already exist, or this halts processing). Callable more than once, targeting different pages or reusing the same partial with a different transform each time. Later calls paint in front of earlier ones whenever z_index ties.

Halts processing if filename doesn't exist or isn't valid JSON. A single malformed block within an otherwise-valid partial (missing a required key, wrong type) is skipped instead - a warning in the Processing Log, not a halt.

transform (all fields optional):

Field Effect
dx_mm, dy_mm Shift every merged block's x_mm/y_mm (default 0)
rotate_deg Adds to each block's own rotation_deg (default 0) - a value of exactly 180 (or an equivalent like -180/540) also repositions every block via a point-reflection through the group's own bounding-box center; any other angle only spins content in place
scale_x, scale_y Multiply each block's own scale_x/scale_y (default 1) - content-only, never touches x_mm/y_mm/width_mm/height_mm
align_top_mm, align_bottom_mm Replaces dy_mm - shifts the group so its own top/bottom edge lands at this absolute y_mm, recomputed from current geometry every call
addPage({ width: 210, height: 297, backgroundColor: "#FFFFFF" });
overlay("overlay.json");
overlay("mask.json", 1, { dx_mm: 40, rotate_deg: 180, scale_x: -1 });

Repeating the same partial at several hand-computed positions (a card design placed six times on a sheet, say) can also be done visually, with an Overlay block placed directly in the Overlay Editor instead of a script loop - see the overlay block type on {overlay}.json.

Page refinement

Four functions that adjust an already-addPage()'d page. All four act immediately, ordinary and order-sensitive like everything else in process.js - a call reflects (or changes) whatever's true in the model at that exact point in the script, not some later, fully-resolved state. There's no re-run of any kind: process.js always runs exactly once, top to bottom.

setPageHeight(page, height) / setPageWidth(page, width)

Set that page's height/width directly and literally, replacing whatever it was - no implicit "trim to content" computation. The primary tool for variable-length pages - receipts, or any layout whose content extent isn't known until the script finishes (a repeating line-items loop, for example) - is composing this from getBlockBounds() yourself:

addPage({ width: 80, height: 500, backgroundColor: "#FFFFFF" }); // generous ceiling
overlay("header.json");
var y = 35;
for (var i = 0; i < inputs.line_items.length; i++) {
  overlay("line_item.json", 1, { dy_mm: y });
  y += 6;
}
overlay("totals.json", 1, { dy_mm: y });

var b = getBlockBounds(1);
setPageHeight(1, b.y + b.height + 5); // trim to (max block bottom) + 5mm

Calling this more than once for the same page: the last call wins, plainly - each call just overwrites the page's height/width, nothing more. A PDF-resource-backed page can only ever shrink, never grow past its current size (a call that would grow it is silently clamped, with a warning in the Processing Log - background rasters can be cropped smaller but not stretched larger without visibly degrading).

setPageTransform(page, transform) / setTransform(transform)

Same transform shape overlay() takes. setPageTransform sets it as a property of that page; setTransform (no page argument) sets it for the whole document. Either way, it's resolved once, at generation time, over every block on the page (or every page, for setTransform) - including ones added by an overlay() call that comes after this one in the script - never a retroactive rewrite of a block's own stored x_mm/y_mm/ etc. setTransform composes over every page's own setPageTransform, as the outermost layer.

One important difference from overlay()'s own scale_x/scale_y: here they're geometric, not content-only - they multiply x_mm and width_mm together (resp. y_mm/height_mm), anchored at the page's own origin. That's what makes this actually useful for its main purpose:

// The physical printer is 77mm wide, but this content was designed for 80mm.
addPage({ width: 77, height: 200, backgroundColor: "#FFFFFF" }); // real physical width
overlay("receipt_content.json"); // authored assuming 80mm
setPageTransform(1, { scale_x: 77 / 80 }); // remaps content to fit; addPage()'s width/height stays untouched

Queries

Three functions that read geometry the script can't compute itself. Each is a plain, synchronous read of whatever's true in the model at the moment it's called - the same as reading a property off any ordinary object mid-construction. Calling one for a page that hasn't been addPage()'d yet halts processing, same as overlay()/setPageHeight() would for the same mistake.

getBlockBounds(page)

Returns {x, y, width, height} in mm - the union rectangle of everything placed on that page so far - or null if the page has no blocks yet.

getPageSize(page)

Returns {width, height} in mm - that page's own current size, reflecting a setPageHeight()/setPageWidth() call already made. Mostly useful for a PDF-resource-backed page with no explicit override, where the real rasterized size otherwise isn't knowable from the script.

getPdfPageSize(file, pdfPage)

Returns {width, height} in mm - the natural size of a page of a PDF resource file, independent of any addPage() call (no page needs to reference it first). Halts processing for a missing file or out-of-range page, same as any other host function error.

Project introspection

Four read-only lookups - every value here is knowable before process.js even starts running, so unlike the queries above, none of these depend on where in the script you call them.

Function Returns
getProject() {name, description, notes, categories, defaultWidthMm, defaultHeightMm, defaultBackgroundColor} - project.ypf's own metadata
listAssets() {images, fonts, pdfs} - the project folder's own files, each a sorted array of filenames
getFormFields() [{id, type, label, fields}] - form.json's field tree; fields is non-empty only for a repeating group, recursing into its own child fields
getImageDimensions(file) {width, height} in pixels (not mm), or null for a missing/undecodable/SVG file
var project = getProject();
if (project.categories.indexOf("automotive") != -1) {
  overlay("dealer_disclaimer.json");
}

if (listAssets().images.indexOf("dealer_logo.png") != -1) {
  overlay("logo.json");
}

Formatting

Plain JavaScript, not locale-aware - useful shorthand, not a substitute for real internationalization.

Function Example
formatNumber(value, decimals) formatNumber(1234.5)"1,234.50" (decimals default 2)
formatCurrency(value, currencyCode) formatCurrency(1234.5)"$1,234.50"; formatCurrency(-5, "EUR")"-€5.00" (default "USD"; USD/EUR/GBP/JPY get a real symbol, any other code prints as "CODE "; JPY defaults to 0 decimals)
formatDate(value, pattern) formatDate(new Date(2024, 0, 5))"2024-01-05" (default pattern "YYYY-MM-DD"); pattern tokens: YYYY, MM, DD, HH, mm, ss