pretext-pdf 0.1.1 → 0.3.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.
package/README.md CHANGED
@@ -1,92 +1,81 @@
1
1
  # pretext-pdf
2
2
 
3
3
  > **Declarative JSON → PDF generation with professional typography.**
4
- >
4
+ >
5
5
  > Build sophisticated, multi-page documents with precise text layout, international support, and zero browser overhead.
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/pretext-pdf)](https://www.npmjs.com/package/pretext-pdf)
8
8
  [![npm downloads](https://img.shields.io/npm/dw/pretext-pdf)](https://www.npmjs.com/package/pretext-pdf)
9
+ [![CI](https://github.com/Himaan1998Y/pretext-pdf/actions/workflows/ci.yml/badge.svg)](https://github.com/Himaan1998Y/pretext-pdf/actions)
9
10
  [![TypeScript](https://img.shields.io/badge/typescript-strict-blue)](https://www.typescriptlang.org/)
10
- [![Tests](https://img.shields.io/badge/tests-75%2B-brightgreen)](#test-coverage)
11
+ [![Tests](https://img.shields.io/badge/tests-146%2B-brightgreen)](#test-coverage)
11
12
  [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
12
13
 
13
14
  ---
14
15
 
15
16
  ## Why pretext-pdf?
16
17
 
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)
18
+ | | pdfmake | Puppeteer | **pretext-pdf** |
19
+ |---|---|---|---|
20
+ | Easy declarative API | | | |
21
+ | Professional typography | | | |
22
+ | Lightweight (no browser) | ✅ | ❌ | ✅ |
23
+ | International text (RTL/CJK) | ❌ | ✅ | ✅ |
24
+ | Pure Node.js | ✅ | ❌ | ✅ |
25
+ | Hyperlinks + annotations | ❌ | ✅ | ✅ |
26
+ | Document assembly | ❌ | ❌ | ✅ |
21
27
 
22
- ### The Solution
23
- **pretext-pdf** bridges the gap: **declarative + professional typography + lightweight**.
28
+ ### Powered by [pretext](https://github.com/chenglou/pretext)
29
+
30
+ Pretext is a precision text layout engine by [Cheng Lou](https://github.com/chenglou) (React core team, Midjourney).
24
31
 
25
32
  ```
26
- Easy | Professional | Lightweight
27
- pdfmake: ✅ | ❌ | ✅
28
- Puppeteer: ❌ | ✅ | ❌
29
- pretext-pdf: ✅ | ✅ | ✅
33
+ JSON descriptor pretext layout pdf-lib renderer → PDF bytes
34
+ (kerning, (annotations,
35
+ hyphenation, encryption,
36
+ RTL, CJK) hyperlinks)
30
37
  ```
31
38
 
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
39
  ---
41
40
 
42
- ## Features
41
+ ## Output Samples
43
42
 
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
43
+ Real documents generated with pretext-pdf:
60
44
 
61
- ---
45
+ | Invoice | Market Report | Resume / CV |
46
+ |---------|--------------|-------------|
47
+ | [![Invoice](docs/screenshots/showcase-invoice.png)](examples/showcase-invoice.ts) | [![Report](docs/screenshots/showcase-report.png)](examples/showcase-report.ts) | [![Resume](docs/screenshots/showcase-resume.png)](examples/showcase-resume.ts) |
48
+ | [View source](examples/showcase-invoice.ts) | [View source](examples/showcase-report.ts) | [View source](examples/showcase-resume.ts) |
62
49
 
63
- ## Quick Start
50
+ ---
64
51
 
65
- ### Installation
52
+ ## Install
66
53
 
67
54
  ```bash
68
55
  npm install pretext-pdf
69
56
  ```
70
57
 
71
- ### Basic Example
58
+ Optional peer dependencies:
59
+ ```bash
60
+ npm install @cantoo/pdf-lib # Required for encryption
61
+ npm install @napi-rs/canvas # Required for SVG support (auto-installed in most setups)
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Quick Start
72
67
 
73
68
  ```typescript
74
69
  import { render } from 'pretext-pdf'
70
+ import { writeFileSync } from 'fs'
75
71
 
76
72
  const pdf = await render({
77
73
  pageSize: 'A4',
78
- margins: { top: 40, bottom: 40, left: 40, right: 40 },
74
+ margins: { top: 40, bottom: 40, left: 50, right: 50 },
75
+ metadata: { title: 'My Invoice', author: 'Acme Corp' },
79
76
  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
- },
77
+ { type: 'heading', level: 1, text: 'Invoice #12345' },
78
+ { type: 'paragraph', text: 'Thank you for your business.', fontSize: 12 },
90
79
  {
91
80
  type: 'table',
92
81
  columns: [
@@ -96,193 +85,159 @@ const pdf = await render({
96
85
  ],
97
86
  rows: [
98
87
  { Item: 'Professional Services', Qty: '10', Price: '$1,000' },
99
- { Item: 'Hosting', Qty: '1', Price: '$500' },
88
+ { Item: 'Hosting (annual)', Qty: '1', Price: '$500' },
100
89
  ],
101
90
  },
91
+ { type: 'paragraph', text: 'Total: $1,500', align: 'right', fontWeight: 700 },
102
92
  ],
103
93
  })
104
94
 
105
- // Write to file or send to client
106
- import fs from 'fs'
107
- fs.writeFileSync('invoice.pdf', pdf)
95
+ writeFileSync('invoice.pdf', pdf)
108
96
  ```
109
97
 
110
- ### Using the Builder API
98
+ ### Builder API
111
99
 
112
100
  ```typescript
113
101
  import { createPdf } from 'pretext-pdf'
114
102
 
115
103
  const pdf = await createPdf({ pageSize: 'A4' })
116
104
  .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
- })
105
+ .addText('Fluent chainable API.')
106
+ .addTable({ columns: [{ name: 'Col A' }, { name: 'Col B' }], rows: [{ 'Col A': 'x', 'Col B': 'y' }] })
122
107
  .build()
123
108
  ```
124
109
 
125
110
  ---
126
111
 
127
- ## Documentation
112
+ ## Features
128
113
 
129
114
  ### Element Types
130
115
 
131
- | Element | Description |
116
+ | Element | What it does |
132
117
  |---------|-------------|
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
118
+ | `paragraph` | Text block font, size, color, align, background, letterSpacing, smallCaps |
119
+ | `heading` | H1H4 with bookmarks, URL links, internal anchors |
120
+ | `table` | Fixed/proportional columns, colspan, repeating headers across page breaks |
121
+ | `image` | PNG/JPG/WebP with sizing, alignment, auto-format detection |
122
+ | `list` | Ordered/unordered, nested, custom markers |
123
+ | `code` | Monospace block with background and padding |
124
+ | `blockquote` | Left border + background |
125
+ | `rich-paragraph` | Mixed bold/italic/color/size/super/subscript spans with inline hyperlinks |
126
+ | `svg` | Embedded SVG graphics with auto-sizing from viewBox |
127
+ | `toc` | Auto-generated table of contents with accurate page numbers (two-pass) |
128
+ | `comment` | PDF sticky-note annotation (visible in Acrobat/Preview sidebar) |
129
+ | `hr` | Horizontal rule |
130
+ | `spacer` | Fixed-height gap |
131
+ | `page-break` | Force new page |
132
+
133
+ ### Document Features
134
+
135
+ | Feature | Config key | Notes |
136
+ |---------|-----------|-------|
137
+ | Watermarks | `doc.watermark` | Text or image, opacity, rotation |
138
+ | Encryption | `doc.encryption` | Password + granular permissions |
139
+ | PDF Bookmarks | `doc.bookmarks` | Auto-generated from headings |
140
+ | Hyphenation | `doc.hyphenation` | Liang's algorithm, `language: 'en-us'` |
141
+ | Headers/Footers | `doc.header` / `doc.footer` | `{{pageNumber}}` / `{{totalPages}}` tokens |
142
+ | Metadata | `doc.metadata` | Title, author, subject, keywords, `language` (PDF /Lang), `producer` |
143
+
144
+ ### Phase 8 Features
145
+
146
+ | Feature | API |
147
+ |---------|-----|
148
+ | **Hyperlinks** | `paragraph.url`, `heading.url`, `heading.anchor`, `span.href` |
149
+ | **Inline formatting** | `span.verticalAlign: 'superscript'\|'subscript'`, `paragraph.letterSpacing`, `heading.smallCaps` |
150
+ | **Sticky notes** | `{ type: 'comment', contents: '...' }`, `paragraph.annotation` |
151
+ | **Document assembly** | `merge(pdfs)`, `assemble(parts)` |
152
+ | **Interactive forms** | `{ type: 'form-field', fieldType: 'text'\|'checkbox'\|'radio'\|'dropdown'\|'button' }`, `doc.flattenForms` |
153
+ | **Signature placeholder** | `doc.signature: { signerName, reason, location, x, y, page }` |
154
+ | **Callout boxes** | `{ type: 'callout', content, style: 'info'\|'warning'\|'tip'\|'note', title }` |
151
155
 
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
- }
156
+ ---
157
+
158
+ ## Examples
159
+
160
+ Run working examples from the `examples/` directory:
161
+
162
+ ```bash
163
+ # Phase 7 examples
164
+ npm run example # Basic invoice
165
+ npm run example:watermark # Text/image watermarks
166
+ npm run example:bookmarks # PDF outline/bookmarks
167
+ npm run example:toc # Auto table of contents
168
+ npm run example:rtl # Arabic/Hebrew RTL text
169
+ npm run example:encryption # Password-protected PDF
170
+
171
+ # Phase 8 examples
172
+ npm run example:hyperlinks # External links, email links, internal anchors
173
+ npm run example:annotations # Sticky notes on elements
174
+ npm run example:assembly # Merge and assemble multiple PDFs
175
+ npm run example:inline # Superscript, subscript, letter-spacing, small-caps
176
+ npm run example:forms # Interactive form fields (text, checkbox, radio, dropdown)
177
+ npm run example:callout # Callout boxes (info, warning, tip, note presets)
184
178
  ```
185
179
 
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 |
180
+ All examples write output to `output/*.pdf`.
208
181
 
209
182
  ---
210
183
 
211
184
  ## API Reference
212
185
 
213
- ### `render(doc: DocConfig): Promise<Uint8Array>`
214
- Render a document configuration to PDF bytes.
186
+ ### `render(doc): Promise<Uint8Array>`
215
187
 
216
188
  ```typescript
189
+ import { render } from 'pretext-pdf'
190
+
217
191
  const pdf = await render({
218
- pageSize: 'A4',
219
- content: [...]
192
+ pageSize: 'A4', // 'A4' | 'A3' | 'A5' | 'Letter' | 'Legal' | [w, h]
193
+ margins: { top: 72, bottom: 72, left: 72, right: 72 },
194
+ defaultFont: 'Inter', // Inter 400 bundled; load others via doc.fonts
195
+ defaultFontSize: 12,
196
+ metadata: {
197
+ title: 'Document Title',
198
+ author: 'Author Name',
199
+ subject: 'Description',
200
+ keywords: ['pdf', 'report'],
201
+ },
202
+ watermark: { text: 'DRAFT', opacity: 0.15, rotation: -45 },
203
+ encryption: { userPassword: 'open', ownerPassword: 'admin', permissions: { printing: true, copying: false } },
204
+ bookmarks: { minLevel: 1, maxLevel: 3 },
205
+ hyphenation: { language: 'en-us', minWordLength: 6 },
206
+ header: { text: 'My Document — {{pageNumber}} of {{totalPages}}', align: 'right' },
207
+ footer: { text: 'Confidential', align: 'center', color: '#999999' },
208
+ content: [ /* ContentElement[] */ ],
220
209
  })
221
- // pdf is a Uint8Array — write to file or send to client
222
210
  ```
223
211
 
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
- ```
212
+ ### `merge(pdfs): Promise<Uint8Array>`
235
213
 
236
- ### `assemble(parts): Promise<Uint8Array>`
237
- Merge multiple PDFs into a single document.
214
+ Combine pre-rendered PDFs:
238
215
 
239
216
  ```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.
217
+ import { merge } from 'pretext-pdf'
249
218
 
250
- ```typescript
251
- const combined = await merge([pdf1, pdf2, pdf3])
219
+ const combined = await merge([coverPdf, bodyPdf, appendixPdf])
252
220
  ```
253
221
 
254
- ---
222
+ ### `assemble(parts): Promise<Uint8Array>`
255
223
 
256
- ## Examples
224
+ Mix new document configs with existing PDFs:
257
225
 
258
- Phase 7 examples are in the `examples/` directory and can be run via npm scripts:
226
+ ```typescript
227
+ import { assemble } from 'pretext-pdf'
259
228
 
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
229
+ const report = await assemble([
230
+ { pdf: existingCoverPdf },
231
+ { doc: { content: [...] } }, // rendered fresh
232
+ { pdf: standardTermsPdf },
233
+ ])
266
234
  ```
267
235
 
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
236
  ---
282
237
 
283
238
  ## Error Handling
284
239
 
285
- All errors throw a `PretextPdfError` with a specific code:
240
+ Every error throws `PretextPdfError` with a typed code:
286
241
 
287
242
  ```typescript
288
243
  import { render, PretextPdfError } from 'pretext-pdf'
@@ -291,112 +246,118 @@ try {
291
246
  const pdf = await render(config)
292
247
  } catch (err) {
293
248
  if (err instanceof PretextPdfError) {
294
- console.error(err.code) // e.g., 'FONT_LOAD_FAILED', 'IMAGE_TOO_TALL'
295
- console.error(err.message)
249
+ switch (err.code) {
250
+ case 'VALIDATION_ERROR': // Invalid config
251
+ case 'FONT_LOAD_FAILED': // Font file not found
252
+ case 'IMAGE_TOO_TALL': // Image doesn't fit on page
253
+ case 'ENCRYPTION_NOT_AVAILABLE': // @cantoo/pdf-lib not installed
254
+ case 'ASSEMBLY_EMPTY': // merge/assemble called with empty array
255
+ // ... see CHANGELOG.md for full list
256
+ }
296
257
  }
297
258
  }
298
259
  ```
299
260
 
300
- See [CHANGELOG.md](CHANGELOG.md) for all error codes.
301
-
302
261
  ---
303
262
 
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)
263
+ ## Test Coverage
311
264
 
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)
265
+ 113 tests across all phases:
317
266
 
318
- ### vs. Typst
319
- - JavaScript ecosystem (can use npm packages)
320
- - Faster compilation
321
- - Typst has more advanced layout features (floats, complex positioning)
267
+ ```bash
268
+ npm test # All 113 tests
269
+ npm run test:unit # Validation, builder, rich-text unit tests
270
+ npm run test:e2e # End-to-end render tests
271
+ npm run test:phases # Phase 7A-7G + Phase 8A/8C/8G/8H feature tests
272
+ ```
322
273
 
323
274
  ---
324
275
 
325
- ## Browser Support
276
+ ## Custom Fonts
326
277
 
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).
278
+ ```typescript
279
+ const pdf = await render({
280
+ fonts: [
281
+ { family: 'Roboto', weight: 400, src: '/path/to/Roboto-Regular.ttf' },
282
+ { family: 'Roboto', weight: 700, src: '/path/to/Roboto-Bold.ttf' },
283
+ { family: 'Roboto', style: 'italic', src: '/path/to/Roboto-Italic.ttf' },
284
+ ],
285
+ defaultFont: 'Roboto',
286
+ content: [
287
+ { type: 'paragraph', text: 'Uses Roboto font' },
288
+ { type: 'paragraph', text: 'Bold text', fontWeight: 700 },
289
+ ],
290
+ })
291
+ ```
331
292
 
332
293
  ---
333
294
 
334
- ## Test Coverage
335
-
336
- All phases have comprehensive test coverage:
295
+ ## Rich Text
337
296
 
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)
297
+ ```typescript
298
+ {
299
+ type: 'rich-paragraph',
300
+ fontSize: 13,
301
+ spans: [
302
+ { text: 'Normal ' },
303
+ { text: 'bold', fontWeight: 700 },
304
+ { text: ' and ', fontStyle: 'italic' },
305
+ { text: 'colored', color: '#e63946' },
306
+ { text: ' and ' },
307
+ { text: 'linked', href: 'https://example.com', underline: true, color: '#0070f3' },
308
+ { text: '. Also: E=mc' },
309
+ { text: '2', verticalAlign: 'superscript' },
310
+ { text: ' and H' },
311
+ { text: '2', verticalAlign: 'subscript' },
312
+ { text: 'O.' },
313
+ ],
314
+ }
342
315
  ```
343
316
 
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
317
  ---
351
318
 
352
- ## Contributing
319
+ ## Roadmap
353
320
 
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)
321
+ | Phase | Feature | Status |
322
+ |-------|---------|--------|
323
+ | 1–4 | Core engine, pagination, typography | ✅ |
324
+ | 5 | Rich text, builder API | |
325
+ | 6 | Headers/footers, columns, decoration | ✅ |
326
+ | 7A | PDF Bookmarks / Outline | ✅ |
327
+ | 7B | Watermarks | ✅ |
328
+ | 7C | Hyphenation | ✅ |
329
+ | 7D | Table of Contents | ✅ |
330
+ | 7E | SVG support | ✅ |
331
+ | 7F | RTL text (Arabic/Hebrew) | ✅ |
332
+ | 7G | Encryption | ✅ |
333
+ | 8G | Hyperlinks | ✅ |
334
+ | 8H | Inline formatting (super/subscript, letterSpacing, smallCaps) | ✅ |
335
+ | 8A | Sticky note annotations | ✅ |
336
+ | 8C | Document assembly (merge + assemble) | ✅ |
337
+ | 8F | Document metadata (language, producer) | ✅ |
338
+ | 8B | Interactive forms (text/checkbox/radio/dropdown/button) | ✅ |
339
+ | 8E | Signature placeholder | ✅ |
340
+ | 8D | Callout boxes (info/warning/tip/note) | ✅ |
359
341
 
360
342
  ---
361
343
 
362
- ## Roadmap
363
-
364
- ### Near-term (Phase 8)
365
- - ✅ All Phase 8 features (hyperlinks, forms, annotations, assembly, signatures)
344
+ ## Contributing
366
345
 
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)
346
+ See [CONTRIBUTING.md](CONTRIBUTING.md). TDD approach — write tests first.
374
347
 
375
348
  ---
376
349
 
377
350
  ## License
378
351
 
379
- [MIT](LICENSE) — Use freely in commercial and personal projects.
352
+ [MIT](LICENSE)
380
353
 
381
354
  ---
382
355
 
383
356
  ## Credits
384
357
 
385
- Built by [Himanshu Jain](https://github.com/Himanshu-Jain-32) on top of:
358
+ Built by [Himanshu Jain](https://github.com/Himaan1998Y) on top of:
386
359
  - **[pretext](https://github.com/chenglou/pretext)** — Text layout engine (Cheng Lou)
387
360
  - **[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
- ---
361
+ - **[@napi-rs/canvas](https://github.com/napi-rs/canvas)** — Server-side Canvas API for Node.js
401
362
 
402
- **Happy PDF generating!** 🎉
363
+ Questions? [Open an issue](https://github.com/Himaan1998Y/pretext-pdf/issues)
package/dist/errors.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Error codes for PretextPdfError */
2
- export type ErrorCode = 'VALIDATION_ERROR' | 'FONT_LOAD_FAILED' | 'FONT_EMBED_FAILED' | 'PAGE_TOO_SMALL' | 'CANVAS_UNAVAILABLE' | 'PAGE_LIMIT_EXCEEDED' | 'IMAGE_LOAD_FAILED' | 'IMAGE_FORMAT_MISMATCH' | 'IMAGE_TOO_TALL' | 'TABLE_COLUMN_OVERFLOW' | 'TABLE_COLUMN_TOO_NARROW' | 'MONOSPACE_FONT_REQUIRED' | 'ITALIC_FONT_NOT_LOADED' | 'FONT_NOT_LOADED' | 'COLUMN_WIDTH_TOO_NARROW' | 'COLSPAN_OVERFLOW' | 'ENCRYPTION_NOT_AVAILABLE' | 'UNSUPPORTED_LANGUAGE' | 'SVG_RENDER_FAILED' | 'WATERMARK_ROTATION_OUT_OF_RANGE' | 'SVG_INVALID_MARKUP';
2
+ export type ErrorCode = 'VALIDATION_ERROR' | 'FONT_LOAD_FAILED' | 'FONT_EMBED_FAILED' | 'PAGE_TOO_SMALL' | 'CANVAS_UNAVAILABLE' | 'PAGE_LIMIT_EXCEEDED' | 'IMAGE_LOAD_FAILED' | 'IMAGE_FORMAT_MISMATCH' | 'IMAGE_TOO_TALL' | 'TABLE_COLUMN_OVERFLOW' | 'TABLE_COLUMN_TOO_NARROW' | 'MONOSPACE_FONT_REQUIRED' | 'ITALIC_FONT_NOT_LOADED' | 'FONT_NOT_LOADED' | 'COLUMN_WIDTH_TOO_NARROW' | 'COLSPAN_OVERFLOW' | 'ENCRYPTION_NOT_AVAILABLE' | 'UNSUPPORTED_LANGUAGE' | 'SVG_RENDER_FAILED' | 'WATERMARK_ROTATION_OUT_OF_RANGE' | 'SVG_INVALID_MARKUP' | 'ASSEMBLY_EMPTY' | 'ASSEMBLY_FAILED' | 'FORM_FIELD_NAME_DUPLICATE' | 'FORM_FLATTEN_FAILED';
3
3
  export declare class PretextPdfError extends Error {
4
4
  readonly code: ErrorCode;
5
5
  constructor(code: ErrorCode, message: string);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,MAAM,MAAM,SAAS,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,mBAAmB,GACnB,gBAAgB,GAChB,oBAAoB,GACpB,qBAAqB,GACrB,mBAAmB,GACnB,uBAAuB,GACvB,gBAAgB,GAChB,uBAAuB,GACvB,yBAAyB,GACzB,yBAAyB,GACzB,wBAAwB,GACxB,iBAAiB,GACjB,yBAAyB,GACzB,kBAAkB,GAClB,0BAA0B,GAC1B,sBAAsB,GACtB,mBAAmB,GACnB,iCAAiC,GACjC,oBAAoB,CAAA;AAExB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;gBAEZ,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM;CAS7C"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,MAAM,MAAM,SAAS,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,mBAAmB,GACnB,gBAAgB,GAChB,oBAAoB,GACpB,qBAAqB,GACrB,mBAAmB,GACnB,uBAAuB,GACvB,gBAAgB,GAChB,uBAAuB,GACvB,yBAAyB,GACzB,yBAAyB,GACzB,wBAAwB,GACxB,iBAAiB,GACjB,yBAAyB,GACzB,kBAAkB,GAClB,0BAA0B,GAC1B,sBAAsB,GACtB,mBAAmB,GACnB,iCAAiC,GACjC,oBAAoB,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,2BAA2B,GAC3B,qBAAqB,CAAA;AAEzB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;gBAEZ,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM;CAS7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAwBA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAW;IAExB,YAAY,IAAe,EAAE,OAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,qCAAqC;QACrC,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA4BA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAW;IAExB,YAAY,IAAe,EAAE,OAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,qCAAqC;QACrC,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;CACF"}