Skip to content

Render manifest: the portable render request

A render manifest is the portable description of what to render: input, template, fonts, locale, conformance profile, signature policy, output target, and a Fast-Web-View flag. Every transport builds and submits the same RenderManifest, whether it is a command-line interface (CLI), a framework integration, a software-as-a-service (SaaS) application programming interface (API), or a future stream connector. The contract is deterministic: two equal manifests serialize to byte-identical JavaScript Object Notation (JSON), and each manifest carries a derived idempotency key so downstream systems can recognize and deduplicate the same render.

The manifest is a Core contract. It defines the schema; the engine and the premium stream processor execute the request.

Terminal window
composer require nextpdf/core:^3

Use the builder as the stable construction path, or use RenderManifest::fromArray() for a deserialized manifest. The raw constructor is @internal: new optional fields are appended with defaults, so the builder and fromArray() absorb them without breaking your call sites.

examples/manifest/build.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Manifest\OutputObjectKey;
use NextPDF\Manifest\RenderManifestBuilder;
use NextPDF\Manifest\TemplateRef;
$manifest = RenderManifestBuilder::create('invoice-2026-0001')
->withInlineInput('<h1>Invoice 2026-0001</h1>')
->withTemplate(TemplateRef::html())
->withOutputKey(OutputObjectKey::file('invoices', '2026-0001.pdf'))
->withLocale('en-US')
->linearized()
->build();
// Deterministic, canonical-key-order JSON — equal manifests are byte-identical.
$json = $manifest->toJson();
FieldMeaning
jobIdCaller-stable logical identifier, echoed in events and receipts. Excluded from the idempotency key.
idempotencyKeyDeterministic deduplication key (see below).
inputRender data source: inline payload, a Uniform Resource Identifier (URI), or a dataset reference plus content hash.
templateThe rendering front end and an optional template identifier.
fontsDeclared font set (empty by default).
localeA BCP-47 language tag (en-US by default).
conformanceProfileA stable profile identifier (none by default).
signaturePolicyA stable policy identifier (none by default).
targetOutput object key, format, and clobber policy.
linearizeFast-Web-View flag; composes with linearization.
metadataOpaque scalar key/value map echoed into events.

IdempotencyKey::derive() hashes only the render-determining subset of the manifest: template, input content hash, fonts, locale, conformance, signature policy, the linearize flag, and the target key. It deliberately excludes jobId and metadata, so two requests with identical render-determining inputs, including the output target, share a key and can be deduplicated even when their job identifiers or tracking metadata differ. A caller may also supply an explicit key; isDerived() reports which path produced it.

RenderManifest::SCHEMA_VERSION pins the schema version (currently 1.0). Within a major version, evolution is additive-only: a reader tolerates unknown keys from a newer minor schema, but rejects a newer major version. SchemaCompatibility::assertReadable() enforces this in fromArray(), and canRead() / isForwardRead() let you test compatibility without throwing.

Because the manifest is a readonly data transfer object (DTO) whose constructor is @internal, build it through the builder or fromArray(). New fields are added as defaulted constructor parameters, so adding a field does not break those call sites.

RenderManifestValidator::validate() is non-throwing and collect-all: it returns every problem it finds instead of failing on the first one. It rejects unsafe output keys (traversal, absolute paths, stream wrappers), more than one input source, an unknown conformance profile identifier, an invalid BCP-47 locale, and a clobber-policy mismatch. warnings() returns advisory, non-blocking notes.

use NextPDF\Manifest\RenderManifestValidator;
$problems = (new RenderManifestValidator())->validate($manifest);
if ($problems !== []) {
// Each entry is a stable, human-readable problem string.
}

SingleDocumentRenderer turns one manifest into one Portable Document Format (PDF) deterministically. It is pure: it returns bytes plus their sha-256 digest and writes no file, so staging and exactly-once commit stay the caller’s (or the stream processor’s) concern.

use NextPDF\Manifest\Render\SingleDocumentRenderer;
use NextPDF\Manifest\Render\StandaloneDocumentFactory;
$renderer = new SingleDocumentRenderer(new StandaloneDocumentFactory());
$outcome = $renderer->render($manifest);
$bytes = $outcome->bytes;
$digest = $outcome->sha256;
$pages = $outcome->pageCount;
TypeKindKey membersStabilitySince
RenderManifestreadonly DTOtoArray(), toJson(), fromArray(), canonicalDigestInput(), SCHEMA_VERSIONstable3.2.0
RenderManifestBuilderbuildercreate(), with*(), linearized(), build()stable3.2.0
IdempotencyKeyvalue objectof(), derive(), equals(), isDerived()stable3.2.0
SchemaCompatibilityhelpercanRead(), isForwardRead(), assertReadable()stable3.2.0
RenderManifestValidatorservicevalidate(), warnings()stable3.2.0
SingleDocumentRendererservicerender(): RenderOutcomestable3.2.0

fromArray() throws RenderManifestException when a required field is missing or the schema version is unreadable.