Work in progress — this reference is being written in the open. Unfinished pages are excluded from search engines.
Paged · IDML Reference
Test corpus

The docs examples loop

Every example in this reference is a hand-authored, unzipped IDML package that CI assembles into a real .idml and runs through the pinned renderer on each build — a structural parse-and-build gate that breaks the docs if the renderer ever stops accepting it.

Pro· explanation

Every example in this reference is validated against the real renderer on every build — so a page can never describe something the renderer rejects.

In short: The examples you see embedded throughout this reference are not illustrations typed into the prose. Each one lives in this repository as a hand-authored, unzipped IDML package — readable XML parts you can review part by part. On every build, CI zips each package into a real .idml (reproducing the exact byte rules the renderer expects) and runs it through paged-inspect, the renderer's introspection tool, built from a pinned commit of the engine. If the renderer stops accepting an example, the docs build breaks in the same run. That is the living-docs guarantee: the reference and the renderer cannot quietly drift apart.

Examples are stored unzipped

An IDML file is a ZIP archive of XML parts (see Package anatomy). We could store each example as a finished .idml, but a binary ZIP is opaque in review — you cannot see in a pull request what changed, and you cannot tell at a glance whether a part is honestly hand-written or copied. So examples are stored the other way round: unzipped, one directory per example under examples/, with the package parts as plain files and an example.json manifest beside them.

Storing them unzipped buys three things. The parts are diff-able in review, so the clean-room rule that examples are ours — authored, not transcribed from the spec — is auditable line by line. The chapter-relevant part (the story, the spread, whatever the page is teaching) can be imported as text straight into the MDX view the reader sees. And each example stays a minimum viable fragment: the smallest package that demonstrates one concept, with the complete-but-uninteresting skeleton kept present-but-quiet around it.

The parts are authored clean-room, grounded in our own parser (core/crates/paged-parse) and the paged-gen generators — never copied from the IDML specification. smallest-valid/ is the worked reference: a one-page, one-story package that the renderer reports as exactly one page, one frame, one story, one paragraph, one run.

Everything a complete IDML package needs, and nothing more.Stories/Story_ustory.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging" DOMVersion="20.0">
  <Story Self="ustory">
    <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/$ID/[No paragraph style]">
      <CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]">
        <Content>Hello, paged media.</Content>
      </CharacterStyleRange>
    </ParagraphStyleRange>
  </Story>
</idPkg:Story>

Assembling to a real .idml

A pile of XML files is not yet an IDML — the format has byte-level rules about how the ZIP is laid out, and a renderer that is strict about them (as ours is) will reject a naively zipped package. So the docs carry an assembler (scripts/examples/assemble.ts) that reproduces those rules exactly, mirroring the generator's own packaging logic in the engine:

RuleWhat the assembler does
mimetype first, storedThe mimetype part is the first entry in the archive and is stored uncompressed, with no trailing newline.
everything else deflatedAll other parts are compressed normally.
canonical entry orderParts are emitted in a fixed order — mimetype, designmap, then META-INF, Resources, XML, MasterSpreads, Spreads, Stories.
fixed timestampsEvery entry uses a fixed 1980-01-01 timestamp, so the same package always assembles to byte-identical output.

The same assembler feeds two consumers, and that is deliberate: it feeds the CI validator today, and it is wired to feed the WebGPU live preview later. Because both get bytes from the same function, the package CI validates and the package the reader will eventually see rendered are byte-for-byte identical — there is no "works in the test, differs in the preview" gap.

Validating against the pinned renderer

With a real .idml in hand, the validator (scripts/examples/validate.ts) runs it through paged-inspect --json and checks the result against the expect counts in the example's manifest — pages, frames, stories, paragraphs, runs. If the renderer reports different totals than the manifest claims, or refuses the package outright, the example fails and the build fails with it. An example can also be marked pathological, which inverts the check: it is expected to make the renderer surface a diagnostic, and it fails if the renderer accepts it.

The renderer used is not "whatever is latest." It is built from a specific commit of the engine, pinned in this repo's core.pin. This is what ties the docs version to the renderer's version: a given state of the docs validates against a known renderer, the pin moves deliberately by pull request, and a weekly drift job re-validates against the engine's main to surface breakage early — without silently changing what the published docs were tested against. See The renderer for what paged-inspect is introspecting.

A structural gate, not a visual one

Here is the honest boundary, and it is the most important thing on this page. The examples gate is structural, not pixel-level. paged-inspect opens the package and builds the renderer's internal model — it parses the XML, resolves the document, builds the display list — and reports the structure it found. It does not rasterise. It needs no GPU and no fonts.

Supportedexamples gate: parse + build-document via paged-inspect; structural counts only, no rasterisation

That makes the examples gate the mirror image of the fidelity gate. The fidelity gate is pixel-deep but narrow — it proves a handful of generated fixtures match InDesign, down to the colour of individual pixels. The examples gate is broad but shallow — it touches every documented construct that has an example and proves the renderer still accepts and models it, but it never checks that the result looks right. An example can be green here and still have a rendering bug, because nothing in this loop ever paints it. We say so plainly rather than letting a green check imply more than it proves.

Why this makes the docs living

Documentation rots when it describes a system that has moved on underneath it. The examples loop is the mechanism that stops that here. Because every example is a real package run through the real renderer on every build, a page cannot keep claiming the renderer accepts a construct after the renderer has stopped — the example that backs the claim would fail first, and the build would go red. The reference is not asserted to match the renderer; it is tested to, continuously. That is the guarantee these docs are built on, and it is worth knowing exactly how far it reaches: it guarantees the renderer still takes the package, not that the page looks the way the prose says it should.

Frequently asked questions

Why store examples unzipped instead of as finished .idml files? For reviewability and reuse. Unzipped, the XML parts are diff-able in a pull request, so a reviewer can confirm each example is hand-authored clean-room rather than copied — and the chapter-relevant part can be imported straight into the page as readable text. A committed binary ZIP would be opaque on both counts. The .idml is a build artifact, assembled on demand, never committed.

What exactly breaks the build when an example "fails"? Either the renderer refuses the assembled package (a parse or build-document error), or paged-inspect reports structural counts — pages, frames, stories, paragraphs, runs — that disagree with the example's manifest. For an example marked pathological, it is the opposite: failing means the renderer accepted something that was supposed to surface a diagnostic. Any of these returns a non-zero exit and fails the docs build.

Does a green examples gate mean the example renders correctly? No. The gate is structural: it proves the renderer parses the package and builds its internal model with the expected shape. It does not rasterise, so it says nothing about pixels — colour, glyph placement, layout fidelity. Visual correctness is the job of the fidelity loop, which runs over the generated corpus and diffs against InDesign. The two gates are complementary, and neither stands in for the other.

On this page