@libpdf/core 0.2.6 → 0.2.8

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/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-15K8U1wQ.mjs";
2
+ import { LRUCache } from "lru-cache";
2
3
  import pako, { deflate, inflate } from "pako";
3
4
  import { cbc, ecb } from "@noble/ciphers/aes.js";
4
5
  import { randomBytes } from "@noble/ciphers/utils.js";
@@ -10,7 +11,7 @@ import { createCMSECDSASignature } from "pkijs";
10
11
  import { base64 } from "@scure/base";
11
12
 
12
13
  //#region package.json
13
- var version = "0.2.6";
14
+ var version = "0.2.8";
14
15
 
15
16
  //#endregion
16
17
  //#region src/objects/pdf-array.ts
@@ -112,6 +113,80 @@ var PdfArray = class PdfArray {
112
113
  }
113
114
  };
114
115
 
116
+ //#endregion
117
+ //#region src/helpers/buffer.ts
118
+ /**
119
+ * Buffer utilities for working with ArrayBuffer and Uint8Array.
120
+ */
121
+ /**
122
+ * Ensure we have a proper ArrayBuffer (not SharedArrayBuffer or slice).
123
+ *
124
+ * Web Crypto APIs require a true ArrayBuffer, not a view into one.
125
+ *
126
+ * @param data - Uint8Array to convert
127
+ * @returns ArrayBuffer containing the data
128
+ */
129
+ function toArrayBuffer(data) {
130
+ if (data.buffer instanceof ArrayBuffer && data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) return data.buffer;
131
+ return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
132
+ }
133
+ /**
134
+ * Concatenate multiple Uint8Arrays into a single Uint8Array.
135
+ *
136
+ * @param arrays - Arrays to concatenate
137
+ * @returns Single Uint8Array containing all data
138
+ */
139
+ function concatBytes(arrays) {
140
+ const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
141
+ const result = new Uint8Array(totalLength);
142
+ let offset = 0;
143
+ for (const arr of arrays) {
144
+ result.set(arr, offset);
145
+ offset += arr.length;
146
+ }
147
+ return result;
148
+ }
149
+ /** Pre-computed hex lookup: byte value → "XX" uppercase string. */
150
+ const HEX_TABLE = new Array(256);
151
+ for (let i = 0; i < 256; i++) HEX_TABLE[i] = i.toString(16).toUpperCase().padStart(2, "0");
152
+ /**
153
+ * Convert bytes to uppercase hex string.
154
+ *
155
+ * @param bytes - Raw bytes
156
+ * @returns Hex string (e.g., "48656C6C6F")
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * bytesToHex(new Uint8Array([72, 101, 108, 108, 111])) // "48656C6C6F"
161
+ * ```
162
+ */
163
+ function bytesToHex(bytes) {
164
+ let hex = "";
165
+ for (const byte of bytes) hex += HEX_TABLE[byte];
166
+ return hex;
167
+ }
168
+ /**
169
+ * Convert a hex string to bytes.
170
+ *
171
+ * Whitespace is ignored. Odd-length strings are padded with trailing 0.
172
+ *
173
+ * @param hex - Hex string (e.g., "48656C6C6F" or "48 65 6C 6C 6F")
174
+ * @returns Decoded bytes
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * hexToBytes("48656C6C6F") // Uint8Array([72, 101, 108, 108, 111])
179
+ * hexToBytes("ABC") // Uint8Array([171, 192]) - padded to "ABC0"
180
+ * ```
181
+ */
182
+ function hexToBytes(hex) {
183
+ const clean = hex.replace(/\s/g, "");
184
+ const padded = clean.length % 2 === 1 ? `${clean}0` : clean;
185
+ const bytes = new Uint8Array(padded.length / 2);
186
+ for (let i = 0; i < bytes.length; i++) bytes[i] = Number.parseInt(padded.slice(i * 2, i * 2 + 2), 16);
187
+ return bytes;
188
+ }
189
+
115
190
  //#endregion
116
191
  //#region src/helpers/chars.ts
117
192
  /**
@@ -221,83 +296,6 @@ function hexValue(byte) {
221
296
  */
222
297
  const SINGLE_BYTE_MASK = 255;
223
298
 
224
- //#endregion
225
- //#region src/helpers/lru-cache.ts
226
- /**
227
- * Simple LRU (Least Recently Used) cache implementation.
228
- *
229
- * Used for interning frequently-used PDF objects (PdfName, PdfRef)
230
- * while preventing unbounded memory growth.
231
- */
232
- /**
233
- * A bounded cache that evicts least-recently-used entries when full.
234
- *
235
- * @typeParam K - Key type
236
- * @typeParam V - Value type
237
- */
238
- var LRUCache = class {
239
- maxSize;
240
- cache = /* @__PURE__ */ new Map();
241
- /**
242
- * Create a new LRU cache.
243
- *
244
- * @param maxSize - Maximum number of entries to retain (default: 10000)
245
- */
246
- constructor(maxSize = 1e4) {
247
- this.maxSize = maxSize;
248
- }
249
- /**
250
- * Get a value from the cache, updating its recency.
251
- *
252
- * @returns The cached value, or undefined if not present
253
- */
254
- get(key$1) {
255
- const value = this.cache.get(key$1);
256
- if (value !== void 0) {
257
- this.cache.delete(key$1);
258
- this.cache.set(key$1, value);
259
- }
260
- return value;
261
- }
262
- /**
263
- * Check if a key exists in the cache (without updating recency).
264
- */
265
- has(key$1) {
266
- return this.cache.has(key$1);
267
- }
268
- /**
269
- * Add or update a value in the cache.
270
- *
271
- * If the cache is at capacity, the least-recently-used entry is evicted.
272
- */
273
- set(key$1, value) {
274
- if (this.cache.has(key$1)) this.cache.delete(key$1);
275
- else if (this.cache.size >= this.maxSize) {
276
- const oldestKey = this.cache.keys().next().value;
277
- if (oldestKey !== void 0) this.cache.delete(oldestKey);
278
- }
279
- this.cache.set(key$1, value);
280
- }
281
- /**
282
- * Remove a value from the cache.
283
- */
284
- delete(key$1) {
285
- return this.cache.delete(key$1);
286
- }
287
- /**
288
- * Clear all entries from the cache.
289
- */
290
- clear() {
291
- this.cache.clear();
292
- }
293
- /**
294
- * Get the current number of entries in the cache.
295
- */
296
- get size() {
297
- return this.cache.size;
298
- }
299
- };
300
-
301
299
  //#endregion
