modern-pdf-lib 0.12.1 → 0.13.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/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { $ as setFillColor, $t as showTextArray, A as endMarkedContent, At as moveTo, B as saveState, Bt as endText, C as beginArtifact, Ct as ellipsePath, D as beginMarkedContentWithProperties, Dt as fillEvenOdd, E as beginMarkedContentSequence, Et as fillAndStroke, F as radians, Ft as setLineJoin, G as applyFillColor, Gt as setFont, H as setGraphicsState, Ht as moveTextSetLeading, I as radiansToDegrees, It as setLineWidth, J as colorToComponents, Jt as setTextMatrix, K as applyStrokeColor, Kt as setFontSize, L as restoreState, Lt as setMiterLimit, M as concatMatrix, Mt as setDashPattern, N as degrees, Nt as setFlatness, O as createMarkedContentScope, Ot as fillEvenOddAndStroke, P as degreesToRadians, Pt as setLineCap, Q as setColorSpace, Qt as showText, R as rotate, Rt as stroke, S as createAnnotation, St as curveToInitial, T as beginMarkedContent, Tt as fill, U as skew, Ut as nextLine, V as scale, Vt as moveText, W as translate, Wt as setCharacterSpacing, X as grayscale, Xt as setTextRise, Y as componentsToColor, Yt as setTextRenderingMode, Z as rgb, Zt as setWordSpacing, _ as parseSvgTransform, _t as closeFillAndStroke, a as applyRedactions, at as setStrokeColorCmyk, b as annotationFromDict, bt as curveTo, c as PdfLayer, ct as setStrokeColorSpace, d as endLayerContent, dt as drawImageXObject, en as showTextHex, et as setFillColorCmyk, f as drawSvgOnPage, ft as drawXObject, g as parseSvgPath, gt as closeAndStroke, h as parseSvgColor, ht as clipEvenOdd, i as wrapText, it as setStrokeColor, j as wrapInMarkedContent, jt as rectangle, k as endArtifact, kt as lineTo, l as PdfLayerManager, lt as setStrokingColor, m as parseSvg, mt as clip, n as PdfPage, nn as showTextWithSpacing, nt as setFillColorRgb, o as getRedactionMarks, ot as setStrokeColorGray, p as svgToPdfOperators, pt as circlePath, q as cmyk, qt as setLeading, rt as setFillingColor, s as markForRedaction, st as setStrokeColorRgb, t as PageSizes, tn as showTextNextLine, tt as setFillColorGray, u as beginLayerContent, ut as drawImageWithMatrix, v as AnnotationFlags, vt as closeFillEvenOddAndStroke, w as beginArtifactWithType, wt as endPath, x as buildAnnotationDict, xt as curveToFinal, y as PdfAnnotation, yt as closePath, z as rotationMatrix, zt as beginText } from "./pdfPage-JiCJLV3x.mjs";
1
+ import { $ as colorToComponents, $t as setTextMatrix, A as beginMarkedContent, At as fill, B as radians, Bt as setLineJoin, C as AnnotationFlags, Ct as closeFillEvenOddAndStroke, D as createAnnotation, Dt as curveToInitial, E as buildAnnotationDict, Et as curveToFinal, F as endMarkedContent, Ft as moveTo, G as saveState, Gt as endText, H as restoreState, Ht as setMiterLimit, I as wrapInMarkedContent, It as rectangle, J as skew, Jt as nextLine, K as scale, Kt as moveText, L as concatMatrix, Lt as setDashPattern, M as beginMarkedContentWithProperties, Mt as fillEvenOdd, N as createMarkedContentScope, Nt as fillEvenOddAndStroke, O as beginArtifact, Ot as ellipsePath, P as endArtifact, Pt as lineTo, Q as cmyk, Qt as setLeading, R as degrees, Rt as setFlatness, S as parseSvgTransform, St as closeFillAndStroke, T as annotationFromDict, Tt as curveTo, U as rotate, Ut as stroke, V as radiansToDegrees, Vt as setLineWidth, W as rotationMatrix, Wt as beginText, X as applyFillColor, Xt as setFont, Y as translate, Yt as setCharacterSpacing, Z as applyStrokeColor, Zt as setFontSize, _ as drawSvgOnPage, _t as drawXObject, a as buildGradientObjects, an as showTextHex, at as setFillColorCmyk, b as parseSvgColor, bt as clipEvenOdd, c as radialGradient, ct as setFillingColor, d as getRedactionMarks, dt as setStrokeColorGray, en as setTextRenderingMode, et as componentsToColor, f as markForRedaction, ft as setStrokeColorRgb, g as endLayerContent, gt as drawImageXObject, h as beginLayerContent, ht as drawImageWithMatrix, i as wrapText, in as showTextArray, it as setFillColor, j as beginMarkedContentSequence, jt as fillAndStroke, k as beginArtifactWithType, kt as endPath, l as tilingPattern, lt as setStrokeColor, m as PdfLayerManager, mt as setStrokingColor, n as PdfPage, nn as setWordSpacing, nt as rgb, o as buildPatternObjects, on as showTextNextLine, ot as setFillColorGray, p as PdfLayer, pt as setStrokeColorSpace, q as setGraphicsState, qt as moveTextSetLeading, rn as showText, rt as setColorSpace, s as linearGradient, sn as showTextWithSpacing, st as setFillColorRgb, t as PageSizes, tn as setTextRise, tt as grayscale, u as applyRedactions, ut as setStrokeColorCmyk, v as svgToPdfOperators, vt as circlePath, w as PdfAnnotation, wt as closePath, x as parseSvgPath, xt as closeAndStroke, y as parseSvg, yt as clip, z as degreesToRadians, zt as setLineCap } from "./pdfPage-B7vA518n.mjs";
2
2
  import { a as formatPdfDate, c as PdfBool, d as PdfNull, f as PdfNumber, g as PdfString, h as PdfStream, i as buildPageTree, l as PdfDict, m as PdfRef, n as buildDocumentStructure, p as PdfObjectRegistry, r as buildInfoDict, s as PdfArray, t as buildCatalog, u as PdfName } from "./pdfCatalog-CTfeeqtF.mjs";
3
- import { n as isAvailable, t as deflateSync$1 } from "./libdeflateWasm-CRFwmrSl.mjs";
3
+ import { n as isAvailable, t as deflateSync$1 } from "./libdeflateWasm-DlHgU5oy.mjs";
4
4
  import { i as subsetFont, n as computeSubsetTag, t as buildSubsetCmap } from "./fontSubset-ZpLoOZ2e.mjs";
5
- import { t as embedPng } from "./pngEmbed-CHyesD7i.mjs";
5
+ import { t as embedPng } from "./pngEmbed-DTOqgEUC.mjs";
6
6
  import { t as decompressSync } from "./fflateAdapter-DX0VqT5k.mjs";
7
7
  import { deflateSync, inflateSync, unzlibSync } from "fflate";
8
8
 
