Pulp EngineDocument Rendering
Get started

Office-authored templates

Fill tagged DOCX, XLSX and PPTX files while retaining authored OOXML styles, headers, footers, lists, section settings, formulas and embedded media. Save in standard Transitional OOXML. Strict OOXML, macros and encryption are rejected. Enable OFFICE_TEMPLATES_ENABLED=true. Native output needs no Office installation; PDF output uses the optional Gotenberg LibreOffice sidecar.

Tags and scoping

Put {{customer.name}} in text or a cell. Split Word/PowerPoint text runs are normalized with the first run’s formatting; smart quotes inside tags are normalized. Shared helpers include {{currency total "EUR"}} and {{date issued "long"}}. Values are XML escaped. Triple braces, partials and prototype access are rejected.

  • {{#each lines}} ... {{/each}} repeats content.
  • {{#if approved}} ... {{else}} ... {{/if}} selects content.
  • {{#with customer}} ... {{/with}} changes the current object.
  • Loops support {{name}}, {{@index}}, {{../customer.name}} and {{@root.customer.name}}.
  • A block within one paragraph repeats inline. Across sibling paragraphs it repeats the paragraph band. Across cells in one table row it repeats that row; across sibling rows it repeats the row band.
  • Control-only paragraphs/rows are removed. A structural else needs its own paragraph or row. Blocks cannot cross tables, parts or unrelated parents.
  • PowerPoint supports text, table rows, paragraph bands and pictures. Slide repetition is not supported.

Lint reports part/paragraph, sheet/cell or slide/shape locations and nearby schema paths for unbound tags. Omitted schemas are inferred as a draft: loops become arrays and leaves become strings. Review numeric/date types before use. Use explicit paths rather than block aliases for schema inference.

Images

Set an embedded picture’s alt text to exactly {{image logoUrl}}. Supply a tenant-owned asset reference or base64 image data URI. Remote protocols are rejected. Original frame size and crop remain. SVG input is sanitized and rasterized for compatibility. Images are limited to 20 MB.

Excel values and formulas

A cell containing one tag receives a numeric or boolean value when data has that type. A date-styled cell receives an Excel date serial, including the workbook’s 1904 date system. Authored styles and number formats remain. Other values stay strings; a string beginning with = never becomes a formula.

Row bands expand and rows/cells are renumbered. Formula references, merges, dimensions, tables, filters, conditional-format/data-validation ranges and defined names shift. A range covering the band expands: SUM(D7:D7) becomes SUM(D7:D16) for ten items. Shared formulas are resolved before expansion. Cached formula values and calculation chains are removed and a full calculation is requested on open. Chart references shift and pivot caches request refresh.

Complex workbooks need representative Excel validation. Structured formulas, dynamic arrays, drawings around repeated bands, external workbooks and pivot layouts are outside the initial fidelity guarantee. Native output retains links; PDF conversion removes external relationships from a copy and runs without egress.

API

Operation Endpoint
Create POST /templates/office
Replace source/metadata PUT /templates/{key}/office
Download original GET /templates/{key}/office/source?version=
Inspect tags GET /templates/{key}/office/tags?version=
Stateless lint POST /templates/office/lint

Authoring/source endpoints require tenant-scoped template permissions. Create, replace and lint accept multipart file and meta; meta is a JSON object encoded as a string. Create requires key and name. Optional metadata includes description, version, inputSchema, fieldMappings, dataOptions, parameters, dataSources, i18n and locale-only renderConfig. Lint accepts inputSchema. Replace requires a quoted current-version If-Match. Replacing creates a version; JSON metadata updates cannot switch between Office and visual kinds.

Source Native PDF HTML/CSV/other native formats
DOCX DOCX Gotenberg 422 office_format_unsupported
XLSX XLSX Gotenberg 422 office_format_unsupported
PPTX PPTX Gotenberg 422 office_format_unsupported

Use existing production, batch, async-job, scheduled-render, label and variant endpoints. PPTX output also requires PPTX_ENABLED and tenant feature permission. dryRun fills/validates without returning an artifact or requiring PDF conversion. Missing conversion returns 503 office_conversion_unavailable; rejected, timed out or invalid conversion returns 502 office_conversion_failed.

POST /render/preview/office accepts {template, version?, data, format} and returns a private uncached binary. Preview routes must be enabled separately.

Private storage and lifetime

Source bytes are content-addressed by SHA-256 and tenant scoped. Set TEMPLATE_SOURCE_DIR (default ./template-sources) or a private TEMPLATE_SOURCE_BUCKET using configured S3 credentials. No public source URL is exposed, including when image assets are public. Preserve source storage alongside template metadata.

Versions, labels and restores reference immutable hashes. Trash and private drafts retain sources. Permanent template purge collects blobs without any remaining tenant version/draft reference. Database gates coordinate publication and collection across replicas; file mode supports one API process. Tenant purge removes private sources. Portable backups and tenant export/import/clone include and verify source bytes/hashes.

Limits and licensing

Default uploads are limited to 20 MB (OFFICE_TEMPLATE_MAX_BYTES, maximum 100 MB). Packages are limited to 2,000 entries, 100 MB expanded input, 20 MB per XML part, 250,000 XML nodes/attributes and nesting depth 128. Unsafe paths, checksums, DOCTYPEs and invalid blocks are rejected. Expansion is bounded to 10,000 evaluation steps and 100 MB output. PDF conversion defaults to 60 seconds (OFFICE_PDF_TIMEOUT_MS) with bounded, signature-checked responses.

Unlicensed builds mark Word section footers, Excel sheets/print footers and every PowerPoint slide. Rendering/conversion remain commercial. Read-only parsing, normalization, tag analysis and linting bundled in the MIT CLI are MIT licensed; see module licensing.

Run offline:

pulp-engine office lint contract.docx --schema schema.json --strict

The command prints JSON and fails for invalid files/tags. --strict also fails for unbound-path warnings.