pretext-pdf 0.5.2 → 0.8.0

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 +462 -351
  2. package/README.md +749 -568
  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 +67 -8
  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 +175 -130
package/CHANGELOG.md CHANGED
@@ -1,351 +1,462 @@
1
- # Changelog
2
-
3
- All notable changes to pretext-pdf are documented here.
4
- Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
5
-
6
- ---
7
-
8
- ## [Unreleased]
9
-
10
- ### Planned (Phase 9+)
11
-
12
- - Phase 9A: Digital signatures (cryptographic PKCS#7 via `@signpdf/signpdf`)
13
- - Phase 9B: Image floats (text flowing alongside images — requires paginator rewrite)
14
- - Phase 9C: Font subsetting pre-computation (explicit glyph hints for icon fonts)
15
-
16
- ---
17
-
18
- ## [0.5.2]2026-04-13
19
-
20
- ### Added
21
-
22
- - **`onImageLoadError` callback on `PdfDocument`** — gives callers control over image load failures. Return `'skip'` to silently omit the image (preserves existing default behavior). Return `'throw'` to abort rendering with the original error. Previously, all image failures were silently downgraded to `console.warn` with no way to detect them programmatically.
23
-
24
- ```typescript
25
- await render({
26
- content: [...],
27
- onImageLoadError: (src, error) => {
28
- myLogger.warn('Image skipped', { src, error })
29
- return 'skip' // or 'throw' to abort
30
- }
31
- })
32
- ```
33
-
34
- ---
35
-
36
- ## [0.4.0] — 2026-04-08
37
-
38
- ### Breaking Changes
39
- - **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.
40
- - **`ENCRYPTION_NOT_AVAILABLE` error code removed** — encryption is now always available. Update any `switch` statements that handled this code.
41
-
42
- ### Why this change
43
- `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.
44
-
45
- ### Added
46
- - `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.
47
- - `docs/ROADMAP.md` — public multi-phase development plan
48
-
49
- ### Changed
50
- - `@chenglou/pretext` version pinned to exact `0.0.3` (no caret) — prevents surprise breaking changes from upstream auto-updates
51
- - `test:contract` script added — runs the pretext API contract test before the full test suite
52
- - All internal comments updated from `pdf-lib` to `@cantoo/pdf-lib`
53
-
54
- ---
55
-
56
- ## [0.3.1] 2026-04-08
57
-
58
- ### Fixed
59
- - **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.
60
-
61
- ---
62
-
63
- ## [0.3.0] 2026-04-08
64
-
65
- ### Added (Phase 8B Interactive Forms)
66
- - New `form-field` element type — creates interactive AcroForm fields in PDFs
67
- - `fieldType: 'text' | 'checkbox' | 'radio' | 'dropdown' | 'button'`
68
- - `label` renders above the field as static text
69
- - Text fields: `defaultValue`, `multiline`, `placeholder`, `maxLength`
70
- - Checkboxes: `checked` initial state
71
- - Radio groups and dropdowns: `options` array, `defaultSelected`
72
- - `doc.flattenForms: true` — bakes all fields into static content
73
- - Custom `borderColor`, `backgroundColor`, `width`, `height`, `fontSize` per field
74
- - New error codes: `FORM_FIELD_NAME_DUPLICATE` (duplicate `name` across fields), `FORM_FLATTEN_FAILED`
75
- - Post-render `form.updateFieldAppearances()` ensures proper display in all PDF readers
76
- - 10 comprehensive tests covering all form field types
77
-
78
- ### Added (Phase 8E — Signature Placeholder)
79
- - `doc.signature` — visual signature box drawn on a specified page
80
- - Fields: `signerName`, `reason`, `location`, `x`, `y`, `width`, `height`, `page`, `borderColor`, `fontSize`
81
- - Draws signature line, date line, and optional text inside a bordered rectangle
82
- - `page` is 0-indexed, defaults to last page, clamps gracefully if out of range
83
- - 6 comprehensive tests
84
-
85
- ### Added (Phase 8D — Callout Boxes)
86
- - New `callout` element type — styled highlight box with optional title
87
- - Preset styles: `style: 'info'` (blue), `'warning'` (amber), `'tip'` (green), `'note'` (gray)
88
- - Optional `title` rendered bold above content with left border accent
89
- - Fully customizable: `backgroundColor`, `borderColor`, `color`, `titleColor`, `padding`
90
- - Paginates correctly across pages (reuses blockquote pagination logic)
91
- - 8 comprehensive tests
92
-
93
- ### Added (Phase 8F — Document Metadata Extensions)
94
- - `doc.metadata.language` — sets PDF `/Lang` catalog entry (BCP47 tag e.g. `'en-US'`, `'hi'`)
95
- - `doc.metadata.producer` — sets PDF producer field (e.g. `'MyApp v2.1'`)
96
- - Both fields validate as non-empty strings
97
- - 5 comprehensive tests
98
-
99
- ---
100
-
101
- ## [0.2.0] — 2026-04-08
102
-
103
- ### Added (Phase 8H — Inline Formatting)
104
- - `verticalAlign: 'superscript' | 'subscript'` on `InlineSpan` in rich-paragraphs
105
- - Superscript renders at 65% font size, baseline shifted up by 40% of font size
106
- - Subscript renders at 65% font size, baseline shifted down by 20% of font size
107
- - `letterSpacing?: number` on `ParagraphElement`, `HeadingElement`, `RichParagraphElement` — extra pt between characters
108
- - `smallCaps?: boolean` on those same three element types — simulated via uppercase + 80% fontSize
109
- - Character-by-character rendering for letterSpacing (pdf-lib has no native spacing param)
110
- - 8 comprehensive tests covering all inline formatting functionality
111
-
112
- ### Added (Phase 8A — Annotations/Comments)
113
- - New `comment` element type — sticky note annotation at position in document
114
- - `annotation?: AnnotationSpec` on `ParagraphElement` and `HeadingElement` — attach note to element
115
- - Supports: `contents`, `author`, `color` (hex), `open` (popup default state)
116
- - Uses PDF `Subtype: 'Text'` annotation (sticky note icon in PDF viewers)
117
- - 8 comprehensive tests covering all annotation functionality
118
-
119
- ### Added (Phase 8C — Document Assembly)
120
- - New `merge(pdfs: Uint8Array[])` exported function — combine pre-rendered PDFs
121
- - New `assemble(parts: AssemblyPart[])` exported function — mix rendered docs + existing PDFs
122
- - `AssemblyPart` interface: `{ doc?: PdfDocument, pdf?: Uint8Array }`
123
- - New error codes: `ASSEMBLY_EMPTY`, `ASSEMBLY_FAILED`
124
- - 8 comprehensive tests covering all assembly functionality
125
-
126
- ### Fixed
127
- - **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`.
128
-
129
- ---
130
-
131
- ## [0.1.1]2026-04-08
132
-
133
- ### Added
134
- - **Phase 8G: Hyperlinks** — Complete link annotation support:
135
- - `paragraph.url` for external URI links on paragraphs
136
- - `heading.url` for external URI links on headings
137
- - `heading.anchor` for named PDF destinations (internal cross-references)
138
- - `InlineSpan.href` for external and internal `#anchorId` links in rich-paragraphs
139
- - `mailto:` scheme support for email links
140
- - GoTo annotations for internal anchor references
141
- - 9 comprehensive tests covering all hyperlink functionality
142
-
143
- ### Fixed
144
- - **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).
145
- - **Node.js version compatibility**: Replaced `--experimental-strip-types` with `tsx` runner to support Node.js 18.x, 20.x, and 22.x in CI
146
- - **Broken CI examples**: Removed references to non-existent Phase 8 example scripts from GitHub Actions workflow
147
- - **README examples mismatch**: Updated Examples section to only list 5 existing Phase 7 examples (watermark, bookmarks, toc, rtl, encryption)
148
- - **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
149
-
150
- ### Changed
151
- - `test:unit` now runs only `test/paginate-basic.test.ts` (fast, no canvas overhead)
152
- - Reorganized test scripts: `test:unit`, `test:validate`, `test:e2e`, `test:phases` for better memory management
153
- - Moved internal planning documentation to archive (preserved, not published)
154
- - `devDependencies`: Added `@napi-rs/canvas` explicitly (was missing, causing CI failures)
155
-
156
- ### Added
157
- - `CONTRIBUTING.md`: Development setup, TDD workflow, PR process guide
158
- - `CHENG_LOU_EMAIL_DRAFT.md`: Template for requesting endorsement from pretext creator
159
- - `examples/comparison-pdfmake.ts`: pdfmake version of invoice for typography comparison
160
-
161
- ---
162
-
163
- ## [0.1.0] 2026-04-07
164
-
165
- ### Added (Phase 7G — Encryption)
166
- - `doc.encryption` configuration for password-protecting PDFs
167
- - User password and owner password support
168
- - Granular permission restrictions: printing, copying, modifying, annotating
169
- - Lazy-loads `@cantoo/pdf-lib` (optional peer dependency) zero cost when not used
170
- - Error code: `ENCRYPTION_NOT_AVAILABLE` when encryption is requested but dependency not installed
171
-
172
- ### Added (Phase 7F — RTL Text Support)
173
- - Right-to-left text support for Arabic, Hebrew, and other RTL languages
174
- - Unicode bidirectional text algorithm via `bidi-js`
175
- - `dir` attribute on text elements: `'ltr'` | `'rtl'` | `'auto'` for per-element control
176
- - RTL text works with headings, paragraphs, lists, tables, and all text elements
177
- - Automatic detection of mixed LTR/RTL content
178
-
179
- ### Added (Phase 7E SVG Support)
180
- - `{ type: 'svg', svg: '<...' }` element for embedding SVG graphics
181
- - SVG rasterization via `@napi-rs/canvas`
182
- - ViewBox auto-sizing: automatic height calculation from viewBox aspect ratio
183
- - Explicit sizing: `width` and `height` parameters for precise control
184
- - Alignment options: `align: 'left' | 'center' | 'right'`
185
- - Multi-page support: SVGs paginate correctly across page breaks
186
- - Error code: `SVG_RENDER_FAILED` for SVG rasterization errors
187
-
188
- ### Added (Phase 7D — Table of Contents)
189
- - `{ type: 'toc' }` element for automatic TOC generation
190
- - Two-pass rendering pipeline ensures accurate page numbers
191
- - Configurable: `title`, `showTitle`, `minLevel`/`maxLevel`, dot leaders, level indentation
192
- - Auto-indexed from heading structure (H1, H2, H3, etc.)
193
- - Supports custom formatting via `fontSize`, `color`, `spaceAfter` parameters
194
-
195
- ### Added (Phase 7C — Hyphenation)
196
- - Automatic word hyphenation for better justified text layout
197
- - `doc.hyphenation: { language: 'en-US' }` for document-level config
198
- - Liang's algorithm via `hypher` package for accurate break points
199
- - Configurable: `minWordLength`, `leftMin`, `rightMin`, per-element `hyphenate: false` opt-out
200
- - Language support: includes `hyphenation.en-us` (additional languages via npm packages)
201
- - Error code: `UNSUPPORTED_LANGUAGE` when language not available
202
-
203
- ### Added (Phase 7B — Watermarks)
204
- - `doc.watermark` for text or image watermarks on every page
205
- - Text watermarks: `text`, `fontSize`, `fontWeight`, `color`, `opacity`, `rotation`
206
- - Image watermarks: `image` (Uint8Array), `opacity`, `rotation`, `color` (tint)
207
- - Watermarks render behind content (lower z-index)
208
- - Rotation bounds: -360 rotation 360 degrees
209
- - Validation: must provide either text or image, never both required
210
-
211
- ### Added (Phase 7A — Bookmarks / PDF Outline)
212
- - PDF sidebar bookmarks auto-generated from heading structure
213
- - Enabled by default: `bookmarks: true` or `bookmarks: { minLevel: 1, maxLevel: 3 }`
214
- - Level filtering: include/exclude heading levels from outline
215
- - Per-heading opt-out: `bookmark: false` on heading elements
216
- - Keyboard navigation: Cmd/Ctrl+Opt/Alt+O in PDF readers to toggle bookmark sidebar
217
-
218
- ### Added (Phase 6 Advanced Features)
219
- - Header and footer support with {{pageNumber}} and {{totalPages}} tokens
220
- - Text decoration: strikethrough, underline
221
- - Text alignment: left, center, right, justify
222
- - Line height control: custom line-height multipliers
223
- - Column layout with multi-column content flow
224
- - Tables with colspan/rowspan support
225
-
226
- ### Added (Phase 5 Rich Text / Builder API)
227
- - Fluent builder API for programmatic document construction
228
- - Rich text element with nested formatting (bold, italic, links)
229
- - Inline code and code blocks with syntax highlighting
230
- - Block quotes with custom styling
231
- - Horizontal rules (hr element)
232
- - Numbered and bulleted lists with nesting
233
-
234
- ### Added (Phases 1–4 Core Engine)
235
- - Core PDF generation via `pdf-lib`
236
- - Element types: paragraph, heading, table, image, list, code, blockquote
237
- - Font support: Inter bundled, custom TTF embedding
238
- - Document metadata: title, author, subject, keywords, created date
239
- - Page sizing: A4, A3, A5, Letter, Legal, or custom dimensions
240
- - Margins: top, bottom, left, right per page
241
- - Multi-page pagination with orphan/widow control
242
- - Image formats: PNG, JPG, WebP
243
- - Table features: custom column widths, cell padding, borders, header styling
244
- - Colors: hex color codes throughout (text, backgrounds, borders)
245
-
246
- ---
247
-
248
- ## Features by Phase
249
-
250
- | Phase | Feature | Status | Tests | Version |
251
- |-------|---------|--------|-------|---------|
252
- | 7A | Bookmarks / PDF Outline | Complete | 8 | 0.1.0 |
253
- | 7B | Watermarks | ✅ Complete | 8 | 0.1.0 |
254
- | 7C | Hyphenation | ✅ Complete | 10 | 0.1.0 |
255
- | 7D | Table of Contents | ✅ Complete | 10 | 0.1.0 |
256
- | 7E | SVG Support | Complete | 8 | 0.1.0 |
257
- | 7F | RTL Text Support | Complete | 12 | 0.1.0 |
258
- | 7G | Encryption | Complete | 7 | 0.1.0 |
259
- | Integration | Feature Combinations | ✅ Complete | 6 | 0.1.0 |
260
- | 6 | Advanced Features | ✅ Complete | — | 0.1.0 |
261
- | 5 | Rich Text / Builder API | ✅ Complete | — | 0.1.0 |
262
- | 1–4 | Core Engine | ✅ Complete | — | 0.1.0 |
263
-
264
- ---
265
-
266
- ## How to Use Examples
267
-
268
- All Phase 7 features have working examples in the `examples/` directory. Run them with:
269
-
270
- ```bash
271
- npm run example:watermark # Phase 7B — Watermarks
272
- npm run example:bookmarks # Phase 7ABookmarks
273
- npm run example:toc # Phase 7D — Table of Contents
274
- npm run example:rtl # Phase 7F RTL Text Support
275
- npm run example:encryption # Phase 7G Encryption
276
- ```
277
-
278
- PDF output is written to `output/phase7-*.pdf`.
279
-
280
- ---
281
-
282
- ## Test Coverage
283
-
284
- All phases include comprehensive test coverage:
285
-
286
- ```bash
287
- npm test # Run all 75+ tests
288
- npm run test:unit # Unit tests only
289
- npm run test:e2e # End-to-end tests
290
- npm run test:visual # Visual regression tests
291
- ```
292
-
293
- ---
294
-
295
- ## Dependencies
296
-
297
- ### Required
298
- - `@chenglou/pretext` Pretext text layout engine
299
- - `pdf-lib` — PDF document manipulation
300
- - `@fontsource/inter` Font: Inter (bundled)
301
- - `bidi-js` Bidirectional text algorithm (Phase 7F)
302
- - `hypher` Hyphenation algorithm (Phase 7C)
303
- - `hyphenation.en-us` English hyphenation patterns (Phase 7C)
304
-
305
- ### Optional (Peer)
306
- - `@napi-rs/canvas` — SVG rasterization (Phase 7E)
307
- - `@cantoo/pdf-lib` PDF encryption (Phase 7G)
308
-
309
- ---
310
-
311
- ## Architecture
312
-
313
- pretext-pdf uses a modular, layered architecture:
314
-
315
- ```
316
- render(doc) validate layout paginate → render-pages → PDF bytes
317
- ```
318
-
319
- Each phase adds features orthogonally:
320
- - Phase 1–4: Core engine, pagination, typography
321
- - Phase 5: Rich text and builder patterns
322
- - Phase 6: Advanced layout and formatting
323
- - Phase 7: Security, internationalization, accessibility
324
-
325
- ---
326
-
327
- ## Future Roadmap
328
-
329
- Potential Phase 8+ features (not yet implemented):
330
- - Hyperlinks and anchors
331
- - Justified text alignment
332
- - Enhanced text decorations
333
- - Font subsetting for file size reduction
334
- - Browser compatibility improvements
335
- - Performance optimizations
336
-
337
- ---
338
-
339
- ## Contributing
340
-
341
- pretext-pdf is actively maintained. To report issues, request features, or contribute:
342
-
343
- 1. Check existing issues on the project repo
344
- 2. Write failing tests first (TDD)
345
- 3. Submit pull requests with test coverage ≥80%
346
-
347
- ---
348
-
349
- ## License
350
-
351
- Check LICENSE file in repository root.
1
+ # Changelog
2
+
3
+ <!-- markdownlint-disable MD024 -->
4
+
5
+ All notable changes to pretext-pdf are documented here.
6
+ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/)
7
+
8
+ ---
9
+
10
+ ## [0.8.0] 2026-04-19
11
+
12
+ ### Added
13
+
14
+ - **`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.
15
+ - **`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.
16
+ - **`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.
17
+ - **`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.
18
+ - **`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).
19
+ - **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`.
20
+
21
+ ---
22
+
23
+ ## [0.7.1] — 2026-04-19
24
+
25
+ ### Changed
26
+
27
+ - **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.
28
+
29
+ ### Fixed
30
+
31
+ - **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.
32
+ - **3 phase-11 list tests corrected** — test data incorrectly contained 3-level nesting while named "2-level"; data trimmed to match documented contract.
33
+
34
+ ---
35
+
36
+ ## [Unreleased]
37
+
38
+ ### Added (Phase 11 — Cross-cutting Enhancements)
39
+
40
+ - **`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).
41
+ - **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'`.
42
+ - **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.
43
+ - **`onFormFieldError` callback** `doc.onFormFieldError: (name, err) => 'skip' | 'throw'` mirrors `onImageLoadError`. Controls render behaviour when a form field fails.
44
+ - **`createFootnoteSet(defs)`** — helper exported from `pretext-pdf` that generates footnote definition/reference pairs with globally unique IDs. Returns `Array<{ id, def }>`.
45
+ - **`renderDate` field** — `doc.renderDate: Date | string` overrides the PDF creation date. Useful for reproducible builds and testing.
46
+ - **`{{date}}` and `{{author}}` tokens** in header/footer text join existing `{{pageNumber}}` / `{{totalPages}}`. `{{date}}` resolves from `renderDate`; `{{author}}` resolves from `doc.metadata.author`.
47
+ - **`tabularNumbers`** on `rich-paragraph` — digits rendered at uniform slot width (widest digit in font), so columns of numbers align without OpenType TNUM feature.
48
+ - **`smallCaps` + `letterSpacing` per span** — `InlineSpan.smallCaps` and `InlineSpan.letterSpacing` now respected in `rich-paragraph` rendering.
49
+ - **Per-span `fontSize`** — `InlineSpan.fontSize` overrides the element-level font size for that span. Enables mixed-size text in a single paragraph.
50
+
51
+ ### Fixed (Phase 11)
52
+
53
+ - `resolveTokens()` used `.replace()` (replaces first occurrence only) — changed to `.replaceAll()` for all four tokens.
54
+ - Table span grid: continuation-row cursor was advancing by 1 instead of `colspan` when skipping a spanned column — now advances by full span width.
55
+ - Font family names now validated for safe characters (`/^[a-zA-Z0-9 _-]+$/`) in `requireFamily()` — rejects null bytes and control characters.
56
+ - Annotation `color` and `author` fields now validated in `validateElement()` for both `paragraph` and `heading` annotations.
57
+ - `buildOutlineTree` memoizes `parentIdxOf()` into a pre-computed array — eliminates O(n²) scan for documents with large heading counts.
58
+ - Table grid-line renderer pre-computes active boundary set — eliminates O(rows × cols) inner loop for large tables.
59
+ - `addLinkAnnotation()` re-validates URL scheme at render time (defense-in-depth; `validate.ts` is the primary gate).
60
+
61
+ ### Planned (Phase 9+)
62
+
63
+ - Phase 9A: Digital signatures (cryptographic PKCS#7 via `@signpdf/signpdf`)
64
+ - Phase 9B: Image floats (text flowing alongside images — requires paginator rewrite)
65
+ - Phase 9C: Font subsetting pre-computation (explicit glyph hints for icon fonts)
66
+
67
+ ---
68
+
69
+ ## [0.7.0] 2026-04-17
70
+
71
+ ### Added
72
+
73
+ - **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.
74
+ - **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.
75
+ - **`## Performance` section in README** measured render times and PDF sizes for 1-page, 10-page, and mixed-element documents. Font subsetting behaviour documented.
76
+ - **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.
77
+ - **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.
78
+
79
+ ### Changed
80
+
81
+ - **`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.
82
+ - **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.
83
+ - **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.
84
+
85
+ ### Fixed
86
+
87
+ - **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 } }`.
88
+ - **StackBlitz integration** — added `.stackblitzrc` so WebContainer auto-runs `npm start` and opens the browser preview on port 3000.
89
+
90
+ ---
91
+
92
+ ## [0.5.3] — 2026-04-16
93
+
94
+ ### Changed
95
+
96
+ - **Upgraded `@chenglou/pretext` from 0.0.3 to 0.0.5** — picks up improved text analysis accuracy (~35% larger analysis module), better measurement precision, extracted bidi-data module for cleaner tree-shaking, and new `rich-inline` export (not yet used by pretext-pdf). No breaking changes — `prepareWithSegments()` and `layoutWithLines()` APIs are unchanged. All 223 tests pass, 3 example PDFs visually verified (RTL, TOC, hyperlinks).
97
+
98
+ ---
99
+
100
+ ## [0.5.2] — 2026-04-13
101
+
102
+ ### Added
103
+
104
+ - **`onImageLoadError` callback on `PdfDocument`** — gives callers control over image load failures. Return `'skip'` to silently omit the image (preserves existing default behavior). Return `'throw'` to abort rendering with the original error. Previously, all image failures were silently downgraded to `console.warn` with no way to detect them programmatically.
105
+
106
+ ```typescript
107
+ await render({
108
+ content: [...],
109
+ onImageLoadError: (src, error) => {
110
+ myLogger.warn('Image skipped', { src, error })
111
+ return 'skip' // or 'throw' to abort
112
+ }
113
+ })
114
+ ```
115
+
116
+ ---
117
+
118
+ ## [0.4.0] — 2026-04-08
119
+
120
+ ### Breaking Changes
121
+
122
+ - **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.
123
+ - **`ENCRYPTION_NOT_AVAILABLE` error code removed** — encryption is now always available. Update any `switch` statements that handled this code.
124
+
125
+ ### Why this change
126
+
127
+ `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.
128
+
129
+ ### Added
130
+
131
+ - `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.
132
+ - `docs/ROADMAP.md` — public multi-phase development plan
133
+
134
+ ### Changed
135
+
136
+ - `@chenglou/pretext` version pinned to exact `0.0.3` (no caret) prevents surprise breaking changes from upstream auto-updates
137
+ - `test:contract` script added runs the pretext API contract test before the full test suite
138
+ - All internal comments updated from `pdf-lib` to `@cantoo/pdf-lib`
139
+
140
+ ---
141
+
142
+ ## [0.3.1] — 2026-04-08
143
+
144
+ ### Fixed
145
+
146
+ - **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.
147
+
148
+ ---
149
+
150
+ ## [0.3.0] — 2026-04-08
151
+
152
+ ### Added (Phase 8B Interactive Forms)
153
+
154
+ - New `form-field` element type creates interactive AcroForm fields in PDFs
155
+ - `fieldType: 'text' | 'checkbox' | 'radio' | 'dropdown' | 'button'`
156
+ - `label` renders above the field as static text
157
+ - Text fields: `defaultValue`, `multiline`, `placeholder`, `maxLength`
158
+ - Checkboxes: `checked` initial state
159
+ - Radio groups and dropdowns: `options` array, `defaultSelected`
160
+ - `doc.flattenForms: true` — bakes all fields into static content
161
+ - Custom `borderColor`, `backgroundColor`, `width`, `height`, `fontSize` per field
162
+ - New error codes: `FORM_FIELD_NAME_DUPLICATE` (duplicate `name` across fields), `FORM_FLATTEN_FAILED`
163
+ - Post-render `form.updateFieldAppearances()` ensures proper display in all PDF readers
164
+ - 10 comprehensive tests covering all form field types
165
+
166
+ ### Added (Phase 8E Signature Placeholder)
167
+
168
+ - `doc.signature` visual signature box drawn on a specified page
169
+ - Fields: `signerName`, `reason`, `location`, `x`, `y`, `width`, `height`, `page`, `borderColor`, `fontSize`
170
+ - Draws signature line, date line, and optional text inside a bordered rectangle
171
+ - `page` is 0-indexed, defaults to last page, clamps gracefully if out of range
172
+ - 6 comprehensive tests
173
+
174
+ ### Added (Phase 8D Callout Boxes)
175
+
176
+ - New `callout` element type styled highlight box with optional title
177
+ - Preset styles: `style: 'info'` (blue), `'warning'` (amber), `'tip'` (green), `'note'` (gray)
178
+ - Optional `title` rendered bold above content with left border accent
179
+ - Fully customizable: `backgroundColor`, `borderColor`, `color`, `titleColor`, `padding`
180
+ - Paginates correctly across pages (reuses blockquote pagination logic)
181
+ - 8 comprehensive tests
182
+
183
+ ### Added (Phase 8F Document Metadata Extensions)
184
+
185
+ - `doc.metadata.language` sets PDF `/Lang` catalog entry (BCP47 tag e.g. `'en-US'`, `'hi'`)
186
+ - `doc.metadata.producer` sets PDF producer field (e.g. `'MyApp v2.1'`)
187
+ - Both fields validate as non-empty strings
188
+ - 5 comprehensive tests
189
+
190
+ ---
191
+
192
+ ## [0.2.0] 2026-04-08
193
+
194
+ ### Added (Phase 8H — Inline Formatting)
195
+
196
+ - `verticalAlign: 'superscript' | 'subscript'` on `InlineSpan` in rich-paragraphs
197
+ - Superscript renders at 65% font size, baseline shifted up by 40% of font size
198
+ - Subscript renders at 65% font size, baseline shifted down by 20% of font size
199
+ - `letterSpacing?: number` on `ParagraphElement`, `HeadingElement`, `RichParagraphElement` — extra pt between characters
200
+ - `smallCaps?: boolean` on those same three element types — simulated via uppercase + 80% fontSize
201
+ - Character-by-character rendering for letterSpacing (pdf-lib has no native spacing param)
202
+ - 8 comprehensive tests covering all inline formatting functionality
203
+
204
+ ### Added (Phase 8A Annotations/Comments)
205
+
206
+ - New `comment` element type sticky note annotation at position in document
207
+ - `annotation?: AnnotationSpec` on `ParagraphElement` and `HeadingElement` — attach note to element
208
+ - Supports: `contents`, `author`, `color` (hex), `open` (popup default state)
209
+ - Uses PDF `Subtype: 'Text'` annotation (sticky note icon in PDF viewers)
210
+ - 8 comprehensive tests covering all annotation functionality
211
+
212
+ ### Added (Phase 8C Document Assembly)
213
+
214
+ - New `merge(pdfs: Uint8Array[])` exported function combine pre-rendered PDFs
215
+ - New `assemble(parts: AssemblyPart[])` exported function — mix rendered docs + existing PDFs
216
+ - `AssemblyPart` interface: `{ doc?: PdfDocument, pdf?: Uint8Array }`
217
+ - New error codes: `ASSEMBLY_EMPTY`, `ASSEMBLY_FAILED`
218
+ - 8 comprehensive tests covering all assembly functionality
219
+
220
+ ### Fixed
221
+
222
+ - **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`.
223
+
224
+ ---
225
+
226
+ ## [0.1.1]2026-04-08
227
+
228
+ ### Added
229
+
230
+ - **Phase 8G: Hyperlinks** Complete link annotation support:
231
+ - `paragraph.url` for external URI links on paragraphs
232
+ - `heading.url` for external URI links on headings
233
+ - `heading.anchor` for named PDF destinations (internal cross-references)
234
+ - `InlineSpan.href` for external and internal `#anchorId` links in rich-paragraphs
235
+ - `mailto:` scheme support for email links
236
+ - GoTo annotations for internal anchor references
237
+ - 9 comprehensive tests covering all hyperlink functionality
238
+
239
+ ### Fixed
240
+
241
+ - **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).
242
+ - **Node.js version compatibility**: Replaced `--experimental-strip-types` with `tsx` runner to support Node.js 18.x, 20.x, and 22.x in CI
243
+ - **Broken CI examples**: Removed references to non-existent Phase 8 example scripts from GitHub Actions workflow
244
+ - **README examples mismatch**: Updated Examples section to only list 5 existing Phase 7 examples (watermark, bookmarks, toc, rtl, encryption)
245
+ - **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
246
+
247
+ ### Changed
248
+
249
+ - `test:unit` now runs only `test/paginate-basic.test.ts` (fast, no canvas overhead)
250
+ - Reorganized test scripts: `test:unit`, `test:validate`, `test:e2e`, `test:phases` for better memory management
251
+ - Moved internal planning documentation to archive (preserved, not published)
252
+ - `devDependencies`: Added `@napi-rs/canvas` explicitly (was missing, causing CI failures)
253
+
254
+ ### Added
255
+
256
+ - `CONTRIBUTING.md`: Development setup, TDD workflow, PR process guide
257
+ - `CHENG_LOU_EMAIL_DRAFT.md`: Template for requesting endorsement from pretext creator
258
+ - `examples/comparison-pdfmake.ts`: pdfmake version of invoice for typography comparison
259
+
260
+ ---
261
+
262
+ ## [0.1.0] — 2026-04-07
263
+
264
+ ### Added (Phase 7G — Encryption)
265
+
266
+ - `doc.encryption` configuration for password-protecting PDFs
267
+ - User password and owner password support
268
+ - Granular permission restrictions: printing, copying, modifying, annotating
269
+ - Lazy-loads `@cantoo/pdf-lib` (optional peer dependency) — zero cost when not used
270
+ - Error code: `ENCRYPTION_NOT_AVAILABLE` when encryption is requested but dependency not installed
271
+
272
+ ### Added (Phase 7FRTL Text Support)
273
+
274
+ - Right-to-left text support for Arabic, Hebrew, and other RTL languages
275
+ - Unicode bidirectional text algorithm via `bidi-js`
276
+ - `dir` attribute on text elements: `'ltr'` | `'rtl'` | `'auto'` for per-element control
277
+ - RTL text works with headings, paragraphs, lists, tables, and all text elements
278
+ - Automatic detection of mixed LTR/RTL content
279
+
280
+ ### Added (Phase 7E — SVG Support)
281
+
282
+ - `{ type: 'svg', svg: '<...' }` element for embedding SVG graphics
283
+ - SVG rasterization via `@napi-rs/canvas`
284
+ - ViewBox auto-sizing: automatic height calculation from viewBox aspect ratio
285
+ - Explicit sizing: `width` and `height` parameters for precise control
286
+ - Alignment options: `align: 'left' | 'center' | 'right'`
287
+ - Multi-page support: SVGs paginate correctly across page breaks
288
+ - Error code: `SVG_RENDER_FAILED` for SVG rasterization errors
289
+
290
+ ### Added (Phase 7D Table of Contents)
291
+
292
+ - `{ type: 'toc' }` element for automatic TOC generation
293
+ - Two-pass rendering pipeline ensures accurate page numbers
294
+ - Configurable: `title`, `showTitle`, `minLevel`/`maxLevel`, dot leaders, level indentation
295
+ - Auto-indexed from heading structure (H1, H2, H3, etc.)
296
+ - Supports custom formatting via `fontSize`, `color`, `spaceAfter` parameters
297
+
298
+ ### Added (Phase 7C Hyphenation)
299
+
300
+ - Automatic word hyphenation for better justified text layout
301
+ - `doc.hyphenation: { language: 'en-US' }` for document-level config
302
+ - Liang's algorithm via `hypher` package for accurate break points
303
+ - Configurable: `minWordLength`, `leftMin`, `rightMin`, per-element `hyphenate: false` opt-out
304
+ - Language support: includes `hyphenation.en-us` (additional languages via npm packages)
305
+ - Error code: `UNSUPPORTED_LANGUAGE` when language not available
306
+
307
+ ### Added (Phase 7B Watermarks)
308
+
309
+ - `doc.watermark` for text or image watermarks on every page
310
+ - Text watermarks: `text`, `fontSize`, `fontWeight`, `color`, `opacity`, `rotation`
311
+ - Image watermarks: `image` (Uint8Array), `opacity`, `rotation`, `color` (tint)
312
+ - Watermarks render behind content (lower z-index)
313
+ - Rotation bounds: -360 rotation ≤ 360 degrees
314
+ - Validation: must provide either text or image, never both required
315
+
316
+ ### Added (Phase 7A Bookmarks / PDF Outline)
317
+
318
+ - PDF sidebar bookmarks auto-generated from heading structure
319
+ - Enabled by default: `bookmarks: true` or `bookmarks: { minLevel: 1, maxLevel: 3 }`
320
+ - Level filtering: include/exclude heading levels from outline
321
+ - Per-heading opt-out: `bookmark: false` on heading elements
322
+ - Keyboard navigation: Cmd/Ctrl+Opt/Alt+O in PDF readers to toggle bookmark sidebar
323
+
324
+ ### Added (Phase 6 — Advanced Features)
325
+
326
+ - Header and footer support with {{pageNumber}} and {{totalPages}} tokens
327
+ - Text decoration: strikethrough, underline
328
+ - Text alignment: left, center, right, justify
329
+ - Line height control: custom line-height multipliers
330
+ - Column layout with multi-column content flow
331
+ - Tables with colspan/rowspan support
332
+
333
+ ### Added (Phase 5 Rich Text / Builder API)
334
+
335
+ - Fluent builder API for programmatic document construction
336
+ - Rich text element with nested formatting (bold, italic, links)
337
+ - Inline code and code blocks with syntax highlighting
338
+ - Block quotes with custom styling
339
+ - Horizontal rules (hr element)
340
+ - Numbered and bulleted lists with nesting
341
+
342
+ ### Added (Phases 1–4 — Core Engine)
343
+
344
+ - Core PDF generation via `pdf-lib`
345
+ - Element types: paragraph, heading, table, image, list, code, blockquote
346
+ - Font support: Inter bundled, custom TTF embedding
347
+ - Document metadata: title, author, subject, keywords, created date
348
+ - Page sizing: A4, A3, A5, Letter, Legal, or custom dimensions
349
+ - Margins: top, bottom, left, right per page
350
+ - Multi-page pagination with orphan/widow control
351
+ - Image formats: PNG, JPG, WebP
352
+ - Table features: custom column widths, cell padding, borders, header styling
353
+ - Colors: hex color codes throughout (text, backgrounds, borders)
354
+
355
+ ---
356
+
357
+ ## Features by Phase
358
+
359
+ | Phase | Feature | Status | Tests | Version |
360
+ |-------|---------|--------|-------|---------|
361
+ | 7A | Bookmarks / PDF Outline | ✅ Complete | 8 | 0.1.0 |
362
+ | 7B | Watermarks | ✅ Complete | 8 | 0.1.0 |
363
+ | 7C | Hyphenation | ✅ Complete | 10 | 0.1.0 |
364
+ | 7D | Table of Contents | ✅ Complete | 10 | 0.1.0 |
365
+ | 7E | SVG Support | ✅ Complete | 8 | 0.1.0 |
366
+ | 7F | RTL Text Support | ✅ Complete | 12 | 0.1.0 |
367
+ | 7G | Encryption | ✅ Complete | 7 | 0.1.0 |
368
+ | Integration | Feature Combinations | ✅ Complete | 6 | 0.1.0 |
369
+ | 6 | Advanced Features | ✅ Complete | — | 0.1.0 |
370
+ | 5 | Rich Text / Builder API | ✅ Complete | — | 0.1.0 |
371
+ | 1–4 | Core Engine | ✅ Complete | — | 0.1.0 |
372
+
373
+ ---
374
+
375
+ ## How to Use Examples
376
+
377
+ All Phase 7 features have working examples in the `examples/` directory. Run them with:
378
+
379
+ ```bash
380
+ npm run example:watermark # Phase 7B — Watermarks
381
+ npm run example:bookmarks # Phase 7A — Bookmarks
382
+ npm run example:toc # Phase 7D — Table of Contents
383
+ npm run example:rtl # Phase 7F — RTL Text Support
384
+ npm run example:encryption # Phase 7G — Encryption
385
+ ```
386
+
387
+ PDF output is written to `output/phase7-*.pdf`.
388
+
389
+ ---
390
+
391
+ ## Test Coverage
392
+
393
+ All phases include comprehensive test coverage:
394
+
395
+ ```bash
396
+ npm test # Run all 75+ tests
397
+ npm run test:unit # Unit tests only
398
+ npm run test:e2e # End-to-end tests
399
+ npm run test:visual # Visual regression tests
400
+ ```
401
+
402
+ ---
403
+
404
+ ## Dependencies
405
+
406
+ ### Required
407
+
408
+ - `@chenglou/pretext` — Pretext text layout engine
409
+ - `pdf-lib` — PDF document manipulation
410
+ - `@fontsource/inter` — Font: Inter (bundled)
411
+ - `bidi-js` — Bidirectional text algorithm (Phase 7F)
412
+ - `hypher` — Hyphenation algorithm (Phase 7C)
413
+ - `hyphenation.en-us` — English hyphenation patterns (Phase 7C)
414
+
415
+ ### Optional (Peer)
416
+
417
+ - `@napi-rs/canvas` — SVG rasterization (Phase 7E)
418
+ - `@cantoo/pdf-lib` — PDF encryption (Phase 7G)
419
+
420
+ ---
421
+
422
+ ## Architecture
423
+
424
+ pretext-pdf uses a modular, layered architecture:
425
+
426
+ ```
427
+ render(doc) → validate → layout → paginate → render-pages → PDF bytes
428
+ ```
429
+
430
+ Each phase adds features orthogonally:
431
+ - Phase 1–4: Core engine, pagination, typography
432
+ - Phase 5: Rich text and builder patterns
433
+ - Phase 6: Advanced layout and formatting
434
+ - Phase 7: Security, internationalization, accessibility
435
+
436
+ ---
437
+
438
+ ## Future Roadmap
439
+
440
+ Potential Phase 8+ features (not yet implemented):
441
+ - Hyperlinks and anchors
442
+ - Justified text alignment
443
+ - Enhanced text decorations
444
+ - Font subsetting for file size reduction
445
+ - Browser compatibility improvements
446
+ - Performance optimizations
447
+
448
+ ---
449
+
450
+ ## Contributing
451
+
452
+ pretext-pdf is actively maintained. To report issues, request features, or contribute:
453
+
454
+ 1. Check existing issues on the project repo
455
+ 2. Write failing tests first (TDD)
456
+ 3. Submit pull requests with test coverage ≥80%
457
+
458
+ ---
459
+
460
+ ## License
461
+
462
+ Check LICENSE file in repository root.