@@ -3235,7 +3235,7 @@ function extractMetricsWasm(fontData) {
3235
3235
  * Tracks which glyphs have been used so that subsetting can be
3236
3236
  * performed at save time, and provides text measurement methods.
3237
3237
  *
3238
- * Create via {@link embedFont}.
3238
+ * Create via `PdfDocument.embedFont()`.
3239
3239
  */
3240
3240
  var EmbeddedFont = class {
3241
3241
  /** The raw font file bytes. */
@@ -3580,6 +3580,53 @@ function findTable(data, tag) {
3580
3580
  }
3581
3581
  }
3582
3582
 
3583
+ //#endregion
3584
+ //#region src/parser/parseError.ts
3585
+ /**
3586
+ * @module parser/parseError
3587
+ * Structured error class for PDF parsing failures.
3588
+ * @packageDocumentation
3589
+ */
3590
+ var PdfParseError = class extends Error {
3591
+ name = "PdfParseError";
3592
+ offset;
3593
+ expected;
3594
+ actual;
3595
+ hexContext;
3596
+ constructor(options) {
3597
+ const hexCtx = options.data ? formatHexContext(options.data, options.offset) : "";
3598
+ const parts = [options.message];
3599
+ if (options.expected) parts.push(` Expected: ${options.expected}`);
3600
+ if (options.actual) parts.push(` Got: ${options.actual}`);
3601
+ if (hexCtx) parts.push(` Context:\n${hexCtx}`);
3602
+ super(parts.join("\n"), options.cause ? { cause: options.cause } : void 0);
3603
+ this.offset = options.offset;
3604
+ this.expected = options.expected ?? "";
3605
+ this.actual = options.actual ?? "";
3606
+ this.hexContext = hexCtx;
3607
+ }
3608
+ };
3609
+ function formatHexContext(data, offset, windowSize = 16) {
3610
+ const start = Math.max(0, offset - windowSize);
3611
+ const end = Math.min(data.length, offset + windowSize);
3612
+ const slice = data.subarray(start, end);
3613
+ const hexParts = [];
3614
+ const asciiParts = [];
3615
+ for (let i = 0; i < slice.length; i++) {
3616
+ const byte = slice[i];
3617
+ const isErrorByte = start + i === offset;
3618
+ const hex = byte.toString(16).padStart(2, "0");
3619
+ hexParts.push(isErrorByte ? `[${hex}]` : ` ${hex} `);
3620
+ asciiParts.push(byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : ".");
3621
+ }
3622
+ return [
3623
+ ` Offset ${start}:`,
3624
+ ` Hex: ${hexParts.join("")}`,
3625
+ ` ASCII: ${asciiParts.join(" ")}`,
3626
+ ` Error at offset ${offset} (marked with [])`
3627
+ ].join("\n");
3628
+ }
3629
+
3583
3630
  //#endregion
3584
3631
  //#region src/parser/lexer.ts
3585
3632
  /**
@@ -3735,7 +3782,7 @@ const hexVal = /* @__PURE__ */ (() => {
3735
3782
  */
3736
3783
  var PdfLexer = class {
3737
3784
  /** The raw PDF bytes being tokenized. */
3738
- data;
3785
+ _data;
3739
3786
  /** Total length of the input (cached for hot loops). */
3740
3787
  len;
3741
3788
  /** Current read position (byte offset). */
@@ -3758,11 +3805,20 @@ var PdfLexer = class {
3758
3805
  * must not mutate it while the lexer is in use.
3759
3806
  */
3760
3807
  constructor(data) {
3761
- this.data = data;
3808
+ this._data = data;
3762
3809
  this.len = data.length;
3763
3810
  this.position = 0;
3764
3811
  }
3765
3812
  /**
3813
+ * Public accessor for the raw PDF byte buffer.
3814
+ *
3815
+ * This allows other parsers (e.g. object parser, xref parser) to
3816
+ * include hex-context dumps in structured error messages.
3817
+ */
3818
+ get rawData() {
3819
+ return this._data;
3820
+ }
3821
+ /**
3766
3822
  * Consume and return the next token.
3767
3823
  *
3768
3824
  * Returns a token with `type === TokenType.EOF` when the input is
@@ -3798,8 +3854,14 @@ var PdfLexer = class {
3798
3854
  * @throws If there are not enough bytes remaining.
3799
3855
  */
3800
3856
  readStreamData(length) {
3801
- if (this.position + length > this.len) throw new Error(`PdfLexer.readStreamData: requested ${length} bytes at offset ${this.position}, but only ${this.len - this.position} remain`);
3802
- const slice = this.data.subarray(this.position, this.position + length);
3857
+ if (this.position + length > this.len) throw new PdfParseError({
3858
+ message: `PdfLexer.readStreamData: requested ${length} bytes at offset ${this.position}, but only ${this.len - this.position} remain`,
3859
+ offset: this.position,
3860
+ expected: `${length} bytes available`,
3861
+ actual: `${this.len - this.position} bytes remaining`,
3862
+ data: this._data
3863
+ });
3864
+ const slice = this._data.subarray(this.position, this.position + length);
3803
3865
  this.position += length;
3804
3866
  return slice;
3805
3867
  }
@@ -3812,7 +3874,13 @@ var PdfLexer = class {
3812
3874
  * @throws If the offset is out of range.
3813
3875
  */
3814
3876
  seek(offset) {
3815
- if (offset < 0 || offset > this.len) throw new RangeError(`PdfLexer.seek: offset ${offset} is outside [0, ${this.len}]`);
3877
+ if (offset < 0 || offset > this.len) throw new PdfParseError({
3878
+ message: `PdfLexer.seek: offset ${offset} is outside [0, ${this.len}]`,
3879
+ offset,
3880
+ expected: `offset in range [0, ${this.len}]`,
3881
+ actual: `${offset}`,
3882
+ data: this._data
3883
+ });
3816
3884
  this.position = offset;
3817
3885
  this.peeked = null;
3818
3886
  }
@@ -3825,7 +3893,7 @@ var PdfLexer = class {
3825
3893
  */
3826
3894
  byteAt(offset) {
3827
3895
  if (offset < 0 || offset >= this.len) return -1;
3828
- return this.data[offset];
3896
+ return this._data[offset];
3829
3897
  }
3830
3898
  /**
3831
3899
  * Return the total length of the input buffer (in bytes).
@@ -3841,7 +3909,7 @@ var PdfLexer = class {
3841
3909
  * extracting stream data.
3842
3910
  */
3843
3911
  skipWhitespace() {
3844
- const d = this.data;
3912
+ const d = this._data;
3845
3913
  let pos = this.position;
3846
3914
  while (pos < this.len) {
3847
3915
  const b = d[pos];
@@ -3875,7 +3943,7 @@ var PdfLexer = class {
3875
3943
  value: null,
3876
3944
  offset: this.position
3877
3945
  };
3878
- const d = this.data;
3946
+ const d = this._data;
3879
3947
  const startPos = this.position;
3880
3948
  const b = d[startPos];
3881
3949
  if (b === CH_LBRACKET) {
@@ -3915,7 +3983,13 @@ var PdfLexer = class {
3915
3983
  };
3916
3984
  }
3917
3985
  this.position++;
3918
- throw new Error(`PdfLexer: unexpected '>' at offset ${startPos} (expected '>>' for dict end)`);
3986
+ throw new PdfParseError({
3987
+ message: `PdfLexer: unexpected '>' at offset ${startPos} (expected '>>' for dict end)`,
3988
+ offset: startPos,
3989
+ expected: "'>>' (dict end delimiter)",
3990
+ actual: "'>' (lone angle bracket)",
3991
+ data: this._data
3992
+ });
3919
3993
  }
3920
3994
  if (b === CH_LPAREN) return this.readLiteralString(startPos);
3921
3995
  if (b === CH_SLASH) return this.readName(startPos);
@@ -3930,7 +4004,7 @@ var PdfLexer = class {
3930
4004
  * `-.002`, `+.5`). Scientific notation is **not** permitted.
3931
4005
  */
3932
4006
  readNumber(startPos) {
3933
- const d = this.data;
4007
+ const d = this._data;
3934
4008
  let pos = startPos;
3935
4009
  let hasSign = false;
3936
4010
  let hasDot = false;
@@ -3974,7 +4048,7 @@ var PdfLexer = class {
3974
4048
  * parentheses.
3975
4049
  */
3976
4050
  readLiteralString(startPos) {
3977
- const d = this.data;
4051
+ const d = this._data;
3978
4052
  let pos = startPos + 1;
3979
4053
  let depth = 1;
3980
4054
  let result = "";
@@ -4066,7 +4140,13 @@ var PdfLexer = class {
4066
4140
  result += String.fromCharCode(c);
4067
4141
  pos++;
4068
4142
  }
4069
- if (depth !== 0) throw new Error(`PdfLexer: unterminated literal string starting at offset ${startPos}`);
4143
+ if (depth !== 0) throw new PdfParseError({
4144
+ message: `PdfLexer: unterminated literal string starting at offset ${startPos}`,
4145
+ offset: startPos,
4146
+ expected: "closing ')' for literal string",
4147
+ actual: "end of input",
4148
+ data: this._data
4149
+ });
4070
4150
  this.position = pos;
4071
4151
  return {
4072
4152
  type: TokenType$1.LiteralString,
@@ -4081,7 +4161,7 @@ var PdfLexer = class {
4081
4161
  * odd, a trailing `0` is assumed (per spec SS 7.3.4.3).
4082
4162
  */
4083
4163
  readHexString(startPos) {
4084
- const d = this.data;
4164
+ const d = this._data;
4085
4165
  let pos = startPos + 1;
4086
4166
  let hex = "";
4087
4167
  while (pos < this.len) {
@@ -4094,7 +4174,13 @@ var PdfLexer = class {
4094
4174
  pos++;
4095
4175
  continue;
4096
4176
  }
4097
- if (hexVal[c] === -1) throw new Error(`PdfLexer: invalid hex digit 0x${c.toString(16).padStart(2, "0")} at offset ${pos} in hex string starting at ${startPos}`);
4177
+ if (hexVal[c] === -1) throw new PdfParseError({
4178
+ message: `PdfLexer: invalid hex digit 0x${c.toString(16).padStart(2, "0")} at offset ${pos} in hex string starting at ${startPos}`,
4179
+ offset: pos,
4180
+ expected: "hex digit (0-9, a-f, A-F)",
4181
+ actual: `0x${c.toString(16).padStart(2, "0")}`,
4182
+ data: this._data
4183
+ });
4098
4184
  hex += String.fromCharCode(c);
4099
4185
  pos++;
4100
4186
  }
@@ -4119,17 +4205,29 @@ var PdfLexer = class {
4119
4205
  * **includes** the leading `/`.
4120
4206
  */
4121
4207
  readName(startPos) {
4122
- const d = this.data;
4208
+ const d = this._data;
4123
4209
  let pos = startPos + 1;
4124
4210
  let name = "/";
4125
4211
  while (pos < this.len) {
4126
4212
  const c = d[pos];
4127
4213
  if (isWhitespace[c] || isDelimiter[c]) break;
4128
4214
  if (c === CH_HASH) {
4129
- if (pos + 2 >= this.len) throw new Error(`PdfLexer: incomplete #XX escape in name at offset ${pos}`);
4215
+ if (pos + 2 >= this.len) throw new PdfParseError({
4216
+ message: `PdfLexer: incomplete #XX escape in name at offset ${pos}`,
4217
+ offset: pos,
4218
+ expected: "two hex digits after # in name",
4219
+ actual: "end of input",
4220
+ data: this._data
4221
+ });
4130
4222
  const hi = hexVal[d[pos + 1]];
4131
4223
  const lo = hexVal[d[pos + 2]];
4132
- if (hi === -1 || lo === -1) throw new Error(`PdfLexer: invalid #XX escape in name at offset ${pos}`);
4224
+ if (hi === -1 || lo === -1) throw new PdfParseError({
4225
+ message: `PdfLexer: invalid #XX escape in name at offset ${pos}`,
4226
+ offset: pos,
4227
+ expected: "valid hex digits after # in name",
4228
+ actual: `#${String.fromCharCode(d[pos + 1])}${String.fromCharCode(d[pos + 2])}`,
4229
+ data: this._data
4230
+ });
4133
4231
  name += String.fromCharCode(hi << 4 | lo);
4134
4232
  pos += 3;
4135
4233
  continue;
@@ -4151,7 +4249,7 @@ var PdfLexer = class {
4151
4249
  * available for callers that want to preserve them.
4152
4250
  */
4153
4251
  readComment(startPos) {
4154
- const d = this.data;
4252
+ const d = this._data;
4155
4253
  let pos = startPos + 1;
4156
4254
  const begin = pos;
4157
4255
  while (pos < this.len) {
@@ -4176,7 +4274,7 @@ var PdfLexer = class {
4176
4274
  * handle them.
4177
4275
  */
4178
4276
  readKeyword(startPos) {
4179
- const d = this.data;
4277
+ const d = this._data;
4180
4278
  let pos = startPos;
4181
4279
  while (pos < this.len) {
4182
4280
  const c = d[pos];
@@ -4255,7 +4353,7 @@ var PdfLexer = class {
4255
4353
  * because it avoids the per-call overhead of the streaming decoder.
4256
4354
  */
4257
4355
  bytesToAscii(from, to) {
4258
- const d = this.data;
4356
+ const d = this._data;
4259
4357
  let s = "";
4260
4358
  for (let i = from; i < to; i++) s += String.fromCharCode(d[i]);
4261
4359
  return s;
@@ -4331,13 +4429,31 @@ var PdfObjectParser = class {
4331
4429
  */
4332
4430
  parseIndirectObject() {
4333
4431
  const objNumToken = this.lexer.nextToken();
4334
- if (objNumToken.type !== TokenType$1.Number || !Number.isInteger(objNumToken.value)) throw new Error(`PdfObjectParser.parseIndirectObject: expected integer object number at offset ${objNumToken.offset}, got ${TokenType$1[objNumToken.type]} (${String(objNumToken.value)})`);
4432
+ if (objNumToken.type !== TokenType$1.Number || !Number.isInteger(objNumToken.value)) throw new PdfParseError({
4433
+ message: `PdfObjectParser.parseIndirectObject: expected integer object number at offset ${objNumToken.offset}, got ${TokenType$1[objNumToken.type]} (${String(objNumToken.value)})`,
4434
+ offset: objNumToken.offset,
4435
+ expected: "integer object number",
4436
+ actual: `${TokenType$1[objNumToken.type]} (${String(objNumToken.value)})`,
4437
+ data: this.lexer.rawData
4438
+ });
4335
4439
  const objNum = objNumToken.value;
4336
4440
  const genNumToken = this.lexer.nextToken();
4337
- if (genNumToken.type !== TokenType$1.Number || !Number.isInteger(genNumToken.value)) throw new Error(`PdfObjectParser.parseIndirectObject: expected integer generation number at offset ${genNumToken.offset}, got ${TokenType$1[genNumToken.type]} (${String(genNumToken.value)})`);
4441
+ if (genNumToken.type !== TokenType$1.Number || !Number.isInteger(genNumToken.value)) throw new PdfParseError({
4442
+ message: `PdfObjectParser.parseIndirectObject: expected integer generation number at offset ${genNumToken.offset}, got ${TokenType$1[genNumToken.type]} (${String(genNumToken.value)})`,
4443
+ offset: genNumToken.offset,
4444
+ expected: "integer generation number",
4445
+ actual: `${TokenType$1[genNumToken.type]} (${String(genNumToken.value)})`,
4446
+ data: this.lexer.rawData
4447
+ });
4338
4448
  const genNum = genNumToken.value;
4339
4449
  const objKw = this.lexer.nextToken();
4340
- if (objKw.type !== TokenType$1.ObjKeyword) throw new Error(`PdfObjectParser.parseIndirectObject: expected 'obj' keyword at offset ${objKw.offset}, got ${TokenType$1[objKw.type]} (${String(objKw.value)})`);
4450
+ if (objKw.type !== TokenType$1.ObjKeyword) throw new PdfParseError({
4451
+ message: `PdfObjectParser.parseIndirectObject: expected 'obj' keyword at offset ${objKw.offset}, got ${TokenType$1[objKw.type]} (${String(objKw.value)})`,
4452
+ offset: objKw.offset,
4453
+ expected: "'obj' keyword",
4454
+ actual: `${TokenType$1[objKw.type]} (${String(objKw.value)})`,
4455
+ data: this.lexer.rawData
4456
+ });
4341
4457
  return this.parseIndirectObjectBody(objNum, genNum);
4342
4458
  }
4343
4459
  /**
@@ -4382,8 +4498,20 @@ var PdfObjectParser = class {
4382
4498
  case TokenType$1.Name: return PdfName.of(token.value);
4383
4499
  case TokenType$1.ArrayStart: return this.parseArray();
4384
4500
  case TokenType$1.DictStart: return this.parseDictOrStream();
4385
- case TokenType$1.EOF: throw new Error(`PdfObjectParser: unexpected end of input at offset ${token.offset}`);
4386
- default: throw new Error(`PdfObjectParser: unexpected token ${TokenType$1[token.type]} (${String(token.value)}) at offset ${token.offset}`);
4501
+ case TokenType$1.EOF: throw new PdfParseError({
4502
+ message: `PdfObjectParser: unexpected end of input at offset ${token.offset}`,
4503
+ offset: token.offset,
4504
+ expected: "a PDF object (number, string, name, array, dict, etc.)",
4505
+ actual: "end of input",
4506
+ data: this.lexer.rawData
4507
+ });
4508
+ default: throw new PdfParseError({
4509
+ message: `PdfObjectParser: unexpected token ${TokenType$1[token.type]} (${String(token.value)}) at offset ${token.offset}`,
4510
+ offset: token.offset,
4511
+ expected: "a PDF object (number, string, name, array, dict, etc.)",
4512
+ actual: `${TokenType$1[token.type]} (${String(token.value)})`,
4513
+ data: this.lexer.rawData
4514
+ });
4387
4515
  }
4388
4516
  }
4389
4517
  /**
@@ -4429,7 +4557,13 @@ var PdfObjectParser = class {
4429
4557
  if (this.lexer.peekToken().type === TokenType$1.StreamKeyword) finalObject = this.readStream(object);
4430
4558
  }
4431
4559
  const endToken = this.lexer.nextToken();
4432
- if (endToken.type !== TokenType$1.EndObjKeyword) throw new Error(`PdfObjectParser: expected 'endobj' for object ${objNum} ${genNum} at offset ${endToken.offset}, got ${TokenType$1[endToken.type]} (${String(endToken.value)})`);
4560
+ if (endToken.type !== TokenType$1.EndObjKeyword) throw new PdfParseError({
4561
+ message: `PdfObjectParser: expected 'endobj' for object ${objNum} ${genNum} at offset ${endToken.offset}, got ${TokenType$1[endToken.type]} (${String(endToken.value)})`,
4562
+ offset: endToken.offset,
4563
+ expected: `'endobj' keyword for object ${objNum} ${genNum}`,
4564
+ actual: `${TokenType$1[endToken.type]} (${String(endToken.value)})`,
4565
+ data: this.lexer.rawData
4566
+ });
4433
4567
  this.registry.registerWithRef(ref, finalObject);
4434
4568
  return {
4435
4569
  ref,
@@ -4449,7 +4583,13 @@ var PdfObjectParser = class {
4449
4583
  this.lexer.nextToken();
4450
4584
  break;
4451
4585
  }
4452
- if (peek.type === TokenType$1.EOF) throw new Error(`PdfObjectParser: unterminated array at offset ${peek.offset}`);
4586
+ if (peek.type === TokenType$1.EOF) throw new PdfParseError({
4587
+ message: `PdfObjectParser: unterminated array at offset ${peek.offset}`,
4588
+ offset: peek.offset,
4589
+ expected: "']' to close array",
4590
+ actual: "end of input",
4591
+ data: this.lexer.rawData
4592
+ });
4453
4593
  items.push(this.parseObject());
4454
4594
  }
4455
4595
  return new PdfArray(items);
@@ -4478,9 +4618,21 @@ var PdfObjectParser = class {
4478
4618
  this.lexer.nextToken();
4479
4619
  break;
4480
4620
  }
4481
- if (peek.type === TokenType$1.EOF) throw new Error(`PdfObjectParser: unterminated dictionary at offset ${peek.offset}`);
4621
+ if (peek.type === TokenType$1.EOF) throw new PdfParseError({
4622
+ message: `PdfObjectParser: unterminated dictionary at offset ${peek.offset}`,
4623
+ offset: peek.offset,
4624
+ expected: "'>>' to close dictionary",
4625
+ actual: "end of input",
4626
+ data: this.lexer.rawData
4627
+ });
4482
4628
  const keyToken = this.lexer.nextToken();
4483
- if (keyToken.type !== TokenType$1.Name) throw new Error(`PdfObjectParser: expected name as dictionary key at offset ${keyToken.offset}, got ${TokenType$1[keyToken.type]} (${String(keyToken.value)})`);
4629
+ if (keyToken.type !== TokenType$1.Name) throw new PdfParseError({
4630
+ message: `PdfObjectParser: expected name as dictionary key at offset ${keyToken.offset}, got ${TokenType$1[keyToken.type]} (${String(keyToken.value)})`,
4631
+ offset: keyToken.offset,
4632
+ expected: "name token as dictionary key",
4633
+ actual: `${TokenType$1[keyToken.type]} (${String(keyToken.value)})`,
4634
+ data: this.lexer.rawData
4635
+ });
4484
4636
  const key = keyToken.value;
4485
4637
  const value = this.parseObject();
4486
4638
  dict.set(key, value);
@@ -4499,13 +4651,25 @@ var PdfObjectParser = class {
4499
4651
  */
4500
4652
  readStream(dict) {
4501
4653
  const streamKw = this.lexer.nextToken();
4502
- if (streamKw.type !== TokenType$1.StreamKeyword) throw new Error(`PdfObjectParser.readStream: expected 'stream' keyword at offset ${streamKw.offset}, got ${TokenType$1[streamKw.type]}`);
4654
+ if (streamKw.type !== TokenType$1.StreamKeyword) throw new PdfParseError({
4655
+ message: `PdfObjectParser.readStream: expected 'stream' keyword at offset ${streamKw.offset}, got ${TokenType$1[streamKw.type]}`,
4656
+ offset: streamKw.offset,
4657
+ expected: "'stream' keyword",
4658
+ actual: `${TokenType$1[streamKw.type]}`,
4659
+ data: this.lexer.rawData
4660
+ });
4503
4661
  this.skipStreamEol();
4504
4662
  const length = this.resolveStreamLength(dict);
4505
4663
  const data = this.lexer.readStreamData(length);
4506
4664
  this.skipEndstreamWhitespace();
4507
4665
  const endKw = this.lexer.nextToken();
4508
- if (endKw.type !== TokenType$1.EndStreamKeyword) throw new Error(`PdfObjectParser.readStream: expected 'endstream' keyword at offset ${endKw.offset}, got ${TokenType$1[endKw.type]} (${String(endKw.value)})`);
4666
+ if (endKw.type !== TokenType$1.EndStreamKeyword) throw new PdfParseError({
4667
+ message: `PdfObjectParser.readStream: expected 'endstream' keyword at offset ${endKw.offset}, got ${TokenType$1[endKw.type]} (${String(endKw.value)})`,
4668
+ offset: endKw.offset,
4669
+ expected: "'endstream' keyword",
4670
+ actual: `${TokenType$1[endKw.type]} (${String(endKw.value)})`,
4671
+ data: this.lexer.rawData
4672
+ });
4509
4673
  return new PdfStream(dict, data);
4510
4674
  }
4511
4675
  /**
@@ -4550,18 +4714,42 @@ var PdfObjectParser = class {
4550
4714
  */
4551
4715
  resolveStreamLength(dict) {
4552
4716
  const lengthObj = dict.get("/Length");
4553
- if (lengthObj === void 0) throw new Error("PdfObjectParser.resolveStreamLength: stream dictionary is missing /Length");
4717
+ if (lengthObj === void 0) throw new PdfParseError({
4718
+ message: "PdfObjectParser.resolveStreamLength: stream dictionary is missing /Length",
4719
+ offset: this.lexer.position,
4720
+ expected: "/Length entry in stream dictionary",
4721
+ actual: "no /Length entry",
4722
+ data: this.lexer.rawData
4723
+ });
4554
4724
  if (lengthObj.kind === "number") return lengthObj.value;
4555
4725
  if (lengthObj.kind === "ref") {
4556
4726
  const ref = lengthObj;
4557
4727
  const resolved = this.registry.resolve(ref);
4558
4728
  if (resolved !== void 0) {
4559
- if (resolved.kind !== "number") throw new Error(`PdfObjectParser.resolveStreamLength: /Length reference ${ref.objectNumber} ${ref.generationNumber} R resolved to ${resolved.kind}, expected number`);
4729
+ if (resolved.kind !== "number") throw new PdfParseError({
4730
+ message: `PdfObjectParser.resolveStreamLength: /Length reference ${ref.objectNumber} ${ref.generationNumber} R resolved to ${resolved.kind}, expected number`,
4731
+ offset: this.lexer.position,
4732
+ expected: "number for /Length reference",
4733
+ actual: `${resolved.kind}`,
4734
+ data: this.lexer.rawData
4735
+ });
4560
4736
  return resolved.value;
4561
4737
  }
4562
- throw new Error(`PdfObjectParser.resolveStreamLength: /Length reference ${ref.objectNumber} ${ref.generationNumber} R is not yet resolved in the registry. Parse the referenced object first.`);
4738
+ throw new PdfParseError({
4739
+ message: `PdfObjectParser.resolveStreamLength: /Length reference ${ref.objectNumber} ${ref.generationNumber} R is not yet resolved in the registry. Parse the referenced object first.`,
4740
+ offset: this.lexer.position,
4741
+ expected: "resolved /Length reference",
4742
+ actual: `unresolved reference ${ref.objectNumber} ${ref.generationNumber} R`,
4743
+ data: this.lexer.rawData
4744
+ });
4563
4745
  }
4564
- throw new Error(`PdfObjectParser.resolveStreamLength: /Length must be a number or indirect reference, got ${lengthObj.kind}`);
4746
+ throw new PdfParseError({
4747
+ message: `PdfObjectParser.resolveStreamLength: /Length must be a number or indirect reference, got ${lengthObj.kind}`,
4748
+ offset: this.lexer.position,
4749
+ expected: "number or indirect reference for /Length",
4750
+ actual: `${lengthObj.kind}`,
4751
+ data: this.lexer.rawData
4752
+ });
4565
4753
  }
4566
4754
  };
4567
4755
 
@@ -4642,10 +4830,10 @@ async function tryLoadLibdeflate() {
4642
4830
  if (libdeflateAttempted) return libdeflateEngine;
4643
4831
  libdeflateAttempted = true;
4644
4832
  try {
4645
- const { LibdeflateWasm: LibdeflateCtor, initDeflateWasm } = await import("./libdeflateWasm-CRFwmrSl.mjs").then((n) => n.r);
4833
+ const { LibdeflateWasm: LibdeflateCtor, initDeflateWasm } = await import("./libdeflateWasm-DlHgU5oy.mjs").then((n) => n.r);
4646
4834
  let customBytes;
4647
4835
  try {
4648
- const { getWasmLoaderConfig } = await import("./loader-CZMj0gBy.mjs");
4836
+ const { getWasmLoaderConfig } = await import("./loader-CQfoGFp9.mjs");
4649
4837
  customBytes = getWasmLoaderConfig().moduleBytes?.["libdeflate"];
4650
4838
  } catch {}
4651
4839
  await initDeflateWasm(customBytes);
@@ -4763,9 +4951,19 @@ function stringToLatin1Bytes(str) {
4763
4951
  */
4764
4952
  function extractTrailer(dict) {
4765
4953
  const sizeVal = numVal$2(dict.get("/Size"));
4766
- if (sizeVal === void 0) throw new Error("Invalid PDF: trailer dictionary missing /Size entry");
4954
+ if (sizeVal === void 0) throw new PdfParseError({
4955
+ message: "Invalid PDF: trailer dictionary missing /Size entry",
4956
+ offset: 0,
4957
+ expected: "/Size entry in trailer dictionary",
4958
+ actual: "no /Size entry"
4959
+ });
4767
4960
  const rootRef = refVal(dict.get("/Root"));
4768
- if (rootRef === void 0) throw new Error("Invalid PDF: trailer dictionary missing /Root entry");
4961
+ if (rootRef === void 0) throw new PdfParseError({
4962
+ message: "Invalid PDF: trailer dictionary missing /Root entry",
4963
+ offset: 0,
4964
+ expected: "/Root entry in trailer dictionary",
4965
+ actual: "no /Root entry"
4966
+ });
4769
4967
  const trailer = {
4770
4968
  size: sizeVal,
4771
4969
  rootRef
@@ -4804,7 +5002,12 @@ async function decodeStream$1(streamData, dict) {
4804
5002
  const arr = filter;
4805
5003
  if (arr.length > 0 && arr.items[0].kind === "name") filterName = arr.items[0].value;
4806
5004
  }
4807
- if (filterName !== "/FlateDecode") throw new Error(`Unsupported xref stream filter: ${filterName ?? "unknown"}. Only /FlateDecode is supported for cross-reference streams.`);
5005
+ if (filterName !== "/FlateDecode") throw new PdfParseError({
5006
+ message: `Unsupported xref stream filter: ${filterName ?? "unknown"}. Only /FlateDecode is supported for cross-reference streams.`,
5007
+ offset: 0,
5008
+ expected: "/FlateDecode filter",
5009
+ actual: `${filterName ?? "unknown"} filter`
5010
+ });
4808
5011
  let decompressed = await decompress(streamData);
4809
5012
  const decodeParms = dict.get("/DecodeParms");
4810
5013
  if (decodeParms !== void 0 && decodeParms.kind === "dict") {
@@ -4938,11 +5141,30 @@ var XrefParser = class {
4938
5141
  const startPos = this.data.length - searchWindow;
4939
5142
  const tail = TEXT_DECODER$1.decode(this.data.subarray(startPos, this.data.length));
4940
5143
  const idx = tail.lastIndexOf("startxref");
4941
- if (idx === -1) throw new Error("Invalid PDF: could not find \"startxref\" marker in the last 2048 bytes. The file may be truncated or corrupt.");
4942
- const match = tail.substring(idx + 9).trim().match(/^(\d+)/);
4943
- if (!match) throw new Error("Invalid PDF: \"startxref\" found but no valid offset follows it.");
5144
+ if (idx === -1) throw new PdfParseError({
5145
+ message: "Invalid PDF: could not find \"startxref\" marker in the last 2048 bytes. The file may be truncated or corrupt.",
5146
+ offset: startPos,
5147
+ expected: "\"startxref\" marker near end of file",
5148
+ actual: "no \"startxref\" found",
5149
+ data: this.data
5150
+ });
5151
+ const afterKeyword = tail.substring(idx + 9).trim();
5152
+ const match = afterKeyword.match(/^(\d+)/);
5153
+ if (!match) throw new PdfParseError({
5154
+ message: "Invalid PDF: \"startxref\" found but no valid offset follows it.",
5155
+ offset: startPos + idx,
5156
+ expected: "decimal offset after \"startxref\"",
5157
+ actual: `"${afterKeyword.substring(0, 20)}"`,
5158
+ data: this.data
5159
+ });
4944
5160
  const offset = parseInt(match[1], 10);
4945
- if (offset < 0 || offset >= this.data.length) throw new Error(`Invalid PDF: startxref offset ${offset} is out of range (file size: ${this.data.length}).`);
5161
+ if (offset < 0 || offset >= this.data.length) throw new PdfParseError({
5162
+ message: `Invalid PDF: startxref offset ${offset} is out of range (file size: ${this.data.length}).`,
5163
+ offset: startPos + idx,
5164
+ expected: `offset in range [0, ${this.data.length})`,
5165
+ actual: `${offset}`,
5166
+ data: this.data
5167
+ });
4946
5168
  return offset;
4947
5169
  }
4948
5170
  /**
@@ -4995,7 +5217,13 @@ var XrefParser = class {
4995
5217
  const prevVal = numVal$2(trailerDict.get("/Prev"));
4996
5218
  currentOffset = prevVal !== void 0 && prevVal >= 0 ? prevVal : void 0;
4997
5219
  }
4998
- if (primaryTrailer === void 0) throw new Error("Invalid PDF: could not extract a valid trailer from the cross-reference structure.");
5220
+ if (primaryTrailer === void 0) throw new PdfParseError({
5221
+ message: "Invalid PDF: could not extract a valid trailer from the cross-reference structure.",
5222
+ offset: startOffset,
5223
+ expected: "valid trailer dictionary in cross-reference structure",
5224
+ actual: "no valid trailer found",
5225
+ data: this.data
5226
+ });
4999
5227
  return {
5000
5228
  entries,
5001
5229
  trailer: primaryTrailer
@@ -5023,14 +5251,26 @@ var XrefParser = class {
5023
5251
  const entries = [];
5024
5252
  let pos = offset;
5025
5253
  const xrefTag = TEXT_DECODER$1.decode(this.data.subarray(pos, pos + 4));
5026
- if (xrefTag !== "xref") throw new Error(`Invalid PDF: expected "xref" keyword at offset ${offset}, found "${xrefTag}".`);
5254
+ if (xrefTag !== "xref") throw new PdfParseError({
5255
+ message: `Invalid PDF: expected "xref" keyword at offset ${offset}, found "${xrefTag}".`,
5256
+ offset,
5257
+ expected: "\"xref\" keyword",
5258
+ actual: `"${xrefTag}"`,
5259
+ data: this.data
5260
+ });
5027
5261
  pos += 4;
5028
5262
  pos = this.skipWhitespaceAt(pos);
5029
5263
  while (pos < this.data.length) {
5030
5264
  if (TEXT_DECODER$1.decode(this.data.subarray(pos, Math.min(pos + 7, this.data.length))).startsWith("trailer")) break;
5031
5265
  const headerLine = this.readLineAt(pos);
5032
5266
  const headerMatch = headerLine.text.trim().match(/^(\d+)\s+(\d+)/);
5033
- if (!headerMatch) throw new Error(`Invalid PDF: malformed xref subsection header at offset ${pos}: "${headerLine.text.trim()}"`);
5267
+ if (!headerMatch) throw new PdfParseError({
5268
+ message: `Invalid PDF: malformed xref subsection header at offset ${pos}: "${headerLine.text.trim()}"`,
5269
+ offset: pos,
5270
+ expected: "xref subsection header \"firstObjNum count\"",
5271
+ actual: `"${headerLine.text.trim()}"`,
5272
+ data: this.data
5273
+ });
5034
5274
  const firstObjNum = parseInt(headerMatch[1], 10);
5035
5275
  const count = parseInt(headerMatch[2], 10);
5036
5276
  pos = headerLine.nextPos;
@@ -5038,7 +5278,13 @@ var XrefParser = class {
5038
5278
  const objectNumber = firstObjNum + i;
5039
5279
  const entryText = this.readXrefEntryAt(pos);
5040
5280
  const entryMatch = entryText.text.trim().match(/^(\d{10})\s+(\d{5})\s+([fn])/);
5041
- if (!entryMatch) throw new Error(`Invalid PDF: malformed xref entry at offset ${pos} for object ${objectNumber}: "${entryText.text.trim()}"`);
5281
+ if (!entryMatch) throw new PdfParseError({
5282
+ message: `Invalid PDF: malformed xref entry at offset ${pos} for object ${objectNumber}: "${entryText.text.trim()}"`,
5283
+ offset: pos,
5284
+ expected: "xref entry \"OOOOOOOOOO GGGGG f/n\"",
5285
+ actual: `"${entryText.text.trim()}"`,
5286
+ data: this.data
5287
+ });
5042
5288
  const entryOffset = parseInt(entryMatch[1], 10);
5043
5289
  const gen = parseInt(entryMatch[2], 10);
5044
5290
  const marker = entryMatch[3];
@@ -5052,10 +5298,22 @@ var XrefParser = class {
5052
5298
  }
5053
5299
  }
5054
5300
  const trailerTag = TEXT_DECODER$1.decode(this.data.subarray(pos, pos + 7));
5055
- if (trailerTag !== "trailer") throw new Error(`Invalid PDF: expected "trailer" keyword at offset ${pos}, found "${trailerTag}".`);
5301
+ if (trailerTag !== "trailer") throw new PdfParseError({
5302
+ message: `Invalid PDF: expected "trailer" keyword at offset ${pos}, found "${trailerTag}".`,
5303
+ offset: pos,
5304
+ expected: "\"trailer\" keyword",
5305
+ actual: `"${trailerTag}"`,
5306
+ data: this.data
5307
+ });
5056
5308
  pos += 7;
5057
5309
  const trailerObj = this.objectParser.parseObjectAt(pos);
5058
- if (trailerObj.kind !== "dict") throw new Error(`Invalid PDF: expected dictionary after "trailer" keyword at offset ${pos}, got ${trailerObj.kind}.`);
5310
+ if (trailerObj.kind !== "dict") throw new PdfParseError({
5311
+ message: `Invalid PDF: expected dictionary after "trailer" keyword at offset ${pos}, got ${trailerObj.kind}.`,
5312
+ offset: pos,
5313
+ expected: "dictionary after \"trailer\" keyword",
5314
+ actual: `${trailerObj.kind}`,
5315
+ data: this.data
5316
+ });
5059
5317
  return {
5060
5318
  entries,
5061
5319
  trailerDict: trailerObj
@@ -5074,22 +5332,58 @@ var XrefParser = class {
5074
5332
  */
5075
5333
  async parseXrefStream(offset) {
5076
5334
  const { object } = this.objectParser.parseIndirectObjectAt(offset);
5077
- if (object.kind !== "stream") throw new Error(`Invalid PDF: expected stream object at offset ${offset} for xref stream, got ${object.kind}.`);
5335
+ if (object.kind !== "stream") throw new PdfParseError({
5336
+ message: `Invalid PDF: expected stream object at offset ${offset} for xref stream, got ${object.kind}.`,
5337
+ offset,
5338
+ expected: "stream object for xref stream",
5339
+ actual: `${object.kind}`,
5340
+ data: this.data
5341
+ });
5078
5342
  const stream = object;
5079
5343
  const dict = stream.dict;
5080
5344
  const typeObj = dict.get("/Type");
5081
- if (typeObj === void 0 || typeObj.kind !== "name" || typeObj.value !== "/XRef") throw new Error(`Invalid PDF: cross-reference stream at offset ${offset} does not have /Type /XRef.`);
5345
+ if (typeObj === void 0 || typeObj.kind !== "name" || typeObj.value !== "/XRef") throw new PdfParseError({
5346
+ message: `Invalid PDF: cross-reference stream at offset ${offset} does not have /Type /XRef.`,
5347
+ offset,
5348
+ expected: "/Type /XRef in cross-reference stream dictionary",
5349
+ actual: typeObj ? `${typeObj.kind}` : "no /Type entry",
5350
+ data: this.data
5351
+ });
5082
5352
  const wObj = dict.get("/W");
5083
- if (wObj === void 0 || wObj.kind !== "array") throw new Error("Invalid PDF: cross-reference stream missing /W (field widths) array.");
5353
+ if (wObj === void 0 || wObj.kind !== "array") throw new PdfParseError({
5354
+ message: "Invalid PDF: cross-reference stream missing /W (field widths) array.",
5355
+ offset,
5356
+ expected: "/W array in cross-reference stream",
5357
+ actual: wObj ? `${wObj.kind}` : "no /W entry",
5358
+ data: this.data
5359
+ });
5084
5360
  const wArr = wObj;
5085
- if (wArr.length < 3) throw new Error("Invalid PDF: cross-reference stream /W array must have at least 3 elements.");
5361
+ if (wArr.length < 3) throw new PdfParseError({
5362
+ message: "Invalid PDF: cross-reference stream /W array must have at least 3 elements.",
5363
+ offset,
5364
+ expected: "/W array with at least 3 elements",
5365
+ actual: `/W array with ${wArr.length} element(s)`,
5366
+ data: this.data
5367
+ });
5086
5368
  const w0 = numVal$2(wArr.items[0]) ?? 0;
5087
5369
  const w1 = numVal$2(wArr.items[1]) ?? 0;
5088
5370
  const w2 = numVal$2(wArr.items[2]) ?? 0;
5089
5371
  const entryWidth = w0 + w1 + w2;
5090
- if (entryWidth === 0) throw new Error("Invalid PDF: cross-reference stream /W widths sum to 0.");
5372
+ if (entryWidth === 0) throw new PdfParseError({
5373
+ message: "Invalid PDF: cross-reference stream /W widths sum to 0.",
5374
+ offset,
5375
+ expected: "non-zero sum of /W widths",
5376
+ actual: "0",
5377
+ data: this.data
5378
+ });
5091
5379
  const size = numVal$2(dict.get("/Size"));
5092
- if (size === void 0) throw new Error("Invalid PDF: cross-reference stream missing /Size.");
5380
+ if (size === void 0) throw new PdfParseError({
5381
+ message: "Invalid PDF: cross-reference stream missing /Size.",
5382
+ offset,
5383
+ expected: "/Size entry in cross-reference stream",
5384
+ actual: "no /Size entry",
5385
+ data: this.data
5386
+ });
5093
5387
  let subsections;
5094
5388
  const indexObj = dict.get("/Index");
5095
5389
  if (indexObj !== void 0 && indexObj.kind === "array") {
@@ -5179,7 +5473,13 @@ var XrefParser = class {
5179
5473
  type: "in-use"
5180
5474
  });
5181
5475
  }
5182
- if (entries.size === 0) throw new Error("Invalid PDF: could not find any indirect objects during recovery scan. The file may not be a valid PDF.");
5476
+ if (entries.size === 0) throw new PdfParseError({
5477
+ message: "Invalid PDF: could not find any indirect objects during recovery scan. The file may not be a valid PDF.",
5478
+ offset: 0,
5479
+ expected: "at least one \"N G obj\" pattern in file",
5480
+ actual: "no indirect objects found",
5481
+ data: this.data
5482
+ });
5183
5483
  let rootRef;
5184
5484
  let infoRef;
5185
5485
  const trailerIdx = fileText.lastIndexOf("trailer");
@@ -5202,7 +5502,13 @@ var XrefParser = class {
5202
5502
  }
5203
5503
  }
5204
5504
  } catch {}
5205
- if (rootRef === void 0) throw new Error("Invalid PDF: recovery scan could not locate the document catalog (/Root). The file appears to be severely corrupt.");
5505
+ if (rootRef === void 0) throw new PdfParseError({
5506
+ message: "Invalid PDF: recovery scan could not locate the document catalog (/Root). The file appears to be severely corrupt.",
5507
+ offset: 0,
5508
+ expected: "document catalog (/Type /Catalog) in recovery scan",
5509
+ actual: "no /Root or /Catalog found",
5510
+ data: this.data
5511
+ });
5206
5512
  const trailer = {
5207
5513
  size: (entries.size > 0 ? entries.keys().reduce((max, n) => Math.max(max, n), 0) : 0) + 1,
5208
5514
  rootRef
@@ -6915,7 +7221,13 @@ var PdfDocumentParser = class {
6915
7221
  * @param data The raw PDF file bytes as a Uint8Array.
6916
7222
  */
6917
7223
  constructor(data) {
6918
- if (data.length < 8) throw new Error("Invalid PDF: file is too short to contain a valid PDF header.");
7224
+ if (data.length < 8) throw new PdfParseError({
7225
+ message: "Invalid PDF: file is too short to contain a valid PDF header.",
7226
+ offset: 0,
7227
+ expected: "at least 8 bytes for a valid PDF header",
7228
+ actual: `${data.length} bytes`,
7229
+ data
7230
+ });
6919
7231
  this.data = data;
6920
7232
  }
6921
7233
  /**
@@ -6966,7 +7278,14 @@ var PdfDocumentParser = class {
6966
7278
  const { object } = this.objectParser.parseIndirectObjectAt(entry.offset);
6967
7279
  resolved = object;
6968
7280
  } catch (err) {
6969
- throw new Error(`Failed to parse indirect object ${objNum} ${entry.generationNumber} at offset ${entry.offset}`, { cause: err });
7281
+ throw new PdfParseError({
7282
+ message: `Failed to parse indirect object ${objNum} ${entry.generationNumber} at offset ${entry.offset}`,
7283
+ offset: entry.offset,
7284
+ expected: `valid indirect object ${objNum} ${entry.generationNumber}`,
7285
+ actual: "parse failure",
7286
+ data: this.data,
7287
+ cause: err instanceof Error ? err : void 0
7288
+ });
6970
7289
  }
6971
7290
  this.objectCache.set(objNum, resolved);
6972
7291
  return resolved;
@@ -6990,7 +7309,13 @@ var PdfDocumentParser = class {
6990
7309
  const encryptRef = this.trailer.encryptRef;
6991
7310
  this.encryptDictObjNum = encryptRef.objectNumber;
6992
7311
  const encryptObj = this.resolveRef(encryptRef);
6993
- if (encryptObj.kind !== "dict") throw new Error("Invalid PDF: /Encrypt entry is not a dictionary.");
7312
+ if (encryptObj.kind !== "dict") throw new PdfParseError({
7313
+ message: "Invalid PDF: /Encrypt entry is not a dictionary.",
7314
+ offset: 0,
7315
+ expected: "dictionary for /Encrypt entry",
7316
+ actual: `${encryptObj.kind}`,
7317
+ data: this.data
7318
+ });
6994
7319
  const encryptDict = encryptObj;
6995
7320
  const fileId = this.trailer.id?.[0] ?? new Uint8Array(0);
6996
7321
  const passwordsToTry = [];
@@ -7005,8 +7330,21 @@ var PdfDocumentParser = class {
7005
7330
  } catch (err) {
7006
7331
  lastError = err instanceof Error ? err : new Error(String(err));
7007
7332
  }
7008
- if (password !== void 0) throw new Error(`Failed to decrypt PDF: the provided password is incorrect. ${lastError?.message ?? ""}`);
7009
- else throw new Error("This PDF is encrypted and requires a password. Pass { password: \"...\" } in the options to decrypt it.");
7333
+ if (password !== void 0) throw new PdfParseError({
7334
+ message: `Failed to decrypt PDF: the provided password is incorrect. ${lastError?.message ?? ""}`,
7335
+ offset: 0,
7336
+ expected: "valid decryption password",
7337
+ actual: "incorrect password",
7338
+ data: this.data,
7339
+ cause: lastError
7340
+ });
7341
+ else throw new PdfParseError({
7342
+ message: "This PDF is encrypted and requires a password. Pass { password: \"...\" } in the options to decrypt it.",
7343
+ offset: 0,
7344
+ expected: "unencrypted PDF or password option",
7345
+ actual: "encrypted PDF without password",
7346
+ data: this.data
7347
+ });
7010
7348
  }
7011
7349
  /**
7012
7350
  * Decrypt all resolved objects in the object cache.
@@ -7102,7 +7440,12 @@ var PdfDocumentParser = class {
7102
7440
  * @throws If the page index is out of range.
7103
7441
  */
7104
7442
  getPageDict(pageIndex) {
7105
- if (pageIndex < 0 || pageIndex >= this.flattenedPages.length) throw new Error(`Page index ${pageIndex} is out of range. The document has ${this.flattenedPages.length} page(s).`);
7443
+ if (pageIndex < 0 || pageIndex >= this.flattenedPages.length) throw new PdfParseError({
7444
+ message: `Page index ${pageIndex} is out of range. The document has ${this.flattenedPages.length} page(s).`,
7445
+ offset: 0,
7446
+ expected: `page index in range [0, ${this.flattenedPages.length})`,
7447
+ actual: `${pageIndex}`
7448
+ });
7106
7449
  return this.flattenedPages[pageIndex].dict;
7107
7450
  }
7108
7451
  /**
@@ -7122,16 +7465,34 @@ var PdfDocumentParser = class {
7122
7465
  validateHeader() {
7123
7466
  const header = TEXT_DECODER.decode(this.data.subarray(0, Math.min(1024, this.data.length)));
7124
7467
  const pdfIdx = header.indexOf("%PDF-");
7125
- if (pdfIdx === -1 || pdfIdx > 1024) throw new Error("Invalid PDF: file does not start with \"%PDF-\" header. This may not be a PDF file.");
7468
+ if (pdfIdx === -1 || pdfIdx > 1024) throw new PdfParseError({
7469
+ message: "Invalid PDF: file does not start with \"%PDF-\" header. This may not be a PDF file.",
7470
+ offset: 0,
7471
+ expected: "\"%PDF-\" header near start of file",
7472
+ actual: "no \"%PDF-\" marker found",
7473
+ data: this.data
7474
+ });
7126
7475
  const versionMatch = header.substring(pdfIdx).match(/%PDF-(\d+\.\d+)/);
7127
- if (!versionMatch) throw new Error("Invalid PDF: could not parse version from header.");
7476
+ if (!versionMatch) throw new PdfParseError({
7477
+ message: "Invalid PDF: could not parse version from header.",
7478
+ offset: pdfIdx,
7479
+ expected: "\"%PDF-X.Y\" version string",
7480
+ actual: `"${header.substring(pdfIdx, pdfIdx + 10)}"`,
7481
+ data: this.data
7482
+ });
7128
7483
  this.pdfVersion = versionMatch[1];
7129
7484
  const majorMinor = this.pdfVersion.split(".");
7130
7485
  const major = parseInt(majorMinor[0], 10);
7131
7486
  const minor = parseInt(majorMinor[1], 10);
7132
7487
  if (major === 1 && minor >= 0 && minor <= 9) return;
7133
7488
  if (major === 2 && minor >= 0) return;
7134
- throw new Error(`Unsupported PDF version: ${this.pdfVersion}. Expected PDF 1.0-1.9 or 2.0+.`);
7489
+ throw new PdfParseError({
7490
+ message: `Unsupported PDF version: ${this.pdfVersion}. Expected PDF 1.0-1.9 or 2.0+.`,
7491
+ offset: 0,
7492
+ expected: "PDF version 1.0-1.9 or 2.0+",
7493
+ actual: `PDF version ${this.pdfVersion}`,
7494
+ data: this.data
7495
+ });
7135
7496
  }
7136
7497
  /**
7137
7498
  * Resolve the document catalog dictionary from the trailer's /Root
@@ -7139,12 +7500,24 @@ var PdfDocumentParser = class {
7139
7500
  */
7140
7501
  resolveCatalog() {
7141
7502
  const rootObj = this.resolveRef(this.trailer.rootRef);
7142
- if (rootObj.kind !== "dict") throw new Error(`Invalid PDF: /Root (catalog) at object ${this.trailer.rootRef.objectNumber} is not a dictionary (got ${rootObj.kind}).`);
7503
+ if (rootObj.kind !== "dict") throw new PdfParseError({
7504
+ message: `Invalid PDF: /Root (catalog) at object ${this.trailer.rootRef.objectNumber} is not a dictionary (got ${rootObj.kind}).`,
7505
+ offset: 0,
7506
+ expected: "dictionary for /Root (catalog)",
7507
+ actual: `${rootObj.kind}`,
7508
+ data: this.data
7509
+ });
7143
7510
  const catalog = rootObj;
7144
7511
  const typeObj = catalog.get("/Type");
7145
7512
  if (typeObj !== void 0 && typeObj.kind === "name") {
7146
7513
  const typeName = typeObj.value;
7147
- if (typeName !== "/Catalog") throw new Error(`Invalid PDF: /Root dictionary has /Type "${typeName}", expected "/Catalog".`);
7514
+ if (typeName !== "/Catalog") throw new PdfParseError({
7515
+ message: `Invalid PDF: /Root dictionary has /Type "${typeName}", expected "/Catalog".`,
7516
+ offset: 0,
7517
+ expected: "/Type /Catalog in /Root dictionary",
7518
+ actual: `/Type "${typeName}"`,
7519
+ data: this.data
7520
+ });
7148
7521
  }
7149
7522
  return catalog;
7150
7523
  }
@@ -7158,9 +7531,21 @@ var PdfDocumentParser = class {
7158
7531
  */
7159
7532
  resolvePageTree(catalog) {
7160
7533
  const pagesRef = catalog.get("/Pages");
7161
- if (pagesRef === void 0) throw new Error("Invalid PDF: catalog dictionary missing /Pages entry.");
7534
+ if (pagesRef === void 0) throw new PdfParseError({
7535
+ message: "Invalid PDF: catalog dictionary missing /Pages entry.",
7536
+ offset: 0,
7537
+ expected: "/Pages entry in catalog dictionary",
7538
+ actual: "no /Pages entry",
7539
+ data: this.data
7540
+ });
7162
7541
  const pagesObj = this.resolveObject(pagesRef);
7163
- if (pagesObj.kind !== "dict") throw new Error(`Invalid PDF: /Pages entry is not a dictionary (got ${pagesObj.kind}).`);
7542
+ if (pagesObj.kind !== "dict") throw new PdfParseError({
7543
+ message: `Invalid PDF: /Pages entry is not a dictionary (got ${pagesObj.kind}).`,
7544
+ offset: 0,
7545
+ expected: "dictionary for /Pages entry",
7546
+ actual: `${pagesObj.kind}`,
7547
+ data: this.data
7548
+ });
7164
7549
  const pages = [];
7165
7550
  this.traversePageTree(pagesObj, {}, pages, /* @__PURE__ */ new Set());
7166
7551
  return pages;
@@ -7197,7 +7582,13 @@ var PdfDocumentParser = class {
7197
7582
  }
7198
7583
  if (typeName === "/Page") {
7199
7584
  const mediaBox = currentInherited.mediaBox;
7200
- if (mediaBox === void 0) throw new Error("Invalid PDF: /Page node has no /MediaBox (not even inherited).");
7585
+ if (mediaBox === void 0) throw new PdfParseError({
7586
+ message: "Invalid PDF: /Page node has no /MediaBox (not even inherited).",
7587
+ offset: 0,
7588
+ expected: "/MediaBox on page node or inherited from parent",
7589
+ actual: "no /MediaBox found",
7590
+ data: this.data
7591
+ });
7201
7592
  result.push({
7202
7593
  dict: node,
7203
7594
  mediaBox,
@@ -7209,7 +7600,13 @@ var PdfDocumentParser = class {
7209
7600
  const kidsObj = node.get("/Kids");
7210
7601
  if (kidsObj === void 0) return;
7211
7602
  const kids = this.resolveObject(kidsObj);
7212
- if (kids.kind !== "array") throw new Error(`Invalid PDF: /Pages /Kids is not an array (got ${kids.kind}).`);
7603
+ if (kids.kind !== "array") throw new PdfParseError({
7604
+ message: `Invalid PDF: /Pages /Kids is not an array (got ${kids.kind}).`,
7605
+ offset: 0,
7606
+ expected: "array for /Pages /Kids",
7607
+ actual: `${kids.kind}`,
7608
+ data: this.data
7609
+ });
7213
7610
  const kidsArr = kids;
7214
7611
  for (let i = 0; i < kidsArr.length; i++) {
7215
7612
  const kidRef = kidsArr.items[i];
@@ -7245,22 +7642,53 @@ var PdfDocumentParser = class {
7245
7642
  return PdfNull.instance;
7246
7643
  }
7247
7644
  const containerEntry = this.xrefEntries.get(containerNum);
7248
- if (containerEntry === void 0 || containerEntry.type !== "in-use") throw new Error(`Invalid PDF: object stream ${containerNum} referenced by compressed object ${entry.objectNumber} not found in xref table.`);
7645
+ if (containerEntry === void 0 || containerEntry.type !== "in-use") throw new PdfParseError({
7646
+ message: `Invalid PDF: object stream ${containerNum} referenced by compressed object ${entry.objectNumber} not found in xref table.`,
7647
+ offset: 0,
7648
+ expected: `in-use xref entry for object stream ${containerNum}`,
7649
+ actual: containerEntry ? `${containerEntry.type} entry` : "no entry",
7650
+ data: this.data
7651
+ });
7249
7652
  let containerObj;
7250
7653
  try {
7251
7654
  const { object } = this.objectParser.parseIndirectObjectAt(containerEntry.offset);
7252
7655
  containerObj = object;
7253
7656
  } catch (err) {
7254
- throw new Error(`Failed to parse object stream ${containerNum} at offset ${containerEntry.offset}`, { cause: err });
7657
+ throw new PdfParseError({
7658
+ message: `Failed to parse object stream ${containerNum} at offset ${containerEntry.offset}`,
7659
+ offset: containerEntry.offset,
7660
+ expected: `valid object stream ${containerNum}`,
7661
+ actual: "parse failure",
7662
+ data: this.data,
7663
+ cause: err instanceof Error ? err : void 0
7664
+ });
7255
7665
  }
7256
- if (containerObj.kind !== "stream") throw new Error(`Invalid PDF: object ${containerNum} expected to be an object stream but is ${containerObj.kind}.`);
7666
+ if (containerObj.kind !== "stream") throw new PdfParseError({
7667
+ message: `Invalid PDF: object ${containerNum} expected to be an object stream but is ${containerObj.kind}.`,
7668
+ offset: containerEntry.offset,
7669
+ expected: "stream object for object stream",
7670
+ actual: `${containerObj.kind}`,
7671
+ data: this.data
7672
+ });
7257
7673
  const containerStream = containerObj;
7258
7674
  const containerDict = containerStream.dict;
7259
7675
  const typeObj = containerDict.get("/Type");
7260
- if (typeObj === void 0 || typeObj.kind !== "name" || typeObj.value !== "/ObjStm") throw new Error(`Invalid PDF: object ${containerNum} is not an object stream (/Type /ObjStm).`);
7676
+ if (typeObj === void 0 || typeObj.kind !== "name" || typeObj.value !== "/ObjStm") throw new PdfParseError({
7677
+ message: `Invalid PDF: object ${containerNum} is not an object stream (/Type /ObjStm).`,
7678
+ offset: containerEntry.offset,
7679
+ expected: "/Type /ObjStm in object stream dictionary",
7680
+ actual: typeObj ? `${typeObj.kind}` : "no /Type entry",
7681
+ data: this.data
7682
+ });
7261
7683
  const n = numVal$1(containerDict.get("/N"));
7262
7684
  const first = numVal$1(containerDict.get("/First"));
7263
- if (n === void 0 || first === void 0) throw new Error(`Invalid PDF: object stream ${containerNum} missing /N or /First entries.`);
7685
+ if (n === void 0 || first === void 0) throw new PdfParseError({
7686
+ message: `Invalid PDF: object stream ${containerNum} missing /N or /First entries.`,
7687
+ offset: containerEntry.offset,
7688
+ expected: "/N and /First entries in object stream",
7689
+ actual: `${n === void 0 ? "no /N" : "/N present"}, ${first === void 0 ? "no /First" : "/First present"}`,
7690
+ data: this.data
7691
+ });
7264
7692
  const decompressedData = this.decompressStreamSync(containerStream);
7265
7693
  const headerTokens = TEXT_DECODER.decode(decompressedData.subarray(0, first)).trim().split(/\s+/);
7266
7694
  const objEntries = [];
@@ -7657,7 +8085,7 @@ function undoPngPredictorSync(data, columns) {
7657
8085
  * string.
7658
8086
  *
7659
8087
  * This is the primary entry point for parsing existing PDFs. It creates
7660
- * a {@link PdfDocumentParser}, runs the full parse pipeline, and returns
8088
+ * a `PdfDocumentParser`, runs the full parse pipeline, and returns
7661
8089
  * a populated {@link PdfDocument}.
7662
8090
  *
7663
8091
  * @param data The PDF data as a `Uint8Array`, `ArrayBuffer`, or a
@@ -14211,6 +14639,9 @@ function packBits(pixels, columns, blackIs1) {
14211
14639
  /**
14212
14640
  * Decode JBIG2Decode stream data.
14213
14641
  *
14642
+ * Uses the pure-JS decoder (synchronous). For optional WASM
14643
+ * acceleration, use {@link decodeJBIG2Async}.
14644
+ *
14214
14645
  * @param data - The JBIG2-encoded content stream bytes.
14215
14646
  * @param parms - Optional `/DecodeParms` dictionary. May contain a
14216
14647
  * `/JBIG2Globals` key with a PdfDict/stream holding
@@ -17535,6 +17966,93 @@ function embedPageAsFormXObject(page, sourceRegistry, targetRegistry, xObjectNam
17535
17966
  };
17536
17967
  }
17537
17968
 
17969
+ //#endregion
17970
+ //#region src/core/pdfDocumentEmbed.ts
17971
+ /**
17972
+ * @module core/pdfDocumentEmbed
17973
+ *
17974
+ * Pure helper functions for font CMap generation and image format
17975
+ * validation/parsing. Extracted from {@link pdfDocument} to keep the
17976
+ * PdfDocument class focused on high-level document management.
17977
+ */
17978
+ /**
17979
+ * Build a /ToUnicode CMap from a font's cmap table.
17980
+ * Maps glyph IDs (used as CIDs with Identity-H) to Unicode codepoints.
17981
+ *
17982
+ * @param cmapTable Mapping from Unicode codepoint to glyph ID.
17983
+ * @returns A CMap string suitable for a PDF /ToUnicode stream.
17984
+ */
17985
+ function buildToUnicodeCmap(cmapTable) {
17986
+ const gidToUnicode = /* @__PURE__ */ new Map();
17987
+ for (const [codepoint, gid] of cmapTable) if (!gidToUnicode.has(gid)) gidToUnicode.set(gid, codepoint);
17988
+ const entries = gidToUnicode.entries().toArray().sort((a, b) => a[0] - b[0]);
17989
+ const lines = [];
17990
+ lines.push("/CIDInit /ProcSet findresource begin");
17991
+ lines.push("12 dict begin");
17992
+ lines.push("begincmap");
17993
+ lines.push("/CIDSystemInfo");
17994
+ lines.push("<< /Registry (Adobe)");
17995
+ lines.push("/Ordering (UCS)");
17996
+ lines.push("/Supplement 0");
17997
+ lines.push(">> def");
17998
+ lines.push("/CMapName /Adobe-Identity-UCS def");
17999
+ lines.push("/CMapType 2 def");
18000
+ lines.push("1 begincodespacerange");
18001
+ lines.push("<0000> <FFFF>");
18002
+ lines.push("endcodespacerange");
18003
+ const CHUNK_SIZE = 100;
18004
+ for (let i = 0; i < entries.length; i += CHUNK_SIZE) {
18005
+ const chunk = entries.slice(i, i + CHUNK_SIZE);
18006
+ lines.push(`${chunk.length} beginbfchar`);
18007
+ for (const [gid, codepoint] of chunk) {
18008
+ const gidHex = gid.toString(16).padStart(4, "0").toUpperCase();
18009
+ const uniHex = codepoint.toString(16).padStart(4, "0").toUpperCase();
18010
+ lines.push(`<${gidHex}> <${uniHex}>`);
18011
+ }
18012
+ lines.push("endbfchar");
18013
+ }
18014
+ lines.push("endcmap");
18015
+ lines.push("CMapName currentdict /CMap defineresource pop");
18016
+ lines.push("end");
18017
+ lines.push("end");
18018
+ return lines.join("\n");
18019
+ }
18020
+ /**
18021
+ * Validate a JPEG file signature (SOI marker).
18022
+ *
18023
+ * @param data The raw file bytes.
18024
+ * @throws If the data is too short or the SOI marker is invalid.
18025
+ */
18026
+ function validateJpegSignature(data) {
18027
+ if (data.length < 2 || data[0] !== 255 || data[1] !== 216) throw new Error("Invalid JPEG: bad SOI marker");
18028
+ }
18029
+ /**
18030
+ * Parse width, height, and component count from a JPEG's SOF marker.
18031
+ *
18032
+ * @param data The raw JPEG file bytes.
18033
+ * @returns Width, height, and number of color components.
18034
+ */
18035
+ function parseJpegDimensions(data) {
18036
+ validateJpegSignature(data);
18037
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
18038
+ let offset = 2;
18039
+ while (offset < data.length - 1) {
18040
+ if (data[offset] !== 255) throw new Error("Invalid JPEG: expected marker");
18041
+ const marker = data[offset + 1];
18042
+ if (marker >= 192 && marker <= 195 || marker >= 197 && marker <= 199 || marker >= 201 && marker <= 203 || marker >= 205 && marker <= 207) {
18043
+ const height = view.getUint16(offset + 5, false);
18044
+ return {
18045
+ width: view.getUint16(offset + 7, false),
18046
+ height,
18047
+ components: data[offset + 9]
18048
+ };
18049
+ }
18050
+ const segmentLength = view.getUint16(offset + 2, false);
18051
+ offset += 2 + segmentLength;
18052
+ }
18053
+ throw new Error("Invalid JPEG: SOF marker not found");
18054
+ }
18055
+
17538
18056
  //#endregion
17539
18057
  //#region src/core/pdfDocument.ts
17540
18058
  /**
@@ -17938,7 +18456,7 @@ var PdfDocument = class PdfDocument {
17938
18456
  cidFontDict.set("/DW", PdfNumber.of(Math.round(metrics.defaultWidth * scale)));
17939
18457
  cidFontDict.set("/CIDToGIDMap", PdfName.of("Identity"));
17940
18458
  const cidFontRef = this.registry.register(cidFontDict);
17941
- const toUnicodeCmapStr = this.buildToUnicodeCmap(metrics.cmapTable);
18459
+ const toUnicodeCmapStr = buildToUnicodeCmap(metrics.cmapTable);
17942
18460
  const toUnicodeStream = PdfStream.fromString(toUnicodeCmapStr);
17943
18461
  const toUnicodeRef = this.registry.register(toUnicodeStream);
17944
18462
  const type0Dict = new PdfDict();
@@ -18055,7 +18573,7 @@ var PdfDocument = class PdfDocument {
18055
18573
  cidFontDict.set("/W", wArray);
18056
18574
  cidFontDict.set("/DW", PdfNumber.of(Math.round(metrics.defaultWidth * scale)));
18057
18575
  const cidFontRef = this.registry.register(cidFontDict);
18058
- const toUnicodeCmapStr = this.buildToUnicodeCmap(metrics.cmapTable);
18576
+ const toUnicodeCmapStr = buildToUnicodeCmap(metrics.cmapTable);
18059
18577
  const toUnicodeStream = PdfStream.fromString(toUnicodeCmapStr);
18060
18578
  const toUnicodeRef = this.registry.register(toUnicodeStream);
18061
18579
  const type0Dict = new PdfDict();
@@ -18101,46 +18619,6 @@ var PdfDocument = class PdfDocument {
18101
18619
  return fontRef;
18102
18620
  }
18103
18621
  /**
18104
- * Build a /ToUnicode CMap from the font's cmap table.
18105
- * Maps glyph IDs (used as CIDs with Identity-H) to Unicode codepoints.
18106
- * @internal
18107
- */
18108
- buildToUnicodeCmap(cmapTable) {
18109
- const gidToUnicode = /* @__PURE__ */ new Map();
18110
- for (const [codepoint, gid] of cmapTable) if (!gidToUnicode.has(gid)) gidToUnicode.set(gid, codepoint);
18111
- const entries = gidToUnicode.entries().toArray().sort((a, b) => a[0] - b[0]);
18112
- const lines = [];
18113
- lines.push("/CIDInit /ProcSet findresource begin");
18114
- lines.push("12 dict begin");
18115
- lines.push("begincmap");
18116
- lines.push("/CIDSystemInfo");
18117
- lines.push("<< /Registry (Adobe)");
18118
- lines.push("/Ordering (UCS)");
18119
- lines.push("/Supplement 0");
18120
- lines.push(">> def");
18121
- lines.push("/CMapName /Adobe-Identity-UCS def");
18122
- lines.push("/CMapType 2 def");
18123
- lines.push("1 begincodespacerange");
18124
- lines.push("<0000> <FFFF>");
18125
- lines.push("endcodespacerange");
18126
- const CHUNK_SIZE = 100;
18127
- for (let i = 0; i < entries.length; i += CHUNK_SIZE) {
18128
- const chunk = entries.slice(i, i + CHUNK_SIZE);
18129
- lines.push(`${chunk.length} beginbfchar`);
18130
- for (const [gid, codepoint] of chunk) {
18131
- const gidHex = gid.toString(16).padStart(4, "0").toUpperCase();
18132
- const uniHex = codepoint.toString(16).padStart(4, "0").toUpperCase();
18133
- lines.push(`<${gidHex}> <${uniHex}>`);
18134
- }
18135
- lines.push("endbfchar");
18136
- }
18137
- lines.push("endcmap");
18138
- lines.push("CMapName currentdict /CMap defineresource pop");
18139
- lines.push("end");
18140
- lines.push("end");
18141
- return lines.join("\n");
18142
- }
18143
- /**
18144
18622
  * Embed a PNG image.
18145
18623
  *
18146
18624
  * Fully decodes the PNG (including filter reconstruction and alpha
@@ -18809,6 +19287,79 @@ var PdfDocument = class PdfDocument {
18809
19287
  addWatermark(this, options);
18810
19288
  }
18811
19289
  /**
19290
+ * Create a soft mask Form XObject that can be used with
19291
+ * {@link PdfPage.applySoftMask}.
19292
+ *
19293
+ * The builder callback receives a {@link SoftMaskBuilder} with methods
19294
+ * for generating grayscale content where white (`1`) represents fully
19295
+ * opaque regions and black (`0`) represents fully transparent regions.
19296
+ *
19297
+ * The returned {@link SoftMaskRef} is passed to
19298
+ * {@link PdfPage.applySoftMask} to activate the mask for subsequent
19299
+ * drawing operations on that page.
19300
+ *
19301
+ * @param width Width of the mask in points.
19302
+ * @param height Height of the mask in points.
19303
+ * @param builder Callback that draws the mask content.
19304
+ * @returns A reference to the soft mask Form XObject.
19305
+ *
19306
+ * @example
19307
+ * ```ts
19308
+ * const mask = doc.createSoftMask(200, 200, (b) => {
19309
+ * // White background = fully opaque
19310
+ * b.drawRectangle(0, 0, 200, 200, 1);
19311
+ * // Black circle = fully transparent hole
19312
+ * b.drawCircle(100, 100, 80, 0);
19313
+ * });
19314
+ * page.applySoftMask(mask);
19315
+ * page.drawRectangle({ x: 50, y: 50, width: 200, height: 200, color: rgb(1, 0, 0) });
19316
+ * page.clearSoftMask();
19317
+ * ```
19318
+ */
19319
+ createSoftMask(width, height, builder) {
19320
+ const maskOps = [];
19321
+ const kappa = .5522847498;
19322
+ builder({
19323
+ drawRectangle(x, y, w, h, gray) {
19324
+ maskOps.push(`${gray} g`);
19325
+ maskOps.push(`${x} ${y} ${w} ${h} re`);
19326
+ maskOps.push("f");
19327
+ },
19328
+ drawCircle(cx, cy, r, gray) {
19329
+ maskOps.push(`${gray} g`);
19330
+ const ox = r * kappa;
19331
+ const oy = r * kappa;
19332
+ maskOps.push(`${cx - r} ${cy} m`);
19333
+ maskOps.push(`${cx - r} ${cy + oy} ${cx - ox} ${cy + r} ${cx} ${cy + r} c`);
19334
+ maskOps.push(`${cx + ox} ${cy + r} ${cx + r} ${cy + oy} ${cx + r} ${cy} c`);
19335
+ maskOps.push(`${cx + r} ${cy - oy} ${cx + ox} ${cy - r} ${cx} ${cy - r} c`);
19336
+ maskOps.push(`${cx - ox} ${cy - r} ${cx - r} ${cy - oy} ${cx - r} ${cy} c`);
19337
+ maskOps.push("f");
19338
+ },
19339
+ pushRawOperators(ops) {
19340
+ maskOps.push(ops);
19341
+ }
19342
+ });
19343
+ const groupDict = new PdfDict();
19344
+ groupDict.set("/S", PdfName.of("Transparency"));
19345
+ groupDict.set("/CS", PdfName.of("DeviceGray"));
19346
+ const formDict = new PdfDict();
19347
+ formDict.set("/Type", PdfName.of("XObject"));
19348
+ formDict.set("/Subtype", PdfName.of("Form"));
19349
+ formDict.set("/BBox", PdfArray.fromNumbers([
19350
+ 0,
19351
+ 0,
19352
+ width,
19353
+ height
19354
+ ]));
19355
+ formDict.set("/Group", groupDict);
19356
+ const stream = PdfStream.fromString(maskOps.join("\n"), formDict);
19357
+ return {
19358
+ _tag: "softMask",
19359
+ ref: this.registry.register(stream)
19360
+ };
19361
+ }
19362
+ /**
18812
19363
  * Apply all pending redactions across all pages.
18813
19364
  *
18814
19365
  * Redaction marks are added to individual pages using
@@ -19043,30 +19594,6 @@ var PdfDocument = class PdfDocument {
19043
19594
  function createPdf() {
19044
19595
  return new PdfDocument();
19045
19596
  }
19046
- /**
19047
- * Parse width, height, and component count from a JPEG's SOF marker.
19048
- * @internal
19049
- */
19050
- function parseJpegDimensions(data) {
19051
- if (data.length < 2 || data[0] !== 255 || data[1] !== 216) throw new Error("Invalid JPEG: bad SOI marker");
19052
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
19053
- let offset = 2;
19054
- while (offset < data.length - 1) {
19055
- if (data[offset] !== 255) throw new Error("Invalid JPEG: expected marker");
19056
- const marker = data[offset + 1];
19057
- if (marker >= 192 && marker <= 195 || marker >= 197 && marker <= 199 || marker >= 201 && marker <= 203 || marker >= 205 && marker <= 207) {
19058
- const height = view.getUint16(offset + 5, false);
19059
- return {
19060
- width: view.getUint16(offset + 7, false),
19061
- height,
19062
- components: data[offset + 9]
19063
- };
19064
- }
19065
- const segmentLength = view.getUint16(offset + 2, false);
19066
- offset += 2 + segmentLength;
19067
- }
19068
- throw new Error("Invalid JPEG: SOF marker not found");
19069
- }
19070
19597
 
19071
19598
  //#endregion
19072
19599
  //#region src/utils/pdfValueHelpers.ts
@@ -19810,7 +20337,7 @@ function compressStream(stream, level) {
19810
20337
  */
19811
20338
  async function saveDocumentIncremental(originalBytes, doc, options) {
19812
20339
  const { buildDocumentStructure } = await import("./pdfCatalog-CTfeeqtF.mjs").then((n) => n.o);
19813
- const { PdfPage: _PdfPage } = await import("./pdfPage-JiCJLV3x.mjs").then((n) => n.r);
20340
+ const { PdfPage: _PdfPage } = await import("./pdfPage-B7vA518n.mjs").then((n) => n.r);
19814
20341
  const registry = doc.getRegistry();
19815
20342
  const structure = buildDocumentStructure(doc.getInternalPages().map((p) => p.finalize()), {
19816
20343
  producer: doc.getProducer(),
@@ -23354,10 +23881,10 @@ async function initWasm(options) {
23354
23881
  if (options === void 0 || typeof options === "string" || options instanceof URL) return;
23355
23882
  if (wasmInitialized) return;
23356
23883
  const inits = [];
23357
- if (options.deflate || options.deflateWasm) inits.push(import("./libdeflateWasm-CRFwmrSl.mjs").then((n) => n.r).then(async ({ initDeflateWasm }) => {
23884
+ if (options.deflate || options.deflateWasm) inits.push(import("./libdeflateWasm-DlHgU5oy.mjs").then((n) => n.r).then(async ({ initDeflateWasm }) => {
23358
23885
  await initDeflateWasm(options.deflateWasm);
23359
23886
  }));
23360
- if (options.png || options.pngWasm) inits.push(import("./pngEmbed-CHyesD7i.mjs").then((n) => n.n).then(async ({ initPngWasm }) => {
23887
+ if (options.png || options.pngWasm) inits.push(import("./pngEmbed-DTOqgEUC.mjs").then((n) => n.n).then(async ({ initPngWasm }) => {
23361
23888
  await initPngWasm(options.pngWasm);
23362
23889
  }));
23363
23890
  if (options.fonts || options.fontWasm) inits.push(import("./fontSubset-ZpLoOZ2e.mjs").then((n) => n.r).then(async ({ initSubsetWasm }) => {
@@ -23368,5 +23895,5 @@ async function initWasm(options) {
23368
23895
  }
23369
23896
 
23370
23897
  //#endregion
23371
- export { AnnotationFlags, BlendMode, ChangeTracker, CombedTextLayoutError, EmbeddedFont, EncryptedPdfError, ExceededMaxLengthError, FieldAlreadyExistsError, FieldExistsAsNonTerminalError, FieldFlags, FontNotEmbeddedError, ForeignPageError, ImageAlignment, InvalidFieldNamePartError, LineCapStyle, LineJoinStyle, MissingOnValueCheckError, NoSuchFieldError, PDFOperator, PageSizes, ParseSpeeds, PdfAnnotation, PdfArray, PdfBool, PdfButtonField, PdfCheckboxField, PdfCircleAnnotation, PdfDict, PdfDocument, PdfDropdownField, PdfEncryptionHandler, PdfField, PdfForm, PdfFreeTextAnnotation, PdfHighlightAnnotation, PdfInkAnnotation, PdfLayer, PdfLayerManager, PdfLineAnnotation, PdfLinkAnnotation, PdfListboxField, PdfName, PdfNull, PdfNumber, PdfObjectRegistry, PdfOutlineItem, PdfOutlineTree, PdfPage, PdfPolyLineAnnotation, PdfPolygonAnnotation, PdfRadioGroup, PdfRedactAnnotation, PdfRef, PdfSignatureField, PdfSquareAnnotation, PdfSquigglyAnnotation, PdfStampAnnotation, PdfStream, PdfStreamWriter, PdfStrikeOutAnnotation, PdfString, PdfStructureElement, PdfStructureTree, PdfTextAnnotation, PdfTextField, PdfUnderlineAnnotation, PdfViewerPreferences, PdfWriter, RemovePageFromEmptyDocumentError, RichTextFieldReadError, StandardFonts, TextAlignment, TextRenderingMode, UnexpectedFieldTypeError, addWatermark, addWatermarkToPage, aesDecryptCBC, aesEncryptCBC, annotationFromDict, applyFillColor, applyRedactions, applyStrokeColor, asNumber, asPDFName, asPDFNumber, attachFile, base64Decode, base64Encode, beginArtifact, beginArtifactWithType, beginLayerContent, beginMarkedContent, beginMarkedContentSequence, beginMarkedContentWithProperties, beginText, buildAnnotationDict, buildCatalog, buildDocumentStructure, buildEmbeddedFilesNameTree, buildInfoDict, buildPageTree, buildPkcs7Signature, buildTimestampRequest, buildViewerPreferencesDict, buildXmpMetadata, checkAccessibility, circlePath, clipEvenOdd, clip as clipOp, closeAndStroke, closeFillAndStroke, closeFillEvenOddAndStroke, closePath as closePathOp, cmyk, colorToComponents, componentsToColor, computeFileEncryptionKey, computeFontSize, computeSignatureHash, concatMatrix, concatMatrix as concatTransformationMatrix, copyPages, createAnnotation, createMarkedContentScope, createPdf, createXmpStream, cropPage, curveToFinal, curveToInitial, curveTo as curveToOp, decodePermissions, decodeStream, degrees, degreesToRadians, drawImageWithMatrix, drawImageXObject, drawXObject as drawObject, drawXObject, drawSvgOnPage, ellipsePath, embedPageAsFormXObject, embedSignature, encodeContextTag, encodeInteger, encodeLength, encodeOID, encodeOctetString, encodePermissions, encodePrintableString, encodeSequence, encodeSet, encodeUTCTime, encodeUtf8String, endArtifact, endLayerContent, endMarkedContent, endPath as endPathOp, endText, enforcePdfA, extractMetrics, extractText, extractTextWithPositions, fillAndStroke as fillAndStrokeOp, fillEvenOdd, fillEvenOddAndStroke, fill as fillOp, findSignatures, formatPdfDate, generateButtonAppearance, generateCheckboxAppearance, generateCircleAppearance, generateDropdownAppearance, generateFreeTextAppearance, generateHighlightAppearance, generateInkAppearance, generateLineAppearance, generateListboxAppearance, generateRadioAppearance, generateSignatureAppearance, generateSquareAppearance, generateSquigglyAppearance, generateStrikeOutAppearance, generateTextAppearance, generateUnderlineAppearance, getAttachments, getPageSize, getRedactionMarks, getSignatures, grayscale, initWasm, insertPage, isAccessible, isLinearized, isOpenTypeCFF, isTrueType, layoutCombedText, layoutMultilineText, layoutSinglelineText, lineTo as lineToOp, linearizePdf, loadPdf, markForRedaction, md5, mergePdfs, movePage, moveText as moveTextOp, moveTextSetLeading, moveTo as moveToOp, nextLine as nextLineOp, parseContentStream, parseSvg, parseSvgColor, parseSvgPath, parseSvgTransform, parseTimestampResponse, parseViewerPreferences, parseXmpMetadata, restoreState as popGraphicsState, restoreState, prepareForSigning, saveState as pushGraphicsState, saveState, radians, radiansToDegrees, rc4, rectangle as rectangleOp, removePage, removePages, requestTimestamp, resizePage, reversePages, rgb, rotateAllPages, rotate as rotateOp, rotatePage, rotationMatrix, saveDocumentIncremental, saveIncremental, scale as scaleOp, serializePdf, setCharacterSpacing as setCharacterSpacingOp, setCharacterSpacing as setCharacterSqueeze, setColorSpace, setDashPattern as setDashPatternOp, setFillColor, setFillColorCmyk, setFillColorGray, setFillColorRgb, setFillingColor, setFlatness, setFont as setFontAndSize, setFont as setFontOp, setFontSize as setFontSizeOp, setGraphicsState as setGraphicsStateOp, setLeading as setLeadingOp, setLeading as setLineHeight, setLineCap as setLineCapOp, setLineJoin as setLineJoinOp, setLineWidth as setLineWidthOp, setMiterLimit, setStrokeColor, setStrokeColorCmyk, setStrokeColorGray, setStrokeColorRgb, setStrokeColorSpace, setStrokingColor, setTextMatrix as setTextMatrixOp, setTextRenderingMode as setTextRenderingModeOp, setTextRise as setTextRiseOp, setWordSpacing as setWordSpacingOp, sha256, sha384, sha512, showTextArray, showTextHex, showTextNextLine, showText as showTextOp, showTextWithSpacing, signPdf, skew as skewOp, splitPdf, stroke as strokeOp, summarizeIssues, svgToPdfOperators, translate as translateOp, validatePdfA, verifyOwnerPassword, verifySignature, verifySignatures, verifyUserPassword, wrapInMarkedContent };
23898
+ export { AnnotationFlags, BlendMode, ChangeTracker, CombedTextLayoutError, EmbeddedFont, EncryptedPdfError, ExceededMaxLengthError, FieldAlreadyExistsError, FieldExistsAsNonTerminalError, FieldFlags, FontNotEmbeddedError, ForeignPageError, ImageAlignment, InvalidFieldNamePartError, LineCapStyle, LineJoinStyle, MissingOnValueCheckError, NoSuchFieldError, PDFOperator, PageSizes, ParseSpeeds, PdfAnnotation, PdfArray, PdfBool, PdfButtonField, PdfCheckboxField, PdfCircleAnnotation, PdfDict, PdfDocument, PdfDropdownField, PdfEncryptionHandler, PdfField, PdfForm, PdfFreeTextAnnotation, PdfHighlightAnnotation, PdfInkAnnotation, PdfLayer, PdfLayerManager, PdfLineAnnotation, PdfLinkAnnotation, PdfListboxField, PdfName, PdfNull, PdfNumber, PdfObjectRegistry, PdfOutlineItem, PdfOutlineTree, PdfPage, PdfParseError, PdfPolyLineAnnotation, PdfPolygonAnnotation, PdfRadioGroup, PdfRedactAnnotation, PdfRef, PdfSignatureField, PdfSquareAnnotation, PdfSquigglyAnnotation, PdfStampAnnotation, PdfStream, PdfStreamWriter, PdfStrikeOutAnnotation, PdfString, PdfStructureElement, PdfStructureTree, PdfTextAnnotation, PdfTextField, PdfUnderlineAnnotation, PdfViewerPreferences, PdfWriter, RemovePageFromEmptyDocumentError, RichTextFieldReadError, StandardFonts, TextAlignment, TextRenderingMode, UnexpectedFieldTypeError, addWatermark, addWatermarkToPage, aesDecryptCBC, aesEncryptCBC, annotationFromDict, applyFillColor, applyRedactions, applyStrokeColor, asNumber, asPDFName, asPDFNumber, attachFile, base64Decode, base64Encode, beginArtifact, beginArtifactWithType, beginLayerContent, beginMarkedContent, beginMarkedContentSequence, beginMarkedContentWithProperties, beginText, buildAnnotationDict, buildCatalog, buildDocumentStructure, buildEmbeddedFilesNameTree, buildGradientObjects, buildInfoDict, buildPageTree, buildPatternObjects, buildPkcs7Signature, buildTimestampRequest, buildViewerPreferencesDict, buildXmpMetadata, checkAccessibility, circlePath, clipEvenOdd, clip as clipOp, closeAndStroke, closeFillAndStroke, closeFillEvenOddAndStroke, closePath as closePathOp, cmyk, colorToComponents, componentsToColor, computeFileEncryptionKey, computeFontSize, computeSignatureHash, concatMatrix, concatMatrix as concatTransformationMatrix, copyPages, createAnnotation, createMarkedContentScope, createPdf, createXmpStream, cropPage, curveToFinal, curveToInitial, curveTo as curveToOp, decodePermissions, decodeStream, degrees, degreesToRadians, drawImageWithMatrix, drawImageXObject, drawXObject as drawObject, drawXObject, drawSvgOnPage, ellipsePath, embedPageAsFormXObject, embedSignature, encodeContextTag, encodeInteger, encodeLength, encodeOID, encodeOctetString, encodePermissions, encodePrintableString, encodeSequence, encodeSet, encodeUTCTime, encodeUtf8String, endArtifact, endLayerContent, endMarkedContent, endPath as endPathOp, endText, enforcePdfA, extractMetrics, extractText, extractTextWithPositions, fillAndStroke as fillAndStrokeOp, fillEvenOdd, fillEvenOddAndStroke, fill as fillOp, findSignatures, formatHexContext, formatPdfDate, generateButtonAppearance, generateCheckboxAppearance, generateCircleAppearance, generateDropdownAppearance, generateFreeTextAppearance, generateHighlightAppearance, generateInkAppearance, generateLineAppearance, generateListboxAppearance, generateRadioAppearance, generateSignatureAppearance, generateSquareAppearance, generateSquigglyAppearance, generateStrikeOutAppearance, generateTextAppearance, generateUnderlineAppearance, getAttachments, getPageSize, getRedactionMarks, getSignatures, grayscale, initWasm, insertPage, isAccessible, isLinearized, isOpenTypeCFF, isTrueType, layoutCombedText, layoutMultilineText, layoutSinglelineText, lineTo as lineToOp, linearGradient, linearizePdf, loadPdf, markForRedaction, md5, mergePdfs, movePage, moveText as moveTextOp, moveTextSetLeading, moveTo as moveToOp, nextLine as nextLineOp, parseContentStream, parseSvg, parseSvgColor, parseSvgPath, parseSvgTransform, parseTimestampResponse, parseViewerPreferences, parseXmpMetadata, restoreState as popGraphicsState, restoreState, prepareForSigning, saveState as pushGraphicsState, saveState, radialGradient, radians, radiansToDegrees, rc4, rectangle as rectangleOp, removePage, removePages, requestTimestamp, resizePage, reversePages, rgb, rotateAllPages, rotate as rotateOp, rotatePage, rotationMatrix, saveDocumentIncremental, saveIncremental, scale as scaleOp, serializePdf, setCharacterSpacing as setCharacterSpacingOp, setCharacterSpacing as setCharacterSqueeze, setColorSpace, setDashPattern as setDashPatternOp, setFillColor, setFillColorCmyk, setFillColorGray, setFillColorRgb, setFillingColor, setFlatness, setFont as setFontAndSize, setFont as setFontOp, setFontSize as setFontSizeOp, setGraphicsState as setGraphicsStateOp, setLeading as setLeadingOp, setLeading as setLineHeight, setLineCap as setLineCapOp, setLineJoin as setLineJoinOp, setLineWidth as setLineWidthOp, setMiterLimit, setStrokeColor, setStrokeColorCmyk, setStrokeColorGray, setStrokeColorRgb, setStrokeColorSpace, setStrokingColor, setTextMatrix as setTextMatrixOp, setTextRenderingMode as setTextRenderingModeOp, setTextRise as setTextRiseOp, setWordSpacing as setWordSpacingOp, sha256, sha384, sha512, showTextArray, showTextHex, showTextNextLine, showText as showTextOp, showTextWithSpacing, signPdf, skew as skewOp, splitPdf, stroke as strokeOp, summarizeIssues, svgToPdfOperators, tilingPattern, translate as translateOp, validatePdfA, verifyOwnerPassword, verifySignature, verifySignatures, verifyUserPassword, wrapInMarkedContent };
23372
23899
  //# sourceMappingURL=index.mjs.map