reamkit 1.26.0 → 1.28.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.
Files changed (85) hide show
  1. package/README.md +14 -8
  2. package/dist/esm/core/converter/ream.d.ts +0 -9
  3. package/dist/esm/core/converter/ream.js +0 -1
  4. package/dist/esm/core/document-model/types.d.ts +5 -3
  5. package/dist/esm/core/font/index.d.ts +1 -0
  6. package/dist/esm/core/font/ligatures.d.ts +17 -0
  7. package/dist/esm/core/font/ligatures.js +49 -0
  8. package/dist/esm/core/font/ttf-parser.d.ts +2 -1
  9. package/dist/esm/core/font/ttf-parser.js +11 -2
  10. package/dist/esm/core/fonts/remote-fonts.d.ts +1 -1
  11. package/dist/esm/core/fonts/remote-fonts.js +47 -2
  12. package/dist/esm/core/fonts/scripts.js +10 -5
  13. package/dist/esm/excel/header-footer.js +53 -8
  14. package/dist/esm/layout/styled-layout.js +53 -3
  15. package/dist/esm/pdf/cid-font.js +35 -8
  16. package/dist/esm/pdf-reader/annot-draw.d.ts +65 -0
  17. package/dist/esm/pdf-reader/annot-draw.js +487 -0
  18. package/dist/esm/pdf-reader/annots.d.ts +0 -12
  19. package/dist/esm/pdf-reader/annots.js +30 -4
  20. package/dist/esm/pdf-reader/ccitt.d.ts +20 -3
  21. package/dist/esm/pdf-reader/ccitt.js +102 -6
  22. package/dist/esm/pdf-reader/cff-outline.d.ts +36 -0
  23. package/dist/esm/pdf-reader/cff-outline.js +1122 -0
  24. package/dist/esm/pdf-reader/cie-color.d.ts +33 -0
  25. package/dist/esm/pdf-reader/cie-color.js +112 -0
  26. package/dist/esm/pdf-reader/cmap.js +5 -2
  27. package/dist/esm/pdf-reader/content.d.ts +123 -3
  28. package/dist/esm/pdf-reader/content.js +232 -54
  29. package/dist/esm/pdf-reader/dingbats.d.ts +11 -0
  30. package/dist/esm/pdf-reader/dingbats.js +1033 -0
  31. package/dist/esm/pdf-reader/display.d.ts +1 -1
  32. package/dist/esm/pdf-reader/display.js +61 -6
  33. package/dist/esm/pdf-reader/document.d.ts +6 -0
  34. package/dist/esm/pdf-reader/document.js +55 -10
  35. package/dist/esm/pdf-reader/embedded-fonts.d.ts +23 -3
  36. package/dist/esm/pdf-reader/embedded-fonts.js +36 -4
  37. package/dist/esm/pdf-reader/encodings.d.ts +25 -0
  38. package/dist/esm/pdf-reader/encodings.js +110 -0
  39. package/dist/esm/pdf-reader/flow-build.d.ts +19 -5
  40. package/dist/esm/pdf-reader/flow-build.js +111 -11
  41. package/dist/esm/pdf-reader/font.js +403 -21
  42. package/dist/esm/pdf-reader/function.d.ts +16 -0
  43. package/dist/esm/pdf-reader/function.js +414 -0
  44. package/dist/esm/pdf-reader/glyf-outline.d.ts +43 -0
  45. package/dist/esm/pdf-reader/glyf-outline.js +351 -0
  46. package/dist/esm/pdf-reader/glyph-names.js +20 -0
  47. package/dist/esm/pdf-reader/icc.d.ts +10 -0
  48. package/dist/esm/pdf-reader/icc.js +210 -0
  49. package/dist/esm/pdf-reader/image-decode.d.ts +9 -4
  50. package/dist/esm/pdf-reader/image-decode.js +274 -72
  51. package/dist/esm/pdf-reader/images.d.ts +18 -0
  52. package/dist/esm/pdf-reader/images.js +154 -11
  53. package/dist/esm/pdf-reader/jbig2.d.ts +23 -0
  54. package/dist/esm/pdf-reader/jbig2.js +126 -32
  55. package/dist/esm/pdf-reader/layout.d.ts +32 -0
  56. package/dist/esm/pdf-reader/layout.js +1316 -64
  57. package/dist/esm/pdf-reader/lexer.d.ts +2 -0
  58. package/dist/esm/pdf-reader/lexer.js +4 -0
  59. package/dist/esm/pdf-reader/math-rows.d.ts +23 -0
  60. package/dist/esm/pdf-reader/math-rows.js +198 -0
  61. package/dist/esm/pdf-reader/optional-content.d.ts +36 -0
  62. package/dist/esm/pdf-reader/optional-content.js +93 -0
  63. package/dist/esm/pdf-reader/predefined-cmap.d.ts +21 -0
  64. package/dist/esm/pdf-reader/predefined-cmap.js +102 -0
  65. package/dist/esm/pdf-reader/reader.d.ts +6 -6
  66. package/dist/esm/pdf-reader/reader.js +102 -16
  67. package/dist/esm/pdf-reader/shading.d.ts +139 -8
  68. package/dist/esm/pdf-reader/shading.js +309 -39
  69. package/dist/esm/pdf-reader/standard-metrics.d.ts +8 -0
  70. package/dist/esm/pdf-reader/standard-metrics.js +18 -0
  71. package/dist/esm/pdf-reader/standard-widths.d.ts +20 -0
  72. package/dist/esm/pdf-reader/standard-widths.js +62 -0
  73. package/dist/esm/pdf-reader/stream-filters.d.ts +6 -0
  74. package/dist/esm/pdf-reader/stream-filters.js +67 -0
  75. package/dist/esm/pdf-reader/tagged.js +204 -32
  76. package/dist/esm/pdf-reader/text-rules.d.ts +16 -0
  77. package/dist/esm/pdf-reader/text-rules.js +185 -0
  78. package/dist/esm/pdf-reader/text.js +157 -4
  79. package/dist/esm/pdf-reader/type1-outline.d.ts +21 -0
  80. package/dist/esm/pdf-reader/type1-outline.js +576 -0
  81. package/dist/esm/pdf-reader/vector.d.ts +5 -0
  82. package/dist/esm/pdf-reader/vector.js +79 -27
  83. package/dist/esm/word/document-parser.js +4 -1
  84. package/dist/esm/word/docx-writer.js +74 -11
  85. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { PDF_NULL, PdfHexString, PdfName } from "../pdf/objects.js";