302
300
  //#region src/objects/pdf-name.ts
303
301
  const NAME_NEEDS_ESCAPE = new Set([
@@ -305,11 +303,20 @@ const NAME_NEEDS_ESCAPE = new Set([
305
303
  ...DELIMITERS,
306
304
  CHAR_HASH
307
305
  ]);
306
+ /** Module-level encoder — avoids constructing one per escapeName call. */
307
+ const textEncoder = new TextEncoder();
308
308
  /**
309
- * Default cache size for PdfName interning.
310
- * Can be overridden via PdfName.setCacheSize().
309
+ * Check whether a name is pure "safe" ASCII — every char is printable ASCII
310
+ * (33–126) and not in the escape set. If so, no escaping is needed and we
311
+ * can skip the TextEncoder entirely.
311
312
  */
312
- const DEFAULT_NAME_CACHE_SIZE = 1e4;
313
+ function isSimpleAsciiName(name) {
314
+ for (let i = 0; i < name.length; i++) {
315
+ const c = name.charCodeAt(i);
316
+ if (c < 33 || c > 126 || NAME_NEEDS_ESCAPE.has(c)) return false;
317
+ }
318
+ return true;
319
+ }
313
320
  /**
314
321
  * Escape a PDF name for serialization.
315
322
  *
@@ -319,13 +326,19 @@ const DEFAULT_NAME_CACHE_SIZE = 1e4;
319
326
  * - The # character itself
320
327
  */
321
328
  function escapeName$1(name) {
322
- const bytes = new TextEncoder().encode(name);
329
+ if (isSimpleAsciiName(name)) return name;
330
+ const bytes = textEncoder.encode(name);
323
331
  let result = "";
324
- for (const byte of bytes) if (byte < 33 || byte > 126 || NAME_NEEDS_ESCAPE.has(byte)) result += `#${byte.toString(16).toUpperCase().padStart(2, "0")}`;
332
+ for (const byte of bytes) if (byte < 33 || byte > 126 || NAME_NEEDS_ESCAPE.has(byte)) result += `#${HEX_TABLE[byte]}`;
325
333
  else result += String.fromCharCode(byte);
326
334
  return result;
327
335
  }
328
336
  /**
337
+ * Default cache size for PdfName interning.
338
+ * Can be overridden via PdfName.setCacheSize().
339
+ */
340
+ const DEFAULT_NAME_CACHE_SIZE = 1e4;
341
+ /**
329
342
  * PDF name object (interned).
330
343
  *
331
344
  * In PDF: `/Type`, `/Page`, `/Length`
@@ -340,7 +353,7 @@ var PdfName = class PdfName {
340
353
  get type() {
341
354
  return "name";
342
355
  }
343
- static cache = new LRUCache(DEFAULT_NAME_CACHE_SIZE);
356
+ static cache = new LRUCache({ max: DEFAULT_NAME_CACHE_SIZE });
344
357
  /**
345
358
  * Pre-cached common names that should never be evicted.
346
359
  * These are stored separately from the LRU cache.
@@ -359,6 +372,8 @@ var PdfName = class PdfName {
359
372
  static Length = PdfName.createPermanent("Length");
360
373
  static Filter = PdfName.createPermanent("Filter");
361
374
  static FlateDecode = PdfName.createPermanent("FlateDecode");
375
+ /** Cached serialized form (e.g. "/Type"). Computed lazily on first toBytes(). */
376
+ cachedBytes = null;
362
377
  constructor(value) {
363
378
  this.value = value;
364
379
  }
@@ -394,7 +409,13 @@ var PdfName = class PdfName {
394
409
  return PdfName.cache.size;
395
410
  }
396
411
  toBytes(writer) {
397
- writer.writeAscii(`/${escapeName$1(this.value)}`);
412
+ let bytes = this.cachedBytes;
413
+ if (bytes === null) {
414
+ const escaped = escapeName$1(this.value);
415
+ bytes = textEncoder.encode(`/${escaped}`);
416
+ this.cachedBytes = bytes;
417
+ }
418
+ writer.writeBytes(bytes);
398
419
  }
399
420
  /**
400
421
  * Create a permanent (non-evictable) name.
@@ -427,7 +448,7 @@ var PdfRef = class PdfRef {
427
448
  get type() {
428
449
  return "ref";
429
450
  }
430
- static cache = new LRUCache(DEFAULT_REF_CACHE_SIZE);
451
+ static cache = new LRUCache({ max: DEFAULT_REF_CACHE_SIZE });
431
452
  constructor(objectNumber, generation) {
432
453
  this.objectNumber = objectNumber;
433
454
  this.generation = generation;
@@ -2044,77 +2065,6 @@ var PdfStream = class PdfStream extends PdfDict {
2044
2065
  }
2045
2066
  };
2046
2067
 
2047
- //#endregion
2048
- //#region src/helpers/buffer.ts
2049
- /**
2050
- * Buffer utilities for working with ArrayBuffer and Uint8Array.
2051
- */
2052
- /**
2053
- * Ensure we have a proper ArrayBuffer (not SharedArrayBuffer or slice).
2054
- *
2055
- * Web Crypto APIs require a true ArrayBuffer, not a view into one.
2056
- *
2057
- * @param data - Uint8Array to convert
2058
- * @returns ArrayBuffer containing the data
2059
- */
2060
- function toArrayBuffer(data) {
2061
- if (data.buffer instanceof ArrayBuffer && data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) return data.buffer;
2062
- return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
2063
- }
2064
- /**
2065
- * Concatenate multiple Uint8Arrays into a single Uint8Array.
2066
- *
2067
- * @param arrays - Arrays to concatenate
2068
- * @returns Single Uint8Array containing all data
2069
- */
2070
- function concatBytes(arrays) {
2071
- const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
2072
- const result = new Uint8Array(totalLength);
2073
- let offset = 0;
2074
- for (const arr of arrays) {
2075
- result.set(arr, offset);
2076
- offset += arr.length;
2077
- }
2078
- return result;
2079
- }
2080
- /**
2081
- * Convert bytes to uppercase hex string.
2082
- *
2083
- * @param bytes - Raw bytes
2084
- * @returns Hex string (e.g., "48656C6C6F")
2085
- *
2086
- * @example
2087
- * ```ts
2088
- * bytesToHex(new Uint8Array([72, 101, 108, 108, 111])) // "48656C6C6F"
2089
- * ```
2090
- */
2091
- function bytesToHex(bytes) {
2092
- let hex = "";
2093
- for (const byte of bytes) hex += byte.toString(16).toUpperCase().padStart(2, "0");
2094
- return hex;
2095
- }
2096
- /**
2097
- * Convert a hex string to bytes.
2098
- *
2099
- * Whitespace is ignored. Odd-length strings are padded with trailing 0.
2100
- *
2101
- * @param hex - Hex string (e.g., "48656C6C6F" or "48 65 6C 6C 6F")
2102
- * @returns Decoded bytes
2103
- *
2104
- * @example
2105
- * ```ts
2106
- * hexToBytes("48656C6C6F") // Uint8Array([72, 101, 108, 108, 111])
2107
- * hexToBytes("ABC") // Uint8Array([171, 192]) - padded to "ABC0"
2108
- * ```
2109
- */
2110
- function hexToBytes(hex) {
2111
- const clean = hex.replace(/\s/g, "");
2112
- const padded = clean.length % 2 === 1 ? `${clean}0` : clean;
2113
- const bytes = new Uint8Array(padded.length / 2);
2114
- for (let i = 0; i < bytes.length; i++) bytes[i] = Number.parseInt(padded.slice(i * 2, i * 2 + 2), 16);
2115
- return bytes;
2116
- }
2117
-
2118
2068
  //#endregion
2119
2069
  //#region src/content/operators.ts
2120
2070
  /**
@@ -11699,6 +11649,8 @@ var EmbeddedFont = class EmbeddedFont extends PdfFont {
11699
11649
  _subsetTag = null;
11700
11650
  /** Whether this font is used in a form field (prevents subsetting) */
11701
11651
  _usedInForm = false;
11652
+ /** Pre-allocated PDF reference (set by PDFFonts.embed()) */
11653
+ _ref = null;
11702
11654
  /** Cached descriptor */
11703
11655
  _descriptor = null;
11704
11656
  constructor(fontProgram, fontData) {
@@ -11895,6 +11847,27 @@ var EmbeddedFont = class EmbeddedFont extends PdfFont {
11895
11847
  this._subsetTag = null;
11896
11848
  }
11897
11849
  /**
11850
+ * Get the pre-allocated PDF reference for this font.
11851
+ *
11852
+ * Set by `PDFFonts.embed()`. At save time, the actual font objects
11853
+ * (Type0 dict, CIDFont, FontDescriptor, font program, ToUnicode)
11854
+ * are created and registered at this ref.
11855
+ *
11856
+ * @throws {Error} if the font was not embedded via `pdf.embedFont()`
11857
+ */
11858
+ get ref() {
11859
+ if (!this._ref) throw new Error("Font has no PDF reference. Use pdf.embedFont() to embed fonts.");
11860
+ return this._ref;
11861
+ }
11862
+ /**
11863
+ * Set the pre-allocated PDF reference.
11864
+ *
11865
+ * @internal Called by PDFFonts.embed()
11866
+ */
11867
+ setRef(ref) {
11868
+ this._ref = ref;
11869
+ }
11870
+ /**
11898
11871
  * Mark this font as used in a form field.
11899
11872
  *
11900
11873
  * Fonts used in form fields cannot be subsetted because users may type
@@ -18674,10 +18647,8 @@ function buildResources$2(ctx, font, fontName) {
18674
18647
  const resources = new PdfDict();
18675
18648
  const fonts = new PdfDict();
18676
18649
  const cleanName = fontName.startsWith("/") ? fontName.slice(1) : fontName;
18677
- if (isEmbeddedFont(font)) {
18678
- const fontRef = ctx.registry.register(buildEmbeddedFontDict$2(font));
18679
- fonts.set(cleanName, fontRef);
18680
- } else if (isExistingFont(font) && font.ref) fonts.set(cleanName, font.ref);
18650
+ if (isEmbeddedFont(font)) fonts.set(cleanName, font.ref);
18651
+ else if (isExistingFont(font) && font.ref) fonts.set(cleanName, font.ref);
18681
18652
  else {
18682
18653
  const fontDict = new PdfDict();
18683
18654
  fontDict.set("Type", PdfName.of("Font"));
@@ -18688,14 +18659,6 @@ function buildResources$2(ctx, font, fontName) {
18688
18659
  resources.set("Font", fonts);
18689
18660
  return resources;
18690
18661
  }
18691
- function buildEmbeddedFontDict$2(font) {
18692
- const dict = new PdfDict();
18693
- dict.set("Type", PdfName.of("Font"));
18694
- dict.set("Subtype", PdfName.of("Type0"));
18695
- dict.set("BaseFont", PdfName.of(font.baseFontName));
18696
- dict.set("Encoding", PdfName.of("Identity-H"));
18697
- return dict;
18698
- }
18699
18662
 
18700
18663
  //#endregion
18701
18664
  //#region src/document/forms/choice-appearance.ts
@@ -18888,10 +18851,8 @@ function buildResources$1(ctx, font, fontName) {
18888
18851
  const resources = new PdfDict();
18889
18852
  const fonts = new PdfDict();
18890
18853
  const cleanName = fontName.startsWith("/") ? fontName.slice(1) : fontName;
18891
- if (isEmbeddedFont(font)) {
18892
- const fontRef = ctx.registry.register(buildEmbeddedFontDict$1(font));
18893
- fonts.set(cleanName, fontRef);
18894
- } else if (isExistingFont(font) && font.ref) fonts.set(cleanName, font.ref);
18854
+ if (isEmbeddedFont(font)) fonts.set(cleanName, font.ref);
18855
+ else if (isExistingFont(font) && font.ref) fonts.set(cleanName, font.ref);
18895
18856
  else {
18896
18857
  const fontDict = new PdfDict();
18897
18858
  fontDict.set("Type", PdfName.of("Font"));
@@ -18902,14 +18863,6 @@ function buildResources$1(ctx, font, fontName) {
18902
18863
  resources.set("Font", fonts);
18903
18864
  return resources;
18904
18865
  }
18905
- function buildEmbeddedFontDict$1(font) {
18906
- const dict = new PdfDict();
18907
- dict.set("Type", PdfName.of("Font"));
18908
- dict.set("Subtype", PdfName.of("Type0"));
18909
- dict.set("BaseFont", PdfName.of(font.baseFontName));
18910
- dict.set("Encoding", PdfName.of("Identity-H"));
18911
- return dict;
18912
- }
18913
18866
 
18914
18867
  //#endregion
18915
18868
  //#region src/document/forms/text-appearance.ts
@@ -19211,10 +19164,8 @@ function buildResources(ctx, font, fontName) {
19211
19164
  const resources = new PdfDict();
19212
19165
  const fonts = new PdfDict();
19213
19166
  const cleanName = fontName.startsWith("/") ? fontName.slice(1) : fontName;
19214
- if (isEmbeddedFont(font)) {
19215
- const fontRef = ctx.registry.register(buildEmbeddedFontDict(font));
19216
- fonts.set(cleanName, fontRef);
19217
- } else if (isExistingFont(font) && font.ref) fonts.set(cleanName, font.ref);
19167
+ if (isEmbeddedFont(font)) fonts.set(cleanName, font.ref);
19168
+ else if (isExistingFont(font) && font.ref) fonts.set(cleanName, font.ref);
19218
19169
  else {
19219
19170
  const fontDict = new PdfDict();
19220
19171
  fontDict.set("Type", PdfName.of("Font"));
@@ -19225,14 +19176,6 @@ function buildResources(ctx, font, fontName) {
19225
19176
  resources.set("Font", fonts);
19226
19177
  return resources;
19227
19178
  }
19228
- function buildEmbeddedFontDict(font) {
19229
- const dict = new PdfDict();
19230
- dict.set("Type", PdfName.of("Font"));
19231
- dict.set("Subtype", PdfName.of("Type0"));
19232
- dict.set("BaseFont", PdfName.of(font.baseFontName));
19233
- dict.set("Encoding", PdfName.of("Identity-H"));
19234
- return dict;
19235
- }
19236
19179
  function calculateAppearanceMatrix(width, height, rotation) {
19237
19180
  switch (Math.abs(rotation)) {
19238
19181
  case 90: return [
@@ -24417,6 +24360,14 @@ function mergeBboxes(boxes) {
24417
24360
  //#endregion
24418
24361
  //#region src/text/line-grouper.ts
24419
24362
  /**
24363
+ * Minimum fraction of consecutive char pairs with decreasing x-positions
24364
+ * (in stream order) to classify a line as "RTL-placed".
24365
+ *
24366
+ * Figma/Canva exports produce ~100% decreasing pairs within words.
24367
+ * 80% tolerates small forward jumps at word boundaries.
24368
+ */
24369
+ const RTL_PLACED_THRESHOLD = .8;
24370
+ /**
24420
24371
  * Group extracted characters into lines and spans.
24421
24372
  *
24422
24373
  * @param chars - Array of extracted characters
@@ -24430,8 +24381,8 @@ function groupCharsIntoLines(chars, options = {}) {
24430
24381
  const lineGroups = groupByBaseline(chars, baselineTolerance);
24431
24382
  const lines = [];
24432
24383
  for (const group of lineGroups) {
24433
- const sorted = [...group].sort((a, b) => a.bbox.x - b.bbox.x);
24434
- const spans = groupIntoSpans(sorted, spaceThreshold);
24384
+ const { chars: sorted, rtlPlaced } = orderLineChars(group);
24385
+ const spans = groupIntoSpans(sorted, spaceThreshold, rtlPlaced);
24435
24386
  if (spans.length === 0) continue;
24436
24387
  const lineText = spans.map((s) => s.text).join("");
24437
24388
  const lineBbox = mergeBboxes(spans.map((s) => s.bbox));
@@ -24447,6 +24398,71 @@ function groupCharsIntoLines(chars, options = {}) {
24447
24398
  return lines;
24448
24399
  }
24449
24400
  /**
24401
+ * Determine the correct character order for a line.
24402
+ *
24403
+ * Design tools like Figma and Canva export PDFs where LTR characters are placed
24404
+ * right-to-left via TJ positioning adjustments (positive values move the pen left).
24405
+ * The font has near-zero glyph widths, so all positioning comes from TJ. Characters
24406
+ * appear in correct reading order in the content stream, but their x-positions
24407
+ * decrease monotonically.
24408
+ *
24409
+ * When this pattern is detected, we preserve content stream order instead of sorting
24410
+ * by x-position, which would reverse the text.
24411
+ *
24412
+ * **Limitation**: Detection requires `sequenceIndex` on every character. If any
24413
+ * character in the group lacks a `sequenceIndex`, we fall back to x-position sorting
24414
+ * because stream order cannot be reliably reconstructed.
24415
+ */
24416
+ function orderLineChars(group) {
24417
+ if (group.length <= 1) return {
24418
+ chars: [...group],
24419
+ rtlPlaced: false
24420
+ };
24421
+ if (!group.every((c) => c.sequenceIndex != null)) return {
24422
+ chars: [...group].sort((a, b) => a.bbox.x - b.bbox.x),
24423
+ rtlPlaced: false
24424
+ };
24425
+ const streamOrder = [...group].sort((a, b) => a.sequenceIndex - b.sequenceIndex);
24426
+ if (isRtlPlaced(streamOrder)) return {
24427
+ chars: streamOrder,
24428
+ rtlPlaced: true
24429
+ };
24430
+ return {
24431
+ chars: [...group].sort((a, b) => a.bbox.x - b.bbox.x),
24432
+ rtlPlaced: false
24433
+ };
24434
+ }
24435
+ /**
24436
+ * Detect whether characters are placed right-to-left in user space while
24437
+ * content stream order represents the correct reading order.
24438
+ *
24439
+ * Returns true when x-positions in stream order are predominantly decreasing
24440
+ * (≥ 80% of consecutive pairs). In that case, position-based sorting would
24441
+ * reverse the reading order, so we preserve stream order instead.
24442
+ *
24443
+ * This covers two real-world scenarios:
24444
+ * - **Design-tool PDFs** (Figma, Canva): LTR text placed right-to-left via
24445
+ * TJ positioning adjustments. Stream order = correct reading order.
24446
+ * - **Genuine RTL text** (Arabic, Hebrew): characters naturally placed
24447
+ * right-to-left. PDF producers typically emit them in reading order, so
24448
+ * stream order is again correct.
24449
+ *
24450
+ * In both cases, when x-positions decrease in stream order, preserving stream
24451
+ * order produces the correct reading order.
24452
+ *
24453
+ * **Known limitation**: mixed bidi text (e.g., Arabic with embedded English)
24454
+ * requires a full Unicode bidi algorithm, which is out of scope for this
24455
+ * heuristic. For mixed lines, neither stream order nor x-sort is fully
24456
+ * correct; a future bidi implementation should replace this heuristic.
24457
+ */
24458
+ function isRtlPlaced(streamOrder) {
24459
+ if (streamOrder.length < 2) return false;
24460
+ let decreasingCount = 0;
24461
+ for (let i = 1; i < streamOrder.length; i++) if (streamOrder[i].bbox.x < streamOrder[i - 1].bbox.x) decreasingCount++;
24462
+ const totalPairs = streamOrder.length - 1;
24463
+ return decreasingCount / totalPairs >= RTL_PLACED_THRESHOLD;
24464
+ }
24465
+ /**
24450
24466
  * Group characters by baseline Y coordinate.
24451
24467
  */
24452
24468
  function groupByBaseline(chars, tolerance) {
@@ -24468,7 +24484,7 @@ function groupByBaseline(chars, tolerance) {
24468
24484
  /**
24469
24485
  * Group characters into spans based on font/size and detect spaces.
24470
24486
  */
24471
- function groupIntoSpans(chars, spaceThreshold) {
24487
+ function groupIntoSpans(chars, spaceThreshold, rtlPlaced) {
24472
24488
  if (chars.length === 0) return [];
24473
24489
  const spans = [];
24474
24490
  let currentSpan = [chars[0]];
@@ -24478,14 +24494,14 @@ function groupIntoSpans(chars, spaceThreshold) {
24478
24494
  const prevChar = chars[i - 1];
24479
24495
  const char = chars[i];
24480
24496
  const fontChanged = char.fontName !== currentFontName || Math.abs(char.fontSize - currentFontSize) > .5;
24481
- const needsSpace = char.bbox.x - (prevChar.bbox.x + prevChar.bbox.width) > (prevChar.fontSize + char.fontSize) / 2 * spaceThreshold;
24497
+ const needsSpace = (rtlPlaced ? prevChar.bbox.x - (char.bbox.x + char.bbox.width) : char.bbox.x - (prevChar.bbox.x + prevChar.bbox.width)) > (prevChar.fontSize + char.fontSize) / 2 * spaceThreshold;
24482
24498
  if (fontChanged) {
24483
24499
  spans.push(buildSpan(currentSpan));
24484
24500
  currentSpan = [char];
24485
24501
  currentFontName = char.fontName;
24486
24502
  currentFontSize = char.fontSize;
24487
24503
  } else if (needsSpace) {
24488
- currentSpan.push(createSpaceChar(prevChar, char));
24504
+ currentSpan.push(createSpaceChar(prevChar, char, rtlPlaced));
24489
24505
  currentSpan.push(char);
24490
24506
  } else currentSpan.push(char);
24491
24507
  }
@@ -24510,9 +24526,9 @@ function buildSpan(chars) {
24510
24526
  /**
24511
24527
  * Create a synthetic space character between two characters.
24512
24528
  */
24513
- function createSpaceChar(before, after) {
24514
- const x = before.bbox.x + before.bbox.width;
24515
- const width = after.bbox.x - x;
24529
+ function createSpaceChar(before, after, rtlPlaced) {
24530
+ const x = rtlPlaced ? after.bbox.x + after.bbox.width : before.bbox.x + before.bbox.width;
24531
+ const width = rtlPlaced ? before.bbox.x - x : after.bbox.x - x;
24516
24532
  return {
24517
24533
  char: " ",
24518
24534
  bbox: {
@@ -24523,7 +24539,8 @@ function createSpaceChar(before, after) {
24523
24539
  },
24524
24540
  fontSize: (before.fontSize + after.fontSize) / 2,
24525
24541
  fontName: before.fontName,
24526
- baseline: (before.baseline + after.baseline) / 2
24542
+ baseline: (before.baseline + after.baseline) / 2,
24543
+ sequenceIndex: before.sequenceIndex != null ? before.sequenceIndex + .5 : void 0
24527
24544
  };
24528
24545
  }
24529
24546
  /**
@@ -26159,7 +26176,8 @@ var TextExtractor = class {
26159
26176
  },
26160
26177
  fontSize: this.state.effectiveFontSize,
26161
26178
  fontName: font.baseFontName,
26162
- baseline: bbox.baseline
26179
+ baseline: bbox.baseline,
26180
+ sequenceIndex: this.chars.length
26163
26181
  });
26164
26182
  const isSpace = char === " " || char === "\xA0";
26165
26183
  this.state.advanceChar(width, isSpace);
@@ -29138,7 +29156,7 @@ const INHERITABLE_PAGE_ATTRS = [
29138
29156
  * @example
29139
29157
  * ```typescript
29140
29158
  * const copier = new ObjectCopier(sourcePdf, destPdf);
29141
- * const copiedPageRef = await copier.copyPage(sourcePageRef);
29159
+ * const copiedPageRef = copier.copyPage(sourcePageRef);
29142
29160
  * destPdf.insertPage(0, copiedPageRef);
29143
29161
  * ```
29144
29162
  */
@@ -29168,14 +29186,14 @@ var ObjectCopier = class {
29168
29186
  * @param srcPageRef Reference to the page in source document
29169
29187
  * @returns Reference to the copied page in destination document
29170
29188
  */
29171
- async copyPage(srcPageRef) {
29189
+ copyPage(srcPageRef) {
29172
29190
  const srcPage = this.source.getObject(srcPageRef);
29173
29191
  if (!(srcPage instanceof PdfDict)) throw new Error(`Page object not found or not a dictionary: ${srcPageRef.objectNumber} ${srcPageRef.generation} R`);
29174
29192
  const cloned = srcPage.clone();
29175
29193
  for (const key$1 of INHERITABLE_PAGE_ATTRS) if (!cloned.has(key$1)) {
29176
29194
  const inherited = this.getInheritedAttribute(srcPage, key$1);
29177
29195
  if (inherited) {
29178
- const copied = await this.copyObject(inherited);
29196
+ const copied = this.copyObject(inherited);
29179
29197
  cloned.set(key$1, copied);
29180
29198
  }
29181
29199
  }
@@ -29184,17 +29202,17 @@ var ObjectCopier = class {
29184
29202
  if (!this.options.includeThumbnails) cloned.delete("Thumb");
29185
29203
  if (!this.options.includeStructure) cloned.delete("StructParents");
29186
29204
  cloned.delete("Parent");
29187
- const copiedPage = await this.copyDictValues(cloned);
29205
+ const copiedPage = this.copyDictValues(cloned);
29188
29206
  return this.dest.register(copiedPage);
29189
29207
  }
29190
29208
  /**
29191
29209
  * Deep copy any PDF object, remapping references to destination.
29192
29210
  */
29193
- async copyObject(obj) {
29194
- if (obj instanceof PdfRef) return await this.copyRef(obj);
29195
- if (obj instanceof PdfStream) return await this.copyStream(obj);
29196
- if (obj instanceof PdfDict) return await this.copyDict(obj);
29197
- if (obj instanceof PdfArray) return await this.copyArray(obj);
29211
+ copyObject(obj) {
29212
+ if (obj instanceof PdfRef) return this.copyRef(obj);
29213
+ if (obj instanceof PdfStream) return this.copyStream(obj);
29214
+ if (obj instanceof PdfDict) return this.copyDict(obj);
29215
+ if (obj instanceof PdfArray) return this.copyArray(obj);
29198
29216
  return obj;
29199
29217
  }
29200
29218
  /**
@@ -29203,7 +29221,7 @@ var ObjectCopier = class {
29203
29221
  * Handles circular references by registering a placeholder before
29204
29222
  * recursively copying the referenced object's contents.
29205
29223
  */
29206
- async copyRef(ref) {
29224
+ copyRef(ref) {
29207
29225
  const key$1 = `${ref.objectNumber}:${ref.generation}`;
29208
29226
  const existing = this.refMap.get(key$1);
29209
29227
  if (existing) return existing;
@@ -29217,7 +29235,7 @@ var ObjectCopier = class {
29217
29235
  if (srcObj instanceof PdfDict) return this.copyDictRef(key$1, srcObj);
29218
29236
  if (srcObj instanceof PdfArray) {
29219
29237
  const items = [];
29220
- for (const item of srcObj) items.push(await this.copyObject(item));
29238
+ for (const item of srcObj) items.push(this.copyObject(item));
29221
29239
  const copiedArr = new PdfArray(items);
29222
29240
  const destRef$1 = this.dest.register(copiedArr);
29223
29241
  this.refMap.set(key$1, destRef$1);
@@ -29230,17 +29248,17 @@ var ObjectCopier = class {
29230
29248
  /**
29231
29249
  * Copy a dict reference, handling circular references.
29232
29250
  */
29233
- async copyDictRef(key$1, srcDict) {
29251
+ copyDictRef(key$1, srcDict) {
29234
29252
  const cloned = srcDict.clone();
29235
29253
  const destRef = this.dest.register(cloned);
29236
29254
  this.refMap.set(key$1, destRef);
29237
- await this.copyDictValues(cloned);
29255
+ this.copyDictValues(cloned);
29238
29256
  return destRef;
29239
29257
  }
29240
29258
  /**
29241
29259
  * Copy a stream reference, handling circular references and encryption.
29242
29260
  */
29243
- async copyStreamRef(key$1, srcStream) {
29261
+ copyStreamRef(key$1, srcStream) {
29244
29262
  const sourceWasEncrypted = this.source.isEncrypted;
29245
29263
  const clonedDict = srcStream.clone();
29246
29264
  let streamData;
@@ -29275,7 +29293,7 @@ var ObjectCopier = class {
29275
29293
  const destRef = this.dest.register(copiedStream);
29276
29294
  this.refMap.set(key$1, destRef);
29277
29295
  for (const [entryKey, value] of clonedDict) {
29278
- const copied = await this.copyObject(value);
29296
+ const copied = this.copyObject(value);
29279
29297
  copiedStream.set(entryKey.value, copied);
29280
29298
  }
29281
29299
  return destRef;
@@ -29283,7 +29301,7 @@ var ObjectCopier = class {
29283
29301
  /**
29284
29302
  * Copy a dictionary, remapping all reference values.
29285
29303
  */
29286
- async copyDict(dict) {
29304
+ copyDict(dict) {
29287
29305
  const cloned = dict.clone();
29288
29306
  return this.copyDictValues(cloned);
29289
29307
  }
@@ -29291,9 +29309,9 @@ var ObjectCopier = class {
29291
29309
  * Copy all values in a dictionary, remapping references.
29292
29310
  * Modifies the dict in place and returns it.
29293
29311
  */
29294
- async copyDictValues(dict) {
29312
+ copyDictValues(dict) {
29295
29313
  for (const [key$1, value] of dict) {
29296
- const copied = await this.copyObject(value);
29314
+ const copied = this.copyObject(value);
29297
29315
  dict.set(key$1.value, copied);
29298
29316
  }
29299
29317
  return dict;
@@ -29301,9 +29319,9 @@ var ObjectCopier = class {
29301
29319
  /**
29302
29320
  * Copy an array, remapping all reference elements.
29303
29321
  */
29304
- async copyArray(arr) {
29322
+ copyArray(arr) {
29305
29323
  const items = [];
29306
- for (const item of arr) items.push(await this.copyObject(item));
29324
+ for (const item of arr) items.push(this.copyObject(item));
29307
29325
  return new PdfArray(items);
29308
29326
  }
29309
29327
  /**
@@ -29312,10 +29330,10 @@ var ObjectCopier = class {
29312
29330
  * If source wasn't encrypted, copies raw encoded bytes (fastest).
29313
29331
  * If source was encrypted, decodes and re-encodes with same filters.
29314
29332
  */
29315
- async copyStream(stream) {
29333
+ copyStream(stream) {
29316
29334
  const sourceWasEncrypted = this.source.isEncrypted;
29317
29335
  const clonedDict = stream.clone();
29318
- await this.copyDictValues(clonedDict);
29336
+ this.copyDictValues(clonedDict);
29319
29337
  if (!sourceWasEncrypted) return new PdfStream(clonedDict, stream.data);
29320
29338
  try {
29321
29339
  const decodedData = stream.getDecodedData();
@@ -30679,15 +30697,21 @@ function aesEncrypt(key$1, plaintext) {
30679
30697
  * @param key - 16 bytes (AES-128) or 32 bytes (AES-256)
30680
30698
  * @param data - IV (16 bytes) + ciphertext
30681
30699
  * @returns Decrypted plaintext
30682
- * @throws {Error} if data is too short or padding is invalid
30700
+ * @throws {Error} if data is too short to contain an IV
30683
30701
  */
30684
30702
  function aesDecrypt(key$1, data) {
30685
30703
  validateAesKey(key$1);
30686
30704
  if (data.length < AES_BLOCK_SIZE) throw new Error(`AES ciphertext too short: expected at least ${AES_BLOCK_SIZE} bytes for IV`);
30687
30705
  if (data.length === AES_BLOCK_SIZE) return new Uint8Array(0);
30688
30706
  const iv = data.subarray(0, AES_BLOCK_SIZE);
30689
- const ciphertext = data.subarray(AES_BLOCK_SIZE);
30690
- if (ciphertext.length % AES_BLOCK_SIZE !== 0) throw new Error(`AES ciphertext length must be multiple of ${AES_BLOCK_SIZE}, got ${ciphertext.length}`);
30707
+ let ciphertext = data.subarray(AES_BLOCK_SIZE);
30708
+ if (ciphertext.length % AES_BLOCK_SIZE !== 0) {
30709
+ const remainder = ciphertext.length % AES_BLOCK_SIZE;
30710
+ const aligned = ciphertext.length - remainder;
30711
+ console.warn(`AES ciphertext length (${ciphertext.length}) is not a multiple of ${AES_BLOCK_SIZE}, truncating ${remainder} trailing bytes`);
30712
+ if (aligned === 0) return new Uint8Array(0);
30713
+ ciphertext = ciphertext.subarray(0, aligned);
30714
+ }
30691
30715
  return cbc(key$1, iv).decrypt(ciphertext);
30692
30716
  }
30693
30717
  /**
@@ -33288,29 +33312,34 @@ var DocumentParser = class {
33288
33312
  * Decrypt an object's strings and stream data.
33289
33313
  */
33290
33314
  const decryptObject = (obj, objNum, genNum) => {
33291
- if (!securityHandler?.isAuthenticated) return obj;
33292
- if (obj instanceof PdfString) return new PdfString(securityHandler.decryptString(obj.bytes, objNum, genNum), obj.format);
33293
- if (obj instanceof PdfArray) {
33294
- const decryptedItems = [];
33295
- for (const item of obj) decryptedItems.push(decryptObject(item, objNum, genNum));
33296
- return new PdfArray(decryptedItems);
33297
- }
33298
- if (obj instanceof PdfStream) {
33299
- const streamType = obj.getName("Type")?.value;
33300
- if (!securityHandler.shouldEncryptStream(streamType)) return obj;
33301
- const newStream = new PdfStream(obj, securityHandler.decryptStream(obj.data, objNum, genNum));
33302
- for (const [key$1, value] of obj) {
33303
- const decryptedValue = decryptObject(value, objNum, genNum);
33304
- if (decryptedValue !== value) newStream.set(key$1.value, decryptedValue);
33315
+ try {
33316
+ if (!securityHandler?.isAuthenticated) return obj;
33317
+ if (obj instanceof PdfString) return new PdfString(securityHandler.decryptString(obj.bytes, objNum, genNum), obj.format);
33318
+ if (obj instanceof PdfArray) {
33319
+ const decryptedItems = [];
33320
+ for (const item of obj) decryptedItems.push(decryptObject(item, objNum, genNum));
33321
+ return new PdfArray(decryptedItems);
33305
33322
  }
33306
- return newStream;
33307
- }
33308
- if (obj instanceof PdfDict) {
33309
- const decryptedDict = new PdfDict();
33310
- for (const [key$1, value] of obj) decryptedDict.set(key$1.value, decryptObject(value, objNum, genNum));
33311
- return decryptedDict;
33323
+ if (obj instanceof PdfStream) {
33324
+ const streamType = obj.getName("Type")?.value;
33325
+ if (!securityHandler.shouldEncryptStream(streamType)) return obj;
33326
+ const newStream = new PdfStream(obj, securityHandler.decryptStream(obj.data, objNum, genNum));
33327
+ for (const [key$1, value] of obj) {
33328
+ const decryptedValue = decryptObject(value, objNum, genNum);
33329
+ if (decryptedValue !== value) newStream.set(key$1.value, decryptedValue);
33330
+ }
33331
+ return newStream;
33332
+ }
33333
+ if (obj instanceof PdfDict) {
33334
+ const decryptedDict = new PdfDict();
33335
+ for (const [key$1, value] of obj) decryptedDict.set(key$1.value, decryptObject(value, objNum, genNum));
33336
+ return decryptedDict;
33337
+ }
33338
+ return obj;
33339
+ } catch (error) {
33340
+ console.warn(`Failed to decrypt object ${objNum} ${genNum}:`, error);
33341
+ return obj;
33312
33342
  }
33313
- return obj;
33314
33343
  };
33315
33344
  const getObject = (ref) => {
33316
33345
  const key$1 = `${ref.objectNumber} ${ref.generation}`;
@@ -33788,11 +33817,12 @@ function writeIndirectObject(writer, ref, obj) {
33788
33817
  * Streams that already have filters are returned unchanged - this includes
33789
33818
  * image formats (DCTDecode, JPXDecode, etc.) that are already compressed.
33790
33819
  */
33791
- function prepareObjectForWrite(obj, compress) {
33820
+ const DEFAULT_COMPRESSION_THRESHOLD = 512;
33821
+ function prepareObjectForWrite(obj, compress, compressionThreshold) {
33792
33822
  if (!(obj instanceof PdfStream)) return obj;
33793
33823
  if (obj.has("Filter")) return obj;
33794
33824
  if (!compress) return obj;
33795
- if (obj.data.length === 0) return obj;
33825
+ if (obj.data.length < compressionThreshold) return obj;
33796
33826
  const compressed = FilterPipeline.encode(obj.data, { name: "FlateDecode" });
33797
33827
  if (compressed.length >= obj.data.length) return obj;
33798
33828
  const compressedStream = new PdfStream(obj, compressed);
@@ -33893,6 +33923,7 @@ function collectReachableRefs(registry, root, info, encrypt) {
33893
33923
  function writeComplete(registry, options) {
33894
33924
  const writer = new ByteWriter();
33895
33925
  const compress = options.compressStreams ?? true;
33926
+ const threshold = options.compressionThreshold ?? DEFAULT_COMPRESSION_THRESHOLD;
33896
33927
  const version$1 = options.version ?? "1.7";
33897
33928
  writer.writeAscii(`%PDF-${version$1}\n`);
33898
33929
  writer.writeBytes(new Uint8Array([
@@ -33908,7 +33939,7 @@ function writeComplete(registry, options) {
33908
33939
  for (const [ref, obj] of registry.entries()) {
33909
33940
  const key$1 = `${ref.objectNumber} ${ref.generation}`;
33910
33941
  if (!reachableKeys.has(key$1)) continue;
33911
- let prepared = prepareObjectForWrite(obj, compress);
33942
+ let prepared = prepareObjectForWrite(obj, compress, threshold);
33912
33943
  if (options.securityHandler && options.encrypt && ref !== options.encrypt) prepared = encryptObject(prepared, {
33913
33944
  handler: options.securityHandler,
33914
33945
  objectNumber: ref.objectNumber,
@@ -33993,12 +34024,13 @@ function writeIncremental(registry, options) {
33993
34024
  xrefOffset: options.originalXRefOffset
33994
34025
  };
33995
34026
  const compress = options.compressStreams ?? true;
34027
+ const threshold = options.compressionThreshold ?? DEFAULT_COMPRESSION_THRESHOLD;
33996
34028
  const writer = new ByteWriter(options.originalBytes);
33997
34029
  const lastByte = options.originalBytes[options.originalBytes.length - 1];
33998
34030
  if (lastByte !== LF && lastByte !== CR) writer.writeByte(10);
33999
34031
  const offsets = /* @__PURE__ */ new Map();
34000
34032
  for (const [ref, obj] of changes.modified) {
34001
- let prepared = prepareObjectForWrite(obj, compress);
34033
+ let prepared = prepareObjectForWrite(obj, compress, threshold);
34002
34034
  if (options.securityHandler && options.encrypt && ref !== options.encrypt) prepared = encryptObject(prepared, {
34003
34035
  handler: options.securityHandler,
34004
34036
  objectNumber: ref.objectNumber,
@@ -34011,7 +34043,7 @@ function writeIncremental(registry, options) {
34011
34043
  writeIndirectObject(writer, ref, prepared);
34012
34044
  }
34013
34045
  for (const [ref, obj] of changes.created) {
34014
- let prepared = prepareObjectForWrite(obj, compress);
34046
+ let prepared = prepareObjectForWrite(obj, compress, threshold);
34015
34047
  if (options.securityHandler && options.encrypt && ref !== options.encrypt) prepared = encryptObject(prepared, {
34016
34048
  handler: options.securityHandler,
34017
34049
  objectNumber: ref.objectNumber,
@@ -36760,6 +36792,7 @@ var PDFFonts = class {
36760
36792
  const font = EmbeddedFont.fromBytes(data, options);
36761
36793
  const ref = this.ctx.registry.allocateRef();
36762
36794
  this.embeddedFonts.set(font, ref);
36795
+ font.setRef(ref);
36763
36796
  return font;
36764
36797
  }
36765
36798
  /**
@@ -37419,13 +37452,7 @@ var PDFForm = class PDFForm {
37419
37452
  * Register an embedded font in the form's default resources.
37420
37453
  */
37421
37454
  registerFontInFormResources(font) {
37422
- const fontRef = this._ctx.registry.register(PdfDict.of({
37423
- Type: PdfName.of("Font"),
37424
- Subtype: PdfName.of("Type0"),
37425
- BaseFont: PdfName.of(font.baseFontName),
37426
- Encoding: PdfName.of("Identity-H")
37427
- }));
37428
- this._acroForm.addFontToResources(fontRef);
37455
+ this._acroForm.addFontToResources(font.ref);
37429
37456
  }
37430
37457
  /**
37431
37458
  * Store field styling metadata for appearance generation.
@@ -40861,7 +40888,7 @@ var PDF = class PDF {
40861
40888
  for (const index of indices) {
40862
40889
  const srcPage = source.getPage(index);
40863
40890
  if (!srcPage) throw new Error(`Source page ${index} not found`);
40864
- const copiedPageRef = await copier.copyPage(srcPage.ref);
40891
+ const copiedPageRef = copier.copyPage(srcPage.ref);
40865
40892
  copiedRefs.push(copiedPageRef);
40866
40893
  }
40867
40894
  let insertIndex = options.insertAt ?? this.getPageCount();
@@ -40937,7 +40964,7 @@ var PDF = class PDF {
40937
40964
  const srcResources = srcPage.dict.getDict("Resources", source.getObject.bind(source));
40938
40965
  let resources;
40939
40966
  if (srcResources) {
40940
- const copied = await copier.copyObject(srcResources);
40967
+ const copied = copier.copyObject(srcResources);
40941
40968
  resources = copied instanceof PdfDict ? copied : new PdfDict();
40942
40969
  } else resources = new PdfDict();
40943
40970
  const mediaBox = srcPage.getMediaBox();
@@ -41915,7 +41942,9 @@ var PDF = class PDF {
41915
41942
  encrypt: encryptRef,
41916
41943
  id: fileId,
41917
41944
  useXRefStream,
41918
- securityHandler
41945
+ securityHandler,
41946
+ compressStreams: options.compressStreams,
41947
+ compressionThreshold: options.compressionThreshold
41919
41948
  });
41920
41949
  this._pendingSecurity = { action: "none" };
41921
41950
  return result$1;
@@ -41927,7 +41956,9 @@ var PDF = class PDF {
41927
41956
  encrypt: encryptRef,
41928
41957
  id: fileId,
41929
41958
  useXRefStream,
41930
- securityHandler
41959
+ securityHandler,
41960
+ compressStreams: options.compressStreams,
41961
+ compressionThreshold: options.compressionThreshold
41931
41962
  });
41932
41963
  this._pendingSecurity = { action: "none" };
41933
41964
  return result;