reamkit 1.27.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 (65) hide show
  1. package/README.md +6 -5
  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.js +114 -1
  17. package/dist/esm/pdf-reader/annots.d.ts +0 -14
  18. package/dist/esm/pdf-reader/annots.js +13 -1
  19. package/dist/esm/pdf-reader/ccitt.d.ts +2 -3
  20. package/dist/esm/pdf-reader/ccitt.js +32 -4
  21. package/dist/esm/pdf-reader/cff-outline.d.ts +36 -0
  22. package/dist/esm/pdf-reader/cff-outline.js +1122 -0
  23. package/dist/esm/pdf-reader/cmap.js +5 -2
  24. package/dist/esm/pdf-reader/content.d.ts +76 -0
  25. package/dist/esm/pdf-reader/content.js +142 -59
  26. package/dist/esm/pdf-reader/dingbats.d.ts +11 -0
  27. package/dist/esm/pdf-reader/dingbats.js +1033 -0
  28. package/dist/esm/pdf-reader/display.js +41 -1
  29. package/dist/esm/pdf-reader/document.js +26 -9
  30. package/dist/esm/pdf-reader/embedded-fonts.d.ts +11 -0
  31. package/dist/esm/pdf-reader/embedded-fonts.js +14 -1
  32. package/dist/esm/pdf-reader/encodings.d.ts +25 -0
  33. package/dist/esm/pdf-reader/encodings.js +110 -0
  34. package/dist/esm/pdf-reader/flow-build.d.ts +9 -3
  35. package/dist/esm/pdf-reader/flow-build.js +13 -7
  36. package/dist/esm/pdf-reader/font.js +329 -8
  37. package/dist/esm/pdf-reader/glyf-outline.d.ts +43 -0
  38. package/dist/esm/pdf-reader/glyf-outline.js +351 -0
  39. package/dist/esm/pdf-reader/glyph-names.js +20 -0
  40. package/dist/esm/pdf-reader/icc.d.ts +10 -0
  41. package/dist/esm/pdf-reader/icc.js +210 -0
  42. package/dist/esm/pdf-reader/image-decode.d.ts +4 -3
  43. package/dist/esm/pdf-reader/image-decode.js +85 -64
  44. package/dist/esm/pdf-reader/images.d.ts +6 -0
  45. package/dist/esm/pdf-reader/images.js +50 -7
  46. package/dist/esm/pdf-reader/jbig2.js +104 -19
  47. package/dist/esm/pdf-reader/layout.js +1148 -45
  48. package/dist/esm/pdf-reader/math-rows.d.ts +23 -0
  49. package/dist/esm/pdf-reader/math-rows.js +198 -0
  50. package/dist/esm/pdf-reader/predefined-cmap.d.ts +21 -0
  51. package/dist/esm/pdf-reader/predefined-cmap.js +102 -0
  52. package/dist/esm/pdf-reader/reader.d.ts +6 -9
  53. package/dist/esm/pdf-reader/reader.js +34 -21
  54. package/dist/esm/pdf-reader/shading.d.ts +81 -2
  55. package/dist/esm/pdf-reader/shading.js +191 -25
  56. package/dist/esm/pdf-reader/stream-filters.d.ts +6 -0
  57. package/dist/esm/pdf-reader/stream-filters.js +67 -0
  58. package/dist/esm/pdf-reader/text-rules.js +83 -10
  59. package/dist/esm/pdf-reader/text.js +82 -3
  60. package/dist/esm/pdf-reader/type1-outline.d.ts +21 -0
  61. package/dist/esm/pdf-reader/type1-outline.js +576 -0
  62. package/dist/esm/pdf-reader/vector.js +43 -4
  63. package/dist/esm/word/document-parser.js +4 -1
  64. package/dist/esm/word/docx-writer.js +1 -0
  65. package/package.json +1 -1
@@ -2,7 +2,9 @@ import { PDF_NULL, PdfHexString, PdfName, PdfStream } from "../pdf/objects.js";
2
2
  import { encodePng } from "../core/png-encode.js";
3
3
  import { lzwDecodeMsb } from "../core/lzw.js";