2
2
  import { Lexer } from "./lexer.js";
3
+ import { cmykHex, grayHex, rgbHex, spaceColor } from "./shading.js";
3
4
  //#region src/pdf-reader/content.ts
4
5
  /**
5
6
  * A painted path emitted by the path-painting operators (§8.5.3), captured in
@@ -91,6 +92,9 @@ function initialState() {
91
92
  fillGradient: void 0,
92
93
  fillPattern: void 0,
93
94
  fillAlpha: 1,
95
+ fillDarkens: false,
96
+ blendMode: void 0,
97
+ softMask: false,
94
98
  clip: void 0
95
99
  };
96
100
  }
@@ -132,30 +136,42 @@ function spaceOf(operands, spaces) {
132
136
  }
133
137
  }
134
138
  /**
135
- * §8.6.8 — the colour a run of `sc` / `scn` components comes to.
136
- *
137
- * The space in force decides, and where it was not read the COUNT is the next
138
- * best witness: three numbers are RGB and four are CMYK on every device space
139
- * there is. One number is the ambiguous case — grey in a device space, but the
140
- * strength of a colorant in a Separation, where 1 is the ink at full and reads
141
- * dark — so a lone component is only taken where the space said what it means.
139
+ * §8.7.4.5 — one colour for a whole sweep: the stop at its middle, or the mean
140
+ * of the two it falls between. A gradient is a shape a run cannot carry, and
141
+ * the end it starts at is no more the colour of the words than the end it
142
+ * finishes at.
142
143
  */
