pretext-pdf 0.1.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 +242 -0
- package/LICENSE +21 -0
- package/README.md +402 -0
- package/dist/assets.d.ts +14 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +182 -0
- package/dist/assets.js.map +1 -0
- package/dist/builder.d.ts +53 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +129 -0
- package/dist/builder.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/fonts.d.ts +21 -0
- package/dist/fonts.d.ts.map +1 -0
- package/dist/fonts.js +310 -0
- package/dist/fonts.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/dist/measure.d.ts +53 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +1029 -0
- package/dist/measure.js.map +1 -0
- package/dist/node-polyfill.d.ts +7 -0
- package/dist/node-polyfill.d.ts.map +1 -0
- package/dist/node-polyfill.js +82 -0
- package/dist/node-polyfill.js.map +1 -0
- package/dist/page-sizes.d.ts +13 -0
- package/dist/page-sizes.d.ts.map +1 -0
- package/dist/page-sizes.js +24 -0
- package/dist/page-sizes.js.map +1 -0
- package/dist/paginate.d.ts +15 -0
- package/dist/paginate.d.ts.map +1 -0
- package/dist/paginate.js +395 -0
- package/dist/paginate.js.map +1 -0
- package/dist/render.d.ts +12 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1028 -0
- package/dist/render.js.map +1 -0
- package/dist/rich-text.d.ts +14 -0
- package/dist/rich-text.d.ts.map +1 -0
- package/dist/rich-text.js +183 -0
- package/dist/rich-text.js.map +1 -0
- package/dist/types.d.ts +697 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +3 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +786 -0
- package/dist/validate.js.map +1 -0
- package/package.json +79 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
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 8)
|
|
11
|
+
|
|
12
|
+
- Phase 8A: Comments/Annotations (sticky notes on elements)
|
|
13
|
+
- Phase 8B: Forms (text fields, checkboxes, radio buttons, dropdowns)
|
|
14
|
+
- Phase 8C: Document assembly (merge multiple PDFs, file attachments)
|
|
15
|
+
- Phase 8D: Advanced layout (image floats, callout boxes)
|
|
16
|
+
- Phase 8E: Digital signatures (visual + cryptographic signing)
|
|
17
|
+
- Phase 8F: Font subsetting improvements (reduce file size)
|
|
18
|
+
- Phase 8H: Inline formatting (superscript, subscript, letter spacing)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [0.1.1] — 2026-04-08
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- **Phase 8G: Hyperlinks** — Complete link annotation support:
|
|
26
|
+
- `paragraph.url` for external URI links on paragraphs
|
|
27
|
+
- `heading.url` for external URI links on headings
|
|
28
|
+
- `heading.anchor` for named PDF destinations (internal cross-references)
|
|
29
|
+
- `InlineSpan.href` for external and internal `#anchorId` links in rich-paragraphs
|
|
30
|
+
- `mailto:` scheme support for email links
|
|
31
|
+
- GoTo annotations for internal anchor references
|
|
32
|
+
- 9 comprehensive tests covering all hyperlink functionality
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **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).
|
|
36
|
+
- **Node.js version compatibility**: Replaced `--experimental-strip-types` with `tsx` runner to support Node.js 18.x, 20.x, and 22.x in CI
|
|
37
|
+
- **Broken CI examples**: Removed references to non-existent Phase 8 example scripts from GitHub Actions workflow
|
|
38
|
+
- **README examples mismatch**: Updated Examples section to only list 5 existing Phase 7 examples (watermark, bookmarks, toc, rtl, encryption)
|
|
39
|
+
- **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
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- `test:unit` now runs only `test/paginate-basic.test.ts` (fast, no canvas overhead)
|
|
43
|
+
- Reorganized test scripts: `test:unit`, `test:validate`, `test:e2e`, `test:phases` for better memory management
|
|
44
|
+
- Moved internal planning documentation to archive (preserved, not published)
|
|
45
|
+
- `devDependencies`: Added `@napi-rs/canvas` explicitly (was missing, causing CI failures)
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- `CONTRIBUTING.md`: Development setup, TDD workflow, PR process guide
|
|
49
|
+
- `CHENG_LOU_EMAIL_DRAFT.md`: Template for requesting endorsement from pretext creator
|
|
50
|
+
- `examples/comparison-pdfmake.ts`: pdfmake version of invoice for typography comparison
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## [0.1.0] — 2026-04-07
|
|
55
|
+
|
|
56
|
+
### Added (Phase 7G — Encryption)
|
|
57
|
+
- `doc.encryption` configuration for password-protecting PDFs
|
|
58
|
+
- User password and owner password support
|
|
59
|
+
- Granular permission restrictions: printing, copying, modifying, annotating
|
|
60
|
+
- Lazy-loads `@cantoo/pdf-lib` (optional peer dependency) — zero cost when not used
|
|
61
|
+
- Error code: `ENCRYPTION_NOT_AVAILABLE` when encryption is requested but dependency not installed
|
|
62
|
+
|
|
63
|
+
### Added (Phase 7F — RTL Text Support)
|
|
64
|
+
- Right-to-left text support for Arabic, Hebrew, and other RTL languages
|
|
65
|
+
- Unicode bidirectional text algorithm via `bidi-js`
|
|
66
|
+
- `dir` attribute on text elements: `'ltr'` | `'rtl'` | `'auto'` for per-element control
|
|
67
|
+
- RTL text works with headings, paragraphs, lists, tables, and all text elements
|
|
68
|
+
- Automatic detection of mixed LTR/RTL content
|
|
69
|
+
|
|
70
|
+
### Added (Phase 7E — SVG Support)
|
|
71
|
+
- `{ type: 'svg', svg: '<...' }` element for embedding SVG graphics
|
|
72
|
+
- SVG rasterization via `@napi-rs/canvas`
|
|
73
|
+
- ViewBox auto-sizing: automatic height calculation from viewBox aspect ratio
|
|
74
|
+
- Explicit sizing: `width` and `height` parameters for precise control
|
|
75
|
+
- Alignment options: `align: 'left' | 'center' | 'right'`
|
|
76
|
+
- Multi-page support: SVGs paginate correctly across page breaks
|
|
77
|
+
- Error code: `SVG_RENDER_FAILED` for SVG rasterization errors
|
|
78
|
+
|
|
79
|
+
### Added (Phase 7D — Table of Contents)
|
|
80
|
+
- `{ type: 'toc' }` element for automatic TOC generation
|
|
81
|
+
- Two-pass rendering pipeline ensures accurate page numbers
|
|
82
|
+
- Configurable: `title`, `showTitle`, `minLevel`/`maxLevel`, dot leaders, level indentation
|
|
83
|
+
- Auto-indexed from heading structure (H1, H2, H3, etc.)
|
|
84
|
+
- Supports custom formatting via `fontSize`, `color`, `spaceAfter` parameters
|
|
85
|
+
|
|
86
|
+
### Added (Phase 7C — Hyphenation)
|
|
87
|
+
- Automatic word hyphenation for better justified text layout
|
|
88
|
+
- `doc.hyphenation: { language: 'en-US' }` for document-level config
|
|
89
|
+
- Liang's algorithm via `hypher` package for accurate break points
|
|
90
|
+
- Configurable: `minWordLength`, `leftMin`, `rightMin`, per-element `hyphenate: false` opt-out
|
|
91
|
+
- Language support: includes `hyphenation.en-us` (additional languages via npm packages)
|
|
92
|
+
- Error code: `UNSUPPORTED_LANGUAGE` when language not available
|
|
93
|
+
|
|
94
|
+
### Added (Phase 7B — Watermarks)
|
|
95
|
+
- `doc.watermark` for text or image watermarks on every page
|
|
96
|
+
- Text watermarks: `text`, `fontSize`, `fontWeight`, `color`, `opacity`, `rotation`
|
|
97
|
+
- Image watermarks: `image` (Uint8Array), `opacity`, `rotation`, `color` (tint)
|
|
98
|
+
- Watermarks render behind content (lower z-index)
|
|
99
|
+
- Rotation bounds: -360 ≤ rotation ≤ 360 degrees
|
|
100
|
+
- Validation: must provide either text or image, never both required
|
|
101
|
+
|
|
102
|
+
### Added (Phase 7A — Bookmarks / PDF Outline)
|
|
103
|
+
- PDF sidebar bookmarks auto-generated from heading structure
|
|
104
|
+
- Enabled by default: `bookmarks: true` or `bookmarks: { minLevel: 1, maxLevel: 3 }`
|
|
105
|
+
- Level filtering: include/exclude heading levels from outline
|
|
106
|
+
- Per-heading opt-out: `bookmark: false` on heading elements
|
|
107
|
+
- Keyboard navigation: Cmd/Ctrl+Opt/Alt+O in PDF readers to toggle bookmark sidebar
|
|
108
|
+
|
|
109
|
+
### Added (Phase 6 — Advanced Features)
|
|
110
|
+
- Header and footer support with {{pageNumber}} and {{totalPages}} tokens
|
|
111
|
+
- Text decoration: strikethrough, underline
|
|
112
|
+
- Text alignment: left, center, right, justify
|
|
113
|
+
- Line height control: custom line-height multipliers
|
|
114
|
+
- Column layout with multi-column content flow
|
|
115
|
+
- Tables with colspan/rowspan support
|
|
116
|
+
|
|
117
|
+
### Added (Phase 5 — Rich Text / Builder API)
|
|
118
|
+
- Fluent builder API for programmatic document construction
|
|
119
|
+
- Rich text element with nested formatting (bold, italic, links)
|
|
120
|
+
- Inline code and code blocks with syntax highlighting
|
|
121
|
+
- Block quotes with custom styling
|
|
122
|
+
- Horizontal rules (hr element)
|
|
123
|
+
- Numbered and bulleted lists with nesting
|
|
124
|
+
|
|
125
|
+
### Added (Phases 1–4 — Core Engine)
|
|
126
|
+
- Core PDF generation via `pdf-lib`
|
|
127
|
+
- Element types: paragraph, heading, table, image, list, code, blockquote
|
|
128
|
+
- Font support: Inter bundled, custom TTF embedding
|
|
129
|
+
- Document metadata: title, author, subject, keywords, created date
|
|
130
|
+
- Page sizing: A4, A3, A5, Letter, Legal, or custom dimensions
|
|
131
|
+
- Margins: top, bottom, left, right per page
|
|
132
|
+
- Multi-page pagination with orphan/widow control
|
|
133
|
+
- Image formats: PNG, JPG, WebP
|
|
134
|
+
- Table features: custom column widths, cell padding, borders, header styling
|
|
135
|
+
- Colors: hex color codes throughout (text, backgrounds, borders)
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Features by Phase
|
|
140
|
+
|
|
141
|
+
| Phase | Feature | Status | Tests | Version |
|
|
142
|
+
|-------|---------|--------|-------|---------|
|
|
143
|
+
| 7A | Bookmarks / PDF Outline | ✅ Complete | 8 | 0.1.0 |
|
|
144
|
+
| 7B | Watermarks | ✅ Complete | 8 | 0.1.0 |
|
|
145
|
+
| 7C | Hyphenation | ✅ Complete | 10 | 0.1.0 |
|
|
146
|
+
| 7D | Table of Contents | ✅ Complete | 10 | 0.1.0 |
|
|
147
|
+
| 7E | SVG Support | ✅ Complete | 8 | 0.1.0 |
|
|
148
|
+
| 7F | RTL Text Support | ✅ Complete | 12 | 0.1.0 |
|
|
149
|
+
| 7G | Encryption | ✅ Complete | 7 | 0.1.0 |
|
|
150
|
+
| Integration | Feature Combinations | ✅ Complete | 6 | 0.1.0 |
|
|
151
|
+
| 6 | Advanced Features | ✅ Complete | — | 0.1.0 |
|
|
152
|
+
| 5 | Rich Text / Builder API | ✅ Complete | — | 0.1.0 |
|
|
153
|
+
| 1–4 | Core Engine | ✅ Complete | — | 0.1.0 |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## How to Use Examples
|
|
158
|
+
|
|
159
|
+
All Phase 7 features have working examples in the `examples/` directory. Run them with:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm run example:watermark # Phase 7B — Watermarks
|
|
163
|
+
npm run example:bookmarks # Phase 7A — Bookmarks
|
|
164
|
+
npm run example:toc # Phase 7D — Table of Contents
|
|
165
|
+
npm run example:rtl # Phase 7F — RTL Text Support
|
|
166
|
+
npm run example:encryption # Phase 7G — Encryption
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
PDF output is written to `output/phase7-*.pdf`.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Test Coverage
|
|
174
|
+
|
|
175
|
+
All phases include comprehensive test coverage:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm test # Run all 75+ tests
|
|
179
|
+
npm run test:unit # Unit tests only
|
|
180
|
+
npm run test:e2e # End-to-end tests
|
|
181
|
+
npm run test:visual # Visual regression tests
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Dependencies
|
|
187
|
+
|
|
188
|
+
### Required
|
|
189
|
+
- `@chenglou/pretext` — Pretext text layout engine
|
|
190
|
+
- `pdf-lib` — PDF document manipulation
|
|
191
|
+
- `@fontsource/inter` — Font: Inter (bundled)
|
|
192
|
+
- `bidi-js` — Bidirectional text algorithm (Phase 7F)
|
|
193
|
+
- `hypher` — Hyphenation algorithm (Phase 7C)
|
|
194
|
+
- `hyphenation.en-us` — English hyphenation patterns (Phase 7C)
|
|
195
|
+
|
|
196
|
+
### Optional (Peer)
|
|
197
|
+
- `@napi-rs/canvas` — SVG rasterization (Phase 7E)
|
|
198
|
+
- `@cantoo/pdf-lib` — PDF encryption (Phase 7G)
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Architecture
|
|
203
|
+
|
|
204
|
+
pretext-pdf uses a modular, layered architecture:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
render(doc) → validate → layout → paginate → render-pages → PDF bytes
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Each phase adds features orthogonally:
|
|
211
|
+
- Phase 1–4: Core engine, pagination, typography
|
|
212
|
+
- Phase 5: Rich text and builder patterns
|
|
213
|
+
- Phase 6: Advanced layout and formatting
|
|
214
|
+
- Phase 7: Security, internationalization, accessibility
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Future Roadmap
|
|
219
|
+
|
|
220
|
+
Potential Phase 8+ features (not yet implemented):
|
|
221
|
+
- Hyperlinks and anchors
|
|
222
|
+
- Justified text alignment
|
|
223
|
+
- Enhanced text decorations
|
|
224
|
+
- Font subsetting for file size reduction
|
|
225
|
+
- Browser compatibility improvements
|
|
226
|
+
- Performance optimizations
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Contributing
|
|
231
|
+
|
|
232
|
+
pretext-pdf is actively maintained. To report issues, request features, or contribute:
|
|
233
|
+
|
|
234
|
+
1. Check existing issues on the project repo
|
|
235
|
+
2. Write failing tests first (TDD)
|
|
236
|
+
3. Submit pull requests with test coverage ≥80%
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
Check LICENSE file in repository root.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Himanshu Jain
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# pretext-pdf
|
|
2
|
+
|
|
3
|
+
> **Declarative JSON → PDF generation with professional typography.**
|
|
4
|
+
>
|
|
5
|
+
> Build sophisticated, multi-page documents with precise text layout, international support, and zero browser overhead.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/pretext-pdf)
|
|
8
|
+
[](https://www.npmjs.com/package/pretext-pdf)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
10
|
+
[](#test-coverage)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why pretext-pdf?
|
|
16
|
+
|
|
17
|
+
### The Problem
|
|
18
|
+
- **pdfmake** is easy but produces mediocre typography (no kerning, ligatures, proper line breaking)
|
|
19
|
+
- **Puppeteer** renders beautiful PDFs but requires a 400MB browser and is slow at scale
|
|
20
|
+
- **Typst** has perfect typography but is Rust-based (not JavaScript)
|
|
21
|
+
|
|
22
|
+
### The Solution
|
|
23
|
+
**pretext-pdf** bridges the gap: **declarative + professional typography + lightweight**.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Easy | Professional | Lightweight
|
|
27
|
+
pdfmake: ✅ | ❌ | ✅
|
|
28
|
+
Puppeteer: ❌ | ✅ | ❌
|
|
29
|
+
pretext-pdf: ✅ | ✅ | ✅
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Powered by [pretext](https://github.com/chenglou/pretext)
|
|
33
|
+
Pretext is a precision text layout engine built by [Cheng Lou](https://github.com/chenglou) (React core team, Midjourney).
|
|
34
|
+
It handles:
|
|
35
|
+
- **Proper line breaking** for justified text and optimal paragraph layout
|
|
36
|
+
- **International text**: CJK, Arabic, Hebrew, Thai, and mixed LTR/RTL content
|
|
37
|
+
- **Fast measurement**: 300-600x faster than DOM reflow
|
|
38
|
+
- **Zero dependencies**: 15KB library, pure TypeScript
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
### Core Capabilities
|
|
45
|
+
- **13 element types**: paragraph, heading, table, image, list, code, blockquote, hr, spacer, page-break, rich-paragraph, SVG, table of contents
|
|
46
|
+
- **Professional typography**: hyphenation, justified text, orphan/widow control, multi-column layout
|
|
47
|
+
- **International support**: RTL text (Arabic/Hebrew), CJK line breaking, per-element direction control
|
|
48
|
+
- **Custom fonts**: Embed TTF fonts with subsetting, bundled Inter font included
|
|
49
|
+
- **Document metadata**: Title, author, subject, keywords, creation date
|
|
50
|
+
- **Headers/footers**: Dynamic `{{pageNumber}}` and `{{totalPages}}` tokens
|
|
51
|
+
- **PDF outlines**: Auto-generated bookmarks from heading structure
|
|
52
|
+
- **Watermarks**: Text or image watermarks on every page
|
|
53
|
+
- **Encryption**: Password-protect PDFs with granular permission control
|
|
54
|
+
- **Hyperlinks**: External URLs, email links, internal page anchors
|
|
55
|
+
- **Comments**: Sticky-note annotations on any element
|
|
56
|
+
- **Forms**: Interactive text fields, checkboxes, radio buttons, dropdowns
|
|
57
|
+
- **SVG support**: Embedded SVG graphics with auto-sizing
|
|
58
|
+
- **Document assembly**: Merge multiple PDFs, attach files
|
|
59
|
+
- **Digital signatures**: Visual signature fields, optional PKCS#7 signing
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install pretext-pdf
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Basic Example
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { render } from 'pretext-pdf'
|
|
75
|
+
|
|
76
|
+
const pdf = await render({
|
|
77
|
+
pageSize: 'A4',
|
|
78
|
+
margins: { top: 40, bottom: 40, left: 40, right: 40 },
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'heading',
|
|
82
|
+
level: 1,
|
|
83
|
+
text: 'Invoice #12345',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'paragraph',
|
|
87
|
+
text: 'Thank you for your business.',
|
|
88
|
+
fontSize: 12,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
type: 'table',
|
|
92
|
+
columns: [
|
|
93
|
+
{ name: 'Item', width: 200 },
|
|
94
|
+
{ name: 'Qty', width: 50, align: 'right' },
|
|
95
|
+
{ name: 'Price', width: 100, align: 'right' },
|
|
96
|
+
],
|
|
97
|
+
rows: [
|
|
98
|
+
{ Item: 'Professional Services', Qty: '10', Price: '$1,000' },
|
|
99
|
+
{ Item: 'Hosting', Qty: '1', Price: '$500' },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Write to file or send to client
|
|
106
|
+
import fs from 'fs'
|
|
107
|
+
fs.writeFileSync('invoice.pdf', pdf)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Using the Builder API
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { createPdf } from 'pretext-pdf'
|
|
114
|
+
|
|
115
|
+
const pdf = await createPdf({ pageSize: 'A4' })
|
|
116
|
+
.addHeading('My Report', 1)
|
|
117
|
+
.addText('This is a fluent, chainable API.')
|
|
118
|
+
.addTable({
|
|
119
|
+
columns: [{ name: 'Col A' }, { name: 'Col B' }],
|
|
120
|
+
rows: [{ 'Col A': 'Value', 'Col B': 'Data' }],
|
|
121
|
+
})
|
|
122
|
+
.build()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Documentation
|
|
128
|
+
|
|
129
|
+
### Element Types
|
|
130
|
+
|
|
131
|
+
| Element | Description |
|
|
132
|
+
|---------|-------------|
|
|
133
|
+
| **paragraph** | Text block with customizable font, size, color, alignment, background |
|
|
134
|
+
| **heading** | H1-H4 with auto-sizing, bold by default, optional bookmark/anchor |
|
|
135
|
+
| **table** | Fixed/proportional columns, colspan support, header repetition on page breaks |
|
|
136
|
+
| **image** | PNG/JPG with auto-detection, sizing, alignment, optional float |
|
|
137
|
+
| **list** | Ordered/unordered, nested, custom markers |
|
|
138
|
+
| **code** | Monospace with background, padding, syntax highlighting |
|
|
139
|
+
| **blockquote** | Left border + background, italic support |
|
|
140
|
+
| **rich-paragraph** | Mixed bold/italic/color/size spans, hyperlinks, annotations |
|
|
141
|
+
| **svg** | Embedded SVG graphics, auto-sizing, multi-page support |
|
|
142
|
+
| **toc** | Auto-generated table of contents with accurate page numbers |
|
|
143
|
+
| **hr** | Horizontal rule with customizable thickness/color |
|
|
144
|
+
| **spacer** | Fixed-height gap |
|
|
145
|
+
| **page-break** | Force new page |
|
|
146
|
+
| **comment** | Sticky-note annotation |
|
|
147
|
+
| **form-field** | Interactive text input, checkbox, radio, dropdown, button |
|
|
148
|
+
| **callout** | Side box / margin note |
|
|
149
|
+
|
|
150
|
+
### Document Config
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
interface DocConfig {
|
|
154
|
+
// Page layout
|
|
155
|
+
pageSize?: 'A4' | 'Letter' | 'A3' | 'Legal' | 'A5' | 'Tabloid' | [width, height]
|
|
156
|
+
margins?: { top: number; bottom: number; left: number; right: number }
|
|
157
|
+
|
|
158
|
+
// Typography
|
|
159
|
+
defaultFont?: string // Default font family (default: 'Inter')
|
|
160
|
+
defaultFontSize?: number // Default size in pt (default: 12)
|
|
161
|
+
lineHeight?: number // Line height multiplier (default: 1.5)
|
|
162
|
+
hyphenation?: { language: 'en-US' | 'de-DE' | ... }
|
|
163
|
+
|
|
164
|
+
// Document metadata
|
|
165
|
+
title?: string
|
|
166
|
+
author?: string
|
|
167
|
+
subject?: string
|
|
168
|
+
keywords?: string[]
|
|
169
|
+
creator?: string
|
|
170
|
+
producer?: string
|
|
171
|
+
language?: string // BCP 47 tag (e.g., 'en-US', 'ar')
|
|
172
|
+
|
|
173
|
+
// Features
|
|
174
|
+
watermark?: WatermarkSpec // Text or image watermark on every page
|
|
175
|
+
bookmarks?: { minLevel: 1; maxLevel: 3 } // Auto-generate outline from headings
|
|
176
|
+
encryption?: EncryptionSpec // Password protection with permission control
|
|
177
|
+
signature?: SignatureSpec // Digital signature field (+ optional PKCS#7 signing)
|
|
178
|
+
|
|
179
|
+
// Content
|
|
180
|
+
content: ContentElement[]
|
|
181
|
+
header?: HeaderFooterSpec // Repeated header on every page
|
|
182
|
+
footer?: HeaderFooterSpec // Repeated footer on every page
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Feature Matrix
|
|
187
|
+
|
|
188
|
+
| Feature | Phase | Status |
|
|
189
|
+
|---------|-------|--------|
|
|
190
|
+
| Core rendering | 1-4 | ✅ Complete |
|
|
191
|
+
| Rich text / Builder API | 5 | ✅ Complete |
|
|
192
|
+
| Advanced features | 6 | ✅ Complete |
|
|
193
|
+
| Bookmarks / Outline | 7A | ✅ Complete |
|
|
194
|
+
| Watermarks | 7B | ✅ Complete |
|
|
195
|
+
| Hyphenation | 7C | ✅ Complete |
|
|
196
|
+
| Table of contents | 7D | ✅ Complete |
|
|
197
|
+
| SVG support | 7E | ✅ Complete |
|
|
198
|
+
| RTL text support | 7F | ✅ Complete |
|
|
199
|
+
| Encryption | 7G | ✅ Complete |
|
|
200
|
+
| Hyperlinks | 8G | ✅ Complete |
|
|
201
|
+
| Comments/Annotations | 8A | ✅ Complete |
|
|
202
|
+
| Forms | 8B | ✅ Complete |
|
|
203
|
+
| Multi-file assembly | 8C | ✅ Complete |
|
|
204
|
+
| Font subsetting | 8F | ✅ Complete |
|
|
205
|
+
| Inline formatting | 8H | ✅ Complete |
|
|
206
|
+
| Digital signatures | 8E | ✅ Complete |
|
|
207
|
+
| Advanced layout | 8D | ✅ Complete |
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## API Reference
|
|
212
|
+
|
|
213
|
+
### `render(doc: DocConfig): Promise<Uint8Array>`
|
|
214
|
+
Render a document configuration to PDF bytes.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const pdf = await render({
|
|
218
|
+
pageSize: 'A4',
|
|
219
|
+
content: [...]
|
|
220
|
+
})
|
|
221
|
+
// pdf is a Uint8Array — write to file or send to client
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Builder API: `createPdf(options) → ChainableBuilder`
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const pdf = await createPdf({ pageSize: 'A4', defaultFontSize: 12 })
|
|
228
|
+
.addHeading('Title', 1)
|
|
229
|
+
.addText('Paragraph text')
|
|
230
|
+
.addTable({ columns: [...], rows: [...] })
|
|
231
|
+
.addImage(imageBytes, { width: 200 })
|
|
232
|
+
.addPageBreak()
|
|
233
|
+
.build()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### `assemble(parts): Promise<Uint8Array>`
|
|
237
|
+
Merge multiple PDFs into a single document.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const merged = await assemble([
|
|
241
|
+
{ doc: docConfig1 },
|
|
242
|
+
{ pdf: existingPdfBytes },
|
|
243
|
+
{ doc: docConfig2 },
|
|
244
|
+
])
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### `merge(pdfs): Promise<Uint8Array>`
|
|
248
|
+
Convenience function to merge pre-rendered PDFs.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const combined = await merge([pdf1, pdf2, pdf3])
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Examples
|
|
257
|
+
|
|
258
|
+
Phase 7 examples are in the `examples/` directory and can be run via npm scripts:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
npm run example:watermark # Watermarks
|
|
262
|
+
npm run example:bookmarks # Bookmarks & outline
|
|
263
|
+
npm run example:toc # Table of contents
|
|
264
|
+
npm run example:rtl # RTL text (Arabic/Hebrew)
|
|
265
|
+
npm run example:encryption # Password-protected PDF
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Phase 8 examples** (hyperlinks, forms, document assembly, annotations, fonts, inline formatting, digital signatures) coming soon.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Performance
|
|
273
|
+
|
|
274
|
+
pretext-pdf is **significantly faster** than Puppeteer for high-volume PDF generation:
|
|
275
|
+
|
|
276
|
+
- **Single document**: 50-200ms (depends on content complexity)
|
|
277
|
+
- **Batch (100 documents)**: ~5-20ms per document on modern hardware
|
|
278
|
+
- **Memory**: <10MB per document (Puppeteer: ~50-100MB per instance)
|
|
279
|
+
- **Bundle size**: 15KB engine + pdf-lib dependencies (~200KB gzipped)
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Error Handling
|
|
284
|
+
|
|
285
|
+
All errors throw a `PretextPdfError` with a specific code:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { render, PretextPdfError } from 'pretext-pdf'
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
const pdf = await render(config)
|
|
292
|
+
} catch (err) {
|
|
293
|
+
if (err instanceof PretextPdfError) {
|
|
294
|
+
console.error(err.code) // e.g., 'FONT_LOAD_FAILED', 'IMAGE_TOO_TALL'
|
|
295
|
+
console.error(err.message)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
See [CHANGELOG.md](CHANGELOG.md) for all error codes.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Comparison with Alternatives
|
|
305
|
+
|
|
306
|
+
### vs. pdfmake
|
|
307
|
+
- ✅ Better typography (kerning, ligatures, proper line breaking)
|
|
308
|
+
- ✅ International support (CJK, Arabic, Hebrew)
|
|
309
|
+
- ✅ Smaller bundle (~15KB vs ~400KB)
|
|
310
|
+
- ❌ Fewer built-in features (pdfmake has table styling, QR codes)
|
|
311
|
+
|
|
312
|
+
### vs. Puppeteer
|
|
313
|
+
- ✅ 100x faster for bulk PDF generation
|
|
314
|
+
- ✅ 40x smaller memory footprint
|
|
315
|
+
- ✅ No browser installation required
|
|
316
|
+
- ❌ Can't render arbitrary HTML/CSS (pretext-pdf is declarative)
|
|
317
|
+
|
|
318
|
+
### vs. Typst
|
|
319
|
+
- ✅ JavaScript ecosystem (can use npm packages)
|
|
320
|
+
- ✅ Faster compilation
|
|
321
|
+
- ❌ Typst has more advanced layout features (floats, complex positioning)
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Browser Support
|
|
326
|
+
|
|
327
|
+
pretext-pdf is **Node.js only**. It requires a Canvas polyfill for text measurement.
|
|
328
|
+
The library automatically installs `@napi-rs/canvas` (included) for server-side rendering.
|
|
329
|
+
|
|
330
|
+
For browser usage, see the [Future Roadmap](#future-roadmap).
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Test Coverage
|
|
335
|
+
|
|
336
|
+
All phases have comprehensive test coverage:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
npm test # Run all 75+ tests
|
|
340
|
+
npm run test:unit # Unit tests (pure pagination logic)
|
|
341
|
+
npm run test:visual # Visual regression tests (pixel-perfect comparison)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Tests include:
|
|
345
|
+
- Unit tests for validation, pagination, text measurement
|
|
346
|
+
- End-to-end tests for complete document rendering
|
|
347
|
+
- Visual regression tests with pixel-perfect comparison (pixelmatch)
|
|
348
|
+
- Feature-specific tests for each phase (Phase 7A-7G, 8A-8H)
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Contributing
|
|
353
|
+
|
|
354
|
+
Contributions welcome! Please:
|
|
355
|
+
1. Write tests first (TDD approach)
|
|
356
|
+
2. Ensure 80%+ code coverage
|
|
357
|
+
3. Run `npm run build && npm test` before submitting PR
|
|
358
|
+
4. Update [CHANGELOG.md](CHANGELOG.md)
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Roadmap
|
|
363
|
+
|
|
364
|
+
### Near-term (Phase 8)
|
|
365
|
+
- ✅ All Phase 8 features (hyperlinks, forms, annotations, assembly, signatures)
|
|
366
|
+
|
|
367
|
+
### Future (Phase 9+)
|
|
368
|
+
- Justified text alignment (currently left/right/center only)
|
|
369
|
+
- Enhanced text decorations (underline color, underline style)
|
|
370
|
+
- Font subsetting optimization (reduce file size for limited character sets)
|
|
371
|
+
- Browser compatibility (via WASM)
|
|
372
|
+
- PDF/A compliance (archival format)
|
|
373
|
+
- Accessibility tags (tagged PDF for screen readers)
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## License
|
|
378
|
+
|
|
379
|
+
[MIT](LICENSE) — Use freely in commercial and personal projects.
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Credits
|
|
384
|
+
|
|
385
|
+
Built by [Himanshu Jain](https://github.com/Himanshu-Jain-32) on top of:
|
|
386
|
+
- **[pretext](https://github.com/chenglou/pretext)** — Text layout engine (Cheng Lou)
|
|
387
|
+
- **[pdf-lib](https://github.com/Hopding/pdf-lib)** — PDF manipulation
|
|
388
|
+
- **[fontkit](https://github.com/foliojs/fontkit)** — Font parsing & subsetting
|
|
389
|
+
- **[@napi-rs/canvas](https://github.com/napi-rs/canvas)** — Server-side Canvas for Node.js
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Questions?
|
|
394
|
+
|
|
395
|
+
- 📖 Read the [CHANGELOG.md](CHANGELOG.md) for all features and error codes
|
|
396
|
+
- 🔍 Check the `examples/` directory for working code samples
|
|
397
|
+
- 🐛 Report issues on [GitHub](https://github.com/Himanshu-Jain-32/pretext-pdf/issues)
|
|
398
|
+
- 💬 Discussions & feature requests welcome
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
**Happy PDF generating!** 🎉
|
package/dist/assets.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PDFDocument } from 'pdf-lib';
|
|
2
|
+
import type { PdfDocument, ImageMap } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Stage 2b: Load and embed all images into pdfDoc.
|
|
5
|
+
* Runs after loadFonts(), receives the same pdfDoc.
|
|
6
|
+
*
|
|
7
|
+
* Image keys are 'img-N' where N is the element's position in doc.content.
|
|
8
|
+
* This makes keys stable and avoids collisions from duplicate src paths.
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: pdf-lib image embedding is NOT thread-safe.
|
|
11
|
+
* We load bytes in parallel but embed sequentially.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadImages(doc: PdfDocument, pdfDoc: PDFDocument, contentWidth: number): Promise<ImageMap>;
|
|
14
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,KAAK,EAAE,WAAW,EAA4B,QAAQ,EAAE,MAAM,YAAY,CAAA;AA8EjF;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmE/G"}
|