4
4
  import { reversePredictor } from "./predictor.js";
5
+ import { ascii85Decode, asciiHexDecode, runLengthDecode } from "./stream-filters.js";
5
6
  import { labToSrgb } from "./cie-color.js";
7
+ import { readFunction } from "./function.js";
6
8
  import { decodeCcitt } from "./ccitt.js";
7
9
  import { decodeJbig2 } from "./jbig2.js";
8
10
  import { decodeJpeg } from "./jpeg.js";
@@ -18,9 +20,10 @@ var MAX_PIXELS = 4e7;
18
20
  * colour spaces; Flate, LZW (EP12), RunLength, ASCII85, ASCIIHex and CCITT
19
21
  * Group 4 / Group 3 1-D fax (EP15) filters; PNG/TIFF predictors; bit depths
20
22
  * 1/2/4/8/16; an `/SMask` folded in as the PNG alpha channel; and a stencil
21
- * `/ImageMask` (§8.9.6.2) given back as RGBA in `fillHex`. Unsupported inputs
22
- * (Separation/DeviceN/Lab, CCITT Group 3 2-D) return a typed failure so the
23
- * caller records a loss.
23
+ * `/ImageMask` (§8.9.6.2) given back as RGBA in `fillHex`; `Lab` (§8.6.5.8) and
24
+ * a `Separation`/`DeviceN` through its own tint transform (§8.6.6.4).
25
+ * Unsupported inputs (CCITT Group 3 2-D) return a typed failure so the caller
26
+ * records a loss.
24
27
  *
25
28
  * @param file The owning file.
26
29
  * @param stream The image XObject, or an inline image wrapped as one.
@@ -300,6 +303,21 @@ function resolveColorSpace(file, csVal) {
300
303
  }
301
304
  };
302
305
  }
306
+ if (tag === "Separation" || tag === "DeviceN" || tag === "I") {
307
+ const names = file.resolve(cs[1] ?? PDF_NULL);
308
+ const components = tag === "Separation" ? 1 : Array.isArray(names) ? names.length : 0;
309
+ const alternate = resolveColorSpace(file, cs[2]);
310
+ const transform = readFunction(file, cs[3]);
311
+ if (components < 1 || !alternate || alternate.kind === "tint" || !transform) return void 0;
312
+ return {
313
+ kind: "tint",
314
+ components,
315
+ tint: {
316
+ transform,
317
+ alternate
318
+ }
319
+ };
320
+ }
303
321
  }
