You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Comprehensive internal performance audit across 35+ hot paths with zero API
surface changes. Fixes CCITT 2D decode bug, customName nullish coalescing,
duplicate hash computation, and unnecessary async. Removes dead code. Updates
all documentation, API reference, VitePress site, and benchmark numbers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+48Lines changed: 48 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
See [VERSIONING.md](./VERSIONING.md) for this project's versioning policy.
7
7
8
+
## [0.14.0] - 2026-02-28
9
+
10
+
### Performance
11
+
12
+
Comprehensive internal performance audit across the entire codebase. All changes are internal hot-path optimizations — zero API surface changes.
13
+
14
+
**Lexer & Content Stream Parser:**
15
+
- String concatenation in `readLiteralString()`, `readHexString()`, `readName()` replaced with `parts[]` + `.join('')` pattern
16
+
-`bytesToAscii()` / `decodeAscii()` replaced with batch `String.fromCharCode.apply()`
17
+
- Hex string parsing rewritten to single-pass direct byte decoding with `hexVal` lookup table (eliminates intermediate string + `parseInt`)
18
+
19
+
**LZW Decompression:**
20
+
- Complete rewrite with pooled flat buffer (`Uint8Array` + `Int32Array` index pairs) replacing per-entry `Uint8Array[]` allocations
21
+
- Identity entries (0-255) initialized once and persist across table resets
22
+
- Pre-allocated output buffer with manual growth instead of `number[]` + `.push()`
23
+
24
+
**XRef Recovery & Parsing:**
25
+
-`rebuildXrefFromScan()` rewritten to scan raw `Uint8Array` bytes directly for `obj` pattern instead of `TextDecoder.decode()` + regex on the entire file
26
+
- Standard xref entries parsed directly from bytes (fixed 20-byte format) without TextDecoder
27
+
- Keyword checks (`xref`, `trailer`) replaced with direct byte comparison
28
+
29
+
**PDF Object Serialization:**
30
+
-`escapeLiteralString()`: 5 chained `.replace()` calls replaced with single-pass character loop
31
+
-`PdfName.serialize()`: String concatenation replaced with array + join
32
+
-`formatNumber()`: Regex trailing-zero trim replaced with manual digit loop
-**CCITT Group 3/4 2D decode bug**: `read2DMode()` returned `HORIZONTAL` for bit pattern `011` instead of `VERTICAL_PLUS_1` — correct logic existed but was unreachable due to premature return. This could cause incorrect rendering of CCITT Group 4 and Group 3 2D compressed images (scanned documents).
47
+
-**`customName` font option ignored for empty strings**: `||` operator treated empty string as falsy, falling through to `postScriptName`. Changed to `??` (nullish coalescing).
48
+
-**`embedPages()` unnecessarily async**: Method was declared `async` but contained only synchronous code, wrapping return in an unnecessary `Promise`. Now returns `EmbeddedPdfPage[]` directly.
49
+
-**Duplicate hash computation in document merge**: `hashBytes()` was called twice on the same stream data during cross-document page copy. Now computed once and reused.
50
+
51
+
### Removed
52
+
- Dead `PdfArr` import alias in `pdfWriter.ts`
53
+
- Unused `objectBuf` variable allocation in object stream serialization
54
+
- Unused `objectContainsPageRef()` function in linearization module
Copy file name to clipboardExpand all lines: docs/api/classes/ChangeTracker.md
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@
6
6
7
7
# Class: ChangeTracker
8
8
9
-
Defined in: [src/core/incrementalWriter.ts:61](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L61)
9
+
Defined in: [src/core/incrementalWriter.ts:61](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L61)
10
10
11
11
Tracks which objects have been added or modified since the document
12
12
was loaded. Only these objects are written during an incremental save.
@@ -17,7 +17,7 @@ was loaded. Only these objects are written during an incremental save.
Defined in: [src/core/incrementalWriter.ts:71](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L71)
20
+
Defined in: [src/core/incrementalWriter.ts:71](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L71)
21
21
22
22
#### Parameters
23
23
@@ -37,7 +37,7 @@ Defined in: [src/core/incrementalWriter.ts:71](https://github.com/ABCrimson/mode
37
37
38
38
> **get****changedCount**(): `number`
39
39
40
-
Defined in: [src/core/incrementalWriter.ts:110](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L110)
40
+
Defined in: [src/core/incrementalWriter.ts:110](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L110)
41
41
42
42
Get the count of changed objects.
43
43
@@ -51,7 +51,7 @@ Get the count of changed objects.
51
51
52
52
> **getChangedObjects**(): `Set`\<`number`\>
53
53
54
-
Defined in: [src/core/incrementalWriter.ts:103](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L103)
54
+
Defined in: [src/core/incrementalWriter.ts:103](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L103)
55
55
56
56
Get all changed object numbers (new + modified).
57
57
@@ -65,7 +65,7 @@ Get all changed object numbers (new + modified).
65
65
66
66
> **isChanged**(`objectNumber`): `boolean`
67
67
68
-
Defined in: [src/core/incrementalWriter.ts:96](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L96)
68
+
Defined in: [src/core/incrementalWriter.ts:96](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L96)
69
69
70
70
Check if an object is new or modified.
71
71
@@ -85,7 +85,7 @@ Check if an object is new or modified.
85
85
86
86
> **markModified**(`objectNumber`): `void`
87
87
88
-
Defined in: [src/core/incrementalWriter.ts:85](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L85)
88
+
Defined in: [src/core/incrementalWriter.ts:85](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L85)
89
89
90
90
Mark an object as modified (existed in the original file).
91
91
@@ -105,7 +105,7 @@ Mark an object as modified (existed in the original file).
105
105
106
106
> **markNew**(`objectNumber`): `void`
107
107
108
-
Defined in: [src/core/incrementalWriter.ts:78](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/core/incrementalWriter.ts#L78)
108
+
Defined in: [src/core/incrementalWriter.ts:78](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/core/incrementalWriter.ts#L78)
109
109
110
110
Mark an object as new (did not exist in the original file).
Copy file name to clipboardExpand all lines: docs/api/classes/EmbeddedFont.md
+15-15Lines changed: 15 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@
6
6
7
7
# Class: EmbeddedFont
8
8
9
-
Defined in: [src/assets/font/fontEmbed.ts:183](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L183)
9
+
Defined in: [src/assets/font/fontEmbed.ts:183](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L183)
10
10
11
11
Represents a TrueType / OpenType font that has been loaded for
12
12
embedding in a PDF document.
@@ -22,7 +22,7 @@ Create via `PdfDocument.embedFont()`.
22
22
23
23
> `readonly`**fontData**: `Uint8Array`
24
24
25
-
Defined in: [src/assets/font/fontEmbed.ts:185](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L185)
25
+
Defined in: [src/assets/font/fontEmbed.ts:185](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L185)
Defined in: [src/assets/font/fontEmbed.ts:188](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L188)
35
+
Defined in: [src/assets/font/fontEmbed.ts:188](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L188)
36
36
37
37
Extracted font metrics.
38
38
@@ -42,7 +42,7 @@ Extracted font metrics.
42
42
43
43
> **ascentAtSize**(`fontSize`): `number`
44
44
45
-
Defined in: [src/assets/font/fontEmbed.ts:255](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L255)
45
+
Defined in: [src/assets/font/fontEmbed.ts:255](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L255)
46
46
47
47
Compute the ascender height at the given font size.
48
48
@@ -66,7 +66,7 @@ The ascender height in points (positive).
Defined in: [src/assets/font/fontEmbed.ts:376](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L376)
69
+
Defined in: [src/assets/font/fontEmbed.ts:376](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L376)
70
70
71
71
Build the complete set of PDF dictionary data needed to embed this
72
72
font. This performs subsetting (if WASM is available) and generates
@@ -86,7 +86,7 @@ The embedding result containing all PDF object data.
86
86
87
87
> **capHeightAtSize**(`fontSize`): `number`
88
88
89
-
Defined in: [src/assets/font/fontEmbed.ts:275](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L275)
89
+
Defined in: [src/assets/font/fontEmbed.ts:275](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L275)
90
90
91
91
Compute the cap height at the given font size.
92
92
@@ -110,7 +110,7 @@ The cap height in points.
110
110
111
111
> **descentAtSize**(`fontSize`): `number`
112
112
113
-
Defined in: [src/assets/font/fontEmbed.ts:265](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L265)
113
+
Defined in: [src/assets/font/fontEmbed.ts:265](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L265)
114
114
115
115
Compute the descender depth at the given font size.
116
116
@@ -134,7 +134,7 @@ The descender depth in points (negative).
134
134
135
135
> **encodeText**(`text`): `string`
136
136
137
-
Defined in: [src/assets/font/fontEmbed.ts:347](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L347)
137
+
Defined in: [src/assets/font/fontEmbed.ts:347](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L347)
138
138
139
139
Encode a text string as hex-encoded CID bytes for use in PDF
140
140
content stream `Tj` / `TJ` operators.
@@ -162,7 +162,7 @@ Hex string (e.g. `"00480065006C006C006F"` for "Hello").
162
162
163
163
> **getUsedGlyphs**(): `ReadonlySet`\<`number`\>
164
164
165
-
Defined in: [src/assets/font/fontEmbed.ts:329](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L329)
165
+
Defined in: [src/assets/font/fontEmbed.ts:329](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L329)
166
166
167
167
Get the set of all glyph IDs that have been used.
168
168
@@ -176,7 +176,7 @@ Get the set of all glyph IDs that have been used.
176
176
177
177
> **heightAtSize**(`fontSize`): `number`
178
178
179
-
Defined in: [src/assets/font/fontEmbed.ts:244](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L244)
179
+
Defined in: [src/assets/font/fontEmbed.ts:244](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L244)
180
180
181
181
Compute the height of the font at the given size.
182
182
@@ -203,7 +203,7 @@ The font height in points.
203
203
204
204
> **lineHeightAtSize**(`fontSize`): `number`
205
205
206
-
Defined in: [src/assets/font/fontEmbed.ts:285](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L285)
206
+
Defined in: [src/assets/font/fontEmbed.ts:285](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L285)
207
207
208
208
Compute the line height (ascent - descent + lineGap) at size.
209
209
@@ -227,7 +227,7 @@ The default line height in points.
227
227
228
228
> **markCodepointUsed**(`codepoint`): `void`
229
229
230
-
Defined in: [src/assets/font/fontEmbed.ts:299](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L299)
230
+
Defined in: [src/assets/font/fontEmbed.ts:299](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L299)
231
231
232
232
Mark a Unicode codepoint as used (records its glyph ID for subsetting).
233
233
@@ -249,7 +249,7 @@ The Unicode codepoint.
249
249
250
250
> **markGlyphUsed**(`glyphId`): `void`
251
251
252
-
Defined in: [src/assets/font/fontEmbed.ts:309](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L309)
252
+
Defined in: [src/assets/font/fontEmbed.ts:309](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L309)
253
253
254
254
Mark a glyph ID as used directly.
255
255
@@ -271,7 +271,7 @@ The glyph ID.
271
271
272
272
> **markTextUsed**(`text`): `void`
273
273
274
-
Defined in: [src/assets/font/fontEmbed.ts:318](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L318)
274
+
Defined in: [src/assets/font/fontEmbed.ts:318](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L318)
Defined in: [src/assets/font/fontEmbed.ts:215](https://github.com/ABCrimson/modern-pdf-lib/blob/6d920621b7c9811412316f53a974cac86961b992/src/assets/font/fontEmbed.ts#L215)
296
+
Defined in: [src/assets/font/fontEmbed.ts:215](https://github.com/ABCrimson/modern-pdf-lib/blob/6ce8fea7ba62114c9bdeda1f601086d76e1fe5d2/src/assets/font/fontEmbed.ts#L215)
297
297
298
298
Compute the width of a text string at the given font size.
0 commit comments