Home  ›  Playgrounds  ›  Scale

How far does it go?

Generate up to 100,000 pages in this tab. No server, no upload, no install. The point is not that it is fast — it is that the document is never fully assembled as one binary, so the ceiling is your machine's memory rather than a hard limit in the library.

Output

Chunk size

Progress

1 / 3 — Building the document model
2 / 3 — Assembling PDF objects
3 / 3 — Encoding and streaming

Why phase 2 has no percentage. pdfnative builds every PDF object before emitting the first byte, and that loop yields nothing while it runs. There is no progress signal to read, so drawing a bar there would mean inventing one. Phase 3 shows a byte counter rather than a percentage for the same reason — the total size is not known until the generator finishes. See the streaming guide for the exact memory profile.

Result

Pages (measured)
Output
Wall clock
Pages / second
Bytes / page

What this demonstrates

The content is deliberately lean

Each page carries a heading and a paragraph — between 496 and 509 bytes of output per page depending on document length (509 at 100,000 pages). That is a deliberate choice, not a flattering one: it makes the page count exact and known before generation starts, which is what lets the pages-per-second figure mean something instead of being derived from a byte estimate. Denser pages (tables, embedded fonts, images) produce proportionally more bytes and take proportionally longer.

If you want to see a realistic document instead of a scalability floor, the PDF Toolkit and Charts playgrounds build ordinary documents with real content.

Code

import { buildDocumentPDFStreamTrue } from 'pdfnative';

// Exactly one page per iteration: a page break separates consecutive pages.
function leanBlocks(pages) {
  const blocks = [];
  for (let i = 0; i < pages; i++) {
    if (i > 0) blocks.push({ type: 'pageBreak' });
    blocks.push({ type: 'heading', text: `Section ${i + 1}`, level: 2 });
    blocks.push({ type: 'paragraph', text: `Page ${i + 1} of ${pages}. …` });
  }
  return blocks;
}

const blocks = leanBlocks(100_000);
let bytes = 0;

// Supply the footer template explicitly: relying on `footerText` also pulls in
// the default template, which stamps `{page}/{pages}` on the right.
for await (const chunk of buildDocumentPDFStreamTrue(
  { title: 'Scale', blocks, layout: { footerTemplate: { center: 'page {page}' } } },
  { maxBlocks: blocks.length + 1024 },   // the default rail is 100,000
  { chunkSize: 262_144 },
)) {
  bytes += chunk.length;                 // …or write it straight to disk
}

Two constraints are validated at the boundary and throw: streaming rejects toc blocks, and rejects the {pages} placeholder in a header or footer template you supply. Worth being precise about the second one: buildDocumentPDFStreamTrue finishes assembling before it yields, so the total page count is in fact known — the rejection is a conservative rule that will matter once page-at-a-time assembly lands. Note also that it only inspects a template you pass; the built-in default does use {pages} and resolves it.

View this page’s source on GitHub →