304
322
  function namedColorSpace(name) {
305
323
  if (name === "DeviceGray" || name === "G" || name === "CalGray") return {
@@ -363,6 +381,29 @@ function toColor(cs, s, px, bpc, decode) {
363
381
  samples: out
364
382
  };
365
383
  }
384
+ if (cs.kind === "tint" && cs.tint) {
385
+ const { transform, alternate } = cs.tint;
386
+ const n = cs.components;
387
+ const cache = /* @__PURE__ */ new Map();
388
+ const out = new Uint8Array(px * 3);
389
+ for (let i = 0; i < px; i++) {
390
+ const comps = [];
391
+ for (let k = 0; k < n; k++) comps.push(c01(s[i * n + k], k));
392
+ const key = comps.join(",");
393
+ let rgb = cache.get(key);
394
+ if (!rgb) {
395
+ rgb = alternateRgb(alternate, transform(comps));
396
+ if (cache.size < MAX_TINT_CACHE) cache.set(key, rgb);
397
+ }
398
+ out[i * 3] = rgb[0];
399
+ out[i * 3 + 1] = rgb[1];
400
+ out[i * 3 + 2] = rgb[2];
401
+ }
402
+ return {
403
+ color: "rgb",
404
+ samples: out
405
+ };
406
+ }
366
407
  if (cs.kind === "lab") {
367
408
  const lab = cs.lab;
368
409
  const out = new Uint8Array(px * 3);
@@ -422,6 +463,47 @@ function toColor(cs, s, px, bpc, decode) {
422
463
  samples: out
423
464
  };
424
465
  }
466
+ /** How many distinct tint tuples are remembered before the cache stops growing. */
467
+ var MAX_TINT_CACHE = 65536;
468
+ /** §8.6.6.4 — what the alternate space makes of the transform's output. */
469
+ function alternateRgb(alternate, out) {
470
+ const at = (i) => Math.min(1, Math.max(0, out[i] ?? 0));
471
+ const to255 = (v) => Math.round(v * 255);
472
+ if (alternate.kind === "cmyk") {
473
+ const k = at(3);
474
+ return [
475
+ to255((1 - at(0)) * (1 - k)),
476
+ to255((1 - at(1)) * (1 - k)),
477
+ to255((1 - at(2)) * (1 - k))
478
+ ];
479
+ }
480
+ if (alternate.kind === "lab" && alternate.lab) {
481
+ const r = alternate.lab.range;
482
+ const [sr, sg, sb] = labToSrgb(alternate.lab.white, [
483
+ at(0) * 100,
484
+ r[0] + at(1) * (r[1] - r[0]),
485
+ r[2] + at(2) * (r[3] - r[2])
486
+ ]);
487
+ return [
488
+ to255(sr),
489
+ to255(sg),
490
+ to255(sb)
491
+ ];
492
+ }
493
+ if (alternate.kind === "gray") {
494
+ const g = to255(at(0));
495
+ return [
496
+ g,
497
+ g,
498
+ g
499
+ ];
500
+ }
501
+ return [
502
+ to255(at(0)),
503
+ to255(at(1)),
504
+ to255(at(2))
505
+ ];
506
+ }
425
507
  function paletteRgb(base, lookup, off) {
426
508
  if (base.kind === "rgb") return [
427
509
  lookup[off] ?? 0,
@@ -651,61 +733,6 @@ function lzwEarlyChange(file, d) {
651
733
  const ec = decodeParmsOf(file, d)?.get("EarlyChange");
652
734
  return ec !== void 0 && file.resolve(ec) === 0 ? 0 : 1;
653
735
  }
654
- function runLengthDecode(data) {
655
- const out = [];
656
- let i = 0;
657
- while (i < data.length) {
658
- const len = data[i++];
659
- if (len === 128) break;
660
- if (len < 128) for (let j = 0; j <= len && i < data.length; j++) out.push(data[i++]);
661
- else {
662
- const b = data[i++] ?? 0;
663
- for (let j = 0; j < 257 - len; j++) out.push(b);
664
- }
665
- }
666
- return Uint8Array.from(out);
667
- }
668
- function ascii85Decode(data) {
669
- const out = [];
670
- let tuple = 0;
671
- let count = 0;
672
- for (const c of data) {
673
- if (c === 126) break;
674
- if (c <= 32) continue;
675
- if (c === 122 && count === 0) {
676
- out.push(0, 0, 0, 0);
677
- continue;
678
- }
679
- if (c < 33 || c > 117) continue;
680
- tuple = tuple * 85 + (c - 33);
681
- if (++count === 5) {
682
- out.push(tuple >>> 24 & 255, tuple >>> 16 & 255, tuple >>> 8 & 255, tuple & 255);
683
- tuple = 0;
684
- count = 0;
685
- }
686
- }
687
- if (count > 0) {
688
- for (let i = count; i < 5; i++) tuple = tuple * 85 + 84;
689
- for (let i = 0; i < count - 1; i++) out.push(tuple >>> 24 - i * 8 & 255);
690
- }
691
- return Uint8Array.from(out);
692
- }
693
- function asciiHexDecode(data) {
694
- const out = [];
695
- let hi = -1;
696
- for (const c of data) {
697
- if (c === 62) break;
698
- const v = hexVal(c);
699
- if (v < 0) continue;
700
- if (hi < 0) hi = v;
701
- else {
702
- out.push(hi << 4 | v);
703
- hi = -1;
704
- }
705
- }
706
- if (hi >= 0) out.push(hi << 4);
707
- return Uint8Array.from(out);
708
- }
709
736
  function decodeParmsOf(file, d) {
710
737
  const p = file.resolve(d.get("DecodeParms") ?? d.get("DP") ?? PDF_NULL);
711
738
  if (p instanceof Map) return p;
@@ -742,12 +769,6 @@ function boolOf(v) {
742
769
  function clamp01(x) {
743
770
  return x < 0 ? 0 : x > 1 ? 1 : x;
744
771
  }
745
- function hexVal(c) {
746
- if (c >= 48 && c <= 57) return c - 48;
747
- if (c >= 65 && c <= 70) return c - 65 + 10;
748
- if (c >= 97 && c <= 102) return c - 97 + 10;
749
- return -1;
750
- }
751
772
  function fail(severity, detail) {
752
773
  return {
753
774
  ok: false,
@@ -29,6 +29,12 @@ export interface PdfImage {
29
29
  readonly crop?: ImageCrop;
30
30
  /** Enclosing marked-content id, if the placement was inside a `/Figure`. */
31
31
  readonly mcid?: number;
32
+ /**
33
+ * §11.6.4.4 `/ca` — how opaque the picture is drawn, `0..1`. Absent is
34
+ * opaque. alphatrans.pdf lays a photograph in at half strength over three
35
+ * coloured squares, and drawn full-strength it hid all three.
36
+ */
37
+ readonly alpha?: number;
32
38
  /**
33
39
  * §8.5.3 — where this was painted, as the chain of positions leading to it.
34
40
  * The same key a lifted path carries, so the two can be ordered against each
@@ -1,12 +1,13 @@
1
1
  import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
2
2
  import { FEATURES } from "../core/ir/features.js";
3
+ import { encodePng } from "../core/png-encode.js";
4
+ import { buildAlphaMap, sampledShading } from "./shading.js";
3
5
  import { interpretContent, multiply } from "./content.js";
4
- import { buildAlphaMap } from "./shading.js";
5
6
  import { collectPageAppearances } from "./annots.js";
6
7
  import { hiddenProperties, hiddenXObject } from "./optional-content.js";
8
+ import { buildFonts } from "./text.js";
7
9
  import { decodePdfImage } from "./image-decode.js";
8
10
  //#region src/pdf-reader/images.ts
9
- var NO_FONTS = /* @__PURE__ */ new Map();
10
11
  var MAX_FORM_DEPTH = 12;
11
12
  var MAX_IMAGES = 4096;
12
13
  /**
@@ -23,6 +24,14 @@ function collectPageImages(file, page) {
23
24
  const lossByDetail = /* @__PURE__ */ new Map();
24
25
  const visiting = /* @__PURE__ */ new Set();
25
26
  const alphaCache = /* @__PURE__ */ new Map();
27
+ const fontCache = /* @__PURE__ */ new Map();
28
+ const fontsOf = (resources) => {
29
+ const had = fontCache.get(resources);
30
+ if (had) return had;
31
+ const made = buildFonts(file, resources);
32
+ fontCache.set(resources, made);
33
+ return made;
34
+ };
26
35
  const addLoss = (severity, detail) => {
27
36
  if (!lossByDetail.has(detail)) lossByDetail.set(detail, {
28
37
  severity,
@@ -38,7 +47,7 @@ function collectPageImages(file, page) {
38
47
  paints = buildAlphaMap(file, resources);
39
48
  alphaCache.set(resources, paints);
40
49
  }
41
- const result = interpretContent(content, NO_FONTS, baseCtm, void 0, paints, void 0, hiddenProperties(file, resources));
50
+ const result = interpretContent(content, fontsOf(resources), baseCtm, void 0, paints, void 0, hiddenProperties(file, resources));
42
51
  const patterns = resources ? file.get(resources, "Pattern") : PDF_NULL;
43
52
  const patternDict = patterns instanceof Map ? patterns : void 0;
44
53
  for (const vector of result.vectors) {
@@ -51,13 +60,46 @@ function collectPageImages(file, page) {
51
60
  walk(patternRes instanceof Map ? patternRes : resources, file.streamData(stream), multiply(matrixOf(file, stream.dict), baseCtm), depth + 1, inheritedMcid, [...prefix, vector.order]);
52
61
  visiting.delete(stream);
53
62
  }
63
+ for (const call of result.glyphs) {
64
+ if (depth >= MAX_FORM_DEPTH || visiting.has(call.stream)) continue;
65
+ visiting.add(call.stream);
66
+ walk(call.resources ?? resources, file.streamData(call.stream), call.ctm, depth + 1, inheritedMcid, [...prefix, call.order]);
67
+ visiting.delete(call.stream);
68
+ }
69
+ for (const paint of result.shadings) {
70
+ if (images.length >= MAX_IMAGES) return;
71
+ const dict = resources ? file.get(resources, "Shading") : PDF_NULL;
72
+ const shading = dict instanceof Map ? file.resolve(dict.get(paint.name) ?? PDF_NULL) : PDF_NULL;
73
+ const sh = shading instanceof PdfStream ? shading.dict : shading instanceof Map ? shading : void 0;
74
+ if (!sh) continue;
75
+ if (paint.masked) continue;
76
+ const sampled = sampledShading(file, sh);
77
+ if (!sampled) continue;
78
+ const placed = multiply(matrixOf(file, sh), paint.ctm);
79
+ const [dx0, dx1, dy0, dy1] = sampled.domain;
80
+ const unit = [
81
+ dx1 - dx0,
82
+ 0,
83
+ 0,
84
+ dy1 - dy0,
85
+ dx0,
86
+ dy0
87
+ ];
88
+ images.push({
89
+ ...geometry(multiply(unit, placed), {
90
+ bytes: encodePng(sampled.size, sampled.size, "rgb", sampled.rgb),
91
+ format: "png"
92
+ }, inheritedMcid, paint.clip),
93
+ orderKey: [...prefix, paint.order]
94
+ });
95
+ }
54
96
  for (const placement of result.images) {
55
97
  if (images.length >= MAX_IMAGES) return;
56
98
  if (placement.inline) {
57
99
  const decoded = decodePdfImage(file, new PdfStream(placement.inline.dict, placement.inline.data), placement.fillHex);
58
100
  if (decoded.ok) {
59
101
  images.push({
60
- ...geometry(placement.ctm, decoded, placement.mcid ?? inheritedMcid, placement.clip),
102
+ ...geometry(placement.ctm, decoded, placement.mcid ?? inheritedMcid, placement.clip, placement.alpha),
61
103
  orderKey: [...prefix, placement.order]
62
104
  });
63
105
  if (decoded.degraded) addLoss("degraded", decoded.degraded);
@@ -73,7 +115,7 @@ function collectPageImages(file, page) {
73
115
  const decoded = decodePdfImage(file, stream, placement.fillHex);
74
116
  if (decoded.ok) {
75
117
  images.push({
76
- ...geometry(placement.ctm, decoded, mcid, placement.clip),
118
+ ...geometry(placement.ctm, decoded, mcid, placement.clip, placement.alpha),
77
119
  orderKey: [...prefix, placement.order]
78
120
  });
79
121
  if (decoded.degraded) addLoss("degraded", decoded.degraded);
@@ -104,7 +146,7 @@ function collectPageImages(file, page) {
104
146
  losses: [...lossByDetail.values()]
105
147
  };
106
148
  }
107
- function geometry(ctm, decoded, mcid, clip) {
149
+ function geometry(ctm, decoded, mcid, clip, alpha) {
108
150
  const widthPt = Math.hypot(ctm[0], ctm[1]) || 1;
109
151
  const heightPt = Math.hypot(ctm[2], ctm[3]) || 1;
110
152
  const angle = Math.atan2(ctm[1], ctm[0]) * 180 / Math.PI;
@@ -130,7 +172,8 @@ function geometry(ctm, decoded, mcid, clip) {
130
172
  y: cy - shownH / 2,
131
173
  ...crop ? { crop } : {},
132
174
  ...Math.abs(angle) > .5 ? { rotationDeg: angle } : {},
133
- ...mcid !== void 0 ? { mcid } : {}
175
+ ...mcid !== void 0 ? { mcid } : {},
176
+ ...alpha !== void 0 ? { alpha } : {}
134
177
  };
135
178
  }
136
179
  /** Below this the clip took a sliver off an edge and is not worth a crop. */
@@ -1809,21 +1809,21 @@ var Corner = /* @__PURE__ */ function(Corner) {
1809
1809
  function symbolCodeLength(symbols) {
1810
1810
  return Math.ceil(Math.log2(Math.max(symbols, 1)));
1811
1811
  }
1812
- function decodeTextRegion(mq, symbols, p, stripsLog) {
1812
+ function decodeTextRegion(mq, symbols, p, stripsLog, lent) {
1813
1813
  const bmp = makeBitmap(p.width, p.height, p.defPixel);
1814
1814
  const strips = 1 << stripsLog;
1815
- const codeLen = symbolCodeLength(symbols.length);
1816
- const iadt = new IntDecoder(mq);
1817
- const iafs = new IntDecoder(mq);
1818
- const iads = new IntDecoder(mq);
1819
- const iait = new IntDecoder(mq);
1820
- const iari = new IntDecoder(mq);
1821
- const iardw = new IntDecoder(mq);
1822
- const iardh = new IntDecoder(mq);
1823
- const iardx = new IntDecoder(mq);
1824
- const iardy = new IntDecoder(mq);
1825
- const iaid = new IdDecoder(mq, codeLen);
1826
- const refineCx = newContexts(8192);
1815
+ const codeLen = lent?.codeLen ?? symbolCodeLength(symbols.length);
1816
+ const iadt = lent?.iadt ?? new IntDecoder(mq);
1817
+ const iafs = lent?.iafs ?? new IntDecoder(mq);
1818
+ const iads = lent?.iads ?? new IntDecoder(mq);
1819
+ const iait = lent?.iait ?? new IntDecoder(mq);
1820
+ const iari = lent?.iari ?? new IntDecoder(mq);
1821
+ const iardw = lent?.iardw ?? new IntDecoder(mq);
1822
+ const iardh = lent?.iardh ?? new IntDecoder(mq);
1823
+ const iardx = lent?.iardx ?? new IntDecoder(mq);
1824
+ const iardy = lent?.iardy ?? new IntDecoder(mq);
1825
+ const iaid = lent?.iaid ?? new IdDecoder(mq, codeLen);
1826
+ const refineCx = lent?.refineCx ?? newContexts(8192);
1827
1827
  const num = (v) => v === OOB ? 0 : v;
1828
1828
  let stripT = -num(iadt.decode()) * strips;
1829
1829
  let firstS = 0;
@@ -1862,10 +1862,10 @@ function decodeTextRegion(mq, symbols, p, stripsLog) {
1862
1862
  compose(bmp, sym, x, y, p.combOp);
1863
1863
  curS += (p.transposed ? sh : sw) - 1;
1864
1864
  placed++;
1865
- if (placed >= p.instances) break;
1866
1865
  const ids = iads.decode();
1867
1866
  if (ids === OOB) break;
1868
1867
  curS += ids + p.dsOffset;
1868
+ if (placed > p.instances * 2 + 16) break;
1869
1869
  }
1870
1870
  }
1871
1871
  return bmp;
@@ -1890,6 +1890,22 @@ function decodeSymbolDictionary(mq, input, newCount, exportCount, template, at,
1890
1890
  const refineCx = newContexts(8192);
1891
1891
  const newSymbols = [];
1892
1892
  const num = (v) => v === OOB ? 0 : v;
1893
+ const codeLen = symbolCodeLength(input.length + newCount);
1894
+ const iaid = new IdDecoder(mq, codeLen);
1895
+ const lent = {
1896
+ codeLen,
1897
+ iadt: new IntDecoder(mq),
1898
+ iafs: new IntDecoder(mq),
1899
+ iads: new IntDecoder(mq),
1900
+ iait: new IntDecoder(mq),
1901
+ iari: new IntDecoder(mq),
1902
+ iardw: new IntDecoder(mq),
1903
+ iardh: new IntDecoder(mq),
1904
+ iardx,
1905
+ iardy,
1906
+ iaid,
1907
+ refineCx
1908
+ };
1893
1909
  let height = 0;
1894
1910
  let guard = 0;
1895
1911
  while (newSymbols.length < newCount && guard++ < 1e4) {
@@ -1909,7 +1925,7 @@ function decodeSymbolDictionary(mq, input, newCount, exportCount, template, at,
1909
1925
  const instances = num(iaai.decode());
1910
1926
  if (instances === 1) {
1911
1927
  const all = [...input, ...newSymbols];
1912
- const id = new IdDecoder(mq, Math.max(1, Math.ceil(Math.log2(Math.max(all.length + newCount, 1))))).decode();
1928
+ const id = iaid.decode();
1913
1929
  const rdx = num(iardx.decode());
1914
1930
  const rdy = num(iardy.decode());
1915
1931
  newSymbols.push(decodeRefinement(mq, refineCx, width, height, rTemplate, all[id] ?? makeBitmap(1, 1), rdx, rdy, rAt, false));
@@ -1928,7 +1944,7 @@ function decodeSymbolDictionary(mq, input, newCount, exportCount, template, at,
1928
1944
  refine: true,
1929
1945
  rTemplate,
1930
1946
  rAt
1931
- }, 0));
1947
+ }, 0, lent));
1932
1948
  }
1933
1949
  }
1934
1950
  }
@@ -2039,15 +2055,73 @@ function decodeTextRegionHuff(bits, data, symbols, p, stripsLog, t) {
2039
2055
  compose(bmp, sym, x, y, p.combOp);
2040
2056
  curS += (p.transposed ? sh : sw) - 1;
2041
2057
  placed++;
2042
- if (placed >= p.instances) break;
2043
2058
  const ids = huffDecode(bits, t.ds);
2044
2059
  if (ids === OOB) break;
2045
2060
  curS += ids + p.dsOffset;
2061
+ if (placed > p.instances * 2 + 16) break;
2046
2062
  }
2047
2063
  }
2048
2064
  return bmp;
2049
2065
  }
2050
2066
  /**
2067
+ * §6.5.8.2 — one shape of a REFINING Huffman dictionary.
2068
+ *
2069
+ * The dictionary states how many instances the shape is made of. One is a
2070
+ * refinement of a symbol it already holds — the id in as many bits as the
2071
+ * dictionary will end with, the offsets through table B.15, and the
2072
+ * refinement's own length through B.1, so the reader steps over exactly that.
2073
+ * Several is a text region of its own, read with the standard tables §6.5.8.2.1
2074
+ * names and with the symbol ids in plain fixed-width codes.
2075
+ *
2076
+ * @returns The shape, which is `width` by `height` whatever it took to make.
2077
+ */
2078
+ function aggregateSymbolHuff(bits, data, symbols, width, height, codeLen, refineCx, agg) {
2079
+ const num = (v) => v === OOB ? 0 : v;
2080
+ const instances = num(huffDecode(bits, agg.inst));
2081
+ const b15 = standardTable(15);
2082
+ const b1 = standardTable(1);
2083
+ if (instances !== 1) {
2084
+ const ids = buildHuffTable(Array.from({ length: 1 << codeLen }, (_, i) => ({
2085
+ prefLen: codeLen,
2086
+ rangeLen: 0,
2087
+ rangeLow: i
2088
+ })));
2089
+ return decodeTextRegionHuff(bits, data, symbols, {
2090
+ width,
2091
+ height,
2092
+ instances: Math.max(1, instances),
2093
+ stripT: 0,
2094
+ refCorner: Corner.TopLeft,
2095
+ transposed: false,
2096
+ combOp: 0,
2097
+ defPixel: 0,
2098
+ dsOffset: 0,
2099
+ refine: true,
2100
+ rTemplate: agg.rTemplate,
2101
+ rAt: agg.rAt
2102
+ }, 0, {
2103
+ fs: standardTable(6),
2104
+ ds: standardTable(8),
2105
+ dt: standardTable(11),
2106
+ rdw: b15,
2107
+ rdh: b15,
2108
+ rdx: b15,
2109
+ rdy: b15,
2110
+ rsize: b1,
2111
+ symbolIds: ids
2112
+ });
2113
+ }
2114
+ const id = bits.readBits(codeLen);
2115
+ const rdx = num(huffDecode(bits, b15));
2116
+ const rdy = num(huffDecode(bits, b15));
2117
+ const size = num(huffDecode(bits, b1));
2118
+ bits.align();
2119
+ const start = bits.bytePos;
2120
+ const bmp = decodeRefinement(new MQDecoder(data.subarray(start)), refineCx, width, height, agg.rTemplate, symbols[id] ?? makeBitmap(1, 1), rdx, rdy, agg.rAt, false);
2121
+ bits.skipTo(start + size);
2122
+ return bmp;
2123
+ }
2124
+ /**
2051
2125
  * §6.5.9 — a Huffman-coded symbol dictionary.
2052
2126
  *
2053
2127
  * The shapes of a height class are not coded one by one: their widths are read
@@ -2055,9 +2129,11 @@ function decodeTextRegionHuff(bits, data, symbols, p, stripsLog, t) {
2055
2129
  * apart by those widths. That is why a Huffman dictionary needs a size field
2056
2130
  * the arithmetic one does not.
2057
2131
  */
2058
- function decodeSymbolDictionaryHuff(bits, data, input, newCount, exportCount, dh, dw, bmSize) {
2132
+ function decodeSymbolDictionaryHuff(bits, data, input, newCount, exportCount, dh, dw, bmSize, agg) {
2059
2133
  const newSymbols = [];
2060
2134
  const num = (v) => v === OOB ? 0 : v;
2135
+ const codeLen = symbolCodeLength(input.length + newCount);
2136
+ const refineCx = newContexts(8192);
2061
2137
  let height = 0;
2062
2138
  let guard = 0;
2063
2139
  while (newSymbols.length < newCount && guard++ < 1e4) {
@@ -2071,9 +2147,14 @@ function decodeSymbolDictionaryHuff(bits, data, input, newCount, exportCount, dh
2071
2147
  if (d === OOB) break;
2072
2148
  width += d;
2073
2149
  if (width <= 0 || width > 16384 || newSymbols.length + widths.length >= newCount + 1) break;
2150
+ if (agg) {
2151
+ newSymbols.push(aggregateSymbolHuff(bits, data, [...input, ...newSymbols], width, height, codeLen, refineCx, agg));
2152
+ continue;
2153
+ }
2074
2154
  widths.push(width);
2075
2155
  totalWidth += width;
2076
2156
  }
2157
+ if (agg) continue;
2077
2158
  if (widths.length === 0) continue;
2078
2159
  const size = num(huffDecode(bits, bmSize));
2079
2160
  bits.align();
@@ -2441,7 +2522,11 @@ function decodeJbig2(data, globals, width, height) {
2441
2522
  const dwSel = flags >> 4 & 3;
2442
2523
  const bmSel = flags >> 6 & 1;
2443
2524
  const bits = new BitReader(bytes.subarray(seg.start + r.pos, seg.end));
2444
- symbolsBySegment.set(seg.number, decodeSymbolDictionaryHuff(bits, bytes.subarray(seg.start + r.pos, seg.end), inherited, newCount, exportCount, pick(dhSel, [4, 5]), pick(dwSel, [2, 3]), bmSel === 1 ? custom[ci++] ?? standardTable(1) : standardTable(1)));
2525
+ symbolsBySegment.set(seg.number, decodeSymbolDictionaryHuff(bits, bytes.subarray(seg.start + r.pos, seg.end), inherited, newCount, exportCount, pick(dhSel, [4, 5]), pick(dwSel, [2, 3]), bmSel === 1 ? custom[ci++] ?? standardTable(1) : standardTable(1), refAgg ? {
2526
+ inst: (flags >> 7 & 1) === 1 ? custom[ci++] ?? standardTable(1) : standardTable(1),
2527
+ rTemplate,
2528
+ rAt: rAt.length > 0 ? rAt : NOMINAL_AT_REFINE
2529
+ } : void 0));
2445
2530
  continue;
2446
2531
  }
2447
2532
  symbolsBySegment.set(seg.number, decodeSymbolDictionary(new MQDecoder(bytes.subarray(seg.start + r.pos, seg.end)), inherited, newCount, exportCount, template, at.length > 0 ? at : NOMINAL_AT[template] ?? [], refAgg, rTemplate, rAt.length > 0 ? rAt : NOMINAL_AT_REFINE));