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.
Files changed (57) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +366 -276
  3. package/dist/assets.d.ts +5 -0
  4. package/dist/assets.d.ts.map +1 -1
  5. package/dist/assets.js +248 -43
  6. package/dist/assets.js.map +1 -1
  7. package/dist/errors.d.ts +1 -1
  8. package/dist/errors.d.ts.map +1 -1
  9. package/dist/errors.js.map +1 -1
  10. package/dist/fonts.d.ts.map +1 -1
  11. package/dist/fonts.js +88 -16
  12. package/dist/fonts.js.map +1 -1
  13. package/dist/index.d.ts +29 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +35 -2
  16. package/dist/index.js.map +1 -1
  17. package/dist/markdown.d.ts +28 -0
  18. package/dist/markdown.d.ts.map +1 -0
  19. package/dist/markdown.js +222 -0
  20. package/dist/markdown.js.map +1 -0
  21. package/dist/measure-blocks.d.ts.map +1 -1
  22. package/dist/measure-blocks.js +347 -62
  23. package/dist/measure-blocks.js.map +1 -1
  24. package/dist/measure-text.d.ts.map +1 -1
  25. package/dist/measure-text.js +1 -8
  26. package/dist/measure-text.js.map +1 -1
  27. package/dist/measure.d.ts.map +1 -1
  28. package/dist/measure.js +13 -21
  29. package/dist/measure.js.map +1 -1
  30. package/dist/render-blocks.d.ts +4 -1
  31. package/dist/render-blocks.d.ts.map +1 -1
  32. package/dist/render-blocks.js +227 -105
  33. package/dist/render-blocks.js.map +1 -1
  34. package/dist/render-extras.d.ts.map +1 -1
  35. package/dist/render-extras.js +72 -71
  36. package/dist/render-extras.js.map +1 -1
  37. package/dist/render-utils.d.ts +9 -2
  38. package/dist/render-utils.d.ts.map +1 -1
  39. package/dist/render-utils.js +24 -13
  40. package/dist/render-utils.js.map +1 -1
  41. package/dist/render.d.ts.map +1 -1
  42. package/dist/render.js +27 -3
  43. package/dist/render.js.map +1 -1
  44. package/dist/rich-text.d.ts +0 -4
  45. package/dist/rich-text.d.ts.map +1 -1
  46. package/dist/rich-text.js +15 -9
  47. package/dist/rich-text.js.map +1 -1
  48. package/dist/templates.d.ts +79 -0
  49. package/dist/templates.d.ts.map +1 -0
  50. package/dist/templates.js +201 -0
  51. package/dist/templates.js.map +1 -0
  52. package/dist/types.d.ts +139 -5
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/validate.d.ts.map +1 -1
  55. package/dist/validate.js +241 -28
  56. package/dist/validate.js.map +1 -1
  57. package/package.json +57 -12