143
- function componentColor(operands, space) {
144
- const nums = operands.filter((o) => typeof o === "number");
145
- if (nums.length === 0) return void 0;
146
- switch (space?.kind ?? (nums.length === 3 ? "rgb" : nums.length === 4 ? "cmyk" : void 0)) {
147
- case "rgb": return nums.length >= 3 ? rgbHex(nums[0], nums[1], nums[2]) : void 0;
148
- case "cmyk": return nums.length >= 4 ? cmykHex(nums[0], nums[1], nums[2], nums[3]) : void 0;
149
- case "gray": return grayHex(nums[0]);
150
- case "tint": return grayHex(1 - Math.max(...nums));
151
- default: return;
152
- }
144
+ function midGradient(gradient) {
145
+ const stops = [...gradient.stops].sort((a, b) => a.offset - b.offset);
146
+ if (stops.length === 0) return "000000";
147
+ const exact = stops.find((s) => Math.abs(s.offset - .5) < 1e-6);
148
+ if (exact) return exact.colorHex;
149
+ const before = [...stops].reverse().find((s) => s.offset <= .5) ?? stops[0];
150
+ const after = stops.find((s) => s.offset >= .5) ?? stops[stops.length - 1];
151
+ const span = after.offset - before.offset;
152
+ const t = span > 0 ? (.5 - before.offset) / span : 0;
153
+ const mix = (at) => {
154
+ const a = parseInt(before.colorHex.slice(at, at + 2), 16);
155
+ const b = parseInt(after.colorHex.slice(at, at + 2), 16);
156
+ return Number.isFinite(a) && Number.isFinite(b) ? Math.round(a + (b - a) * t) : 0;
157
+ };
158
+ return [
159
+ mix(0),
160
+ mix(2),
161
+ mix(4)
162
+ ].map((c) => Math.max(0, Math.min(255, c)).toString(16).padStart(2, "0")).join("").toUpperCase();
153
163
  }
154
- function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__PURE__ */ new Map(), alphas = /* @__PURE__ */ new Map(), spaces = /* @__PURE__ */ new Map()) {
164
+ /** §8.6.8 — the colour a run of `sc` / `scn` operands comes to (see `spaceColor`). */
165
+ function colorOfOperands(operands, space) {
166
+ return spaceColor(operands.filter((o) => typeof o === "number"), space);
167
+ }
168
+ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__PURE__ */ new Map(), alphas = /* @__PURE__ */ new Map(), spaces = /* @__PURE__ */ new Map(), hiddenOc = /* @__PURE__ */ new Set()) {
155
169
  const runs = [];
156
170
  const images = [];
157
171
  const vectors = [];
158
172
  const glyphs = [];
173
+ const painted = [];
174
+ const outlines = [];
159
175
  const lexer = new Lexer(bytes);
160
176
  const stack = [];
161
177
  let state = initialState();
@@ -167,6 +183,9 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
167
183
  let paintOrder = 0;
168
184
  let operands = [];
169
185
  const mcStack = [];
186
+ const ocStack = [];
187
+ let hiddenDepth = 0;
188
+ const visible = () => hiddenDepth === 0;
170
189
  const toPage = (x, y) => [x * state.ctm[0] + y * state.ctm[2] + state.ctm[4], x * state.ctm[1] + y * state.ctm[3] + state.ctm[5]];
171
190
  const moveTo = (x, y) => {
172
191
  const [px, py] = toPage(x, y);
@@ -211,7 +230,7 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
211
230
  return state.lineWidth * scale;
212
231
  };
213
232
  const paintPath = (fill, stroke) => {
214
- if (path.length >= 2 && (fill || stroke)) {
233
+ if (path.length >= 2 && (fill || stroke) && visible()) {
215
234
  const mcid = mcStack.length > 0 ? mcStack[mcStack.length - 1] : void 0;
216
235
  vectors.push({
217
236
  order: paintOrder++,
@@ -220,6 +239,9 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
220
239
  ...fill && state.fillPattern !== void 0 ? { patternName: state.fillPattern } : {},
221
240
  ...fill ? { fillHex: state.fillColor } : {},
222
241
  ...fill && state.fillAlpha < 1 ? { alpha: state.fillAlpha } : {},
242
+ ...fill && state.fillDarkens ? { darkens: true } : {},
243
+ ...state.blendMode !== void 0 ? { blend: state.blendMode } : {},
244
+ ...state.softMask ? { masked: true } : {},
223
245
  ...fill && state.fillGradient ? { gradient: state.fillGradient } : {},
224
246
  ...stroke ? {
225
247
  strokeHex: state.strokeColor,
@@ -249,7 +271,7 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
249
271
  const type3 = state.font.type3;
250
272
  if (type3) {
251
273
  const stream = type3.proc(code);
252
- if (stream) {
274
+ if (stream && visible()) {
253
275
  const scale = [
254
276
  state.fontSize * state.hScale,
255
277
  0,
@@ -266,12 +288,41 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
266
288
  });
267
289
  }
268
290
  }
269
- const w0 = state.font.width(code) / 1e3;
291
+ const outline = state.font.outline;
292
+ if (outline && visible() && state.renderMode !== 3 && state.renderMode !== 7) {
293
+ const segs = outline.path(code);
294
+ if (segs) {
295
+ const scale = [
296
+ state.fontSize * state.hScale,
297
+ 0,
298
+ 0,
299
+ state.fontSize,
300
+ 0,
301
+ state.rise
302
+ ];
303
+ const place = multiply(outline.matrix, multiply(scale, multiply(tm, state.ctm)));
304
+ outlines.push({
305
+ order: paintOrder++,
306
+ segs: segs.map((seg) => mapSeg(seg, place)),
307
+ ...state.clip ? { clip: state.clip } : {},
308
+ fillHex: state.fillColor,
309
+ ...state.fillAlpha < 1 ? { alpha: state.fillAlpha } : {},
310
+ glyph: true
311
+ });
312
+ }
313
+ }
270
314
  const isSpace = state.font.bytesPerCode === 1 && code === 32;
271
- tm = multiply(translation((w0 * state.fontSize + state.charSpacing + (isSpace ? state.wordSpacing : 0)) * state.hScale, 0), tm);
315
+ const spacing = state.charSpacing + (isSpace ? state.wordSpacing : 0);
316
+ const down = state.font.verticalAdvance;
317
+ if (down) {
318
+ tm = multiply(translation(0, down(code) / 1e3 * state.fontSize - spacing), tm);
319
+ return;
320
+ }
321
+ tm = multiply(translation((state.font.width(code) / 1e3 * state.fontSize + spacing) * state.hScale, 0), tm);
272
322
  };
273
323
  const consume = (operand) => {
274
- const codes = splitCodes(toBytes(operand), state.font.bytesPerCode);
324
+ const bytes = toBytes(operand);
325
+ const codes = state.font.splitCodes ? state.font.splitCodes(bytes) : splitCodes(bytes, state.font.bytesPerCode);
275
326
  for (const code of codes) advanceGlyph(code);
276
327
  return state.font.decode(codes);
277
328
  };
@@ -279,8 +330,10 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
279
330
  const text = logicalOrder(shown);
280
331
  if (text.length === 0) return;
281
332
  const scaleY = Math.hypot(origin[2], origin[3]) || 1;
333
+ const scaleX = (Math.hypot(origin[0], origin[1]) || 1) * state.hScale;
282
334
  const mcid = mcStack.length > 0 ? mcStack[mcStack.length - 1] : void 0;
283
335
  const angle = Math.atan2(origin[1], origin[0]) * 180 / Math.PI;
336
+ if (!visible()) return;
284
337
  runs.push({
285
338
  text,
286
339
  x: origin[4],
@@ -288,19 +341,21 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
288
341
  endX: end[4],
289
342
  endY: end[5],
290
343
  ...Math.abs(angle) > UPRIGHT_TOLERANCE_DEG ? { angleDeg: angle } : {},
291
- fontSizePt: state.fontSize * scaleY,
344
+ fontSizePt: Math.abs(state.fontSize) * scaleY,
345
+ spaceWidthPt: (state.font.width(32) / 1e3 * state.fontSize + state.charSpacing + state.wordSpacing) * scaleX,
292
346
  fontKey: state.fontKey,
293
347
  ...state.font.name !== void 0 ? { fontName: state.font.name } : {},
294
348
  ...state.font.type3 ? { type3: true } : {},
295
349
  ...state.renderMode === 3 || state.renderMode === 7 ? { invisible: true } : {},
296
350
  ...state.fillPattern !== void 0 ? { fillPatternName: state.fillPattern } : {},
351
+ ...state.fillGradient ? { gradientFill: true } : {},
297
352
  ...strokesText(state.renderMode) ? {
298
353
  outlineHex: state.strokeColor,
299
354
  outlineWidthPt: ctmLineWidth()
300
355
  } : {},
301
356
  ...state.font.bold ? { bold: true } : {},
302
357
  ...state.font.italic ? { italic: true } : {},
303
- colorHex: state.fillColor,
358
+ colorHex: state.fillGradient ? midGradient(state.fillGradient) : state.fillColor,
304
359
  ...mcid !== void 0 ? { mcid } : {}
305
360
  });
306
361
  };
@@ -392,23 +447,33 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
392
447
  const props = operands[operands.length - 1];
393
448
  const mcidVal = !(tag instanceof PdfName && tag.value === "Artifact") && props instanceof Map ? props.get("MCID") : void 0;
394
449
  mcStack.push(typeof mcidVal === "number" ? mcidVal : void 0);
450
+ const isOc = tag instanceof PdfName && tag.value === "OC" && props instanceof PdfName && hiddenOc.has(props.value);
451
+ ocStack.push(isOc);
452
+ if (isOc) hiddenDepth++;
395
453
  break;
396
454
  }
397
455
  case "BMC":
398
456
  mcStack.push(void 0);
457
+ ocStack.push(false);
399
458
  break;
400
459
  case "EMC":
401
460
  mcStack.pop();
461
+ if (ocStack.pop() === true) hiddenDepth--;
402
462
  break;
403
463
  case "Do": {
404
464
  const nm = operands[0];
405
- if (nm instanceof PdfName) {
465
+ if (nm instanceof PdfName && visible()) {
406
466
  const mcid = mcStack.length > 0 ? mcStack[mcStack.length - 1] : void 0;
407
467
  images.push({
408
468
  order: paintOrder++,
409
469
  name: nm.value,
410
470
  ctm: state.ctm,
411
- ...mcid !== void 0 ? { mcid } : {}
471
+ ...state.clip ? { clip: state.clip } : {},
472
+ fillHex: state.fillColor,
473
+ ...mcid !== void 0 ? { mcid } : {},
474
+ ...state.fillAlpha < 1 ? { alpha: state.fillAlpha } : {},
475
+ ...state.blendMode !== void 0 ? { blend: state.blendMode } : {},
476
+ ...state.softMask ? { masked: true } : {}
412
477
  });
413
478
  }
414
479
  break;
@@ -443,7 +508,7 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
443
508
  state.fillPattern = state.fillGradient ? void 0 : named;
444
509
  break;
445
510
  }
446
- const hex = componentColor(operands, state.fillSpace);
511
+ const hex = colorOfOperands(operands, state.fillSpace);
447
512
  if (hex !== void 0) {
448
513
  state.fillColor = hex;
449
514
  state.fillGradient = void 0;
@@ -454,7 +519,7 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
454
519
  case "SCN":
455
520
  case "SC": {
456
521
  if (operands[operands.length - 1] instanceof PdfName) break;
457
- const hex = componentColor(operands, state.strokeSpace);
522
+ const hex = colorOfOperands(operands, state.strokeSpace);
458
523
  if (hex !== void 0) state.strokeColor = hex;
459
524
  break;
460
525
  }
@@ -506,12 +571,33 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
506
571
  path.push({ op: "close" });
507
572
  paintPath(true, true);
508
573
  break;
574
+ case "sh": {
575
+ const nm = operands[0];
576
+ if (nm instanceof PdfName && visible()) painted.push({
577
+ name: nm.value,
578
+ ctm: state.ctm,
579
+ ...state.clip ? { clip: state.clip } : {},
580
+ ...state.softMask ? { masked: true } : {},
581
+ ...state.fillAlpha < 1 ? { alpha: state.fillAlpha } : {},
582
+ ...state.fillDarkens ? { darkens: true } : {},
583
+ order: paintOrder++
584
+ });
585
+ break;
586
+ }
509
587
  case "n":
510
588
  paintPath(false, false);
511
589
  break;
512
590
  case "gs": {
513
591
  const nm = operands[operands.length - 1];
514
- if (nm instanceof PdfName) state.fillAlpha = alphas.get(nm.value) ?? 1;
592
+ const paint = nm instanceof PdfName ? alphas.get(nm.value) : void 0;
593
+ if (paint) {
594
+ if (paint.alpha !== void 0) state.fillAlpha = paint.alpha;
595
+ if (paint.statesBlend === true) {
596
+ state.fillDarkens = paint.darkens ?? false;
597
+ state.blendMode = paint.blend;
598
+ }
599
+ if (paint.masked !== void 0) state.softMask = paint.masked;
600
+ }
515
601
  break;
516
602
  }
517
603
  case "W":
@@ -545,8 +631,19 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
545
631
  operands.push(readDict(lexer));
546
632
  break;
547
633
  case "keyword":
548
- if (tok.value === "BI") skipInlineImage(lexer);
549
- else exec(tok.value);
634
+ if (tok.value === "BI") {
635
+ const inline = readInlineImage(lexer);
636
+ if (inline && visible()) images.push({
637
+ order: paintOrder++,
638
+ name: "",
639
+ inline,
640
+ ctm: state.ctm,
641
+ ...state.clip ? { clip: state.clip } : {},
642
+ fillHex: state.fillColor,
643
+ ...mcStack.length > 0 ? { mcid: mcStack[mcStack.length - 1] } : {},
644
+ ...state.blendMode !== void 0 ? { blend: state.blendMode } : {}
645
+ });
646
+ } else exec(tok.value);
550
647
  operands = [];
551
648
  break;
552
649
  default:
@@ -558,7 +655,34 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
558
655
  texts: runs,
559
656
  images,
560
657
  vectors,
561
- glyphs
658
+ glyphs,
659
+ shadings: painted,
660
+ outlines
661
+ };
662
+ }
663
+ /** One path segment through a matrix — the glyph's own space onto the page. */
664
+ function mapSeg(seg, m) {
665
+ const at = (x, y) => [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
666
+ if (seg.op === "close") return seg;
667
+ if (seg.op === "cubic") {
668
+ const [x1, y1] = at(seg.x1, seg.y1);
669
+ const [x2, y2] = at(seg.x2, seg.y2);
670
+ const [x, y] = at(seg.x, seg.y);
671
+ return {
672
+ op: "cubic",
673
+ x1,
674
+ y1,
675
+ x2,
676
+ y2,
677
+ x,
678
+ y
679
+ };
680
+ }
681
+ const [x, y] = at(seg.x, seg.y);
682
+ return {
683
+ op: seg.op,
684
+ x,
685
+ y
562
686
  };
563
687
  }
564
688
  /** §9.3.6 — the rendering modes that put a line round the glyphs. */
@@ -610,21 +734,6 @@ function isRightToLeft(text) {
610
734
  function isRtl(cp) {
611
735
  return cp >= 1424 && cp <= 1535 || cp >= 1536 && cp <= 1791 || cp >= 1792 && cp <= 1871 || cp >= 1920 && cp <= 1983 || cp >= 2208 && cp <= 2303 || cp >= 64285 && cp <= 65023 || cp >= 65136 && cp <= 65279;
612
736
  }
613
- function clamp255(v) {
614
- return Math.max(0, Math.min(255, Math.round(v * 255)));
615
- }
616
- function hex2(v) {
617
- return clamp255(v).toString(16).padStart(2, "0");
618
- }
619
- function rgbHex(r, g, b) {
620
- return (hex2(r) + hex2(g) + hex2(b)).toUpperCase();
621
- }
622
- function grayHex(v) {
623
- return rgbHex(v, v, v);
624
- }
625
- function cmykHex(c, m, y, k) {
626
- return rgbHex((1 - c) * (1 - k), (1 - m) * (1 - k), (1 - y) * (1 - k));
627
- }
628
737
  function matrixFromOperands(operands) {
629
738
  const n = (i) => typeof operands[i] === "number" ? operands[i] : 0;
630
739
  return [
@@ -658,9 +767,30 @@ function readArray(lexer) {
658
767
  for (;;) {
659
768
  const tok = lexer.nextToken();
660
769
  if (tok.kind === "arrayClose" || tok.kind === "eof") break;
661
- if (tok.kind === "num") out.push(tok.value);
662
- else if (tok.kind === "str") out.push(tok.value);
663
- else if (tok.kind === "hexstr") out.push(new PdfHexString(tok.bytes));
770
+ switch (tok.kind) {
771
+ case "num":
772
+ out.push(tok.value);
773
+ break;
774
+ case "str":
775
+ out.push(tok.value);
776
+ break;
777
+ case "hexstr":
778
+ out.push(new PdfHexString(tok.bytes));
779
+ break;
780
+ case "name":
781
+ out.push(new PdfName(tok.value));
782
+ break;
783
+ case "arrayOpen":
784
+ out.push(readArray(lexer));
785
+ break;
786
+ case "dictOpen":
787
+ out.push(readDict(lexer));
788
+ break;
789
+ case "keyword":
790
+ if (tok.value === "true" || tok.value === "false") out.push(tok.value === "true");
791
+ break;
792
+ default: break;
793
+ }
664
794
  }
665
795
  return out;
666
796
  }
@@ -687,14 +817,62 @@ function readValue(lexer) {
687
817
  default: return PDF_NULL;
688
818
  }
689
819
  }
690
- function skipInlineImage(lexer) {
820
+ /**
821
+ * §8.9.7 — an inline image: its dictionary between `BI` and `ID`, then its
822
+ * bytes up to `EI`.
823
+ *
824
+ * The bytes are binary and may hold `EI` themselves, so the end is found by
825
+ * MEASURING where the dictionary says how much there is — an unfiltered image
826
+ * is exactly `ceil(W · BPC · components / 8) · H` bytes — and only searched for
827
+ * where a filter makes the length unknowable. images_1bit_grayscale.pdf draws
828
+ * two of them and both were skipped over.
829
+ */
830
+ function readInlineImage(lexer) {
831
+ const dict = /* @__PURE__ */ new Map();
691
832
  for (;;) {
692
833
  const tok = lexer.nextToken();
693
- if (tok.kind === "eof") return;
834
+ if (tok.kind === "eof") return void 0;
694
835
  if (tok.kind === "keyword" && tok.value === "ID") break;
836
+ if (tok.kind === "name") dict.set(tok.value, readValue(lexer));
837
+ }
838
+ const start = lexer.pos + 1;
839
+ const measured = inlineLength(dict);
840
+ let end = measured !== void 0 ? start + measured : -1;
841
+ if (end < 0 || end > lexer.length) {
842
+ end = lexer.indexOfAscii("EI", start);
843
+ if (end < 0) {
844
+ lexer.pos = lexer.length;
845
+ return;
846
+ }
695
847
  }
696
- const ei = lexer.indexOfAscii("EI", lexer.pos);
848
+ const data = lexer.slice(start, Math.min(end, lexer.length));
849
+ const ei = lexer.indexOfAscii("EI", end);
697
850
  lexer.pos = ei < 0 ? lexer.length : ei + 2;
851
+ return {
852
+ dict,
853
+ data
854
+ };
855
+ }
856
+ /** How many bytes an UNFILTERED inline image's samples take, if that is known. */
857
+ function inlineLength(dict) {
858
+ if (dict.has("F") || dict.has("Filter")) return void 0;
859
+ const num = (...keys) => {
860
+ for (const k of keys) {
861
+ const v = dict.get(k);
862
+ if (typeof v === "number") return v;
863
+ }
864
+ return 0;
865
+ };
866
+ const w = num("W", "Width");
867
+ const h = num("H", "Height");
868
+ if (w <= 0 || h <= 0) return void 0;
869
+ const mask = dict.get("IM") === true || dict.get("ImageMask") === true;
870
+ const bpc = mask ? 1 : num("BPC", "BitsPerComponent") || 8;
871
+ const cs = dict.get("CS") ?? dict.get("ColorSpace");
872
+ const name = cs instanceof PdfName ? cs.value : "";
873
+ const comps = mask ? 1 : /^(RGB|DeviceRGB|CalRGB)$/u.test(name) ? 3 : /^(CMYK|DeviceCMYK)$/u.test(name) ? 4 : /^(G|DeviceGray|CalGray|I|Indexed)$/u.test(name) || name === "" ? 1 : 0;
874
+ if (comps === 0) return void 0;
875
+ return ((w * comps * bpc + 7) / 8 | 0) * h;
698
876
  }
699
877
  //#endregion
700
878
  export { IDENTITY, interpretContent, isRightToLeft, multiply };
@@ -0,0 +1,11 @@
1
+ /** The character a ZapfDingbats code stands for, or `undefined` for an unused one. */
2
+ export declare function zapfDingbatsChar(code: number): string | undefined;
3
+ /**
4
+ * The character a dingbat's glyph NAME stands for — `a38` is ✫ — for a font
5
+ * that names its glyphs through `/Differences` (§9.6.6.1).
6
+ *
7
+ * @param name The glyph name.
8
+ */
9
+ export declare function dingbatChar(name: string): string | undefined;
10
+ /** Whether a base font names the ZapfDingbats face (§9.6.2.2 Table 109). */
11
+ export declare function isZapfDingbats(baseFont: string): boolean;