Skip to content

paperjsx/json-to-pdf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

@paperjsx/json-to-pdf

Generate native PDF files from JSON or TypeScript. No headless Chromium, no wkhtmltopdf, no HTML conversion — a custom PDF object model with Yoga flex layout, fontkit / HarfBuzz text shaping, and a progressive capability pipeline.

npm license

npm install @paperjsx/json-to-pdf

Requires Node.js >=18. ESM only.

Quick Start

import { PdfEngine } from "@paperjsx/json-to-pdf";
import { writeFileSync } from "node:fs";

const buffer = await PdfEngine.render({
  meta: { title: "Monthly Update", author: "Acme Inc." },
  page: { size: "Letter", margin: 48 },
  children: [
    { type: "heading", value: "Monthly Update", level: 1 },
    { type: "paragraph", value: "Revenue grew 18% month over month." },
    {
      type: "table",
      columns: [{ width: 120 }, { width: 80, align: "right" }],
      rows: [
        { isHeader: true, cells: [{ value: "Region" }, { value: "Revenue" }] },
        { cells: [{ value: "North America" }, { value: "$5.1M" }] },
        { cells: [{ value: "Europe" }, { value: "$3.6M" }] },
      ],
    },
  ],
});

writeFileSync("update.pdf", buffer);

What You Get

  • Custom PDF object model — full control over PDF output. No dependency on pdfkit or pdf-lib at runtime.
  • Flexbox layout — Yoga-based structured layout with widow/orphan control and Knuth-Plass-style line breaking.
  • Text shaping — fontkit for metrics, HarfBuzz (pro) for emoji / RTL / complex scripts, subset-font to shrink embedded fonts to used glyphs only.
  • Tables with spans — row spans, column spans, cross-page continuation, header repeat.
  • Graphics — solid fills, gradients, strokes, RGB + CMYK color, SVG paths parsed into PDF draw commands.
  • Interactive features — forms, annotations, TOC, bookmarks, named destinations.
  • Accessibility — tagged PDF structure for WCAG compliance (headings, lists, tables marked up for screen readers).
  • PDF/A-2a — ICC profile embedding and XMP metadata for long-term archival (pro).
  • StreamingrenderStream() emits PDF bytes as they're produced for large documents.
  • Digital signatures — OpenSSL-based signing with timestamp support (pro).
  • Deterministic output — same input, byte-identical PDF.

Document Shape

import type { PdfDocument } from "@paperjsx/json-to-pdf";

const doc: PdfDocument = {
  meta: { title, author, subject, keywords },
  page: {
    size: "Letter",            // "Letter" | "A4" | "Legal" | { width, height } (pt)
    orientation: "portrait",
    margin: 48,                // or { top, right, bottom, left }
  },
  defaultFont: "Helvetica",
  children: [ /* PdfNode[] */ ],
};

Elements: heading, paragraph, list, table, image, page-break, divider, container, group, svg, form-field, annotation, toc.

The engine auto-detects which capability phases are needed (simple docs skip table / interactive / accessibility / PDF/A pipelines), so minimal documents render on a minimal codepath.

Streaming

For large documents, stream bytes as they're produced instead of buffering the entire PDF in memory:

import { PdfEngine } from "@paperjsx/json-to-pdf";
import { createWriteStream } from "node:fs";

const out = createWriteStream("large-report.pdf");

await PdfEngine.renderStream(doc, {
  onChunk: (chunk) => out.write(chunk),
  onEnd: () => out.end(),
});

Tables

{
  type: "table",
  columns: [
    { width: 200 },
    { width: 100, align: "right" },
    { width: 100, align: "right" },
  ],
  rows: [
    {
      isHeader: true,
      cells: [{ value: "Item" }, { value: "Qty" }, { value: "Total" }],
    },
    {
      cells: [
        { value: "Enterprise License" },
        { value: "1" },
        { value: "$12,000.00" },
      ],
    },
  ],
  style: { borderColor: "#E5E7EB", headerBackground: "#F3F4F6" },
}

Tables longer than a page continue automatically with the header row repeated. colSpan and rowSpan are validated against the declared column count before rendering.

Fonts

import { PdfEngine } from "@paperjsx/json-to-pdf";
import { readFileSync } from "node:fs";

await PdfEngine.render({
  ...doc,
  fonts: [
    { family: "Inter", weight: 400, source: readFileSync("./Inter-Regular.ttf") },
    { family: "Inter", weight: 700, source: readFileSync("./Inter-Bold.ttf") },
  ],
});

Fonts are subset to only the glyphs actually used in the document, typically reducing PDF size by 80–95%. Standard 14 PDF fonts (Helvetica, Times, Courier, etc.) are built in and do not need to be supplied.

Images

{ type: "image", src: "./logo.png", width: 200, alt: "Acme logo" }
{ type: "image", src: "https://example.com/chart.jpg", width: 400 }
{ type: "image", src: "data:image/png;base64,...", width: 300 }

PNG and JPEG are decoded natively. Remote URLs pass through an SSRF guard (private IP blocklist, scheme allowlist, DNS timeout, 50MB data-URL cap).

Public API

PdfEngine.render(doc, options?)           // Uint8Array
PdfEngine.renderStream(doc, handlers)      // chunked output

// Validation / phase detection
validatePdfDocument(doc)
isPhase3Document(doc), containsTableNode(doc), containsFormNode(doc)

// Utilities
registerFont({ family, weight, source })
parseSvgPath(d)                            // SVG "d" → PDF command array

// Determinism
setDeterministicMode(true)

Full type surface in dist/index.d.ts.

Determinism

import { setDeterministicMode } from "@paperjsx/json-to-pdf";
setDeterministicMode(true);

Freezes creation timestamps, document IDs, and any other sources of nondeterminism. Verified with a byte-equality test suite.

Error Handling

import { PdfError } from "@paperjsx/json-to-pdf";

try {
  await PdfEngine.render(doc);
} catch (err) {
  if (err instanceof PdfError) {
    console.error(err.code, err.phase, err.message);
  }
  throw err;
}

Upgrade to Pro

@paperjsx/json-to-pdf-pro adds:

  • commercial / self-hosted production license
  • HarfBuzz text shaping (RTL, complex scripts, emoji)
  • PDF/A-2a compliance (ICC profiles, XMP metadata)
  • digital signatures with timestamping
  • advanced typography (hyphenation, justification, OpenType features)
  • validation & repair tooling (XRef rebuild, stream length fixing)
  • PDF form fill

The API is identical — swap the import and provide PAPERJSX_LICENSE_KEY.

Links

License

Apache-2.0. See LICENSE.

About

Generate PDF documents from JSON

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors