kordoc 4.24.6 → 4.24.7

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 (139) hide show
  1. package/README.md +3 -2
  2. package/dist/{-7J7Y3XPH.js → -GQDQXRQN.js} +11 -11
  3. package/dist/{chunk-AM5ALISV.cjs → chunk-7BZBMXWD.cjs} +6 -6
  4. package/dist/chunk-7BZBMXWD.cjs.map +1 -0
  5. package/dist/{chunk-VMRH6UVA.js → chunk-7ICC55XY.js} +2 -2
  6. package/dist/{chunk-435ECXNG.cjs → chunk-AYGMF6H5.cjs} +3 -3
  7. package/dist/chunk-AYGMF6H5.cjs.map +1 -0
  8. package/dist/{chunk-3ZLS54V4.js → chunk-BLWDQEBY.js} +2 -2
  9. package/dist/{chunk-WNOWRBM4.js → chunk-BNIXSB6D.js} +2 -2
  10. package/dist/{chunk-5XBZJ3O4.js → chunk-C4AY47C5.js} +2 -2
  11. package/dist/{chunk-PKCOOSDT.js → chunk-CG2Z6NB3.js} +2 -2
  12. package/dist/{chunk-G343MTBX.js → chunk-DEDUWANG.js} +43 -15
  13. package/dist/chunk-DEDUWANG.js.map +1 -0
  14. package/dist/{chunk-TLJJF3J6.js → chunk-DZRXJEOF.js} +3 -3
  15. package/dist/{chunk-TZVVK5MI.js → chunk-E35YPWOX.js} +2 -2
  16. package/dist/{chunk-W75FRCAP.js → chunk-EIGCAAVM.js} +909 -36
  17. package/dist/chunk-EIGCAAVM.js.map +1 -0
  18. package/dist/{chunk-BKTEGPEO.js → chunk-EIUXBJZP.js} +3 -3
  19. package/dist/{chunk-XVTGFK2F.js → chunk-EJPPVB2Y.js} +2 -2
  20. package/dist/{chunk-EVBSSU3N.js → chunk-FIS3WY5Q.js} +2 -2
  21. package/dist/{chunk-GI5LWQ3S.js → chunk-FQVWYJNM.js} +2 -2
  22. package/dist/{chunk-BZW2WS44.js → chunk-GMKFXPEI.js} +82 -715
  23. package/dist/chunk-GMKFXPEI.js.map +1 -0
  24. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  25. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  26. package/dist/{chunk-DFRQD254.js → chunk-JKZY6FX5.js} +2 -2
  27. package/dist/{chunk-XM2Y4DB5.js → chunk-MLFPEETP.js} +2 -2
  28. package/dist/{chunk-JIU7E7Z6.cjs → chunk-MNG25FK4.cjs} +1082 -209
  29. package/dist/chunk-MNG25FK4.cjs.map +1 -0
  30. package/dist/{chunk-5RZBLPVP.js → chunk-N2JZGYFK.js} +2 -2
  31. package/dist/{chunk-GYX26HZR.js → chunk-O4RQCDN2.js} +910 -37
  32. package/dist/chunk-O4RQCDN2.js.map +1 -0
  33. package/dist/{chunk-BBKRJE57.js → chunk-OMJPCWKS.js} +2 -2
  34. package/dist/{chunk-RMUDX5Q2.js → chunk-OPNMXBUR.js} +2 -2
  35. package/dist/chunk-TK3FDAEE.cjs.map +1 -1
  36. package/dist/{chunk-HAMIOQYO.js → chunk-UDUEOBS5.js} +2 -2
  37. package/dist/{chunk-J4XCQNVH.cjs → chunk-V4PS653P.cjs} +140 -773
  38. package/dist/chunk-V4PS653P.cjs.map +1 -0
  39. package/dist/{chunk-ISTIOEIL.cjs → chunk-VHAOAQZZ.cjs} +3 -3
  40. package/dist/chunk-VHAOAQZZ.cjs.map +1 -0
  41. package/dist/{chunk-EBJWBGWF.js → chunk-WVZXPEHX.js} +82 -715
  42. package/dist/chunk-WVZXPEHX.js.map +1 -0
  43. package/dist/{chunk-2JDVPQ33.cjs → chunk-YQB3LPKQ.cjs} +2 -2
  44. package/dist/chunk-YQB3LPKQ.cjs.map +1 -0
  45. package/dist/{chunk-KR4UKOX2.js → chunk-ZQQGV7K2.js} +6 -6
  46. package/dist/chunks-XURCANPP.js +10 -0
  47. package/dist/cli.js +30 -30
  48. package/dist/{formula-DKNZKJ5Z.js → formula-6XLWGUCB.js} +4 -4
  49. package/dist/{formula-D7LBREWP.cjs → formula-C55TRBHH.cjs} +9 -9
  50. package/dist/formula-C55TRBHH.cjs.map +1 -0
  51. package/dist/{formula-T7D5JIZR.js → formula-DCC5EOSX.js} +3 -3
  52. package/dist/{image-ocr-N3I3MUKF.cjs → image-ocr-EV7GVD3W.cjs} +17 -17
  53. package/dist/image-ocr-EV7GVD3W.cjs.map +1 -0
  54. package/dist/{image-ocr-7NCSIZGA.js → image-ocr-K3ZXDUNU.js} +6 -6
  55. package/dist/{image-ocr-5POXVGN2.js → image-ocr-L736MGYD.js} +9 -9
  56. package/dist/index.cjs +729 -701
  57. package/dist/index.cjs.map +1 -1
  58. package/dist/index.js +38 -10
  59. package/dist/index.js.map +1 -1
  60. package/dist/mcp.js +23 -23
  61. package/dist/{model-bundle-XT7G2IJU.js → model-bundle-OOOJFMGR.js} +5 -5
  62. package/dist/{models-AB2VO45L.js → models-ELFZII4R.js} +5 -5
  63. package/dist/page-range-QHHFTL7X.cjs.map +1 -1
  64. package/dist/{parse-worker-v2-YSSAUPWU.js → parse-worker-v2-WS35WZY5.js} +12 -12
  65. package/dist/{parser-KGDUPSA7.js → parser-CNRI55MG.js} +9 -9
  66. package/dist/{parser-ES3I54QL.js → parser-K7Y44QHS.js} +12 -12
  67. package/dist/{parser-AGDY3ZOA.cjs → parser-X6P3JBT2.cjs} +159 -159
  68. package/dist/parser-X6P3JBT2.cjs.map +1 -0
  69. package/dist/{pdf-ocr-7QSP7JLG.js → pdf-ocr-MVNHXGPS.js} +6 -6
  70. package/dist/{pdf-ocr-53WUTX6H.js → pdf-ocr-QQPZLIHM.js} +9 -9
  71. package/dist/pdf-ocr-S6GSKOPT.cjs +31 -0
  72. package/dist/pdf-ocr-S6GSKOPT.cjs.map +1 -0
  73. package/dist/{profile-io-GGEJ3MZW.js → profile-io-GSRLMU4D.js} +3 -3
  74. package/dist/{rasterize-YXICEI5S.js → rasterize-3H5QKDH2.js} +3 -3
  75. package/dist/{redact-doc-OYXBNTWI.js → redact-doc-TLBF7J7W.js} +11 -11
  76. package/dist/{render-4PBNDLK2.js → render-FTCOQ6WO.js} +7 -7
  77. package/dist/seal-74R7KDRY.js +10 -0
  78. package/dist/{setup-WHTMPK74.js → setup-WVZ47AKV.js} +3 -3
  79. package/dist/visual-TEW6Y3NH.js +21 -0
  80. package/dist/{watch-P6UWW52D.js → watch-CA32Z7XB.js} +12 -12
  81. package/package.json +1 -1
  82. package/dist/chunk-2JDVPQ33.cjs.map +0 -1
  83. package/dist/chunk-435ECXNG.cjs.map +0 -1
  84. package/dist/chunk-AM5ALISV.cjs.map +0 -1
  85. package/dist/chunk-BZW2WS44.js.map +0 -1
  86. package/dist/chunk-EBJWBGWF.js.map +0 -1
  87. package/dist/chunk-G343MTBX.js.map +0 -1
  88. package/dist/chunk-GYX26HZR.js.map +0 -1
  89. package/dist/chunk-ISTIOEIL.cjs.map +0 -1
  90. package/dist/chunk-J4XCQNVH.cjs.map +0 -1
  91. package/dist/chunk-JIU7E7Z6.cjs.map +0 -1
  92. package/dist/chunk-W75FRCAP.js.map +0 -1
  93. package/dist/chunks-3MWJ7HOZ.js +0 -10
  94. package/dist/formula-D7LBREWP.cjs.map +0 -1
  95. package/dist/image-ocr-N3I3MUKF.cjs.map +0 -1
  96. package/dist/parser-AGDY3ZOA.cjs.map +0 -1
  97. package/dist/pdf-ocr-X6OAHK46.cjs +0 -31
  98. package/dist/pdf-ocr-X6OAHK46.cjs.map +0 -1
  99. package/dist/seal-KJYAU23C.js +0 -10
  100. package/dist/visual-PHKJTSFR.js +0 -21
  101. /package/dist/{-7J7Y3XPH.js.map → -GQDQXRQN.js.map} +0 -0
  102. /package/dist/{chunk-VMRH6UVA.js.map → chunk-7ICC55XY.js.map} +0 -0
  103. /package/dist/{chunk-3ZLS54V4.js.map → chunk-BLWDQEBY.js.map} +0 -0
  104. /package/dist/{chunk-WNOWRBM4.js.map → chunk-BNIXSB6D.js.map} +0 -0
  105. /package/dist/{chunk-5XBZJ3O4.js.map → chunk-C4AY47C5.js.map} +0 -0
  106. /package/dist/{chunk-PKCOOSDT.js.map → chunk-CG2Z6NB3.js.map} +0 -0
  107. /package/dist/{chunk-TLJJF3J6.js.map → chunk-DZRXJEOF.js.map} +0 -0
  108. /package/dist/{chunk-TZVVK5MI.js.map → chunk-E35YPWOX.js.map} +0 -0
  109. /package/dist/{chunk-BKTEGPEO.js.map → chunk-EIUXBJZP.js.map} +0 -0
  110. /package/dist/{chunk-XVTGFK2F.js.map → chunk-EJPPVB2Y.js.map} +0 -0
  111. /package/dist/{chunk-EVBSSU3N.js.map → chunk-FIS3WY5Q.js.map} +0 -0
  112. /package/dist/{chunk-GI5LWQ3S.js.map → chunk-FQVWYJNM.js.map} +0 -0
  113. /package/dist/{chunk-DFRQD254.js.map → chunk-JKZY6FX5.js.map} +0 -0
  114. /package/dist/{chunk-XM2Y4DB5.js.map → chunk-MLFPEETP.js.map} +0 -0
  115. /package/dist/{chunk-5RZBLPVP.js.map → chunk-N2JZGYFK.js.map} +0 -0
  116. /package/dist/{chunk-BBKRJE57.js.map → chunk-OMJPCWKS.js.map} +0 -0
  117. /package/dist/{chunk-RMUDX5Q2.js.map → chunk-OPNMXBUR.js.map} +0 -0
  118. /package/dist/{chunk-HAMIOQYO.js.map → chunk-UDUEOBS5.js.map} +0 -0
  119. /package/dist/{chunk-KR4UKOX2.js.map → chunk-ZQQGV7K2.js.map} +0 -0
  120. /package/dist/{chunks-3MWJ7HOZ.js.map → chunks-XURCANPP.js.map} +0 -0
  121. /package/dist/{formula-DKNZKJ5Z.js.map → formula-6XLWGUCB.js.map} +0 -0
  122. /package/dist/{formula-T7D5JIZR.js.map → formula-DCC5EOSX.js.map} +0 -0
  123. /package/dist/{image-ocr-7NCSIZGA.js.map → image-ocr-K3ZXDUNU.js.map} +0 -0
  124. /package/dist/{image-ocr-5POXVGN2.js.map → image-ocr-L736MGYD.js.map} +0 -0
  125. /package/dist/{model-bundle-XT7G2IJU.js.map → model-bundle-OOOJFMGR.js.map} +0 -0
  126. /package/dist/{models-AB2VO45L.js.map → models-ELFZII4R.js.map} +0 -0
  127. /package/dist/{parse-worker-v2-YSSAUPWU.js.map → parse-worker-v2-WS35WZY5.js.map} +0 -0
  128. /package/dist/{parser-KGDUPSA7.js.map → parser-CNRI55MG.js.map} +0 -0
  129. /package/dist/{parser-ES3I54QL.js.map → parser-K7Y44QHS.js.map} +0 -0
  130. /package/dist/{pdf-ocr-53WUTX6H.js.map → pdf-ocr-MVNHXGPS.js.map} +0 -0
  131. /package/dist/{pdf-ocr-7QSP7JLG.js.map → pdf-ocr-QQPZLIHM.js.map} +0 -0
  132. /package/dist/{profile-io-GGEJ3MZW.js.map → profile-io-GSRLMU4D.js.map} +0 -0
  133. /package/dist/{rasterize-YXICEI5S.js.map → rasterize-3H5QKDH2.js.map} +0 -0
  134. /package/dist/{redact-doc-OYXBNTWI.js.map → redact-doc-TLBF7J7W.js.map} +0 -0
  135. /package/dist/{render-4PBNDLK2.js.map → render-FTCOQ6WO.js.map} +0 -0
  136. /package/dist/{seal-KJYAU23C.js.map → seal-74R7KDRY.js.map} +0 -0
  137. /package/dist/{setup-WHTMPK74.js.map → setup-WVZ47AKV.js.map} +0 -0
  138. /package/dist/{visual-PHKJTSFR.js.map → visual-TEW6Y3NH.js.map} +0 -0
  139. /package/dist/{watch-P6UWW52D.js.map → watch-CA32Z7XB.js.map} +0 -0
@@ -6,11 +6,772 @@ import {
6
6
  HEADING_RATIO_H3,
7
7
  tidyScriptTags,
8
8
  toTex
9
- } from "./chunk-3ZLS54V4.js";
9
+ } from "./chunk-BLWDQEBY.js";
10
10
  import {
11
11
  safeMax,
12
12
  safeMin
13
- } from "./chunk-TZVVK5MI.js";
13
+ } from "./chunk-E35YPWOX.js";
14
+
15
+ // src/ocr/line-split.ts
16
+ function grayCrop(rgba, pageW, box) {
17
+ const out = new Uint8Array(box.w * box.h);
18
+ for (let y = 0; y < box.h; y++) {
19
+ let si = ((box.y + y) * pageW + box.x) * 4;
20
+ let di = y * box.w;
21
+ for (let x = 0; x < box.w; x++, si += 4, di++) {
22
+ out[di] = rgba[si] * 77 + rgba[si + 1] * 150 + rgba[si + 2] * 29 >> 8;
23
+ }
24
+ }
25
+ return out;
26
+ }
27
+ function inkStats(gray) {
28
+ const hist = new Uint32Array(256);
29
+ for (let i = 0; i < gray.length; i++) hist[gray[i]]++;
30
+ const total = gray.length;
31
+ let sumAll = 0;
32
+ for (let v = 0; v < 256; v++) sumAll += v * hist[v];
33
+ let wB = 0, sumB = 0, best = -1, thr = 127;
34
+ for (let t = 0; t < 256; t++) {
35
+ wB += hist[t];
36
+ if (wB === 0) continue;
37
+ const wF = total - wB;
38
+ if (wF === 0) break;
39
+ sumB += t * hist[t];
40
+ const mB = sumB / wB;
41
+ const mF = (sumAll - sumB) / wF;
42
+ const between = wB * wF * (mB - mF) * (mB - mF);
43
+ if (between > best) {
44
+ best = between;
45
+ thr = t;
46
+ }
47
+ }
48
+ let nDark = 0, sDark = 0;
49
+ for (let v = 0; v <= thr; v++) {
50
+ nDark += hist[v];
51
+ sDark += v * hist[v];
52
+ }
53
+ const nLight = total - nDark;
54
+ const sLight = sumAll - sDark;
55
+ if (nDark === 0 || nLight === 0) return { threshold: thr, darkInk: true, contrast: 0, inkRatio: 0 };
56
+ const darkInk = nDark <= nLight;
57
+ return {
58
+ threshold: thr,
59
+ darkInk,
60
+ contrast: sLight / nLight - sDark / nDark,
61
+ inkRatio: (darkInk ? nDark : nLight) / total
62
+ };
63
+ }
64
+ function splitRowBands(gray, w, h, ink, minBandRatio) {
65
+ const isInk = (v) => ink.darkInk ? v <= ink.threshold : v > ink.threshold;
66
+ const colInk = new Uint32Array(w);
67
+ for (let y = 0; y < h; y++) {
68
+ const off = y * w;
69
+ for (let x = 0; x < w; x++) if (isInk(gray[off + x])) colInk[x]++;
70
+ }
71
+ const ruleCol = new Uint8Array(w);
72
+ let liveW = 0;
73
+ for (let x = 0; x < w; x++) {
74
+ if (colInk[x] >= h * 0.85) ruleCol[x] = 1;
75
+ else liveW++;
76
+ }
77
+ const rowInk = new Uint32Array(h);
78
+ for (let y = 0; y < h; y++) {
79
+ let n = 0;
80
+ const off = y * w;
81
+ for (let x = 0; x < w; x++) if (!ruleCol[x] && isInk(gray[off + x])) n++;
82
+ rowInk[y] = n >= liveW * 0.85 ? 0 : n;
83
+ }
84
+ const blank = Math.max(1, Math.floor(w * 0.02));
85
+ let bands = [];
86
+ let start = -1;
87
+ for (let y = 0; y <= h; y++) {
88
+ const on = y < h && rowInk[y] > blank;
89
+ if (on && start < 0) start = y;
90
+ else if (!on && start >= 0) {
91
+ bands.push([start, y]);
92
+ start = -1;
93
+ }
94
+ }
95
+ const inkCols = (y0, y1) => {
96
+ let x0 = w, x1 = -1;
97
+ for (let y = y0; y < y1; y++) {
98
+ const off = y * w;
99
+ for (let x = 0; x < w; x++) if (!ruleCol[x] && isInk(gray[off + x])) {
100
+ if (x < x0) x0 = x;
101
+ if (x > x1) x1 = x;
102
+ }
103
+ }
104
+ return x1 >= x0 ? [x0, x1 + 1] : [0, w];
105
+ };
106
+ if (bands.length <= 1) return [{ y0: bands[0]?.[0] ?? 0, y1: bands[0]?.[1] ?? h, x0: 0, x1: w }];
107
+ const widths = bands.map(([y0, y1]) => {
108
+ const [a, b] = inkCols(y0, y1);
109
+ return b - a;
110
+ }).sort((a, b) => a - b);
111
+ const charSize = widths[widths.length >> 1];
112
+ const minBand = Math.max(3, minBandRatio * charSize);
113
+ for (let i = 0; i + 1 < bands.length; ) {
114
+ const gap = bands[i + 1][0] - bands[i][1];
115
+ if (gap <= Math.max(2, charSize * 0.15) && bands[i + 1][1] - bands[i][0] <= charSize * 1.3) {
116
+ bands.splice(i, 2, [bands[i][0], bands[i + 1][1]]);
117
+ } else i++;
118
+ }
119
+ while (bands.length > 1 && bands[0][0] <= 1 && bands[0][1] - bands[0][0] < minBand) bands.shift();
120
+ while (bands.length > 1 && bands[bands.length - 1][1] >= h - 1 && bands[bands.length - 1][1] - bands[bands.length - 1][0] < minBand) bands.pop();
121
+ for (; ; ) {
122
+ let idx = -1, minH = Infinity;
123
+ for (let i = 0; i < bands.length; i++) {
124
+ const bh = bands[i][1] - bands[i][0];
125
+ if (bh < minBand && bh < minH) {
126
+ minH = bh;
127
+ idx = i;
128
+ }
129
+ }
130
+ if (idx < 0 || bands.length === 1) break;
131
+ const gapPrev = idx > 0 ? bands[idx][0] - bands[idx - 1][1] : Infinity;
132
+ const gapNext = idx < bands.length - 1 ? bands[idx + 1][0] - bands[idx][1] : Infinity;
133
+ const j = gapPrev <= gapNext ? idx - 1 : idx + 1;
134
+ const a = Math.min(idx, j), b = Math.max(idx, j);
135
+ bands = [...bands.slice(0, a), [bands[a][0], bands[b][1]], ...bands.slice(b + 1)];
136
+ }
137
+ if (bands.length === 1) return [{ y0: bands[0][0], y1: bands[0][1], x0: 0, x1: w }];
138
+ return bands.map(([y0, y1]) => {
139
+ const [x0, x1] = inkCols(y0, y1);
140
+ return { y0, y1, x0, x1 };
141
+ });
142
+ }
143
+ function components(gray, w, h, ink) {
144
+ const isInk = (v) => ink.darkInk ? v <= ink.threshold : v > ink.threshold;
145
+ const comps = [];
146
+ const label = new Int32Array(w * h);
147
+ const stack = [];
148
+ for (let p0 = 0; p0 < w * h; p0++) {
149
+ if (label[p0] || !isInk(gray[p0])) continue;
150
+ const c = { x0: w, x1: 0, y0: h, y1: 0, id: comps.length + 1 };
151
+ label[p0] = c.id;
152
+ stack.push(p0);
153
+ while (stack.length) {
154
+ const p = stack.pop();
155
+ const x = p % w, y = p / w | 0;
156
+ if (x < c.x0) c.x0 = x;
157
+ if (x + 1 > c.x1) c.x1 = x + 1;
158
+ if (y < c.y0) c.y0 = y;
159
+ if (y + 1 > c.y1) c.y1 = y + 1;
160
+ for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) {
161
+ const nx = x + dx, ny = y + dy;
162
+ if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
163
+ const q = ny * w + nx;
164
+ if (!label[q] && isInk(gray[q])) {
165
+ label[q] = c.id;
166
+ stack.push(q);
167
+ }
168
+ }
169
+ }
170
+ if (c.y1 - c.y0 < h * 0.85 && c.x1 - c.x0 < w * 0.85) comps.push(c);
171
+ }
172
+ return { comps, label };
173
+ }
174
+ function leadingTriangle(gray, w, h, ink) {
175
+ const { comps, label } = components(gray, w, h, ink);
176
+ let charH = 0;
177
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
178
+ const first = comps.filter((c) => c.y1 - c.y0 >= charH * 0.4).sort((a, b) => a.x0 - b.x0)[0];
179
+ if (!first || charH < 8) return null;
180
+ const cw = first.x1 - first.x0, ch = first.y1 - first.y0;
181
+ if (cw < ch * 0.8 || cw > ch * 1.8) return null;
182
+ const rowSpan = (y) => {
183
+ let n = 0, lo = -1, hi = -1, runs = 0, prev = false;
184
+ for (let x = first.x0; x < first.x1; x++) {
185
+ const on = label[y * w + x] === first.id;
186
+ if (on) {
187
+ n++;
188
+ if (lo < 0) lo = x - first.x0;
189
+ hi = x - first.x0;
190
+ if (!prev) runs++;
191
+ }
192
+ prev = on;
193
+ }
194
+ return { n, lo, hi, runs };
195
+ };
196
+ const base = Math.max(rowSpan(first.y1 - 1).n, rowSpan(first.y1 - 2).n);
197
+ if (base < cw * 0.7) return null;
198
+ for (let y = first.y0; y < first.y0 + Math.max(1, Math.round(ch * 0.2)); y++) {
199
+ const r = rowSpan(y);
200
+ if (r.n === 0) continue;
201
+ if (r.hi - r.lo + 1 > cw * 0.4 || (r.lo + r.hi) / 2 < cw * 0.25 || (r.lo + r.hi) / 2 > cw * 0.75) return null;
202
+ }
203
+ const mid = rowSpan(first.y0 + Math.round(ch * 0.6));
204
+ if (mid.lo < 0 || mid.lo > cw * 0.3 || mid.hi < cw * 0.7) return null;
205
+ return mid.runs >= 2 ? "\u25B3" : "\u25B2";
206
+ }
207
+ function leaderRuns(gray, w, h, ink, minDots) {
208
+ const comps = components(gray, w, h, ink).comps.sort((a, b) => a.x0 - b.x0);
209
+ const blobs = [];
210
+ for (const c of comps) {
211
+ const last = blobs[blobs.length - 1];
212
+ if (last && c.x0 < last.x1) {
213
+ last.x1 = Math.max(last.x1, c.x1);
214
+ last.y0 = Math.min(last.y0, c.y0);
215
+ last.y1 = Math.max(last.y1, c.y1);
216
+ } else blobs.push({ x0: c.x0, x1: c.x1, y0: c.y0, y1: c.y1 });
217
+ }
218
+ let charH = 0;
219
+ for (const b of blobs) charH = Math.max(charH, b.y1 - b.y0);
220
+ if (charH < 8) return [];
221
+ const small = (c) => c.x1 - c.x0 <= charH * 0.3 && c.y1 - c.y0 <= charH * 0.3;
222
+ let bandTop = h, bandBot = 0;
223
+ for (const c of comps) if (!small(c)) {
224
+ bandTop = Math.min(bandTop, c.y0);
225
+ bandBot = Math.max(bandBot, c.y1);
226
+ }
227
+ const cy = (c) => (c.y0 + c.y1) / 2;
228
+ const isDot = (b) => small(b) && cy(b) >= bandTop && cy(b) <= bandBot;
229
+ const runs = [];
230
+ for (let i = 0; i < blobs.length; ) {
231
+ if (!isDot(blobs[i])) {
232
+ i++;
233
+ continue;
234
+ }
235
+ let j = i;
236
+ while (j + 1 < blobs.length && isDot(blobs[j + 1]) && blobs[j + 1].x0 - blobs[j].x1 <= charH && Math.abs(cy(blobs[j + 1]) - cy(blobs[i])) <= charH * 0.2) j++;
237
+ const x0 = blobs[i].x0, x1 = blobs[j].x1, y = cy(blobs[i]);
238
+ const onLine = comps.some((c) => !small(c) && c.y0 <= y && c.y1 >= y && (c.x1 <= x0 + 2 && c.x1 >= x0 - 2 * charH || c.x0 >= x1 - 2 && c.x0 <= x1 + 2 * charH));
239
+ const run = blobs.slice(i, j + 1).filter((b) => b.x0 > 0 && b.x1 < w);
240
+ const spread = (xs) => {
241
+ const v = [...xs].sort((a, b) => a - b).slice(xs.length >= 5 ? 1 : 0, xs.length >= 5 ? -1 : void 0);
242
+ return v[v.length - 1] / Math.max(1, v[0]);
243
+ };
244
+ const even = run.length >= 2 && spread(run.map((b) => b.x1 - b.x0)) <= 2.5 && spread(run.map((b) => b.y1 - b.y0)) <= 2.5 && spread(run.slice(1).map((b, k) => b.x0 - run[k].x0)) <= 1.8;
245
+ if (j - i + 1 >= minDots && onLine && even) runs.push([x0, x1]);
246
+ i = j + 1;
247
+ }
248
+ return runs;
249
+ }
250
+ function inkBounds(gray, w, h, ink) {
251
+ const isInk = (v) => ink.darkInk ? v <= ink.threshold : v > ink.threshold;
252
+ const colInk = new Uint32Array(w);
253
+ const rowInk = new Uint32Array(h);
254
+ for (let y = 0; y < h; y++) {
255
+ const off = y * w;
256
+ for (let x = 0; x < w; x++) if (isInk(gray[off + x])) {
257
+ colInk[x]++;
258
+ rowInk[y]++;
259
+ }
260
+ }
261
+ const ruleCol = new Uint8Array(w), ruleRow = new Uint8Array(h);
262
+ for (let x = 0; x < w; x++) if (colInk[x] >= h * 0.85) ruleCol[x] = 1;
263
+ for (let y = 0; y < h; y++) if (rowInk[y] >= w * 0.85) ruleRow[y] = 1;
264
+ let x0 = w, x1 = -1, y0 = h, y1 = -1;
265
+ for (let y = 0; y < h; y++) {
266
+ if (ruleRow[y]) continue;
267
+ const off = y * w;
268
+ for (let x = 0; x < w; x++) {
269
+ if (ruleCol[x] || !isInk(gray[off + x])) continue;
270
+ if (x < x0) x0 = x;
271
+ if (x > x1) x1 = x;
272
+ if (y < y0) y0 = y;
273
+ if (y > y1) y1 = y;
274
+ }
275
+ }
276
+ if (x1 < x0 || y1 < y0) return { x0: 0, y0: 0, x1: w, y1: h };
277
+ return { x0, y0, x1: x1 + 1, y1: y1 + 1 };
278
+ }
279
+ function bracketFeatures(gray, w, h, ink, cx) {
280
+ const { comps, label } = components(gray, w, h, ink);
281
+ let charH = 0;
282
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
283
+ if (charH < 8) return null;
284
+ const tol = Math.max(2, charH * 0.3);
285
+ let best = null, bestD = Infinity;
286
+ for (const c of comps) {
287
+ if (c.y1 - c.y0 < charH * 0.3 || c.x1 < cx - tol || c.x0 > cx + tol) continue;
288
+ const d = Math.abs((c.x0 + c.x1) / 2 - cx);
289
+ if (d < bestD) {
290
+ bestD = d;
291
+ best = c;
292
+ }
293
+ }
294
+ if (!best) return null;
295
+ const b = best;
296
+ const cw = b.x1 - b.x0, ch = b.y1 - b.y0;
297
+ if (cw < 3 || ch < 8) return null;
298
+ const spans = [];
299
+ for (let y = b.y0; y < b.y1; y++) {
300
+ let n = 0;
301
+ for (let x = b.x0; x < b.x1; x++) if (label[y * w + x] === b.id) n++;
302
+ spans.push(n);
303
+ }
304
+ const band = Math.max(1, Math.round(ch * 0.15));
305
+ const top = Math.max(...spans.slice(0, band)) / cw, bottom = Math.max(...spans.slice(ch - band)) / cw;
306
+ const midRows = spans.slice(Math.floor(ch * 0.3), Math.ceil(ch * 0.7)).sort((p, q) => p - q);
307
+ const stroke = midRows[midRows.length >> 1] ?? 0;
308
+ const taper = spans.filter((n) => n > stroke && n < cw).length / ch;
309
+ const fill = spans.reduce((a, n) => a + n, 0) / (cw * ch);
310
+ return { top, bottom, taper, fill, cw, ch, charH };
311
+ }
312
+ function romanStems(gray, w, h, ink, prevX, nextX) {
313
+ const { comps, label } = components(gray, w, h, ink);
314
+ let charH = 0;
315
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
316
+ if (charH < 10) return null;
317
+ const x0 = prevX + charH * 0.35, x1 = nextX - charH * 0.35;
318
+ const sel = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6 && (c.x0 + c.x1) / 2 >= x0 && (c.x0 + c.x1) / 2 < x1);
319
+ if (!sel.length) return null;
320
+ const ids = new Set(sel.map((c) => c.id));
321
+ const ux0 = Math.min(...sel.map((c) => c.x0)), ux1 = Math.max(...sel.map((c) => c.x1));
322
+ const uy0 = Math.min(...sel.map((c) => c.y0)), uy1 = Math.max(...sel.map((c) => c.y1));
323
+ const uh = uy1 - uy0;
324
+ const counts = /* @__PURE__ */ new Map();
325
+ let rows = 0;
326
+ for (let y = uy0 + Math.floor(uh * 0.25); y < uy1 - Math.floor(uh * 0.25); y++) {
327
+ let runs = 0, len = 0, thick = false;
328
+ for (let x = ux0; x <= ux1; x++) {
329
+ const on = x < ux1 && ids.has(label[y * w + x]);
330
+ if (on) len++;
331
+ else if (len) {
332
+ runs++;
333
+ if (len > uh * 0.35) thick = true;
334
+ len = 0;
335
+ }
336
+ }
337
+ rows++;
338
+ if (!thick) counts.set(runs, (counts.get(runs) ?? 0) + 1);
339
+ }
340
+ const [n, k] = [...counts].sort((a, b) => b[1] - a[1])[0] ?? [0, 0];
341
+ return n >= 1 && n <= 3 && k >= rows * 0.7 ? n : null;
342
+ }
343
+ function circledAt(gray, w, h, ink, xs) {
344
+ const { comps } = components(gray, w, h, ink);
345
+ let charH = 0;
346
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
347
+ const rings = charH < 10 ? [] : comps.filter((r) => {
348
+ const rw = r.x1 - r.x0, rh = r.y1 - r.y0;
349
+ return rh >= charH * 0.55 && rw >= rh * 0.8 && rw <= rh * 1.25 && comps.some((d) => d !== r && d.x0 > r.x0 && d.x1 < r.x1 && d.y0 > r.y0 && d.y1 < r.y1 && d.y1 - d.y0 >= rh * 0.35);
350
+ });
351
+ return xs.map((x) => rings.some((r) => x >= r.x0 && x <= r.x1));
352
+ }
353
+ function quoteHead(gray, w, h, ink, cx) {
354
+ const { comps, label } = components(gray, w, h, ink);
355
+ let charH = 0;
356
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
357
+ if (charH < 10) return null;
358
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
359
+ if (!tall.length) return null;
360
+ const mid = (Math.min(...tall.map((c) => c.y0)) + Math.max(...tall.map((c) => c.y1))) / 2;
361
+ let best = null, bestD = charH * 0.4;
362
+ for (const c of comps) {
363
+ const ch = c.y1 - c.y0, d = Math.abs((c.x0 + c.x1) / 2 - cx);
364
+ if (ch < charH * 0.12 || ch > charH * 0.5 || (c.y0 + c.y1) / 2 > mid || d > bestD) continue;
365
+ bestD = d;
366
+ best = c;
367
+ }
368
+ if (!best) return null;
369
+ let n = 0, sy = 0;
370
+ for (let y = best.y0; y < best.y1; y++) for (let x = best.x0; x < best.x1; x++) if (label[y * w + x] === best.id) {
371
+ n++;
372
+ sy += y;
373
+ }
374
+ return n ? (sy / n - best.y0) / (best.y1 - best.y0) : null;
375
+ }
376
+ function edgeTrim(gray, w, h, ink) {
377
+ const isInk = (v) => ink.darkInk ? v <= ink.threshold : v > ink.threshold;
378
+ const colInk = new Uint32Array(w);
379
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) if (isInk(gray[y * w + x])) colInk[x]++;
380
+ const ruleCol = new Uint8Array(w);
381
+ let liveW = 0;
382
+ for (let x = 0; x < w; x++) {
383
+ if (colInk[x] >= h * 0.85) ruleCol[x] = 1;
384
+ else liveW++;
385
+ }
386
+ const blank = Math.max(1, Math.floor(w * 0.02));
387
+ const bands = [];
388
+ let start = -1;
389
+ for (let y = 0; y <= h; y++) {
390
+ let n = 0;
391
+ if (y < h) {
392
+ for (let x = 0; x < w; x++) if (!ruleCol[x] && isInk(gray[y * w + x])) n++;
393
+ }
394
+ const on = y < h && n > blank && n < liveW * 0.85;
395
+ if (on && start < 0) start = y;
396
+ else if (!on && start >= 0) {
397
+ bands.push([start, y]);
398
+ start = -1;
399
+ }
400
+ }
401
+ let y0 = 0, y1 = h;
402
+ if (bands.length >= 2 && h >= 24) {
403
+ const mh = Math.max(...bands.map(([a, b]) => b - a));
404
+ const [top, next] = [bands[0], bands[1]], [bot, prev] = [bands[bands.length - 1], bands[bands.length - 2]];
405
+ if (top[0] <= 1 && top[1] - top[0] < mh * 0.45) y0 = top[1] + next[0] >> 1;
406
+ if (bot[1] >= h - 1 && bot[1] - bot[0] < mh * 0.45) y1 = bot[0] + prev[1] + 1 >> 1;
407
+ if (y0 > 0 || y1 < h) {
408
+ let runs = 0, len = 0;
409
+ for (let x = 0; x <= w; x++) {
410
+ let on = false;
411
+ if (x < w && !ruleCol[x]) for (let y = y0; y < y1 && !on; y++) on = isInk(gray[y * w + x]);
412
+ if (on) len++;
413
+ else {
414
+ if (len >= (y1 - y0) * 0.3) runs++;
415
+ len = 0;
416
+ }
417
+ }
418
+ if (runs < 2) {
419
+ y0 = 0;
420
+ y1 = h;
421
+ }
422
+ }
423
+ }
424
+ const textCol = (x) => {
425
+ for (let y = y0; y < y1; y++) if (isInk(gray[y * w + x])) return true;
426
+ return false;
427
+ };
428
+ const border = (x) => colInk[x] >= h * 0.95;
429
+ const gap = Math.max(2, Math.round(h * 0.1));
430
+ const clearFrom = (x, dir) => {
431
+ for (let k = 0; k < gap; k++) {
432
+ const c = x + dir * k;
433
+ if (c < 0 || c >= w || textCol(c)) return false;
434
+ }
435
+ return true;
436
+ };
437
+ let x0 = 0, x1 = w;
438
+ for (let x = 0; x < w * 0.2; x++) {
439
+ if (border(x)) {
440
+ let e = x;
441
+ while (e + 1 < w && border(e + 1)) e++;
442
+ if (clearFrom(e + 1, 1)) x0 = e + 1;
443
+ break;
444
+ }
445
+ if (textCol(x)) break;
446
+ }
447
+ for (let x = w - 1; x >= w * 0.8; x--) {
448
+ if (border(x)) {
449
+ let e = x;
450
+ while (e - 1 >= 0 && border(e - 1)) e--;
451
+ if (clearFrom(e - 1, -1)) x1 = e;
452
+ break;
453
+ }
454
+ if (textCol(x)) break;
455
+ }
456
+ return y0 > 0 || y1 < h || x0 > 0 || x1 < w ? { x0, x1, y0, y1 } : null;
457
+ }
458
+ function tallInkCount(gray, w, h, ink) {
459
+ const { comps } = components(gray, w, h, ink);
460
+ let charH = 0;
461
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
462
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.5).sort((a, b) => a.x0 - b.x0);
463
+ let n = 0, end = -1;
464
+ for (const c of tall) {
465
+ if (c.x0 >= end) n++;
466
+ end = Math.max(end, c.x1);
467
+ }
468
+ return n;
469
+ }
470
+ function gapGlyphs(gray, w, h, ink) {
471
+ const { comps, label } = components(gray, w, h, ink);
472
+ let charH = 0;
473
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
474
+ if (charH < 10) return { glyphs: [], bandH: 0 };
475
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
476
+ const bandT = Math.min(...tall.map((c) => c.y0)), bandB = Math.max(...tall.map((c) => c.y1)), bh = bandB - bandT;
477
+ const inBand = (c) => c.y1 > bandT && c.y0 < bandB;
478
+ const out = [];
479
+ for (const c of comps) {
480
+ if (!inBand(c)) continue;
481
+ const cw = c.x1 - c.x0, ch = c.y1 - c.y0;
482
+ if (ch >= bh * 0.9 && cw <= bh * 0.15) continue;
483
+ const others = comps.filter((o) => o !== c && inBand(o) && !(o.x0 >= c.x0 && o.x1 <= c.x1 && o.y0 >= c.y0 && o.y1 <= c.y1));
484
+ const apart = !others.some((o) => o.x0 < c.x1 + 1 && o.x1 > c.x0 - 1);
485
+ const nearlyApart = !others.some((o) => Math.min(o.x1, c.x1) - Math.max(o.x0, c.x0) > 2);
486
+ const spans = [], runs = [];
487
+ let n = 0;
488
+ for (let y = c.y0; y < c.y1; y++) {
489
+ let s = 0, r = 0, prev = false;
490
+ for (let x = c.x0; x < c.x1; x++) {
491
+ const on = label[y * w + x] === c.id;
492
+ if (on) {
493
+ s++;
494
+ if (!prev) r++;
495
+ }
496
+ prev = on;
497
+ }
498
+ spans.push(s);
499
+ runs.push(r);
500
+ n += s;
501
+ }
502
+ const fill = n / (cw * ch), cy = ((c.y0 + c.y1) / 2 - bandT) / bh;
503
+ const band = Math.max(1, Math.round(ch * 0.15));
504
+ const top = Math.max(...spans.slice(0, band)) / cw, bottom = Math.max(...spans.slice(ch - band)) / cw;
505
+ let mark = null;
506
+ const speck = Math.max(3, bh * 0.08);
507
+ const dot = ch <= bh * 0.25 && cw <= bh * 0.25 && ch >= speck && cw >= speck && cw >= ch * 0.6 && cw <= ch * 1.6;
508
+ const small = ch <= bh * 0.25 && cw <= bh * 0.25 && ch >= 2 && cw >= 2 && cw >= ch * 0.6 && cw <= ch * 1.6 && fill >= 0.6 && cy >= 0.3 && cy <= 0.7;
509
+ if (!apart && !(nearlyApart && ch >= bh * 0.6)) {
510
+ if (small) out.push({ x0: c.x0, x1: c.x1, mark: "\xB7" });
511
+ continue;
512
+ }
513
+ if (!apart) {
514
+ } else if (dot && fill >= 0.6 && cy >= 0.3 && cy <= 0.7) mark = "\xB7";
515
+ else if (small) mark = "\xB7";
516
+ else if (dot && fill >= 0.6 && cy >= 0.75 && cw >= ch * 0.8) mark = ".";
517
+ else if (ch <= bh * 0.4 && ch >= speck && cw >= ch * 0.75 && cw <= ch * 1.3 && fill < 0.6 && runs[ch >> 1] === 2 && cy >= 0.6) mark = "\u3002";
518
+ else if (ch <= bh * 0.15 && cw >= ch * 2 && cw <= bh * 0.8 && fill >= 0.7 && cy >= 0.35 && cy <= 0.75) mark = "-";
519
+ else if (ch >= bh * 0.5 && cw >= ch * 0.9 && cw <= ch * 1.4 && fill >= 0.4 && runs.every((r) => r <= 1) && top <= 0.4 && bottom >= 0.8) mark = "\u25B2";
520
+ else if (ch >= bh * 0.3 && ch <= bh * 0.65 && fill < 0.5) {
521
+ const stem = (from, to) => {
522
+ let sx = 0, k = 0;
523
+ for (let y = from; y < to; y++) for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) {
524
+ sx += x - c.x0;
525
+ k++;
526
+ }
527
+ return k ? sx / k / cw : 0.5;
528
+ };
529
+ if (c.y0 <= bandT + bh * 0.15 && top >= 0.7 && bottom <= 0.5 && stem(c.y0 + band, c.y1) <= 0.35) mark = "\u300C";
530
+ else if (c.y1 >= bandB - bh * 0.15 && bottom >= 0.7 && top <= 0.5 && stem(c.y0, c.y1 - band) >= 0.65) mark = "\u300D";
531
+ } else if (ch >= charH * 0.6 && cw >= ch * 0.8 && cw <= ch * 1.25 && fill < 0.5 && top >= 0.8 && bottom >= 0.8 && runs[ch >> 1] === 2) mark = ch <= charH * 0.92 ? "\u25A1" : "\u25A1\0";
532
+ if (!mark && ch >= bh * 0.6 && cw >= ch * 0.25 && cw <= ch * 0.7 && fill <= 0.4 && runs.every((r) => r <= 1)) {
533
+ const mx = (y) => {
534
+ let sx = 0, k = 0;
535
+ for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) {
536
+ sx += x;
537
+ k++;
538
+ }
539
+ return k ? sx / k : NaN;
540
+ };
541
+ if (mx(c.y0 + band) - mx(c.y1 - 1 - band) >= cw * 0.5) mark = "/";
542
+ }
543
+ if (mark) out.push({ x0: c.x0, x1: c.x1, mark });
544
+ }
545
+ return { glyphs: out, bandH: bh };
546
+ }
547
+ function ringBullet(gray, w, h, ink, cx) {
548
+ const { comps, label } = components(gray, w, h, ink);
549
+ let charH = 0;
550
+ for (const c2 of comps) charH = Math.max(charH, c2.y1 - c2.y0);
551
+ if (charH < 10) return null;
552
+ const tall = comps.filter((c2) => c2.y1 - c2.y0 >= charH * 0.6);
553
+ const bandT = Math.min(...tall.map((c2) => c2.y0)), bh = Math.max(...tall.map((c2) => c2.y1)) - bandT;
554
+ let best = null, bestD = bh * 0.6;
555
+ for (const c2 of comps) {
556
+ const d = Math.abs((c2.x0 + c2.x1) / 2 - cx);
557
+ if (c2.y1 - c2.y0 >= bh * 0.3 && d < bestD) {
558
+ bestD = d;
559
+ best = c2;
560
+ }
561
+ }
562
+ if (!best) return null;
563
+ const c = best, cw = c.x1 - c.x0, ch = c.y1 - c.y0;
564
+ if (cw < ch * 0.85 || cw > ch * 1.2) return null;
565
+ const runs = (xs, at) => {
566
+ let n2 = 0, len = 0, mx2 = 0, mn = Infinity;
567
+ for (const k of [...xs, -1]) {
568
+ if (k >= 0 && at(k)) {
569
+ if (!len) n2++;
570
+ len++;
571
+ mx2 = Math.max(mx2, len);
572
+ } else {
573
+ if (len) mn = Math.min(mn, len);
574
+ len = 0;
575
+ }
576
+ }
577
+ return { n: n2, mx: mx2, mn };
578
+ };
579
+ const my = c.y0 + c.y1 >> 1, mx = c.x0 + c.x1 >> 1;
580
+ const row = runs(Array.from({ length: cw }, (_, k) => c.x0 + k), (x) => label[my * w + x] === c.id);
581
+ const col = runs(Array.from({ length: ch }, (_, k) => c.y0 + k), (y) => label[y * w + mx] === c.id);
582
+ if (row.n !== 2 || col.n !== 2) return null;
583
+ let n = 0;
584
+ for (let y = c.y0; y < c.y1; y++) for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) n++;
585
+ const fill = n / (cw * ch), rel = ch / bh;
586
+ if (rel >= 0.62 && (fill <= 0.23 ? row.mx <= cw * 0.1 : fill <= 0.33 && row.mn <= cw * 0.08)) return "\u25CB";
587
+ if (rel <= 0.58 && fill >= 0.24) return "\u3147";
588
+ if (rel >= 0.6 && rel <= 0.8 && fill >= 0.3 && ((c.y0 + c.y1) / 2 - bandT) / bh >= 0.55) return "o";
589
+ return null;
590
+ }
591
+ function dashRatio(gray, w, h, ink, cx) {
592
+ const { comps } = components(gray, w, h, ink);
593
+ let charH = 0;
594
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
595
+ if (charH < 10) return null;
596
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
597
+ const bh = Math.max(...tall.map((c) => c.y1)) - Math.min(...tall.map((c) => c.y0));
598
+ const dash = comps.filter((c) => c.y1 - c.y0 <= bh * 0.25 && c.x1 - c.x0 >= (c.y1 - c.y0) * 2 && Math.abs((c.x0 + c.x1) / 2 - cx) <= bh * 0.6).sort((a, b) => Math.abs((a.x0 + a.x1) / 2 - cx) - Math.abs((b.x0 + b.x1) / 2 - cx))[0];
599
+ return dash ? (dash.x1 - dash.x0) / bh : null;
600
+ }
601
+ function discAt(gray, w, h, ink, cx) {
602
+ const { comps, label } = components(gray, w, h, ink);
603
+ let charH = 0;
604
+ for (const c2 of comps) charH = Math.max(charH, c2.y1 - c2.y0);
605
+ if (charH < 10) return false;
606
+ const tall = comps.filter((c2) => c2.y1 - c2.y0 >= charH * 0.6);
607
+ const bh = Math.max(...tall.map((c2) => c2.y1)) - Math.min(...tall.map((c2) => c2.y0));
608
+ const c = comps.filter((o) => Math.abs((o.x0 + o.x1) / 2 - cx) <= bh * 0.4).sort((a, b) => Math.abs((a.x0 + a.x1) / 2 - cx) - Math.abs((b.x0 + b.x1) / 2 - cx))[0];
609
+ if (!c) return false;
610
+ const cw = c.x1 - c.x0, ch = c.y1 - c.y0;
611
+ if (ch < bh * 0.45 || ch > bh * 1.05 || cw < ch * 0.8 || cw > ch * 1.25) return false;
612
+ let n = 0;
613
+ for (let y = c.y0; y < c.y1; y++) for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) n++;
614
+ const fill = n / (cw * ch);
615
+ return fill >= 0.65 && fill <= 0.9;
616
+ }
617
+ function serifOne(gray, w, h, ink, cx) {
618
+ const { comps, label } = components(gray, w, h, ink);
619
+ let charH = 0;
620
+ for (const c2 of comps) charH = Math.max(charH, c2.y1 - c2.y0);
621
+ if (charH < 10) return false;
622
+ const c = comps.filter((o) => o.x0 - 3 <= cx && o.x1 + 3 >= cx && o.y1 - o.y0 >= charH * 0.6).sort((a, b) => b.y1 - b.y0 - (a.y1 - a.y0))[0];
623
+ if (!c) return false;
624
+ const ch = c.y1 - c.y0;
625
+ const span2 = (a, b) => {
626
+ let mn = Infinity, mx = -1;
627
+ for (let y = c.y0 + Math.floor(ch * a); y < c.y0 + Math.ceil(ch * b); y++) for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) {
628
+ mn = Math.min(mn, x);
629
+ mx = Math.max(mx, x);
630
+ }
631
+ return [mn, mx];
632
+ };
633
+ const [m0, m1] = span2(0.4, 0.6), [t0, t1] = span2(0, 0.2), [b0, b1] = span2(0.8, 1);
634
+ const sw = m1 - m0 + 1;
635
+ const even = (l, r) => l >= sw * 0.4 && r >= sw * 0.4 && Math.abs(l - r) <= Math.max(l, r) * 0.35;
636
+ return sw >= 2 && sw <= ch * 0.3 && even(m0 - t0, t1 - m1) && even(m0 - b0, b1 - m1);
637
+ }
638
+ function starRun(gray, w, h, ink, cx) {
639
+ const { comps, label } = components(gray, w, h, ink);
640
+ let charH = 0;
641
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
642
+ if (charH < 10) return 0;
643
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
644
+ const bandT = Math.min(...tall.map((c) => c.y0)), bh = Math.max(...tall.map((c) => c.y1)) - bandT;
645
+ const star = (c) => {
646
+ const cw = c.x1 - c.x0, ch = c.y1 - c.y0;
647
+ if (ch < bh * 0.2 || ch > bh * 0.5 || cw < ch * 0.7 || cw > ch * 1.4 || ((c.y0 + c.y1) / 2 - bandT) / bh > 0.55) return false;
648
+ let n = 0;
649
+ for (let y = c.y0; y < c.y1; y++) for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) n++;
650
+ const fill = n / (cw * ch);
651
+ return fill >= 0.25 && fill <= 0.65;
652
+ };
653
+ const stars = comps.filter(star).sort((a, b) => a.x0 - b.x0);
654
+ const k = stars.findIndex((c) => cx >= c.x0 - (c.x1 - c.x0) && cx <= c.x1 + (c.x1 - c.x0));
655
+ if (k < 0) return 0;
656
+ let lo = k, hi = k;
657
+ while (lo > 0 && stars[lo].x0 - stars[lo - 1].x1 <= stars[lo].x1 - stars[lo].x0) lo--;
658
+ while (hi + 1 < stars.length && stars[hi + 1].x0 - stars[hi].x1 <= stars[hi].x1 - stars[hi].x0) hi++;
659
+ return hi - lo + 1;
660
+ }
661
+ var BRACKET_TAPER = 0.16;
662
+ var BRACKET_SOLID = 0.4;
663
+ var BRACKET_WIDE = 0.265;
664
+ var BRACKET_LENS = 0.35;
665
+ function bracketShape(f, close) {
666
+ const [near2, far] = close ? [f.bottom, f.top] : [f.top, f.bottom];
667
+ const paren = close ? ")" : "(";
668
+ if (near2 >= 0.7 && far <= 0.5) return f.taper < BRACKET_TAPER ? close ? "\u300D" : "\u300C" : paren;
669
+ if (near2 >= 0.7 && far >= 0.7 && f.taper >= BRACKET_TAPER) {
670
+ return f.fill >= BRACKET_SOLID && (f.cw >= f.ch * BRACKET_WIDE || f.taper >= BRACKET_LENS) ? close ? "\u3011" : "\u3010" : paren;
671
+ }
672
+ return null;
673
+ }
674
+ function leadingBullet(gray, w, h, ink, firstX) {
675
+ const { comps, label } = components(gray, w, h, ink);
676
+ let charH = 0;
677
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
678
+ if (charH < 10) return null;
679
+ const cand = comps.filter((c) => c.y1 - c.y0 >= charH * 0.15).sort((a, b) => a.x0 - b.x0);
680
+ const first = cand[0];
681
+ if (!first) return null;
682
+ const cl = cand.filter((c) => c.x0 < first.x1 && c.x1 > first.x0);
683
+ const x0 = Math.min(...cl.map((c) => c.x0)), x1 = Math.max(...cl.map((c) => c.x1)), y0 = Math.min(...cl.map((c) => c.y0)), y1 = Math.max(...cl.map((c) => c.y1));
684
+ const cw = x1 - x0, ch = y1 - y0;
685
+ if (cw < ch * 0.8 || cw > ch * 1.25) return null;
686
+ const next = cand.find((c) => c.x0 >= x1);
687
+ if (next && next.x0 - x1 < ch * 0.3) return null;
688
+ let inkN = 0;
689
+ for (let y = y0; y < y1; y++) for (let x = x0; x < x1; x++) if (cl.some((c) => label[y * w + x] === c.id)) inkN++;
690
+ const fill = inkN / (cw * ch);
691
+ const rel = ch / charH;
692
+ const ring = (c) => {
693
+ const a = c.x1 - c.x0, b = c.y1 - c.y0;
694
+ return a >= b * 0.75 && a <= b * 1.33;
695
+ };
696
+ const nested = cl.length >= 2 && cl.some((a) => cl.some((b) => a !== b && b.x0 > a.x0 && b.x1 < a.x1 && b.y0 > a.y0 && b.y1 < a.y1 && ring(b) && Math.abs(b.x0 + b.x1 - (a.x0 + a.x1)) <= (a.x1 - a.x0) * 0.2 && Math.abs(b.y0 + b.y1 - (a.y0 + a.y1)) <= (a.y1 - a.y0) * 0.2));
697
+ const span2 = (y) => {
698
+ let n = 0;
699
+ for (let x = x0; x < x1; x++) if (cl.some((c) => label[y * w + x] === c.id)) n++;
700
+ return n;
701
+ };
702
+ const band = Math.max(1, Math.round(ch * 0.15));
703
+ const edgeFull = (from, dir) => {
704
+ let m = 0;
705
+ for (let k = 0; k < band; k++) m = Math.max(m, span2(from + dir * k));
706
+ return m >= cw * 0.8;
707
+ };
708
+ let midRuns = 0;
709
+ {
710
+ let prev = false;
711
+ const my = y0 + (ch >> 1);
712
+ for (let x = x0; x < x1; x++) {
713
+ const on = cl.some((c) => label[my * w + x] === c.id);
714
+ if (on && !prev) midRuns++;
715
+ prev = on;
716
+ }
717
+ }
718
+ const hollowSquare = cl.length === 1 && fill < 0.5 && midRuns === 2 && edgeFull(y0, 1) && edgeFull(y1 - 1, -1);
719
+ let mark = null;
720
+ if (hollowSquare && rel >= 0.6 && rel <= 0.92) mark = "\u25A1";
721
+ else if (nested && fill < 0.45 && rel >= 0.6) mark = "\u25CE";
722
+ else if (cl.length === 1 && fill >= 0.9 && rel >= 0.28 && rel <= 0.6) mark = "\u25AA";
723
+ else if (cl.length === 1 && fill >= 0.65 && fill <= 0.88 && rel >= 0.5 && rel <= 0.8) mark = "\u25CF";
724
+ if (!mark) return null;
725
+ return { mark, covers: firstX <= x1 + ch * 0.3 };
726
+ }
727
+
728
+ // src/ocr/text-gaps.ts
729
+ var evidence = /* @__PURE__ */ new WeakMap();
730
+ var NO_GAPS = [];
731
+ function rememberOcrTextGaps(item, gaps) {
732
+ if (gaps.length) evidence.set(item, { text: item.text, gaps });
733
+ }
734
+ function ocrTextGaps(item) {
735
+ const saved = evidence.get(item);
736
+ return saved?.text === item.text ? saved.gaps : NO_GAPS;
737
+ }
738
+ function copyOcrTextGaps(source, target, scale = 1, offsetX = 0) {
739
+ const gaps = ocrTextGaps(source);
740
+ if (!gaps.length || source.text !== target.text) return;
741
+ rememberOcrTextGaps(target, scale === 1 && offsetX === 0 ? gaps : gaps.map((g) => ({ ...g, left: g.left * scale + offsetX, right: g.right * scale + offsetX })));
742
+ }
743
+ function findOcrTextGaps(rgba, pageW, box, read, text, steps, stepPx) {
744
+ if (read.replace(/\s/g, "") !== text.replace(/\s/g, "") || [...read].length !== steps.length) return [];
745
+ const visible = [...read].flatMap((c, i) => /\s/.test(c) ? [] : [(steps[i] + 0.5) * stepPx]);
746
+ if (visible.length < 2) return [];
747
+ const gray = grayCrop(rgba, pageW, box), ink = inkStats(gray);
748
+ if (!ink.darkInk || ink.contrast < 35) return [];
749
+ const counts = new Uint32Array(box.w);
750
+ for (let y = 0; y < box.h; y++) for (let x = 0; x < box.w; x++) if (gray[y * box.w + x] <= ink.threshold) counts[x]++;
751
+ const out = [];
752
+ let offset = 0, index = 0;
753
+ for (const c of text) {
754
+ if (!/\s/.test(c)) {
755
+ if (index > 0) {
756
+ const from = Math.max(0, Math.floor(visible[index - 1]) + 1), end = Math.min(box.w, Math.ceil(visible[index]));
757
+ let start = from, left = from, right = from;
758
+ for (let x = from; x <= end; x++) {
759
+ if (x === end || counts[x] > Math.max(1, Math.floor(box.h * 0.03))) {
760
+ if (x - start > right - left) {
761
+ left = start;
762
+ right = x;
763
+ }
764
+ start = x + 1;
765
+ }
766
+ }
767
+ if (right - left >= Math.max(3, box.h * 0.25)) out.push({ at: offset, left: box.x + left, right: box.x + right });
768
+ }
769
+ index++;
770
+ }
771
+ offset += c.length;
772
+ }
773
+ return out;
774
+ }
14
775
 
15
776
  // src/pdf/line-extract.ts
16
777
  import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
@@ -3564,6 +4325,59 @@ function joinClusterCellLines(lines, box, lex) {
3564
4325
  return out;
3565
4326
  }
3566
4327
 
4328
+ // src/pdf/ocr-column-cuts.ts
4329
+ var OCR_NUMERIC_CELL = /^[+-]?\d+(?:[,.]\d+)*%?$/;
4330
+ var OCR_GROUPED_NUMBER = /^[+-]?\d{1,3}(?:\s+\d{3})+(?:[.,]\d+)?%?$/;
4331
+ function supportedOcrColumnCuts(rows) {
4332
+ const cuts = /* @__PURE__ */ new Map();
4333
+ for (const head of rows[0] ?? []) {
4334
+ if (head.items.length !== 1 || head.items[0].fontName !== "ocr") continue;
4335
+ const candidates = [];
4336
+ for (const gap of ocrTextGaps(head.items[0])) {
4337
+ const text = head.items[0].text;
4338
+ if (!/\p{L}/u.test(text.slice(0, gap.at)) || !/\p{L}/u.test(text.slice(gap.at))) continue;
4339
+ const intervals = [];
4340
+ for (const row of rows.slice(1)) {
4341
+ const cell = row.find((c) => c.col === head.col);
4342
+ if (!cell || cell.items.some((it) => it.fontName !== "ocr")) continue;
4343
+ let interval;
4344
+ if (cell.items.length === 1) {
4345
+ const it = cell.items[0];
4346
+ interval = ocrTextGaps(it).find((g) => isOcrNumericSplit(it.text, g.at));
4347
+ } else if (isOcrNumericPair(cell.items)) {
4348
+ const [a, b] = [...cell.items].sort((a2, b2) => a2.x - b2.x);
4349
+ interval = { left: a.x + a.w / 2, right: b.x + b.w / 2 };
4350
+ }
4351
+ if (interval) {
4352
+ const left = Math.max(gap.left, interval.left), right = Math.min(gap.right, interval.right);
4353
+ if (right > left) intervals.push({ left, right });
4354
+ }
4355
+ }
4356
+ const events = intervals.flatMap((i) => [{ x: i.left, delta: 1 }, { x: i.right, delta: -1 }]).sort((a, b) => a.x - b.x);
4357
+ let active = 0, bestCount = 1, bestWidth = 0, cut;
4358
+ for (let i = 0; i < events.length; ) {
4359
+ const x = events[i].x;
4360
+ while (i < events.length && events[i].x === x) active += events[i++].delta;
4361
+ const width = i < events.length ? events[i].x - x : 0;
4362
+ if (active >= 2 && width > 0 && (active > bestCount || active === bestCount && width > bestWidth)) {
4363
+ bestCount = active;
4364
+ bestWidth = width;
4365
+ cut = x + width / 2;
4366
+ }
4367
+ }
4368
+ if (cut !== void 0) candidates.push(cut);
4369
+ }
4370
+ if (candidates.length === 1) cuts.set(head.col, candidates[0]);
4371
+ }
4372
+ return cuts;
4373
+ }
4374
+ function isOcrNumericPair(items) {
4375
+ return items.length === 2 && items.every((it) => it.fontName === "ocr" && OCR_NUMERIC_CELL.test(it.text.trim())) && !OCR_GROUPED_NUMBER.test([...items].sort((a, b) => a.x - b.x).map((it) => it.text.trim()).join(" "));
4376
+ }
4377
+ function isOcrNumericSplit(text, at) {
4378
+ return !OCR_GROUPED_NUMBER.test(text.trim()) && /\s/.test((text[at - 1] ?? "") + (text[at] ?? "")) && OCR_NUMERIC_CELL.test(text.slice(0, at).trim()) && OCR_NUMERIC_CELL.test(text.slice(at).trim());
4379
+ }
4380
+
3567
4381
  // src/pdf/cluster-detector.ts
3568
4382
  var Y_TOL = 3;
3569
4383
  var COL_CLUSTER_TOL = 15;
@@ -4328,17 +5142,21 @@ function assignRowItems(items, columns, numCols, headerFace) {
4328
5142
  return result;
4329
5143
  }
4330
5144
  function buildClusterTable(rows, columns, pageNum, lex) {
4331
- const numCols = columns.length;
4332
5145
  const numRows = rows.length;
4333
- if (numRows < MIN_ROWS || numCols < MIN_COLS) return null;
5146
+ if (numRows < MIN_ROWS || columns.length < MIN_COLS) return null;
5147
+ const headFaces = new Set(rows[0]?.items.map((i) => i.fontName));
5148
+ const headerFace = headFaces.size === 1 && rows.slice(1).every((row) => row.items.every((i) => !headFaces.has(i.fontName))) ? [...headFaces][0] : void 0;
5149
+ const assigned = rows.map((row, r) => assignRowItems(row.items, columns, columns.length, r === 0 ? headerFace : void 0));
5150
+ const cuts = supportedOcrColumnCuts(assigned);
5151
+ const starts = columns.map((_, c) => c + [...cuts.keys()].filter((k) => k < c).length);
5152
+ const numCols = columns.length + cuts.size;
4334
5153
  const cells = Array.from(
4335
5154
  { length: numRows },
4336
5155
  () => Array.from({ length: numCols }, () => ({ text: "", colSpan: 1, rowSpan: 1 }))
4337
5156
  );
4338
5157
  const usedItems = /* @__PURE__ */ new Set();
4339
5158
  const lines = cells.map((row) => row.map(() => []));
4340
- const headFaces = new Set(rows[0]?.items.map((i) => i.fontName));
4341
- const headerFace = headFaces.size === 1 && rows.slice(1).every((row) => row.items.every((i) => !headFaces.has(i.fontName))) ? [...headFaces][0] : void 0;
5159
+ const origins = /* @__PURE__ */ new Map();
4342
5160
  for (let r = 0; r < numRows; r++) {
4343
5161
  const row = rows[r];
4344
5162
  if (row.items.length === 1 && numCols > 1) {
@@ -4346,15 +5164,39 @@ function buildClusterTable(rows, columns, pageNum, lex) {
4346
5164
  usedItems.add(row.items[0]);
4347
5165
  continue;
4348
5166
  }
4349
- const assignments = assignRowItems(row.items, columns, numCols, r === 0 ? headerFace : void 0);
4350
- for (const { col, items } of assignments) {
5167
+ const assignments = assigned[r].flatMap(({ col, items }) => {
5168
+ const target = starts[col], cut = cuts.get(col);
5169
+ if (cut === void 0) return [{ col: target, items, span: 1 }];
5170
+ const gapAt = (it) => ocrTextGaps(it).find((g) => g.left < cut && g.right > cut && (r === 0 || isOcrNumericSplit(it.text, g.at)));
5171
+ const groupedNumber = OCR_GROUPED_NUMBER.test([...items].sort((a, b) => a.x - b.x).map((it) => it.text.trim()).join(" "));
5172
+ if (groupedNumber || !isOcrNumericPair(items) && items.some((it) => it.x < cut && it.x + it.w > cut && !gapAt(it))) return [{ col: target, items, span: 2 }];
5173
+ const parts = [[], []];
5174
+ for (const it of items) {
5175
+ const g = gapAt(it);
5176
+ if (!g) {
5177
+ parts[it.x + it.w / 2 < cut ? 0 : 1].push(it);
5178
+ continue;
5179
+ }
5180
+ const fragments = [
5181
+ { ...it, text: it.text.slice(0, g.at).trimEnd(), w: g.left - it.x },
5182
+ { ...it, text: it.text.slice(g.at).trimStart(), x: g.right, w: it.x + it.w - g.right }
5183
+ ];
5184
+ fragments.forEach((fragment, side) => {
5185
+ origins.set(fragment, it);
5186
+ parts[side].push(fragment);
5187
+ });
5188
+ }
5189
+ return parts.flatMap((part, side) => part.length ? [{ col: target + side, items: part, span: 1 }] : []);
5190
+ });
5191
+ for (const { col, items, span: span2 } of assignments) {
5192
+ cells[r][col].colSpan = span2;
4351
5193
  const cell = lines[r][col];
4352
5194
  if (cell.length) cell[cell.length - 1].spaceAfter = true;
4353
5195
  cell.push(...clusterCellLines(items));
4354
- for (const item of items) usedItems.add(item);
5196
+ for (const item of items) usedItems.add(origins.get(item) ?? item);
4355
5197
  }
4356
5198
  }
4357
- const boxes = columns.map((_, c) => {
5199
+ const boxes = Array.from({ length: numCols }, (_, c) => {
4358
5200
  const col = lines.flatMap((row) => row[c]);
4359
5201
  return { x1: Math.min(...col.map((l) => l.left)), x2: Math.max(...col.map((l) => l.right)) };
4360
5202
  });
@@ -4377,7 +5219,7 @@ function buildClusterTable(rows, columns, pageNum, lex) {
4377
5219
  if (/^[○●▶\-·]/.test(contentText)) continue;
4378
5220
  for (let pr = r - 1; pr >= 0; pr--) {
4379
5221
  if (cells[pr].some((c) => c.text.trim())) {
4380
- if (cells[pr][0].colSpan > 1) break;
5222
+ if (cells[pr].some((c) => c.colSpan > 1)) break;
4381
5223
  for (let c = 0; c < numCols; c++) {
4382
5224
  if (!cells[r][c].text.trim()) continue;
4383
5225
  lines[pr][c].push(...lines[r][c]);
@@ -8571,18 +9413,22 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
8571
9413
  let panels = null;
8572
9414
  if (remaining.length > 0) {
8573
9415
  remaining.sort((a, b) => b.y - a.y || a.x - b.x);
8574
- const clusterItems = remaining.map((i) => ({
8575
- text: i.text,
8576
- x: i.x,
8577
- y: i.y,
8578
- w: i.w,
8579
- h: i.h,
8580
- fontSize: i.fontSize,
8581
- fontName: i.fontName,
8582
- hasSpaceBefore: i.hasSpaceBefore,
8583
- syntheticSpace: i.syntheticSpace,
8584
- spaceAfter: i.spaceAfter
8585
- }));
9416
+ const clusterItems = remaining.map((i) => {
9417
+ const item = {
9418
+ text: i.text,
9419
+ x: i.x,
9420
+ y: i.y,
9421
+ w: i.w,
9422
+ h: i.h,
9423
+ fontSize: i.fontSize,
9424
+ fontName: i.fontName,
9425
+ hasSpaceBefore: i.hasSpaceBefore,
9426
+ syntheticSpace: i.syntheticSpace,
9427
+ spaceAfter: i.spaceAfter
9428
+ };
9429
+ copyOcrTextGaps(i, item);
9430
+ return item;
9431
+ });
8586
9432
  const proseColumns = findTwoColumnProseCutX(clusterItems) !== null || detectPersistentColumnGutter(remaining.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }))) !== null;
8587
9433
  const clusterResults = (proseColumns ? [] : detectClusterTables(clusterItems, pageNum, void 0, lex)).filter((cr) => {
8588
9434
  const b = cr.bbox;
@@ -8898,18 +9744,22 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
8898
9744
  if (cards) return cards.flatMap((group) => extractPageBlocksFallback(group, pageNum, false, detectTables, lex));
8899
9745
  }
8900
9746
  const blocks = [];
8901
- const clusterItems = items.map((i) => ({
8902
- text: i.text,
8903
- x: i.x,
8904
- y: i.y,
8905
- w: i.w,
8906
- h: i.h,
8907
- fontSize: i.fontSize,
8908
- fontName: i.fontName,
8909
- hasSpaceBefore: i.hasSpaceBefore,
8910
- syntheticSpace: i.syntheticSpace,
8911
- spaceAfter: i.spaceAfter
8912
- }));
9747
+ const clusterItems = items.map((i) => {
9748
+ const item = {
9749
+ text: i.text,
9750
+ x: i.x,
9751
+ y: i.y,
9752
+ w: i.w,
9753
+ h: i.h,
9754
+ fontSize: i.fontSize,
9755
+ fontName: i.fontName,
9756
+ hasSpaceBefore: i.hasSpaceBefore,
9757
+ syntheticSpace: i.syntheticSpace,
9758
+ spaceAfter: i.spaceAfter
9759
+ };
9760
+ copyOcrTextGaps(i, item);
9761
+ return item;
9762
+ });
8913
9763
  const textRects = items.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }));
8914
9764
  const imagePanels = fullPage ? splitImagePanels(items, figures) : null;
8915
9765
  if (imagePanels) return imagePanels.flatMap((group) => extractPageBlocksFallback(group, pageNum, false, detectTables, lex));
@@ -9123,6 +9973,29 @@ function rowParagraphs(block) {
9123
9973
  }
9124
9974
 
9125
9975
  export {
9976
+ grayCrop,
9977
+ inkStats,
9978
+ splitRowBands,
9979
+ leadingTriangle,
9980
+ leaderRuns,
9981
+ inkBounds,
9982
+ bracketFeatures,
9983
+ romanStems,
9984
+ circledAt,
9985
+ quoteHead,
9986
+ edgeTrim,
9987
+ tallInkCount,
9988
+ gapGlyphs,
9989
+ ringBullet,
9990
+ dashRatio,
9991
+ discAt,
9992
+ serifOne,
9993
+ starRun,
9994
+ bracketShape,
9995
+ leadingBullet,
9996
+ rememberOcrTextGaps,
9997
+ copyOcrTextGaps,
9998
+ findOcrTextGaps,
9126
9999
  extractLines,
9127
10000
  extractImageRegions,
9128
10001
  NO_EDGES,
@@ -9177,4 +10050,4 @@ export {
9177
10050
  UNRULED_REGION_TABLES,
9178
10051
  mergeOcrImageRegions
9179
10052
  };
9180
- //# sourceMappingURL=chunk-W75FRCAP.js.map
10053
+ //# sourceMappingURL=chunk-EIGCAAVM.js.map