package/CHANGELOG.md CHANGED
@@ -7,8 +7,71 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.8.1] — 2026-04-20
11
+
12
+ ### Fixed
13
+
14
+ - **Browser support** — `pretext-pdf` now imports cleanly in browsers. Module-init in `src/fonts.ts` previously called `fileURLToPath(import.meta.url)` and `createRequire(import.meta.url)` eagerly, which threw `"The URL must be of scheme file"` whenever the module was loaded from a non-`file://` URL (esm.sh, jsdelivr, Vite dev server). Both calls are now gated on a runtime `IS_NODE` check, and the bundled-Inter `BUNDLED_INTER_PATHS` arrays are constructed only in Node.
15
+ - **Browser font-loading errors** — `loadFontBytes` now throws clear `FONT_LOAD_FAILED` messages when bundled Inter or string font paths are requested in a browser, pointing the consumer at the correct workaround (supply `Uint8Array` bytes via `doc.fonts`).
16
+
17
+ ### Notes for browser users
18
+
19
+ - Always supply Inter (or your default font) explicitly via `doc.fonts: [{ family: 'Inter', weight: 400, src: <Uint8Array> }, { family: 'Inter', weight: 700, src: <Uint8Array> }]`. The library cannot read local font files in the browser.
20
+ - SVG / chart / qr-code / barcode elements still depend on `@napi-rs/canvas` at runtime; in the browser, the native `OffscreenCanvas` is used instead and the polyfill is skipped automatically.
21
+
22
+ ---
23
+
24
+ ## [0.8.0] — 2026-04-19
25
+
26
+ ### Added
27
+
28
+ - **`qr-code` element** — generate QR codes as inline PDF content using the `qrcode` optional peer dependency. Supports `data`, `size`, `errorCorrectionLevel` (L/M/Q/H), `foreground`/`background` hex colours, `margin`, `align`, `spaceBefore`/`spaceAfter`. Fully serverless — pure JS, no canvas required.
29
+ - **`barcode` element** — generate 100+ barcode symbologies (EAN-13, Code128, PDF417, QR, DataMatrix, etc.) via the `bwip-js` optional peer dependency. Supports `symbology`, `data`, `width`, `height`, `includeText`, `align`, `spaceBefore`/`spaceAfter`. Pure JS, Lambda/Edge safe.
30
+ - **`chart` element** — embed Vega-Lite charts as vector SVG using `vega` + `vega-lite` optional peer deps. Accepts any Vega-Lite `spec`, `width`, `height`, `caption`, `align`. Rendered with `renderer: 'none'` — zero canvas/puppeteer dependency.
31
+ - **`pretext-pdf/markdown` entry point** — `markdownToContent(md, options?)` converts a Markdown string to `ContentElement[]`. Requires optional `marked` peer dep. Supports headings, bold/italic/links (→ rich-paragraph), lists (2 levels), blockquotes, code blocks, and HR.
32
+ - **`pretext-pdf/templates` entry point** — three typed template functions with zero extra dependencies: `createInvoice(data)` (generic invoice with currency, tax, discount, QR payment), `createGstInvoice(data)` (GST-compliant Indian tax invoice with IGST/CGST+SGST, UPI QR, bank details, amount in words), `createReport(data)` (structured business report with optional TOC).
33
+ - **New error codes** — `QR_DEP_MISSING`, `QR_GENERATE_FAILED`, `BARCODE_DEP_MISSING`, `BARCODE_GENERATE_FAILED`, `BARCODE_SYMBOLOGY_INVALID`, `CHART_DEP_MISSING`, `CHART_SPEC_INVALID`, `CHART_RENDER_FAILED`, `MARKDOWN_DEP_MISSING`.
34
+
35
+ ---
36
+
37
+ ## [0.7.1] — 2026-04-19
38
+
39
+ ### Changed
40
+
41
+ - **Upstream pretext pinned to `f2014338487a`** — picks up unreleased CJK opening-bracket annotation fix, Hangul jamo line-walker alignment fix, and two internal line-object churn reductions. No public API changes.
42
+
43
+ ### Fixed
44
+
45
+ - **List nesting depth enforced at validation** — `ListItem.items` (2nd-level items) now correctly rejects any further `.items` property, matching the documented 2-level maximum. Previously the validation silently passed 3-level data which could cause undefined render behaviour.
46
+ - **3 phase-11 list tests corrected** — test data incorrectly contained 3-level nesting while named "2-level"; data trimmed to match documented contract.
47
+
48
+ ---
49
+
10
50
  ## [Unreleased]
11
51
 
52
+ ### Added (Phase 11 — Cross-cutting Enhancements)
53
+
54
+ - **`floatSpans` on image elements** — rich-text alternative to plain `floatText`. Accepts `InlineSpan[]` for mixed bold/italic/color/link captions beside float images. Mutually exclusive with `floatText` (validated).
55
+ - **2-level list nesting** — `ListItem.items` now supports one further level of nesting (depth 0 → 1 → 2). Unordered marker: `▪`. Ordered: inherits parent counter or restarts via `nestedNumberingStyle: 'restart'`.
56
+ - **Table `rowspan`** — `TableCell.rowspan` spans a cell across multiple rows. Works alongside `colspan`. Origin cell draws background over full span height; continuation rows automatically receive placeholder cells.
57
+ - **`onFormFieldError` callback** — `doc.onFormFieldError: (name, err) => 'skip' | 'throw'` mirrors `onImageLoadError`. Controls render behaviour when a form field fails.
58
+ - **`createFootnoteSet(defs)`** — helper exported from `pretext-pdf` that generates footnote definition/reference pairs with globally unique IDs. Returns `Array<{ id, def }>`.
59
+ - **`renderDate` field** — `doc.renderDate: Date | string` overrides the PDF creation date. Useful for reproducible builds and testing.
60
+ - **`{{date}}` and `{{author}}` tokens** in header/footer text — join existing `{{pageNumber}}` / `{{totalPages}}`. `{{date}}` resolves from `renderDate`; `{{author}}` resolves from `doc.metadata.author`.
61
+ - **`tabularNumbers`** on `rich-paragraph` — digits rendered at uniform slot width (widest digit in font), so columns of numbers align without OpenType TNUM feature.
62
+ - **`smallCaps` + `letterSpacing` per span** — `InlineSpan.smallCaps` and `InlineSpan.letterSpacing` now respected in `rich-paragraph` rendering.
63
+ - **Per-span `fontSize`** — `InlineSpan.fontSize` overrides the element-level font size for that span. Enables mixed-size text in a single paragraph.
64
+
65
+ ### Fixed (Phase 11)
66
+
67
+ - `resolveTokens()` used `.replace()` (replaces first occurrence only) — changed to `.replaceAll()` for all four tokens.
68
+ - Table span grid: continuation-row cursor was advancing by 1 instead of `colspan` when skipping a spanned column — now advances by full span width.
69
+ - Font family names now validated for safe characters (`/^[a-zA-Z0-9 _-]+$/`) in `requireFamily()` — rejects null bytes and control characters.
70
+ - Annotation `color` and `author` fields now validated in `validateElement()` for both `paragraph` and `heading` annotations.
71
+ - `buildOutlineTree` memoizes `parentIdxOf()` into a pre-computed array — eliminates O(n²) scan for documents with large heading counts.
72
+ - Table grid-line renderer pre-computes active boundary set — eliminates O(rows × cols) inner loop for large tables.
73
+ - `addLinkAnnotation()` re-validates URL scheme at render time (defense-in-depth; `validate.ts` is the primary gate).
74
+
12
75
  ### Planned (Phase 9+)
