pretext-pdf 0.5.3 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +115 -0
- package/README.md +366 -276
- package/dist/assets.d.ts +5 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +248 -43
- package/dist/assets.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fonts.d.ts.map +1 -1
- package/dist/fonts.js +88 -16
- package/dist/fonts.js.map +1 -1
- package/dist/index.d.ts +29 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/markdown.d.ts +28 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +222 -0
- package/dist/markdown.js.map +1 -0
- package/dist/measure-blocks.d.ts.map +1 -1
- package/dist/measure-blocks.js +347 -62
- package/dist/measure-blocks.js.map +1 -1
- package/dist/measure-text.d.ts.map +1 -1
- package/dist/measure-text.js +1 -8
- package/dist/measure-text.js.map +1 -1
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +13 -21
- package/dist/measure.js.map +1 -1
- package/dist/render-blocks.d.ts +4 -1
- package/dist/render-blocks.d.ts.map +1 -1
- package/dist/render-blocks.js +227 -105
- package/dist/render-blocks.js.map +1 -1
- package/dist/render-extras.d.ts.map +1 -1
- package/dist/render-extras.js +72 -71
- package/dist/render-extras.js.map +1 -1
- package/dist/render-utils.d.ts +9 -2
- package/dist/render-utils.d.ts.map +1 -1
- package/dist/render-utils.js +24 -13
- package/dist/render-utils.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +27 -3
- package/dist/render.js.map +1 -1
- package/dist/rich-text.d.ts +0 -4
- package/dist/rich-text.d.ts.map +1 -1
- package/dist/rich-text.js +15 -9
- package/dist/rich-text.js.map +1 -1
- package/dist/templates.d.ts +79 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +201 -0
- package/dist/templates.js.map +1 -0
- package/dist/types.d.ts +139 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +241 -28
- package/dist/validate.js.map +1 -1
- package/package.json +57 -12
package/README.md
CHANGED
|
@@ -1,91 +1,109 @@
|
|
|
1
1
|
# pretext-pdf
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **The PDF library AI agents speak natively.**
|
|
4
4
|
>
|
|
5
|
-
>
|
|
5
|
+
> A `PdfDocument` is plain JSON. LLMs emit it in one shot — no codegen, no headless browser, no `eval`. Humans get a strict-typed, declarative API for invoices, reports, and templates.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/pretext-pdf)
|
|
8
8
|
[](https://www.npmjs.com/package/pretext-pdf)
|
|
9
9
|
[](https://github.com/Himaan1998Y/pretext-pdf/actions)
|
|
10
10
|
[](https://www.typescriptlang.org/)
|
|
11
|
-
[](#test-coverage)
|
|
11
|
+
[](#test-coverage)
|
|
13
12
|
[](LICENSE)
|
|
14
13
|
|
|
14
|
+
**[Live demo](https://himaan1998y.github.io/pretext-pdf/)** — edit JSON, render PDFs instantly. No install.
|
|
15
|
+
**[`pretext-pdf-mcp`](https://www.npmjs.com/package/pretext-pdf-mcp)** — drop-in MCP server for Claude / Cursor / Windsurf.
|
|
16
|
+
**[Migrating from pdfmake?](docs/MIGRATION_FROM_PDFMAKE.md)** — every pattern mapped.
|
|
17
|
+
|
|
18
|
+
*Layout powered by [`@chenglou/pretext`](https://github.com/chenglou/pretext) — the precision text-layout engine by [Cheng Lou](https://github.com/chenglou) (React core team, Midjourney).*
|
|
19
|
+
|
|
15
20
|
---
|
|
16
21
|
|
|
17
|
-
##
|
|
22
|
+
## Why pretext-pdf
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
There are three established camps in JS PDF generation, and one gap. pretext-pdf lives in the gap.
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
| | pdfmake / jsPDF / pdfkit | Puppeteer / Playwright | LaTeX / WeasyPrint | **pretext-pdf** |
|
|
27
|
+
|---|---|---|---|---|
|
|
28
|
+
| Lightweight (no Chromium) | ✅ | ❌ ~300 MB | ❌ native binaries | ✅ |
|
|
29
|
+
| Pure ESM, runs in serverless | ✅ | ⚠️ painful in Lambda | ❌ | ✅ |
|
|
30
|
+
| Professional typography (kerning, hyphenation, RTL/CJK) | ❌ | ✅ | ✅ | ✅ |
|
|
31
|
+
| Declarative — describe the document, don't draw it | ⚠️ partial | ❌ | ❌ | ✅ |
|
|
32
|
+
| **LLM emits a working document in one shot** | ❌ requires a code-execution loop | ❌ requires HTML+CSS knowledge | ❌ requires LaTeX knowledge | ✅ pure JSON |
|
|
33
|
+
| MCP server available out of the box | ❌ | ❌ | ❌ | ✅ |
|
|
26
34
|
|
|
27
|
-
**
|
|
35
|
+
**The headline:** every other JS PDF library asks an LLM to *write code*. pretext-pdf asks it for a JSON object. That difference is what makes agent-generated PDFs reliable.
|
|
28
36
|
|
|
29
37
|
---
|
|
30
38
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
| | pdfmake | Puppeteer | **pretext-pdf** |
|
|
34
|
-
|---|---|---|---|
|
|
35
|
-
| Easy declarative API | ✅ | ❌ | ✅ |
|
|
36
|
-
| Professional typography | ❌ | ✅ | ✅ |
|
|
37
|
-
| Lightweight (no browser) | ✅ | ❌ | ✅ |
|
|
38
|
-
| International text (RTL/CJK) | ❌ | ✅ | ✅ |
|
|
39
|
-
| Pure Node.js | ✅ | ❌ | ✅ |
|
|
40
|
-
| Hyperlinks + annotations | ❌ | ✅ | ✅ |
|
|
41
|
-
| Document assembly | ❌ | ❌ | ✅ |
|
|
39
|
+
## Built for AI agents
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
A `PdfDocument` is a plain JSON object. No functions are required. No classes to instantiate. Every field is optional except `type` and a few element-specific essentials. That shape is exactly what an LLM can produce reliably with no tool-use loop.
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
### Drop into Claude / Cursor / Windsurf via MCP
|
|
46
44
|
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"pretext-pdf": {
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["-y", "pretext-pdf-mcp"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
Tools exposed: `generate_pdf`, `generate_invoice`, `generate_report`, `generate_from_markdown`, `list_element_types`. Built on the live [`pretext-pdf-mcp`](https://www.npmjs.com/package/pretext-pdf-mcp) package — versioned alongside this library.
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
### Or call from any agent framework
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
```typescript
|
|
61
|
+
import { render } from 'pretext-pdf'
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
// Whatever produced this JSON — Claude, GPT, a workflow node, a form submission — works the same
|
|
64
|
+
const pdf = await render({
|
|
65
|
+
metadata: { title: 'AI-generated quarterly report' },
|
|
66
|
+
content: [
|
|
67
|
+
{ type: 'heading', level: 1, text: 'Q1 2026 Summary' },
|
|
68
|
+
{ type: 'paragraph', text: 'Revenue grew 18% YoY.' },
|
|
69
|
+
{ type: 'table', columns: [...], rows: [...] },
|
|
70
|
+
],
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Why JSON-first matters for agents
|
|
75
|
+
|
|
76
|
+
- **No code execution loop.** The model returns JSON; you call `render()`. No sandbox, no `vm`, no Vercel Sandbox roundtrip.
|
|
77
|
+
- **Schema-validatable.** Strict TypeScript types double as the contract. Pair with [Anthropic tool use](https://docs.anthropic.com/en/docs/build-with-claude/tool-use) or [Vercel AI SDK structured output](https://sdk.vercel.ai/docs/ai-sdk-core/generating-structured-data) for guaranteed-shape results.
|
|
78
|
+
- **Self-correcting errors.** Every failure throws `PretextPdfError` with a typed `code`. Feed it back to the model and it fixes itself.
|
|
79
|
+
- **Progressive disclosure.** Optional peer deps mean an agent can ask for QR codes, charts, or markdown only when needed — token-efficient prompts.
|
|
64
80
|
|
|
65
81
|
---
|
|
66
82
|
|
|
67
83
|
## Install
|
|
68
84
|
|
|
69
85
|
```bash
|
|
70
|
-
npm install pretext-pdf
|
|
86
|
+
npm install pretext-pdf
|
|
71
87
|
```
|
|
72
88
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```
|
|
89
|
+
> **ESM only** — use `import`, not `require`. Requires Node.js ≥ 18.
|
|
90
|
+
|
|
91
|
+
Optional peer dependencies — install only what you need:
|
|
77
92
|
|
|
78
|
-
Optional peer dependency (for cryptographic signing):
|
|
79
93
|
```bash
|
|
80
|
-
npm install @
|
|
94
|
+
npm install @napi-rs/canvas # SVG / qr-code / barcode / chart elements
|
|
95
|
+
npm install qrcode # qr-code element
|
|
96
|
+
npm install bwip-js # barcode element
|
|
97
|
+
npm install vega vega-lite # chart element (Vega-Lite specs → vector SVG)
|
|
98
|
+
npm install marked # pretext-pdf/markdown entry point
|
|
99
|
+
npm install @signpdf/signpdf # PKCS#7 cryptographic signing
|
|
81
100
|
```
|
|
82
101
|
|
|
83
|
-
> **Encryption is built-in since v0.4.0
|
|
84
|
-
> **Type safety certified v0.4.6** — 100% strict TypeScript, zero any-casts in critical paths.
|
|
102
|
+
> **Encryption is built-in** since v0.4.0 — no extra install needed. Just add `encryption` to your document config.
|
|
85
103
|
|
|
86
104
|
---
|
|
87
105
|
|
|
88
|
-
## Quick
|
|
106
|
+
## Quick start
|
|
89
107
|
|
|
90
108
|
```typescript
|
|
91
109
|
import { render } from 'pretext-pdf'
|
|
@@ -117,7 +135,7 @@ const pdf = await render({
|
|
|
117
135
|
writeFileSync('invoice.pdf', pdf)
|
|
118
136
|
```
|
|
119
137
|
|
|
120
|
-
### Builder API
|
|
138
|
+
### Builder API (fluent style)
|
|
121
139
|
|
|
122
140
|
```typescript
|
|
123
141
|
import { createPdf } from 'pretext-pdf'
|
|
@@ -131,108 +149,162 @@ const pdf = await createPdf({ pageSize: 'A4' })
|
|
|
131
149
|
|
|
132
150
|
---
|
|
133
151
|
|
|
134
|
-
##
|
|
152
|
+
## Output samples
|
|
135
153
|
|
|
136
|
-
|
|
154
|
+
Real documents generated with pretext-pdf:
|
|
137
155
|
|
|
138
|
-
|
|
156
|
+
| Invoice | Market Report | Resume / CV |
|
|
157
|
+
|---------|--------------|-------------|
|
|
158
|
+
| [](examples/showcase-invoice.ts) | [](examples/showcase-report.ts) | [](examples/showcase-resume.ts) |
|
|
159
|
+
| [View source](examples/showcase-invoice.ts) | [View source](examples/showcase-report.ts) | [View source](examples/showcase-resume.ts) |
|
|
139
160
|
|
|
140
|
-
|
|
161
|
+
---
|
|
141
162
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
"pretext-pdf": {
|
|
146
|
-
"command": "npx",
|
|
147
|
-
"args": ["-y", "pretext-pdf-mcp"]
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
```
|
|
163
|
+
## What's in v0.8.0
|
|
164
|
+
|
|
165
|
+
Five new capabilities, all behind optional peer dependencies (zero extra weight if unused):
|
|
152
166
|
|
|
153
|
-
|
|
167
|
+
- **`qr-code`** — scannable QR codes for UPI payments, URLs, vCards. Requires `qrcode`.
|
|
168
|
+
- **`barcode`** — 100+ symbologies (EAN-13, Code128, PDF417, DataMatrix…). Requires `bwip-js`.
|
|
169
|
+
- **`chart`** — embed Vega-Lite specs as crisp vector SVG. Requires `vega` + `vega-lite`.
|
|
170
|
+
- **`pretext-pdf/markdown`** — convert any Markdown string to `ContentElement[]` in one call. Requires `marked`.
|
|
171
|
+
- **`pretext-pdf/templates`** — zero-dep template helpers: `createInvoice`, `createGstInvoice` (India GST / IGST / CGST+SGST), `createReport`.
|
|
154
172
|
|
|
155
|
-
|
|
173
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## India / GST invoicing
|
|
178
|
+
|
|
179
|
+
pretext-pdf has built-in support for Indian invoice requirements:
|
|
180
|
+
|
|
181
|
+
- **₹ symbol** renders correctly (bundled Inter font includes the Rupee glyph)
|
|
182
|
+
- **Indian number formatting** — helper for 1,00,000 notation (not 100,000)
|
|
183
|
+
- **GST structure** — CGST/SGST (intra-state) and IGST (inter-state) table layouts
|
|
184
|
+
- **Amount in words** — Indian numbering system (Lakh/Crore)
|
|
185
|
+
- **SAC/HSN codes** — column support in line-item tables
|
|
186
|
+
|
|
187
|
+
Use the `createGstInvoice` template for a complete GST-compliant invoice in one call:
|
|
156
188
|
|
|
157
189
|
```typescript
|
|
190
|
+
import { createGstInvoice } from 'pretext-pdf/templates'
|
|
158
191
|
import { render } from 'pretext-pdf'
|
|
159
192
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
193
|
+
const content = createGstInvoice({
|
|
194
|
+
supplier: { name: 'Antigravity Systems', address: 'Gurugram, HR', gstin: '06AAACA1234A1ZV', state: 'Haryana' },
|
|
195
|
+
buyer: { name: 'TechStartup Ltd', address: 'Mumbai, MH', gstin: '27AABCB5678B1ZP', state: 'Maharashtra' },
|
|
196
|
+
invoiceNumber: 'INV/2026-27/001',
|
|
197
|
+
invoiceDate: '20 Apr 2026',
|
|
198
|
+
placeOfSupply: 'Maharashtra (27)',
|
|
199
|
+
items: [
|
|
200
|
+
{ description: 'Software Development', hsnSac: '998314', quantity: 80, unit: 'Hrs', rate: 3000, taxRate: 18 },
|
|
201
|
+
],
|
|
202
|
+
isInterState: true, // auto-detected from state fields if omitted
|
|
203
|
+
qrUpiData: 'upi://pay?pa=merchant@hdfc&pn=Antigravity&am=283200',
|
|
204
|
+
bankName: 'HDFC Bank', accountNumber: '501001234567', ifscCode: 'HDFC0001234',
|
|
168
205
|
})
|
|
206
|
+
const pdf = await render({ content })
|
|
169
207
|
```
|
|
170
208
|
|
|
171
|
-
|
|
209
|
+
See [`examples/gst-invoice-india.ts`](examples/gst-invoice-india.ts) for the raw element approach.
|
|
172
210
|
|
|
173
|
-
|
|
174
|
-
- All fields are optional except `type` and element-specific required fields (e.g. `text`, `level`)
|
|
175
|
-
- Errors are typed: `err.code` tells you exactly what went wrong
|
|
176
|
-
- `render()` is fully async, safe to `await` in any context
|
|
177
|
-
- Works in Node.js 18+ and modern browsers (with `@napi-rs/canvas` for SVG)
|
|
211
|
+
---
|
|
178
212
|
|
|
179
|
-
|
|
213
|
+
## Markdown → PDF (`pretext-pdf/markdown`)
|
|
180
214
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
215
|
+
Convert any Markdown string to a `pretext-pdf` document in one call. Requires `marked` peer dep.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { markdownToContent } from 'pretext-pdf/markdown'
|
|
219
|
+
import { render } from 'pretext-pdf'
|
|
220
|
+
import { writeFileSync } from 'fs'
|
|
221
|
+
|
|
222
|
+
const md = `
|
|
223
|
+
# Q1 2026 Report
|
|
224
|
+
|
|
225
|
+
Revenue grew **18%** year-over-year, driven by:
|
|
226
|
+
|
|
227
|
+
- Cloud services (+32%)
|
|
228
|
+
- Enterprise licenses (+12%)
|
|
229
|
+
|
|
230
|
+
> All figures are in USD millions.
|
|
231
|
+
`
|
|
232
|
+
|
|
233
|
+
const content = await markdownToContent(md, {
|
|
234
|
+
codeFontFamily: 'Courier New', // enables fenced code block rendering
|
|
235
|
+
})
|
|
236
|
+
const pdf = await render({ content })
|
|
237
|
+
writeFileSync('report.pdf', pdf)
|
|
186
238
|
```
|
|
187
239
|
|
|
240
|
+
Supported Markdown: headings h1–h4, bold, italic, strikethrough, inline code, links, ordered/unordered lists (2 levels), fenced code blocks, blockquotes, horizontal rules.
|
|
241
|
+
|
|
188
242
|
---
|
|
189
243
|
|
|
190
|
-
##
|
|
244
|
+
## Invoice & report templates (`pretext-pdf/templates`)
|
|
191
245
|
|
|
192
|
-
|
|
246
|
+
Pre-built zero-dependency template functions that generate `ContentElement[]` arrays:
|
|
193
247
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
- **Amount in words** — Indian numbering system (Lakh/Crore)
|
|
198
|
-
- **SAC/HSN codes** — column support in line-item tables
|
|
248
|
+
```typescript
|
|
249
|
+
import { createInvoice, createGstInvoice, createReport } from 'pretext-pdf/templates'
|
|
250
|
+
import { render } from 'pretext-pdf'
|
|
199
251
|
|
|
200
|
-
|
|
252
|
+
// Generic invoice (any currency)
|
|
253
|
+
const invoiceContent = createInvoice({
|
|
254
|
+
from: { name: 'Acme Corp', address: '123 Main St', email: 'billing@acme.com' },
|
|
255
|
+
to: { name: 'Client Ltd', address: '456 Oak Ave' },
|
|
256
|
+
invoiceNumber: 'INV-2026-001', date: '2026-04-20',
|
|
257
|
+
items: [{ description: 'Consulting', quantity: 10, unitPrice: 150 }],
|
|
258
|
+
currency: '$', taxRate: 10, taxLabel: 'GST',
|
|
259
|
+
qrData: 'upi://pay?pa=acme@bank&am=1650',
|
|
260
|
+
})
|
|
201
261
|
|
|
202
|
-
|
|
262
|
+
// Research report with optional TOC
|
|
263
|
+
const reportContent = createReport({
|
|
264
|
+
title: 'Annual Performance Report',
|
|
265
|
+
author: 'Finance Team', date: 'April 2026',
|
|
266
|
+
abstract: 'Revenue grew 18% YoY across all segments.',
|
|
267
|
+
includeTableOfContents: true,
|
|
268
|
+
sections: [
|
|
269
|
+
{ title: 'Revenue', paragraphs: ['Cloud +32%, Enterprise +12%.'], bullets: ['SaaS: $2.8M', 'Services: $1.1M'] },
|
|
270
|
+
],
|
|
271
|
+
})
|
|
203
272
|
|
|
204
|
-
|
|
273
|
+
const pdf = await render({ content: reportContent })
|
|
274
|
+
```
|
|
205
275
|
|
|
206
|
-
|
|
276
|
+
---
|
|
207
277
|
|
|
208
|
-
|
|
209
|
-
- ✅ **Cryptographically signed PDFs** — PKCS#7 signing support (Phase 3)
|
|
210
|
-
- ✅ **Path traversal protection** — Secure file operations with validated paths
|
|
211
|
-
- ✅ **Error sanitization** — No sensitive data in error messages
|
|
212
|
-
- ✅ **Async-safe I/O** — Non-blocking file operations throughout
|
|
213
|
-
- ✅ **Comprehensive test coverage** — 188+ tests with 100% pass rate
|
|
214
|
-
- ✅ **No hardcoded secrets** — Environment-based configuration
|
|
278
|
+
## Element type reference
|
|
215
279
|
|
|
216
|
-
|
|
280
|
+
```
|
|
281
|
+
paragraph heading(1-4) spacer hr page-break
|
|
282
|
+
table image svg list code
|
|
283
|
+
blockquote rich-paragraph callout comment form-field
|
|
284
|
+
toc qr-code barcode chart footnote-def
|
|
285
|
+
```
|
|
217
286
|
|
|
218
287
|
| Element | What it does |
|
|
219
288
|
| --- | --- |
|
|
220
|
-
| `paragraph` | Text block — font, size, color, align, background, letterSpacing, smallCaps |
|
|
221
|
-
| `heading` | H1–H4 with bookmarks, URL links, internal anchors |
|
|
222
|
-
| `table` | Fixed/proportional columns, colspan, repeating headers across page breaks |
|
|
223
|
-
| `image` | PNG/JPG/WebP with sizing, alignment,
|
|
224
|
-
| `list` | Ordered/unordered,
|
|
289
|
+
| `paragraph` | Text block — font, size, color, align, background, letterSpacing, smallCaps, tabularNumbers, multi-column (`columns` + `columnGap`), RTL (`dir`) |
|
|
290
|
+
| `heading` | H1–H4 with bookmarks, URL links, internal anchors, tabularNumbers, RTL (`dir`) |
|
|
291
|
+
| `table` | Fixed/proportional columns, colspan, rowspan, repeating headers across page breaks |
|
|
292
|
+
| `image` | PNG/JPG/WebP with sizing, alignment, float left/right with `floatText` or rich `floatSpans` (mixed-format caption) |
|
|
293
|
+
| `list` | Ordered/unordered, 2-level nesting, `nestedNumberingStyle: 'restart' \| 'continue'` |
|
|
225
294
|
| `code` | Monospace block with background and padding |
|
|
226
295
|
| `blockquote` | Left border + background |
|
|
227
296
|
| `rich-paragraph` | Mixed bold/italic/color/size/super/subscript spans with inline hyperlinks |
|
|
228
297
|
| `svg` | Embedded SVG graphics with auto-sizing from viewBox |
|
|
229
298
|
| `toc` | Auto-generated table of contents with accurate page numbers (two-pass) |
|
|
299
|
+
| `qr-code` | Scannable QR code — UPI payment links, URLs, vCards. `data`, `size`, `errorCorrectionLevel`, `foreground`/`background` color. Requires `qrcode` peer dep. |
|
|
300
|
+
| `barcode` | 100+ symbologies — EAN-13, Code128, PDF417, DataMatrix, and more via `symbology` field. Requires `bwip-js` peer dep. |
|
|
301
|
+
| `chart` | Vega-Lite data visualisation — pass any valid Vega-Lite spec to `spec`. Rendered as vector SVG. Requires `vega` + `vega-lite` peer deps. |
|
|
230
302
|
| `comment` | PDF sticky-note annotation (visible in Acrobat/Preview sidebar) |
|
|
231
303
|
| `hr` | Horizontal rule |
|
|
232
304
|
| `spacer` | Fixed-height gap |
|
|
233
305
|
| `page-break` | Force new page |
|
|
234
306
|
|
|
235
|
-
### Document
|
|
307
|
+
### Document-level features
|
|
236
308
|
|
|
237
309
|
| Feature | Config key | Notes |
|
|
238
310
|
| --- | --- | --- |
|
|
@@ -240,85 +312,21 @@ See [`examples/gst-invoice-india.ts`](examples/gst-invoice-india.ts) for a compl
|
|
|
240
312
|
| Encryption | `doc.encryption` | Password + granular permissions |
|
|
241
313
|
| PDF Bookmarks | `doc.bookmarks` | Auto-generated from headings |
|
|
242
314
|
| Hyphenation | `doc.hyphenation` | Liang's algorithm, `language: 'en-us'` |
|
|
243
|
-
| Headers/Footers | `doc.header` / `doc.footer` | `{{pageNumber}}`
|
|
315
|
+
| Headers/Footers | `doc.header` / `doc.footer` | `{{pageNumber}}`, `{{totalPages}}`, `{{date}}`, `{{author}}` tokens |
|
|
316
|
+
| Per-section overrides | `doc.sections` | Different header/footer/margins per page range |
|
|
244
317
|
| Metadata | `doc.metadata` | Title, author, subject, keywords, `language` (PDF /Lang), `producer` |
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
|
249
|
-
|
|
|
250
|
-
|
|
|
251
|
-
|
|
|
252
|
-
|
|
|
253
|
-
| **Document assembly** | `merge(pdfs)`, `assemble(parts)` |
|
|
254
|
-
| **Interactive forms** | `{ type: 'form-field', fieldType: 'text'\|'checkbox'\|'radio'\|'dropdown'\|'button' }`, `doc.flattenForms` |
|
|
255
|
-
| **Signature placeholder** | `doc.signature: { signerName, reason, location, x, y, page }` |
|
|
256
|
-
| **Callout boxes** | `{ type: 'callout', content, style: 'info'\|'warning'\|'tip'\|'note', title }` |
|
|
257
|
-
|
|
258
|
-
### Type Safety (v0.4.6+)
|
|
259
|
-
|
|
260
|
-
pretext-pdf is built with **strict TypeScript** and **zero any-casts** in critical paths:
|
|
261
|
-
|
|
262
|
-
- **Full type inference** — No need to cast document configs or response types
|
|
263
|
-
- **Element validation** — TypeScript catches invalid element types at compile time
|
|
264
|
-
- **API contract testing** — Every API boundary has comprehensive type tests
|
|
265
|
-
- **Error types** — `PretextPdfError` with typed code field for safe error handling
|
|
266
|
-
- **Module typing** — Complete type definitions for all exports and configurations
|
|
318
|
+
| Hyperlinks | `paragraph.url`, `heading.url`, `heading.anchor`, `span.href` | External, mailto, internal anchors |
|
|
319
|
+
| Inline formatting | `span.verticalAlign: 'superscript'\|'subscript'`, `paragraph.letterSpacing`, `heading.smallCaps` | |
|
|
320
|
+
| Sticky notes | `{ type: 'comment', contents: '...' }`, `paragraph.annotation` | |
|
|
321
|
+
| Document assembly | `merge(pdfs)`, `assemble(parts)` | Combine pre-rendered + freshly rendered |
|
|
322
|
+
| Interactive forms | `{ type: 'form-field', fieldType: 'text'\|'checkbox'\|'radio'\|'dropdown'\|'button' }`, `doc.flattenForms` | |
|
|
323
|
+
| Cryptographic signing | `doc.signature: { p12, passphrase, signerName, reason, location }` | PKCS#7 via optional `@signpdf/signpdf` |
|
|
324
|
+
| Visual signature placeholder | `doc.signature: { signerName, reason, location, x, y, page }` | |
|
|
325
|
+
| Callout boxes | `{ type: 'callout', content, style: 'info'\|'warning'\|'tip'\|'note', title }` | |
|
|
267
326
|
|
|
268
327
|
---
|
|
269
328
|
|
|
270
|
-
##
|
|
271
|
-
|
|
272
|
-
Comprehensive security and quality audit completed. **41 issues identified and fixed across 5 phases:**
|
|
273
|
-
|
|
274
|
-
| Phase | Focus | Issues | Status |
|
|
275
|
-
| --- | --- | --- | --- |
|
|
276
|
-
| 0 | Core rendering | Footnote truncation | ✅ Fixed |
|
|
277
|
-
| 1 | Security hardening | Path validation, async I/O, error handling | ✅ Fixed |
|
|
278
|
-
| 2 | Type safety | Any-cast elimination, module typing | ✅ Fixed |
|
|
279
|
-
| 3 | Test coverage | False-positives, boundary cases, crypto signing | ✅ Fixed |
|
|
280
|
-
| 4 | Code quality | Silent failures → explicit errors, decoupling | ✅ Fixed |
|
|
281
|
-
|
|
282
|
-
**Audit results:**
|
|
283
|
-
|
|
284
|
-
- Zero path traversal vulnerabilities
|
|
285
|
-
- All error messages sanitized (no data leaks)
|
|
286
|
-
- Async file I/O throughout (non-blocking)
|
|
287
|
-
- No hardcoded secrets or credentials
|
|
288
|
-
- 188+ tests, 100% pass rate
|
|
289
|
-
- Production-ready reliability
|
|
290
|
-
|
|
291
|
-
See [SECURITY.md](SECURITY.md) for detailed security policies.
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Examples
|
|
296
|
-
|
|
297
|
-
Run working examples from the `examples/` directory:
|
|
298
|
-
|
|
299
|
-
```bash
|
|
300
|
-
# Phase 7 examples
|
|
301
|
-
npm run example # Basic invoice
|
|
302
|
-
npm run example:watermark # Text/image watermarks
|
|
303
|
-
npm run example:bookmarks # PDF outline/bookmarks
|
|
304
|
-
npm run example:toc # Auto table of contents
|
|
305
|
-
npm run example:rtl # Arabic/Hebrew RTL text
|
|
306
|
-
npm run example:encryption # Password-protected PDF
|
|
307
|
-
|
|
308
|
-
# Phase 8 examples
|
|
309
|
-
npm run example:hyperlinks # External links, email links, internal anchors
|
|
310
|
-
npm run example:annotations # Sticky notes on elements
|
|
311
|
-
npm run example:assembly # Merge and assemble multiple PDFs
|
|
312
|
-
npm run example:inline # Superscript, subscript, letter-spacing, small-caps
|
|
313
|
-
npm run example:forms # Interactive form fields (text, checkbox, radio, dropdown)
|
|
314
|
-
npm run example:callout # Callout boxes (info, warning, tip, note presets)
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
All examples write output to `output/*.pdf`.
|
|
318
|
-
|
|
319
|
-
---
|
|
320
|
-
|
|
321
|
-
## API Reference
|
|
329
|
+
## API reference
|
|
322
330
|
|
|
323
331
|
### `render(doc): Promise<Uint8Array>`
|
|
324
332
|
|
|
@@ -372,9 +380,113 @@ const report = await assemble([
|
|
|
372
380
|
|
|
373
381
|
---
|
|
374
382
|
|
|
375
|
-
##
|
|
383
|
+
## Custom fonts
|
|
376
384
|
|
|
377
|
-
|
|
385
|
+
```typescript
|
|
386
|
+
const pdf = await render({
|
|
387
|
+
fonts: [
|
|
388
|
+
{ family: 'Roboto', weight: 400, src: '/path/to/Roboto-Regular.ttf' },
|
|
389
|
+
{ family: 'Roboto', weight: 700, src: '/path/to/Roboto-Bold.ttf' },
|
|
390
|
+
{ family: 'Roboto', style: 'italic', src: '/path/to/Roboto-Italic.ttf' },
|
|
391
|
+
],
|
|
392
|
+
defaultFont: 'Roboto',
|
|
393
|
+
content: [
|
|
394
|
+
{ type: 'paragraph', text: 'Uses Roboto font' },
|
|
395
|
+
{ type: 'paragraph', text: 'Bold text', fontWeight: 700 },
|
|
396
|
+
],
|
|
397
|
+
})
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
> **Avoid `system-ui`** as a font name on macOS — it triggers a known layout-measurement inaccuracy in Pretext. Always name fonts explicitly.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Rich text
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
{
|
|
408
|
+
type: 'rich-paragraph',
|
|
409
|
+
fontSize: 13,
|
|
410
|
+
spans: [
|
|
411
|
+
{ text: 'Normal ' },
|
|
412
|
+
{ text: 'bold', fontWeight: 700 },
|
|
413
|
+
{ text: ' and ', fontStyle: 'italic' },
|
|
414
|
+
{ text: 'colored', color: '#e63946' },
|
|
415
|
+
{ text: ' and ' },
|
|
416
|
+
{ text: 'linked', href: 'https://example.com', underline: true, color: '#0070f3' },
|
|
417
|
+
{ text: '. Also: E=mc' },
|
|
418
|
+
{ text: '2', verticalAlign: 'superscript' },
|
|
419
|
+
{ text: ' and H' },
|
|
420
|
+
{ text: '2', verticalAlign: 'subscript' },
|
|
421
|
+
{ text: 'O.' },
|
|
422
|
+
],
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Footnotes
|
|
429
|
+
|
|
430
|
+
Use `createFootnoteSet()` to generate matched reference/definition pairs with guaranteed unique IDs:
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
import { render, createFootnoteSet } from 'pretext-pdf'
|
|
434
|
+
|
|
435
|
+
const notes = createFootnoteSet([
|
|
436
|
+
{ text: 'Smith, J. (2022). Typography in PDFs.' },
|
|
437
|
+
{ text: 'Ibid., p. 42.' },
|
|
438
|
+
])
|
|
439
|
+
|
|
440
|
+
await render({
|
|
441
|
+
content: [
|
|
442
|
+
{
|
|
443
|
+
type: 'rich-paragraph',
|
|
444
|
+
spans: [
|
|
445
|
+
{ text: 'See the original research' },
|
|
446
|
+
{ text: '¹', verticalAlign: 'superscript', footnoteRef: notes[0]!.id },
|
|
447
|
+
{ text: ' for details.' },
|
|
448
|
+
],
|
|
449
|
+
},
|
|
450
|
+
...notes.map(n => n.def), // footnote-def elements go at end of document
|
|
451
|
+
],
|
|
452
|
+
})
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Examples
|
|
458
|
+
|
|
459
|
+
Run working examples from the `examples/` directory:
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
# v0.8.0 new element examples (install optional deps first)
|
|
463
|
+
# npm install qrcode bwip-js vega vega-lite marked
|
|
464
|
+
|
|
465
|
+
# Phase 7 examples
|
|
466
|
+
npm run example # Basic invoice
|
|
467
|
+
npm run example:watermark # Text/image watermarks
|
|
468
|
+
npm run example:bookmarks # PDF outline/bookmarks
|
|
469
|
+
npm run example:toc # Auto table of contents
|
|
470
|
+
npm run example:rtl # Arabic/Hebrew RTL text
|
|
471
|
+
npm run example:encryption # Password-protected PDF
|
|
472
|
+
|
|
473
|
+
# Phase 8 examples
|
|
474
|
+
npm run example:hyperlinks # External links, email links, internal anchors
|
|
475
|
+
npm run example:annotations # Sticky notes on elements
|
|
476
|
+
npm run example:assembly # Merge and assemble multiple PDFs
|
|
477
|
+
npm run example:inline # Superscript, subscript, letter-spacing, small-caps
|
|
478
|
+
npm run example:forms # Interactive form fields
|
|
479
|
+
npm run example:callout # Callout boxes (info, warning, tip, note)
|
|
480
|
+
npm run example:gst # India GST-compliant invoice
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
All examples write output to `output/*.pdf`.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Error handling
|
|
488
|
+
|
|
489
|
+
Every error throws `PretextPdfError` with a typed `code` — designed so an LLM (or a human) can self-correct:
|
|
378
490
|
|
|
379
491
|
```typescript
|
|
380
492
|
import { render, PretextPdfError } from 'pretext-pdf'
|
|
@@ -414,24 +526,9 @@ hyphenation: { language: 'en-US' }
|
|
|
414
526
|
hyphenation: { language: 'en-us' }
|
|
415
527
|
```
|
|
416
528
|
|
|
417
|
-
### Encryption
|
|
418
|
-
|
|
419
|
-
Encryption is built-in since v0.4.0. Add `encryption` to your document config:
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
const pdf = await render({
|
|
423
|
-
encryption: {
|
|
424
|
-
userPassword: 'open123',
|
|
425
|
-
ownerPassword: 'admin456',
|
|
426
|
-
permissions: { printing: true, copying: false, modifying: false }
|
|
427
|
-
},
|
|
428
|
-
content: [...]
|
|
429
|
-
})
|
|
430
|
-
```
|
|
431
|
-
|
|
432
529
|
### SVG rendering requires optional dependency
|
|
433
530
|
|
|
434
|
-
Install `@napi-rs/canvas` for SVG support:
|
|
531
|
+
Install `@napi-rs/canvas` for SVG / chart / qr-code / barcode support:
|
|
435
532
|
|
|
436
533
|
```bash
|
|
437
534
|
npm install @napi-rs/canvas
|
|
@@ -446,103 +543,99 @@ Check margins — if left+right margins exceed page width, content width becomes
|
|
|
446
543
|
margins: { top: 36, bottom: 36, left: 36, right: 36 }
|
|
447
544
|
```
|
|
448
545
|
|
|
449
|
-
### Form fields not interactive after flattenForms
|
|
546
|
+
### Form fields not interactive after `flattenForms`
|
|
450
547
|
|
|
451
|
-
`flattenForms: true` bakes fields into static content — by design. Remove it to keep interactive.
|
|
548
|
+
`flattenForms: true` bakes fields into static content — by design. Remove it to keep them interactive.
|
|
452
549
|
|
|
453
550
|
---
|
|
454
551
|
|
|
455
|
-
##
|
|
552
|
+
## Non-goals
|
|
456
553
|
|
|
457
|
-
|
|
554
|
+
What pretext-pdf is **not** trying to be — pick a different tool for these:
|
|
458
555
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
```
|
|
556
|
+
- **Editing or parsing existing PDFs** → [`pdf-lib`](https://github.com/Hopding/pdf-lib), [`pdf-parse`](https://www.npmjs.com/package/pdf-parse)
|
|
557
|
+
- **Filling existing PDF form templates** → [`pdf-lib`](https://github.com/Hopding/pdf-lib), [`pdftk`](https://www.pdflabs.com/tools/pdftk-server/)
|
|
558
|
+
- **Heavily art-directed pages** with CSS grids, SVG illustrations, floats, background images → headless Chrome (Puppeteer) still wins
|
|
559
|
+
- **PDF/A archival, PDF/UA accessibility tagging** → not yet
|
|
560
|
+
- **Print-shop kerning pairs, OpenType ligatures, variable-font axes beyond weight** → Pretext itself doesn't model these
|
|
561
|
+
|
|
562
|
+
---
|
|
467
563
|
|
|
468
|
-
|
|
564
|
+
## Runtime requirements
|
|
565
|
+
|
|
566
|
+
- **Node.js ≥ 18** with `@napi-rs/canvas` peer dep (lazy-loaded — only required when you use SVG/chart/QR/barcode elements)
|
|
567
|
+
- **`Intl.Segmenter`** (built-in on Node 18+ and all modern browsers)
|
|
568
|
+
- **Browser support** — works directly in modern browsers; bring your own font bytes
|
|
569
|
+
- **Cold-start cost** on serverless: `@napi-rs/canvas` adds ~5–10 MB and a few hundred ms on the first request. Subsequent requests in a warm container are sub-second.
|
|
570
|
+
- **Fonts must be fully loaded** before `render()` runs — for browser usage, await `document.fonts.ready` first
|
|
469
571
|
|
|
470
572
|
---
|
|
471
573
|
|
|
472
|
-
##
|
|
574
|
+
## Performance
|
|
473
575
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
],
|
|
486
|
-
})
|
|
487
|
-
```
|
|
576
|
+
Benchmarked on Windows 11 / Node 22 / Intel i7-12th Gen. Numbers are averages over 10 runs, excluding the first cold JIT run.
|
|
577
|
+
|
|
578
|
+
| Document | Render time | PDF size |
|
|
579
|
+
| --- | --- | --- |
|
|
580
|
+
| 1 page (heading + paragraph + list) | ~220 ms | ~45 KB |
|
|
581
|
+
| 10 pages (40 sections, mixed elements) | ~1,100 ms | ~180 KB |
|
|
582
|
+
| Mixed (heading + paragraph + 20-row table + list + hr) | ~290 ms | ~60 KB |
|
|
583
|
+
|
|
584
|
+
**Font subsetting** is automatic for TTF/OTF fonts. Only the glyphs used in the document are embedded, typically reducing PDF size by 40–60% compared to full font embedding. A typical single-font invoice renders under 65 KB. WOFF2 fonts are embedded without subsetting due to an upstream library limitation.
|
|
585
|
+
|
|
586
|
+
For large documents (10,000+ elements), set `NODE_OPTIONS=--max-old-space-size=4096` to prevent GC pressure.
|
|
488
587
|
|
|
489
588
|
---
|
|
490
589
|
|
|
491
|
-
##
|
|
590
|
+
## Test coverage
|
|
492
591
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
{ text: '. Also: E=mc' },
|
|
505
|
-
{ text: '2', verticalAlign: 'superscript' },
|
|
506
|
-
{ text: ' and H' },
|
|
507
|
-
{ text: '2', verticalAlign: 'subscript' },
|
|
508
|
-
{ text: 'O.' },
|
|
509
|
-
],
|
|
510
|
-
}
|
|
592
|
+
598+ tests across all phases with 100% pass rate:
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
npm test # Full suite (unit + e2e + all phases including v0.8.0)
|
|
596
|
+
npm run test:unit # Validation, builder, rich-text unit tests
|
|
597
|
+
npm run test:e2e # End-to-end render tests
|
|
598
|
+
npm run test:10a # QR code + barcode tests
|
|
599
|
+
npm run test:10b # Vega-Lite chart tests
|
|
600
|
+
npm run test:10c # Markdown converter tests
|
|
601
|
+
npm run test:10d # Template function tests
|
|
602
|
+
npm run test:phases # All phase tests (7–11, performance, signatures)
|
|
511
603
|
```
|
|
512
604
|
|
|
605
|
+
**Coverage**: type safety, path validation, error handling, boundary cases, crypto signing, document assembly, all content elements, optional-dep error codes, MCP tool validation.
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Security
|
|
610
|
+
|
|
611
|
+
Comprehensive April 2026 security audit completed — 41 issues identified and fixed across path-traversal protection, async I/O, error sanitization, type-safety, and explicit failure modes. See [SECURITY.md](SECURITY.md) for the disclosure policy and [CHANGELOG.md](CHANGELOG.md) for audit details.
|
|
612
|
+
|
|
613
|
+
Highlights:
|
|
614
|
+
- Zero known path-traversal vulnerabilities; opt-in `allowedFileDirs` lockdown for user-controlled inputs
|
|
615
|
+
- All error messages sanitized — no filesystem paths or secrets leak through
|
|
616
|
+
- Async file I/O throughout (non-blocking)
|
|
617
|
+
- Strict TypeScript with documented `any`-casts only at pdf-lib internal boundaries
|
|
618
|
+
|
|
513
619
|
---
|
|
514
620
|
|
|
515
621
|
## Roadmap
|
|
516
622
|
|
|
517
623
|
| Phase | Feature | Status |
|
|
518
624
|
|-------|---------|--------|
|
|
519
|
-
| 1–
|
|
520
|
-
|
|
|
521
|
-
|
|
|
522
|
-
|
|
|
523
|
-
|
|
|
524
|
-
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
| 7F | RTL text (Arabic/Hebrew) | ✅ |
|
|
528
|
-
| 7G | Encryption | ✅ |
|
|
529
|
-
| 8A | Sticky note annotations | ✅ |
|
|
530
|
-
| 8B | Interactive forms (text/checkbox/radio/dropdown/button) | ✅ |
|
|
531
|
-
| 8C | Document assembly (merge + assemble) | ✅ |
|
|
532
|
-
| 8D | Callout boxes (info/warning/tip/note) | ✅ |
|
|
533
|
-
| 8E | Signature placeholder | ✅ |
|
|
534
|
-
| 8F | Document metadata (language, producer) | ✅ |
|
|
535
|
-
| 8G | Hyperlinks | ✅ |
|
|
536
|
-
| 8H | Inline formatting (super/subscript, letterSpacing, smallCaps) | ✅ |
|
|
537
|
-
| 9A | Digital signatures (cryptographic, PKCS#7) | 🔜 |
|
|
538
|
-
| 9B | Image floats (text flowing around images) | 🔜 |
|
|
539
|
-
| 9C | Font subsetting pre-computation | 🔜 |
|
|
625
|
+
| 1–6 | Core engine, pagination, typography, rich text, builder, columns | ✅ |
|
|
626
|
+
| 7A–G | Bookmarks, watermarks, hyphenation, TOC, SVG, RTL, encryption | ✅ |
|
|
627
|
+
| 8A–H | Annotations, forms, assembly, callouts, signatures, metadata, hyperlinks, inline formatting | ✅ |
|
|
628
|
+
| 9A–C | Cryptographic signatures (PKCS#7), image floats, font subsetting | ✅ |
|
|
629
|
+
| 10A–D | QR codes, barcodes, Vega-Lite charts, Markdown, templates | ✅ |
|
|
630
|
+
| 11+ | Variable fonts, OpenType features, PDF/A, PDF/UA accessibility | 🔜 |
|
|
631
|
+
|
|
632
|
+
See [docs/ROADMAP.md](docs/ROADMAP.md) for the full plan.
|
|
540
633
|
|
|
541
634
|
---
|
|
542
635
|
|
|
543
636
|
## Migration from pdfmake
|
|
544
637
|
|
|
545
|
-
Coming from pdfmake? See the **[Migration Guide](docs/MIGRATION_FROM_PDFMAKE.md)**
|
|
638
|
+
Coming from pdfmake? See the **[Migration Guide](docs/MIGRATION_FROM_PDFMAKE.md)** — every common pdfmake pattern mapped to its pretext-pdf equivalent.
|
|
546
639
|
|
|
547
640
|
---
|
|
548
641
|
|
|
@@ -560,9 +653,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). TDD approach — write tests first.
|
|
|
560
653
|
|
|
561
654
|
## Credits
|
|
562
655
|
|
|
563
|
-
Built by [Himanshu Jain](https://github.com/Himaan1998Y) on
|
|
564
|
-
- **[pretext](https://github.com/chenglou/pretext)** — Text layout engine (Cheng Lou)
|
|
565
|
-
- **[pdf-lib](https://github.com/Hopding/pdf-lib)** — PDF manipulation
|
|
566
|
-
- **[@napi-rs/canvas](https://github.com/napi-rs/canvas)** — Server-side Canvas API for Node.js
|
|
656
|
+
Built by [Himanshu Jain](https://github.com/Himaan1998Y) on the shoulders of [pretext](https://github.com/chenglou/pretext), [pdf-lib](https://github.com/Hopding/pdf-lib), and [@napi-rs/canvas](https://github.com/napi-rs/canvas).
|
|
567
657
|
|
|
568
|
-
Questions? [Open an issue](https://github.com/Himaan1998Y/pretext-pdf/issues)
|
|
658
|
+
Questions? [Open an issue](https://github.com/Himaan1998Y/pretext-pdf/issues) — or try it live at the [demo](https://himaan1998y.github.io/pretext-pdf/).
|