@paperjsx/json-to-docx
v0.0.1
Published
Generate Word documents from JSON
Downloads
200
Maintainers
Readme
@paperjsx/json-to-docx
Generating Word documents from code typically means manipulating raw OOXML XML or using heavyweight browser-based PDF-to-DOCX converters. This package takes a JSON document schema and produces a native .docx binary — no DOM, no Puppeteer, no browser required.
Install
npm install @paperjsx/json-to-docxRequires Node.js >= 18.
Quick Start
import { renderToDocx } from "@paperjsx/json-to-docx";
import { writeFileSync } from "node:fs";
const result = await renderToDocx({
type: "DocxDocument",
pageSize: "a4",
theme: { preset: "corporate" },
pages: [
{
elements: [
{ type: "heading", level: 1, text: "Annual Performance Report" },
{
type: "paragraph",
text: "Revenue grew 34% year-over-year, driven by enterprise expansion in North America and APAC.",
},
{
type: "table",
style: "striped",
rows: [
{ isHeader: true, cells: [{ text: "Region" }, { text: "Revenue" }, { text: "Growth" }] },
{ cells: [{ text: "North America" }, { text: "$5.1M" }, { text: "+28%" }] },
{ cells: [{ text: "Europe" }, { text: "$3.6M" }, { text: "+16%" }] },
{ cells: [{ text: "APAC" }, { text: "$3.5M" }, { text: "+42%" }] },
{ cells: [{ text: "LATAM" }, { text: "$1.9M" }, { text: "+35%" }] },
],
},
{
type: "chart",
chartType: "bar",
series: [
{ name: "2024", dataPoints: [4000, 3100, 2500, 1400] },
{ name: "2025", dataPoints: [5100, 3600, 3500, 1900] },
],
categories: ["NA", "Europe", "APAC", "LATAM"],
},
],
},
],
});
writeFileSync("report.docx", result.buffer);
console.log(result.stats);
// { pages: 1, elements: 4, renderTimeMs: 120 }How It Works
Input JSON is validated against a Zod schema, adapted to a structured document model, then serialized to OOXML XML and packaged as a .docx ZIP archive. Charts are rendered via the built-in PVCE engine. Optional peer dependencies enable PDF export (pdf-lib), image optimization (sharp), and font metrics (opentype.js).
Element Types
| Type | Description | Key Properties |
| --- | --- | --- |
| heading | Section headings (H1-H6) | level, text, bookmark, keepWithNext |
| paragraph | Rich text blocks | text or runs[] (TextRun), indent, keepLines, pageBreakBefore |
| list | Bulleted or numbered lists | style (bullet/number/letter/roman), items[], nested lists |
| table | Data tables | rows[], columns[], style (striped/bordered/modern/minimal/corporate), headerRepeat, colSpan/rowSpan |
| image | Inline or floating images | src (HTTPS or data URI), caption, float (square/tight/behind/inFront), alignment |
| chart | Editable charts | chartType, series[], categories[], legend, axes, title |
| shape | Vector shapes | shapeType (rectangle/ellipse/triangle/diamond/line/arrow), fill, stroke |
| codeBlock | Source code blocks | code, language, lineNumbers |
| divider | Horizontal rules | style (solid/dashed/dotted/double) |
| pageBreak | Explicit page boundary | — |
| container | Layout wrapper | layout (vertical/horizontal/grid), children[] |
API Reference
Core Functions
| Function | Parameters | Returns | Description |
| --- | --- | --- | --- |
| renderToDocx | (input, options?) | Promise<DocxResult> | Render JSON to DOCX binary |
| hydrateDocx | (templateBuffer, data, options?) | Promise<DocxResult> | Fill placeholders in existing DOCX |
| validateDocxDocument | (input) | ValidationResult | Validate input against schema |
| scanForPlaceholders | (templateBuffer) | string[] | List {{placeholder}} tokens in a DOCX template |
DocxResult
{
buffer: Buffer; // The .docx file
mimeType: string; // "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
extension: string; // "docx"
stats: RenderStats; // { pages, elements, renderTimeMs }
warnings: DocxWarning[];
}Render Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| theme | ThemeConfig \| { preset: ThemePresetName } | Default theme | Visual theme |
| onProgress | (progress: RenderProgress) => void | — | Render progress callback |
| signal | AbortSignal | — | Cancellation signal |
Themes
6 built-in theme presets control fonts, colors, heading styles, and table formatting:
| Preset | Heading Font | Body Font | Accent Color | Style |
| --- | --- | --- | --- | --- |
| corporate | Arial | Arial | #1B365D | Clean, professional |
| modern | Helvetica Neue | Helvetica Neue | #2563EB | Minimal, contemporary |
| academic | Times New Roman | Times New Roman | #1A1A1A | Formal, traditional |
| classic | Georgia | Georgia | #4A5568 | Serif, elegant |
| minimal | Inter | Inter | #000000 | Stark, no decoration |
| dark | SF Pro Display | SF Pro Text | #60A5FA | Dark background, light text |
const result = await renderToDocx({
type: "DocxDocument",
theme: { preset: "modern" },
pages: [/* ... */],
});Custom themes override any preset field:
theme: {
preset: "corporate",
colors: { primary: "#7C3AED", accent: "#F59E0B" },
fonts: { heading: "Montserrat", body: "Open Sans" },
}Charts
8 chart types via the built-in PVCE (PaperJSX Visual Chart Engine):
| Type | Variants | Data Format |
| --- | --- | --- |
| bar | Clustered, stacked | categories[] + series[].dataPoints[] |
| column | Clustered, stacked | Same as bar (vertical orientation) |
| line | Standard, smooth | categories[] + series[].dataPoints[] |
| area | Standard, stacked | categories[] + series[].dataPoints[] |
| pie | Standard | categories[] + single series |
| doughnut | Configurable hole size | categories[] + single series |
| scatter | XY points | series[].dataPoints[].{x, y} |
| radar | Standard, filled | categories[] + series[].dataPoints[] |
{
type: "chart",
chartType: "line",
title: "Monthly Active Users",
categories: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
series: [
{ name: "2024", dataPoints: [12000, 14500, 16200, 15800, 18000, 21000] },
{ name: "2025", dataPoints: [18000, 22000, 25000, 24500, 28000, 32000] },
],
legend: { position: "bottom" },
axes: {
y: { title: "Users", numberFormat: "#,##0" },
},
}Template Hydration
Fill {{placeholder}} tokens in existing DOCX files:
import { hydrateDocx, scanForPlaceholders } from "@paperjsx/json-to-docx";
import { readFileSync } from "node:fs";
const template = readFileSync("contract-template.docx");
// Discover available placeholders
const placeholders = await scanForPlaceholders(template);
// ["client_name", "effective_date", "contract_value", "payment_terms"]
// Fill them
const result = await hydrateDocx(template, {
client_name: "Acme Corporation",
effective_date: "March 15, 2026",
contract_value: "$240,000",
payment_terms: "Net 30",
});
writeFileSync("contract-acme.docx", result.buffer);Headers, Footers, and Sections
{
type: "DocxDocument",
headerFooter: {
header: { text: "Confidential — Acme Corp" },
footer: { text: "Page {{pageNumber}} of {{totalPages}}" },
},
pages: [
{
sectionBreak: "nextPage", // "nextPage" | "continuous" | "evenPage" | "oddPage"
elements: [/* ... */],
},
],
}Compliance Validation
Built-in validators for region-specific invoice requirements:
| Validator | Standard | Use Case |
| --- | --- | --- |
| validateIndiaGSTQR | India GST | QR code requirements for Indian tax invoices |
| validateEUReverseCharge | EU VAT | Reverse charge notice for cross-border EU invoices |
| validateBrazilianDanfe | Brazilian DANFE | Electronic invoice document formatting |
import { validateIndiaGSTQR } from "@paperjsx/json-to-docx";
const issues = validateIndiaGSTQR(invoiceData);
// [{ field: "gstin", message: "GSTIN must be 15 characters", severity: "error" }]Secure PDF Export
Convert DOCX output to encrypted PDF (requires pdf-lib peer dependency):
import { SecurePDF } from "@paperjsx/json-to-docx";
const pdf = await SecurePDF.fromDocx(result.buffer, {
userPassword: "reader-password",
ownerPassword: "admin-password",
permissions: { printing: true, copying: false, modifying: false },
});
writeFileSync("report-secured.pdf", pdf);Peer Dependencies
| Package | Required | Purpose |
| --- | --- | --- |
| pdf-lib | Optional | PDF export via SecurePDF |
| sharp | Optional | Image optimization and format conversion |
| opentype.js | Optional | Advanced font metrics and glyph measurement |
Error Handling
import { DOCXError, isDOCXError } from "@paperjsx/json-to-docx";
try {
await renderToDocx(input);
} catch (err) {
if (isDOCXError(err)) {
console.log(err.code); // "VALIDATION_FAILED" | "RENDER_FAILED" | "MEDIA_FETCH_FAILED" | ...
console.log(err.message); // Human-readable description
}
}Limitations
- No browser runtime support. Node.js only.
- Images referenced by URL are fetched at render time. Ensure URLs are accessible from the render environment.
- Complex nested tables (4+ levels deep) may produce layout artifacts in some Word versions.
- PDF export requires the optional
pdf-libpeer dependency.
Documentation
License
Proprietary. Requires a license key from paperjsx.com/pricing.