13
76
 
14
77
  - Phase 9A: Digital signatures (cryptographic PKCS#7 via `@signpdf/signpdf`)
@@ -17,6 +80,29 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
17
80
 
18
81
  ---
19
82
 
83
+ ## [0.7.0] — 2026-04-17
84
+
85
+ ### Added
86
+
87
+ - **6 production templates** (`templates/`) — GST invoice, international invoice, resume, multi-section report, NDA, and meeting minutes. Each is a self-contained `.ts` file outputting a valid PDF. Smoke-tested in Phase 2F Block D.
88
+ - **StackBlitz live demo** (`demo/stackblitz/`) — 4-tab UI (Invoice, Report, Resume, Custom) backed by a Node.js render server. Edit JSON and generate PDFs instantly, no install required. Accessible at the StackBlitz link in the README.
89
+ - **`## Performance` section in README** — measured render times and PDF sizes for 1-page, 10-page, and mixed-element documents. Font subsetting behaviour documented.
90
+ - **Stress tests and benchmarks** (`test/phase-2f-stress.test.ts`) — 32 tests across 4 blocks: large document stress (400-element, 200-row table), edge case stress (CJK, RTL, empty arrays, extreme sizes), timing benchmarks (1-page < 500 ms, 10-page < 5,000 ms), and template smoke tests.
91
+ - **Error code coverage** — new tests for `COLUMN_WIDTH_TOO_NARROW`, `IMAGE_LOAD_FAILED`, `SVG_LOAD_FAILED`, and `ASSEMBLY_FAILED`. 16 of 19 error codes now have direct test coverage.
92
+
93
+ ### Changed
94
+
95
+ - **`as any` audit** — eliminated 10 casts in `validate.ts` by introducing a typed `FormFieldElement` local binding. The remaining 8 instances (pdf-lib interop, dynamic import, internal back-references) are now documented with one-line comments explaining the constraint.
96
+ - **Comparison article** (`docs/articles/pretext-pdf-vs-pdfmake-2026.md`) — 2,200-word draft covering feature matrix, typography quality, API design, performance, and migration quick-start. Marked `published: false` pending live demo.
97
+ - **Migration guide** (`docs/MIGRATION_FROM_PDFMAKE.md`) — 30+ pdfmake → pretext-pdf mappings, complete before/after invoice example, and a quick-start checklist. Linked from README.
98
+
99
+ ### Fixed
100
+
101
+ - **Phase 2F test types** — `fontWeight: 700 as 700` cast in pre-constructed rows array; removed non-existent `creationDate` from `DocumentMetadata`; replaced `allowCopying: false` with correct `encryption: { permissions: { copying: false } }`.
102
+ - **StackBlitz integration** — added `.stackblitzrc` so WebContainer auto-runs `npm start` and opens the browser preview on port 3000.
103
+
104
+ ---
105
+
20
106
  ## [0.5.3] — 2026-04-16
21
107
 
22
108
  ### Changed
@@ -46,17 +132,21 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
46
132
  ## [0.4.0] — 2026-04-08
47
133
 
48
134
  ### Breaking Changes
135
+
49
136
  - **Migrated from `pdf-lib` to `@cantoo/pdf-lib`** — `@cantoo/pdf-lib` is now a direct `dependency` (always installed). Previously it was an optional peer dependency required only for encryption. This removes the `ENCRYPTION_NOT_AVAILABLE` error code and the separate `npm install @cantoo/pdf-lib` installation step. Encryption now works out of the box.
50
137
  - **`ENCRYPTION_NOT_AVAILABLE` error code removed** — encryption is now always available. Update any `switch` statements that handled this code.
51
138
 
52
139
  ### Why this change
140
+
53
141
  `pdf-lib` (the original) has not received a meaningful commit since November 2021. `@cantoo/pdf-lib` is the actively maintained fork (v2.6.5, 107+ releases, MIT license). pretext-pdf was already using `@cantoo/pdf-lib` for encryption — this commit makes it the single source of truth for all PDF operations.
54
142
 
55
143
  ### Added
144
+
56
145
  - `test/pretext-api-contract.test.ts` — canary test that asserts `@chenglou/pretext` exports the exact functions pretext-pdf depends on. Breaks loudly if pretext changes its API.
57
146
  - `docs/ROADMAP.md` — public multi-phase development plan
58
147
 
59
148
  ### Changed
149
+
60
150
  - `@chenglou/pretext` version pinned to exact `0.0.3` (no caret) — prevents surprise breaking changes from upstream auto-updates
61
151
  - `test:contract` script added — runs the pretext API contract test before the full test suite
62
152
  - All internal comments updated from `pdf-lib` to `@cantoo/pdf-lib`
@@ -66,6 +156,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
66
156
  ## [0.3.1] — 2026-04-08
67
157
 
68
158
  ### Fixed
159
+
69
160
  - **Critical: Font resolution when installed as npm package** — `@fontsource/inter` is now resolved via `createRequire(import.meta.url)` instead of a hardcoded relative path. Previously, `path.join(__dirname, '..', 'node_modules', '@fontsource', 'inter', ...)` failed when npm hoisted the dependency to the consumer's top-level `node_modules`, causing `FONT_LOAD_FAILED` on every install. Now resolves correctly regardless of npm hoisting behavior.
70
161
 
71
162
  ---
@@ -73,6 +164,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
73
164
  ## [0.3.0] — 2026-04-08
74
165
 
75
166
  ### Added (Phase 8B — Interactive Forms)
167
+
76
168
  - New `form-field` element type — creates interactive AcroForm fields in PDFs
77
169
  - `fieldType: 'text' | 'checkbox' | 'radio' | 'dropdown' | 'button'`
78
170
  - `label` renders above the field as static text
@@ -86,6 +178,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
86
178
  - 10 comprehensive tests covering all form field types
87
179
 
88
180
  ### Added (Phase 8E — Signature Placeholder)
181
+
89
182
  - `doc.signature` — visual signature box drawn on a specified page
90
183
  - Fields: `signerName`, `reason`, `location`, `x`, `y`, `width`, `height`, `page`, `borderColor`, `fontSize`
91
184
  - Draws signature line, date line, and optional text inside a bordered rectangle
@@ -93,6 +186,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
93
186
  - 6 comprehensive tests
94
187
 
95
188
  ### Added (Phase 8D — Callout Boxes)
189
+
96
190
  - New `callout` element type — styled highlight box with optional title
97
191
  - Preset styles: `style: 'info'` (blue), `'warning'` (amber), `'tip'` (green), `'note'` (gray)
98
192
  - Optional `title` rendered bold above content with left border accent
@@ -101,6 +195,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
101
195
  - 8 comprehensive tests
102
196
 
103
197
  ### Added (Phase 8F — Document Metadata Extensions)
198
+
104
199
  - `doc.metadata.language` — sets PDF `/Lang` catalog entry (BCP47 tag e.g. `'en-US'`, `'hi'`)
105
200
  - `doc.metadata.producer` — sets PDF producer field (e.g. `'MyApp v2.1'`)
106
201
  - Both fields validate as non-empty strings
@@ -111,6 +206,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
111
206
  ## [0.2.0] — 2026-04-08
112
207
 
113
208
  ### Added (Phase 8H — Inline Formatting)
209
+
114
210
  - `verticalAlign: 'superscript' | 'subscript'` on `InlineSpan` in rich-paragraphs
115
211
  - Superscript renders at 65% font size, baseline shifted up by 40% of font size
116
212
  - Subscript renders at 65% font size, baseline shifted down by 20% of font size
@@ -120,6 +216,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
120
216
  - 8 comprehensive tests covering all inline formatting functionality
121
217
 
122
218
  ### Added (Phase 8A — Annotations/Comments)
219
+
123
220
  - New `comment` element type — sticky note annotation at position in document
124
221
  - `annotation?: AnnotationSpec` on `ParagraphElement` and `HeadingElement` — attach note to element
125
222
  - Supports: `contents`, `author`, `color` (hex), `open` (popup default state)
@@ -127,6 +224,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
127
224
  - 8 comprehensive tests covering all annotation functionality
128
225
 
129
226
  ### Added (Phase 8C — Document Assembly)
227
+
130
228
  - New `merge(pdfs: Uint8Array[])` exported function — combine pre-rendered PDFs
131
229
  - New `assemble(parts: AssemblyPart[])` exported function — mix rendered docs + existing PDFs
132
230
  - `AssemblyPart` interface: `{ doc?: PdfDocument, pdf?: Uint8Array }`
@@ -134,6 +232,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
134
232
  - 8 comprehensive tests covering all assembly functionality
135
233
 
136
234
  ### Fixed
235
+
137
236
  - **CI case-sensitivity bug**: `test/phase-7-integration.test.ts` used `'en-US'` (uppercase) for hyphenation language. On Linux CI (case-sensitive filesystem) this failed with `UNSUPPORTED_LANGUAGE`. Changed to `'en-us'` to match package name `hyphenation.en-us`.
138
237
 
139
238
  ---
@@ -141,6 +240,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
141
240
  ## [0.1.1] — 2026-04-08
142
241
 
143
242
  ### Added
243
+
144
244
  - **Phase 8G: Hyperlinks** — Complete link annotation support:
145
245
  - `paragraph.url` for external URI links on paragraphs
146
246
  - `heading.url` for external URI links on headings
@@ -151,6 +251,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
151
251
  - 9 comprehensive tests covering all hyperlink functionality
152
252
 
153
253
  ### Fixed
254
+
154
255
  - **Memory leak in test suite**: Removed module-level `_hypherCache` in `src/measure.ts` that accumulated ~188KB per language across 255+ test runs. Changed from cached Hypher instances to fresh instances per call (negligible performance impact, massive memory savings).
155
256
  - **Node.js version compatibility**: Replaced `--experimental-strip-types` with `tsx` runner to support Node.js 18.x, 20.x, and 22.x in CI
156
257
  - **Broken CI examples**: Removed references to non-existent Phase 8 example scripts from GitHub Actions workflow
@@ -158,12 +259,14 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
158
259
  - **Test suite OOM issues**: Split large test files (paginate.test.ts) into paginate-basic.test.ts to work around Node.js `--experimental-strip-types` heap exhaustion bug on files >17KB
159
260
 
160
261
  ### Changed
262
+
161
263
  - `test:unit` now runs only `test/paginate-basic.test.ts` (fast, no canvas overhead)
162
264
  - Reorganized test scripts: `test:unit`, `test:validate`, `test:e2e`, `test:phases` for better memory management
163
265
  - Moved internal planning documentation to archive (preserved, not published)
164
266
  - `devDependencies`: Added `@napi-rs/canvas` explicitly (was missing, causing CI failures)
165
267
 
166
268
  ### Added
269
+
167
270
  - `CONTRIBUTING.md`: Development setup, TDD workflow, PR process guide
168
271
  - `CHENG_LOU_EMAIL_DRAFT.md`: Template for requesting endorsement from pretext creator
169
272
  - `examples/comparison-pdfmake.ts`: pdfmake version of invoice for typography comparison
@@ -173,6 +276,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
173
276
  ## [0.1.0] — 2026-04-07
174
277
 
175
278
  ### Added (Phase 7G — Encryption)
279
+
176
280
  - `doc.encryption` configuration for password-protecting PDFs
177
281
  - User password and owner password support
178
282
  - Granular permission restrictions: printing, copying, modifying, annotating
@@ -180,6 +284,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
180
284
  - Error code: `ENCRYPTION_NOT_AVAILABLE` when encryption is requested but dependency not installed
181
285
 
182
286
  ### Added (Phase 7F — RTL Text Support)
287
+
183
288
  - Right-to-left text support for Arabic, Hebrew, and other RTL languages
184
289
  - Unicode bidirectional text algorithm via `bidi-js`
185
290
  - `dir` attribute on text elements: `'ltr'` | `'rtl'` | `'auto'` for per-element control
@@ -187,6 +292,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
187
292
  - Automatic detection of mixed LTR/RTL content
188
293
 
189
294
  ### Added (Phase 7E — SVG Support)
295
+
190
296
  - `{ type: 'svg', svg: '<...' }` element for embedding SVG graphics
191
297
  - SVG rasterization via `@napi-rs/canvas`
192
298
  - ViewBox auto-sizing: automatic height calculation from viewBox aspect ratio
@@ -196,6 +302,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
196
302
  - Error code: `SVG_RENDER_FAILED` for SVG rasterization errors
197
303
 
198
304
  ### Added (Phase 7D — Table of Contents)
305
+
199
306
  - `{ type: 'toc' }` element for automatic TOC generation
200
307
  - Two-pass rendering pipeline ensures accurate page numbers
201
308
  - Configurable: `title`, `showTitle`, `minLevel`/`maxLevel`, dot leaders, level indentation
@@ -203,6 +310,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
203
310
  - Supports custom formatting via `fontSize`, `color`, `spaceAfter` parameters
204
311
 
205
312
  ### Added (Phase 7C — Hyphenation)
313
+
206
314
  - Automatic word hyphenation for better justified text layout
207
315
  - `doc.hyphenation: { language: 'en-US' }` for document-level config
208
316
  - Liang's algorithm via `hypher` package for accurate break points
@@ -211,6 +319,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
211
319
  - Error code: `UNSUPPORTED_LANGUAGE` when language not available
212
320
 
213
321
  ### Added (Phase 7B — Watermarks)
322
+
214
323
  - `doc.watermark` for text or image watermarks on every page
215
324
  - Text watermarks: `text`, `fontSize`, `fontWeight`, `color`, `opacity`, `rotation`
216
325
  - Image watermarks: `image` (Uint8Array), `opacity`, `rotation`, `color` (tint)
@@ -219,6 +328,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
219
328
  - Validation: must provide either text or image, never both required
220
329
 
221
330
  ### Added (Phase 7A — Bookmarks / PDF Outline)
331
+
222
332
  - PDF sidebar bookmarks auto-generated from heading structure
223
333
  - Enabled by default: `bookmarks: true` or `bookmarks: { minLevel: 1, maxLevel: 3 }`
224
334
  - Level filtering: include/exclude heading levels from outline
@@ -226,6 +336,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
226
336
  - Keyboard navigation: Cmd/Ctrl+Opt/Alt+O in PDF readers to toggle bookmark sidebar
227
337
 
228
338
  ### Added (Phase 6 — Advanced Features)
339
+
229
340
  - Header and footer support with {{pageNumber}} and {{totalPages}} tokens
230
341
  - Text decoration: strikethrough, underline
231
342
  - Text alignment: left, center, right, justify
@@ -234,6 +345,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
234
345
  - Tables with colspan/rowspan support
235
346
 
236
347
  ### Added (Phase 5 — Rich Text / Builder API)
348
+
237
349
  - Fluent builder API for programmatic document construction
238
350
  - Rich text element with nested formatting (bold, italic, links)
239
351
  - Inline code and code blocks with syntax highlighting
@@ -242,6 +354,7 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
242
354
  - Numbered and bulleted lists with nesting
243
355
 
244
356
  ### Added (Phases 1–4 — Core Engine)
357
+
245
358
  - Core PDF generation via `pdf-lib`
246
359
  - Element types: paragraph, heading, table, image, list, code, blockquote
247
360
  - Font support: Inter bundled, custom TTF embedding
@@ -305,6 +418,7 @@ npm run test:visual # Visual regression tests
305
418
  ## Dependencies
306
419
 
307
420
  ### Required
421
+
308
422
  - `@chenglou/pretext` — Pretext text layout engine
309
423
  - `pdf-lib` — PDF document manipulation
310
424
  - `@fontsource/inter` — Font: Inter (bundled)
@@ -313,6 +427,7 @@ npm run test:visual # Visual regression tests
313
427
  - `hyphenation.en-us` — English hyphenation patterns (Phase 7C)
314
428
 
315
429
  ### Optional (Peer)
430
+
316
431
  - `@napi-rs/canvas` — SVG rasterization (Phase 7E)
317
432
  - `@cantoo/pdf-lib` — PDF encryption (Phase 7G)
318
433