kordoc 4.16.0 → 4.16.2

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 (145) hide show
  1. package/README.md +344 -1135
  2. package/dist/{-VQNLLLPE.js → -4G6MA4X6.js} +11 -11
  3. package/dist/chunk-3IB7LBHC.js +57 -0
  4. package/dist/chunk-3IB7LBHC.js.map +1 -0
  5. package/dist/{chunk-6LMKQM7I.js → chunk-4VODJKFL.js} +3 -3
  6. package/dist/{chunk-UDX7TSOC.js → chunk-5KS5PWJR.js} +2 -2
  7. package/dist/chunk-62W7QCEX.cjs +57 -0
  8. package/dist/chunk-62W7QCEX.cjs.map +1 -0
  9. package/dist/{chunk-NNYF2AGC.js → chunk-7KNGSQU7.js} +4 -4
  10. package/dist/chunk-7KNGSQU7.js.map +1 -0
  11. package/dist/{chunk-DYENR3PX.js → chunk-AUWGDP4G.js} +2 -2
  12. package/dist/{chunk-H6KMTYYZ.cjs → chunk-CEIATRDW.cjs} +3 -3
  13. package/dist/{chunk-H6KMTYYZ.cjs.map → chunk-CEIATRDW.cjs.map} +1 -1
  14. package/dist/{chunk-N6TPODYL.js → chunk-DES2LGFZ.js} +2 -2
  15. package/dist/{chunk-PQRP4NSF.js → chunk-F4NRLLY4.js} +2 -2
  16. package/dist/{chunk-B5NEGP65.cjs → chunk-FKUDAIJI.cjs} +692 -33
  17. package/dist/chunk-FKUDAIJI.cjs.map +1 -0
  18. package/dist/{chunk-XWA625AN.js → chunk-FSDJLAOW.js} +3 -1
  19. package/dist/chunk-FSDJLAOW.js.map +1 -0
  20. package/dist/{chunk-PIMB2YNO.js → chunk-GO5IFOUF.js} +2 -2
  21. package/dist/{chunk-476OYHGZ.js → chunk-IQGIT2VX.js} +37 -7
  22. package/dist/chunk-IQGIT2VX.js.map +1 -0
  23. package/dist/chunk-J3IF6KGW.js +59 -0
  24. package/dist/chunk-J3IF6KGW.js.map +1 -0
  25. package/dist/{chunk-C75WE52C.js → chunk-JELRWAZC.js} +2 -2
  26. package/dist/{chunk-46DZF3RK.js → chunk-JFLAM3MM.js} +2 -2
  27. package/dist/{chunk-A3QDRMVD.js → chunk-KIOQ4N37.js} +2 -2
  28. package/dist/{chunk-TGD7UFHD.js → chunk-LEIAEWZ7.js} +4 -4
  29. package/dist/chunk-LEIAEWZ7.js.map +1 -0
  30. package/dist/{chunk-GOQBON77.js → chunk-MLIS253L.js} +2 -2
  31. package/dist/{chunk-JB5X5NNL.js → chunk-OXLPVQYK.js} +680 -21
  32. package/dist/chunk-OXLPVQYK.js.map +1 -0
  33. package/dist/{chunk-RC4C7Q33.js → chunk-P6ZDXTV6.js} +2 -2
  34. package/dist/{chunk-CB5XDCSL.js → chunk-QOCS7W7R.js} +5034 -4569
  35. package/dist/chunk-QOCS7W7R.js.map +1 -0
  36. package/dist/{chunk-3JWMDL3S.js → chunk-S2PL227Q.js} +2 -2
  37. package/dist/{chunk-HIVK5V27.js → chunk-SFEQYKYJ.js} +133 -110
  38. package/dist/chunk-SFEQYKYJ.js.map +1 -0
  39. package/dist/{chunk-NWODPYBD.cjs → chunk-STM3ORY7.cjs} +2 -2
  40. package/dist/{chunk-NWODPYBD.cjs.map → chunk-STM3ORY7.cjs.map} +1 -1
  41. package/dist/{chunk-43FKGDDO.js → chunk-TXUKTOG2.js} +213 -30
  42. package/dist/chunk-TXUKTOG2.js.map +1 -0
  43. package/dist/{chunk-XHOZB2WP.js → chunk-UTP2WCHS.js} +681 -22
  44. package/dist/chunk-UTP2WCHS.js.map +1 -0
  45. package/dist/{chunk-MH6MAL64.cjs → chunk-WDQTFAXY.cjs} +217 -114
  46. package/dist/chunk-WDQTFAXY.cjs.map +1 -0
  47. package/dist/{chunk-DRY3L5KQ.cjs → chunk-XTFN5WKS.cjs} +7 -7
  48. package/dist/chunk-XTFN5WKS.cjs.map +1 -0
  49. package/dist/chunks-NUNIKS2J.js +10 -0
  50. package/dist/cli.js +33 -30
  51. package/dist/cli.js.map +1 -1
  52. package/dist/{formula-FNQYHMIY.js → formula-EPKLPDU7.js} +3 -3
  53. package/dist/{formula-5775PTP2.js → formula-NARUOXRX.js} +4 -4
  54. package/dist/{formula-VEFKYXPB.cjs → formula-WIMV5TUX.cjs} +9 -9
  55. package/dist/{formula-VEFKYXPB.cjs.map → formula-WIMV5TUX.cjs.map} +1 -1
  56. package/dist/{image-ocr-PYZU4TO7.js → image-ocr-3WJ4QH3T.js} +8 -8
  57. package/dist/{image-ocr-YA2MQBRP.js → image-ocr-5BUJQ4TK.js} +6 -6
  58. package/dist/{image-ocr-6JZR734G.cjs → image-ocr-AXWXR6JV.cjs} +15 -15
  59. package/dist/{image-ocr-6JZR734G.cjs.map → image-ocr-AXWXR6JV.cjs.map} +1 -1
  60. package/dist/index.cjs +1900 -1406
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +24 -6
  63. package/dist/index.d.ts +24 -6
  64. package/dist/index.js +1282 -788
  65. package/dist/index.js.map +1 -1
  66. package/dist/mcp.js +34 -28
  67. package/dist/mcp.js.map +1 -1
  68. package/dist/{model-bundle-O6J7ORVC.js → model-bundle-IMBW7D2Z.js} +5 -5
  69. package/dist/{models-YMOKZGCI.js → models-SY75IBOE.js} +5 -5
  70. package/dist/{parser-YAQV7FBO.cjs → parser-2LU4EVU3.cjs} +360 -219
  71. package/dist/parser-2LU4EVU3.cjs.map +1 -0
  72. package/dist/{parser-3B6I2XL7.js → parser-N6476XAC.js} +225 -84
  73. package/dist/parser-N6476XAC.js.map +1 -0
  74. package/dist/{parser-PXATL5DM.js → parser-ZQM3664Z.js} +222 -81
  75. package/dist/parser-ZQM3664Z.js.map +1 -0
  76. package/dist/pdf-ocr-6DEE23IP.js +15 -0
  77. package/dist/pdf-ocr-GHEJVPX2.cjs +16 -0
  78. package/dist/{pdf-ocr-T2JXQATA.cjs.map → pdf-ocr-GHEJVPX2.cjs.map} +1 -1
  79. package/dist/pdf-ocr-MQTWODVU.js +18 -0
  80. package/dist/{profile-io-ZTV2MKRT.js → profile-io-GJPKCYPT.js} +3 -3
  81. package/dist/{rasterize-MMUZ537K.js → rasterize-UOALJQDS.js} +3 -3
  82. package/dist/{redact-doc-S42XGV5D.js → redact-doc-22SYW3KH.js} +11 -11
  83. package/dist/{render-WCWPZKJ7.js → render-MJ4H3R23.js} +7 -7
  84. package/dist/seal-XWMRUTI4.js +10 -0
  85. package/dist/{setup-D35LKZCH.js → setup-2URYVUEO.js} +3 -3
  86. package/dist/visual-USPS4QRC.js +20 -0
  87. package/dist/{watch-AX64CIMN.js → watch-ALXZGTOB.js} +12 -12
  88. package/package.json +1 -1
  89. package/dist/chunk-43FKGDDO.js.map +0 -1
  90. package/dist/chunk-476OYHGZ.js.map +0 -1
  91. package/dist/chunk-B5NEGP65.cjs.map +0 -1
  92. package/dist/chunk-CB5XDCSL.js.map +0 -1
  93. package/dist/chunk-DRY3L5KQ.cjs.map +0 -1
  94. package/dist/chunk-HIVK5V27.js.map +0 -1
  95. package/dist/chunk-JB5X5NNL.js.map +0 -1
  96. package/dist/chunk-MH6MAL64.cjs.map +0 -1
  97. package/dist/chunk-NNYF2AGC.js.map +0 -1
  98. package/dist/chunk-QJERJZJ6.js +0 -11
  99. package/dist/chunk-QJERJZJ6.js.map +0 -1
  100. package/dist/chunk-TGD7UFHD.js.map +0 -1
  101. package/dist/chunk-UQ6TZDW6.js +0 -13
  102. package/dist/chunk-UQ6TZDW6.js.map +0 -1
  103. package/dist/chunk-XHOZB2WP.js.map +0 -1
  104. package/dist/chunk-XWA625AN.js.map +0 -1
  105. package/dist/chunk-ZY5K3LPM.cjs +0 -11
  106. package/dist/chunk-ZY5K3LPM.cjs.map +0 -1
  107. package/dist/chunks-JD25E5PM.js +0 -10
  108. package/dist/parser-3B6I2XL7.js.map +0 -1
  109. package/dist/parser-PXATL5DM.js.map +0 -1
  110. package/dist/parser-YAQV7FBO.cjs.map +0 -1
  111. package/dist/pdf-ocr-2TTO5PX2.js +0 -15
  112. package/dist/pdf-ocr-T2JXQATA.cjs +0 -16
  113. package/dist/pdf-ocr-WF2NQA6X.js +0 -18
  114. package/dist/seal-WHK4GOHR.js +0 -10
  115. package/dist/visual-WL4TDE5B.js +0 -20
  116. /package/dist/{-VQNLLLPE.js.map → -4G6MA4X6.js.map} +0 -0
  117. /package/dist/{chunk-6LMKQM7I.js.map → chunk-4VODJKFL.js.map} +0 -0
  118. /package/dist/{chunk-UDX7TSOC.js.map → chunk-5KS5PWJR.js.map} +0 -0
  119. /package/dist/{chunk-DYENR3PX.js.map → chunk-AUWGDP4G.js.map} +0 -0
  120. /package/dist/{chunk-N6TPODYL.js.map → chunk-DES2LGFZ.js.map} +0 -0
  121. /package/dist/{chunk-PQRP4NSF.js.map → chunk-F4NRLLY4.js.map} +0 -0
  122. /package/dist/{chunk-PIMB2YNO.js.map → chunk-GO5IFOUF.js.map} +0 -0
  123. /package/dist/{chunk-C75WE52C.js.map → chunk-JELRWAZC.js.map} +0 -0
  124. /package/dist/{chunk-46DZF3RK.js.map → chunk-JFLAM3MM.js.map} +0 -0
  125. /package/dist/{chunk-A3QDRMVD.js.map → chunk-KIOQ4N37.js.map} +0 -0
  126. /package/dist/{chunk-GOQBON77.js.map → chunk-MLIS253L.js.map} +0 -0
  127. /package/dist/{chunk-RC4C7Q33.js.map → chunk-P6ZDXTV6.js.map} +0 -0
  128. /package/dist/{chunk-3JWMDL3S.js.map → chunk-S2PL227Q.js.map} +0 -0
  129. /package/dist/{chunks-JD25E5PM.js.map → chunks-NUNIKS2J.js.map} +0 -0
  130. /package/dist/{formula-FNQYHMIY.js.map → formula-EPKLPDU7.js.map} +0 -0
  131. /package/dist/{formula-5775PTP2.js.map → formula-NARUOXRX.js.map} +0 -0
  132. /package/dist/{image-ocr-PYZU4TO7.js.map → image-ocr-3WJ4QH3T.js.map} +0 -0
  133. /package/dist/{image-ocr-YA2MQBRP.js.map → image-ocr-5BUJQ4TK.js.map} +0 -0
  134. /package/dist/{model-bundle-O6J7ORVC.js.map → model-bundle-IMBW7D2Z.js.map} +0 -0
  135. /package/dist/{models-YMOKZGCI.js.map → models-SY75IBOE.js.map} +0 -0
  136. /package/dist/{pdf-ocr-2TTO5PX2.js.map → pdf-ocr-6DEE23IP.js.map} +0 -0
  137. /package/dist/{pdf-ocr-WF2NQA6X.js.map → pdf-ocr-MQTWODVU.js.map} +0 -0
  138. /package/dist/{profile-io-ZTV2MKRT.js.map → profile-io-GJPKCYPT.js.map} +0 -0
  139. /package/dist/{rasterize-MMUZ537K.js.map → rasterize-UOALJQDS.js.map} +0 -0
  140. /package/dist/{redact-doc-S42XGV5D.js.map → redact-doc-22SYW3KH.js.map} +0 -0
  141. /package/dist/{render-WCWPZKJ7.js.map → render-MJ4H3R23.js.map} +0 -0
  142. /package/dist/{seal-WHK4GOHR.js.map → seal-XWMRUTI4.js.map} +0 -0
  143. /package/dist/{setup-D35LKZCH.js.map → setup-2URYVUEO.js.map} +0 -0
  144. /package/dist/{visual-WL4TDE5B.js.map → visual-USPS4QRC.js.map} +0 -0
  145. /package/dist/{watch-AX64CIMN.js.map → watch-ALXZGTOB.js.map} +0 -0
@@ -6,10 +6,10 @@
6
6
 
7
7
 
8
8
 
9
- var _chunkMH6MAL64cjs = require('./chunk-MH6MAL64.cjs');
9
+ var _chunkWDQTFAXYcjs = require('./chunk-WDQTFAXY.cjs');
10
10
 
11
11
 
12
- var _chunkNWODPYBDcjs = require('./chunk-NWODPYBD.cjs');
12
+ var _chunkSTM3ORY7cjs = require('./chunk-STM3ORY7.cjs');
13
13
 
14
14
  // src/ocr/ruling-lines.ts
15
15
  var INK_LUMA_MAX = 205;
@@ -185,19 +185,19 @@ function solidRuns(mask, base, step, span, minLenPx) {
185
185
  function dottedRuns(mask, base, step, span, minLenPx, maxGap, maxDash, maxPeriod) {
186
186
  const runs = [];
187
187
  const chain = [];
188
- const median = (xs) => xs.sort((a, b) => a - b)[xs.length >> 1];
188
+ const median2 = (xs) => xs.sort((a, b) => a - b)[xs.length >> 1];
189
189
  const flush = () => {
190
190
  const cnt = chain.length >> 1;
191
191
  if (cnt >= DOT_MIN_COUNT && chain[chain.length - 1] - chain[0] + 1 >= minLenPx) {
192
- const periods = [], dashes = [], gaps = [];
192
+ const periods = [], dashes = [], gaps2 = [];
193
193
  for (let k = 0; k < cnt; k++) {
194
194
  dashes.push(chain[2 * k + 1] - chain[2 * k] + 1);
195
195
  if (k) {
196
196
  periods.push(chain[2 * k] - chain[2 * k - 2]);
197
- gaps.push(chain[2 * k] - chain[2 * k - 1] - 1);
197
+ gaps2.push(chain[2 * k] - chain[2 * k - 1] - 1);
198
198
  }
199
199
  }
200
- if (median(periods) <= maxPeriod || median(dashes) >= 1.5 * median(gaps)) runs.push({ lo: chain[0], hi: chain[chain.length - 1] });
200
+ if (median2(periods) <= maxPeriod || median2(dashes) >= 1.5 * median2(gaps2)) runs.push({ lo: chain[0], hi: chain[chain.length - 1] });
201
201
  }
202
202
  chain.length = 0;
203
203
  };
@@ -570,12 +570,614 @@ function inkBounds(gray, w, h, ink) {
570
570
  if (x1 < x0 || y1 < y0) return { x0: 0, y0: 0, x1: w, y1: h };
571
571
  return { x0, y0, x1: x1 + 1, y1: y1 + 1 };
572
572
  }
573
+ function bracketFeatures(gray, w, h, ink, cx) {
574
+ const { comps, label } = components(gray, w, h, ink);
575
+ let charH = 0;
576
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
577
+ if (charH < 8) return null;
578
+ const tol = Math.max(2, charH * 0.3);
579
+ let best = null, bestD = Infinity;
580
+ for (const c of comps) {
581
+ if (c.y1 - c.y0 < charH * 0.3 || c.x1 < cx - tol || c.x0 > cx + tol) continue;
582
+ const d = Math.abs((c.x0 + c.x1) / 2 - cx);
583
+ if (d < bestD) {
584
+ bestD = d;
585
+ best = c;
586
+ }
587
+ }
588
+ if (!best) return null;
589
+ const b = best;
590
+ const cw = b.x1 - b.x0, ch = b.y1 - b.y0;
591
+ if (cw < 3 || ch < 8) return null;
592
+ const spans = [];
593
+ for (let y = b.y0; y < b.y1; y++) {
594
+ let n = 0;
595
+ for (let x = b.x0; x < b.x1; x++) if (label[y * w + x] === b.id) n++;
596
+ spans.push(n);
597
+ }
598
+ const band = Math.max(1, Math.round(ch * 0.15));
599
+ const top = Math.max(...spans.slice(0, band)) / cw, bottom = Math.max(...spans.slice(ch - band)) / cw;
600
+ const midRows = spans.slice(Math.floor(ch * 0.3), Math.ceil(ch * 0.7)).sort((p, q) => p - q);
601
+ const stroke = _nullishCoalesce(midRows[midRows.length >> 1], () => ( 0));
602
+ const taper = spans.filter((n) => n > stroke && n < cw).length / ch;
603
+ const fill = spans.reduce((a, n) => a + n, 0) / (cw * ch);
604
+ return { top, bottom, taper, fill, cw, ch, charH };
605
+ }
606
+ function romanStems(gray, w, h, ink, prevX, nextX) {
607
+ const { comps, label } = components(gray, w, h, ink);
608
+ let charH = 0;
609
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
610
+ if (charH < 10) return null;
611
+ const x0 = prevX + charH * 0.35, x1 = nextX - charH * 0.35;
612
+ const sel = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6 && (c.x0 + c.x1) / 2 >= x0 && (c.x0 + c.x1) / 2 < x1);
613
+ if (!sel.length) return null;
614
+ const ids = new Set(sel.map((c) => c.id));
615
+ const ux0 = Math.min(...sel.map((c) => c.x0)), ux1 = Math.max(...sel.map((c) => c.x1));
616
+ const uy0 = Math.min(...sel.map((c) => c.y0)), uy1 = Math.max(...sel.map((c) => c.y1));
617
+ const uh = uy1 - uy0;
618
+ const counts = /* @__PURE__ */ new Map();
619
+ let rows = 0;
620
+ for (let y = uy0 + Math.floor(uh * 0.25); y < uy1 - Math.floor(uh * 0.25); y++) {
621
+ let runs = 0, len = 0, thick = false;
622
+ for (let x = ux0; x <= ux1; x++) {
623
+ const on = x < ux1 && ids.has(label[y * w + x]);
624
+ if (on) len++;
625
+ else if (len) {
626
+ runs++;
627
+ if (len > uh * 0.35) thick = true;
628
+ len = 0;
629
+ }
630
+ }
631
+ rows++;
632
+ if (!thick) counts.set(runs, (_nullishCoalesce(counts.get(runs), () => ( 0))) + 1);
633
+ }
634
+ const [n, k] = _nullishCoalesce([...counts].sort((a, b) => b[1] - a[1])[0], () => ( [0, 0]));
635
+ return n >= 1 && n <= 3 && k >= rows * 0.7 ? n : null;
636
+ }
637
+ function circledAt(gray, w, h, ink, xs) {
638
+ const { comps } = components(gray, w, h, ink);
639
+ let charH = 0;
640
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
641
+ const rings = charH < 10 ? [] : comps.filter((r) => {
642
+ const rw = r.x1 - r.x0, rh = r.y1 - r.y0;
643
+ 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);
644
+ });
645
+ return xs.map((x) => rings.some((r) => x >= r.x0 && x <= r.x1));
646
+ }
647
+ function quoteHead(gray, w, h, ink, cx) {
648
+ const { comps, label } = components(gray, w, h, ink);
649
+ let charH = 0;
650
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
651
+ if (charH < 10) return null;
652
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
653
+ if (!tall.length) return null;
654
+ const mid = (Math.min(...tall.map((c) => c.y0)) + Math.max(...tall.map((c) => c.y1))) / 2;
655
+ let best = null, bestD = charH * 0.4;
656
+ for (const c of comps) {
657
+ const ch = c.y1 - c.y0, d = Math.abs((c.x0 + c.x1) / 2 - cx);
658
+ if (ch < charH * 0.12 || ch > charH * 0.5 || (c.y0 + c.y1) / 2 > mid || d > bestD) continue;
659
+ bestD = d;
660
+ best = c;
661
+ }
662
+ if (!best) return null;
663
+ let n = 0, sy = 0;
664
+ 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) {
665
+ n++;
666
+ sy += y;
667
+ }
668
+ return n ? (sy / n - best.y0) / (best.y1 - best.y0) : null;
669
+ }
670
+ function edgeTrim(gray, w, h, ink) {
671
+ const isInk = (v) => ink.darkInk ? v <= ink.threshold : v > ink.threshold;
672
+ const colInk = new Uint32Array(w);
673
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) if (isInk(gray[y * w + x])) colInk[x]++;
674
+ const ruleCol = new Uint8Array(w);
675
+ let liveW = 0;
676
+ for (let x = 0; x < w; x++) {
677
+ if (colInk[x] >= h * 0.85) ruleCol[x] = 1;
678
+ else liveW++;
679
+ }
680
+ const blank = Math.max(1, Math.floor(w * 0.02));
681
+ const bands = [];
682
+ let start = -1;
683
+ for (let y = 0; y <= h; y++) {
684
+ let n = 0;
685
+ if (y < h) {
686
+ for (let x = 0; x < w; x++) if (!ruleCol[x] && isInk(gray[y * w + x])) n++;
687
+ }
688
+ const on = y < h && n > blank && n < liveW * 0.85;
689
+ if (on && start < 0) start = y;
690
+ else if (!on && start >= 0) {
691
+ bands.push([start, y]);
692
+ start = -1;
693
+ }
694
+ }
695
+ let y0 = 0, y1 = h;
696
+ if (bands.length >= 2 && h >= 24) {
697
+ const mh = Math.max(...bands.map(([a, b]) => b - a));
698
+ const [top, next] = [bands[0], bands[1]], [bot, prev] = [bands[bands.length - 1], bands[bands.length - 2]];
699
+ if (top[0] <= 1 && top[1] - top[0] < mh * 0.45) y0 = top[1] + next[0] >> 1;
700
+ if (bot[1] >= h - 1 && bot[1] - bot[0] < mh * 0.45) y1 = bot[0] + prev[1] + 1 >> 1;
701
+ if (y0 > 0 || y1 < h) {
702
+ let runs = 0, len = 0;
703
+ for (let x = 0; x <= w; x++) {
704
+ let on = false;
705
+ if (x < w && !ruleCol[x]) for (let y = y0; y < y1 && !on; y++) on = isInk(gray[y * w + x]);
706
+ if (on) len++;
707
+ else {
708
+ if (len >= (y1 - y0) * 0.3) runs++;
709
+ len = 0;
710
+ }
711
+ }
712
+ if (runs < 2) {
713
+ y0 = 0;
714
+ y1 = h;
715
+ }
716
+ }
717
+ }
718
+ const textCol = (x) => {
719
+ for (let y = y0; y < y1; y++) if (isInk(gray[y * w + x])) return true;
720
+ return false;
721
+ };
722
+ const border = (x) => colInk[x] >= h * 0.95;
723
+ const gap = Math.max(2, Math.round(h * 0.1));
724
+ const clearFrom = (x, dir) => {
725
+ for (let k = 0; k < gap; k++) {
726
+ const c = x + dir * k;
727
+ if (c < 0 || c >= w || textCol(c)) return false;
728
+ }
729
+ return true;
730
+ };
731
+ let x0 = 0, x1 = w;
732
+ for (let x = 0; x < w * 0.2; x++) {
733
+ if (border(x)) {
734
+ let e = x;
735
+ while (e + 1 < w && border(e + 1)) e++;
736
+ if (clearFrom(e + 1, 1)) x0 = e + 1;
737
+ break;
738
+ }
739
+ if (textCol(x)) break;
740
+ }
741
+ for (let x = w - 1; x >= w * 0.8; x--) {
742
+ if (border(x)) {
743
+ let e = x;
744
+ while (e - 1 >= 0 && border(e - 1)) e--;
745
+ if (clearFrom(e - 1, -1)) x1 = e;
746
+ break;
747
+ }
748
+ if (textCol(x)) break;
749
+ }
750
+ return y0 > 0 || y1 < h || x0 > 0 || x1 < w ? { x0, x1, y0, y1 } : null;
751
+ }
752
+ function tallInkCount(gray, w, h, ink) {
753
+ const { comps } = components(gray, w, h, ink);
754
+ let charH = 0;
755
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
756
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.5).sort((a, b) => a.x0 - b.x0);
757
+ let n = 0, end = -1;
758
+ for (const c of tall) {
759
+ if (c.x0 >= end) n++;
760
+ end = Math.max(end, c.x1);
761
+ }
762
+ return n;
763
+ }
764
+ function gapGlyphs(gray, w, h, ink) {
765
+ const { comps, label } = components(gray, w, h, ink);
766
+ let charH = 0;
767
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
768
+ if (charH < 10) return { glyphs: [], bandH: 0 };
769
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
770
+ const bandT = Math.min(...tall.map((c) => c.y0)), bandB = Math.max(...tall.map((c) => c.y1)), bh = bandB - bandT;
771
+ const inBand = (c) => c.y1 > bandT && c.y0 < bandB;
772
+ const out = [];
773
+ for (const c of comps) {
774
+ if (!inBand(c)) continue;
775
+ const cw = c.x1 - c.x0, ch = c.y1 - c.y0;
776
+ if (ch >= bh * 0.9 && cw <= bh * 0.15) continue;
777
+ 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));
778
+ const apart = !others.some((o) => o.x0 < c.x1 + 1 && o.x1 > c.x0 - 1);
779
+ const nearlyApart = !others.some((o) => Math.min(o.x1, c.x1) - Math.max(o.x0, c.x0) > 2);
780
+ const spans = [], runs = [];
781
+ let n = 0;
782
+ for (let y = c.y0; y < c.y1; y++) {
783
+ let s = 0, r = 0, prev = false;
784
+ for (let x = c.x0; x < c.x1; x++) {
785
+ const on = label[y * w + x] === c.id;
786
+ if (on) {
787
+ s++;
788
+ if (!prev) r++;
789
+ }
790
+ prev = on;
791
+ }
792
+ spans.push(s);
793
+ runs.push(r);
794
+ n += s;
795
+ }
796
+ const fill = n / (cw * ch), cy = ((c.y0 + c.y1) / 2 - bandT) / bh;
797
+ const band = Math.max(1, Math.round(ch * 0.15));
798
+ const top = Math.max(...spans.slice(0, band)) / cw, bottom = Math.max(...spans.slice(ch - band)) / cw;
799
+ let mark = null;
800
+ const speck = Math.max(3, bh * 0.08);
801
+ const dot = ch <= bh * 0.25 && cw <= bh * 0.25 && ch >= speck && cw >= speck && cw >= ch * 0.6 && cw <= ch * 1.6;
802
+ 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;
803
+ if (!apart && !(nearlyApart && ch >= bh * 0.6)) {
804
+ if (small) out.push({ x0: c.x0, x1: c.x1, mark: "\xB7" });
805
+ continue;
806
+ }
807
+ if (!apart) {
808
+ } else if (dot && fill >= 0.6 && cy >= 0.3 && cy <= 0.7) mark = "\xB7";
809
+ else if (small) mark = "\xB7";
810
+ else if (dot && fill >= 0.6 && cy >= 0.75 && cw >= ch * 0.8) mark = ".";
811
+ 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";
812
+ else if (ch <= bh * 0.15 && cw >= ch * 2 && cw <= bh * 0.8 && fill >= 0.7 && cy >= 0.35 && cy <= 0.75) mark = "-";
813
+ 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";
814
+ else if (ch >= bh * 0.3 && ch <= bh * 0.65 && fill < 0.5) {
815
+ const stem = (from, to) => {
816
+ let sx = 0, k = 0;
817
+ for (let y = from; y < to; y++) for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) {
818
+ sx += x - c.x0;
819
+ k++;
820
+ }
821
+ return k ? sx / k / cw : 0.5;
822
+ };
823
+ if (c.y0 <= bandT + bh * 0.15 && top >= 0.7 && bottom <= 0.5 && stem(c.y0 + band, c.y1) <= 0.35) mark = "\u300C";
824
+ else if (c.y1 >= bandB - bh * 0.15 && bottom >= 0.7 && top <= 0.5 && stem(c.y0, c.y1 - band) >= 0.65) mark = "\u300D";
825
+ } 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";
826
+ if (!mark && ch >= bh * 0.6 && cw >= ch * 0.25 && cw <= ch * 0.7 && fill <= 0.4 && runs.every((r) => r <= 1)) {
827
+ const mx = (y) => {
828
+ let sx = 0, k = 0;
829
+ for (let x = c.x0; x < c.x1; x++) if (label[y * w + x] === c.id) {
830
+ sx += x;
831
+ k++;
832
+ }
833
+ return k ? sx / k : NaN;
834
+ };
835
+ if (mx(c.y0 + band) - mx(c.y1 - 1 - band) >= cw * 0.5) mark = "/";
836
+ }
837
+ if (mark) out.push({ x0: c.x0, x1: c.x1, mark });
838
+ }
839
+ return { glyphs: out, bandH: bh };
840
+ }
841
+ function ringBullet(gray, w, h, ink, cx) {
842
+ const { comps, label } = components(gray, w, h, ink);
843
+ let charH = 0;
844
+ for (const c2 of comps) charH = Math.max(charH, c2.y1 - c2.y0);
845
+ if (charH < 10) return null;
846
+ const tall = comps.filter((c2) => c2.y1 - c2.y0 >= charH * 0.6);
847
+ const bandT = Math.min(...tall.map((c2) => c2.y0)), bh = Math.max(...tall.map((c2) => c2.y1)) - bandT;
848
+ let best = null, bestD = bh * 0.6;
849
+ for (const c2 of comps) {
850
+ const d = Math.abs((c2.x0 + c2.x1) / 2 - cx);
851
+ if (c2.y1 - c2.y0 >= bh * 0.3 && d < bestD) {
852
+ bestD = d;
853
+ best = c2;
854
+ }
855
+ }
856
+ if (!best) return null;
857
+ const c = best, cw = c.x1 - c.x0, ch = c.y1 - c.y0;
858
+ if (cw < ch * 0.85 || cw > ch * 1.2) return null;
859
+ const runs = (xs, at) => {
860
+ let n2 = 0, len = 0, mx2 = 0, mn = Infinity;
861
+ for (const k of [...xs, -1]) {
862
+ if (k >= 0 && at(k)) {
863
+ if (!len) n2++;
864
+ len++;
865
+ mx2 = Math.max(mx2, len);
866
+ } else {
867
+ if (len) mn = Math.min(mn, len);
868
+ len = 0;
869
+ }
870
+ }
871
+ return { n: n2, mx: mx2, mn };
872
+ };
873
+ const my = c.y0 + c.y1 >> 1, mx = c.x0 + c.x1 >> 1;
874
+ const row = runs(Array.from({ length: cw }, (_, k) => c.x0 + k), (x) => label[my * w + x] === c.id);
875
+ const col = runs(Array.from({ length: ch }, (_, k) => c.y0 + k), (y) => label[y * w + mx] === c.id);
876
+ if (row.n !== 2 || col.n !== 2) return null;
877
+ let n = 0;
878
+ 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++;
879
+ const fill = n / (cw * ch), rel = ch / bh;
880
+ if (rel >= 0.62 && (fill <= 0.23 ? row.mx <= cw * 0.1 : fill <= 0.33 && row.mn <= cw * 0.08)) return "\u25CB";
881
+ if (rel <= 0.58 && fill >= 0.24) return "\u3147";
882
+ if (rel >= 0.6 && rel <= 0.8 && fill >= 0.3 && ((c.y0 + c.y1) / 2 - bandT) / bh >= 0.55) return "o";
883
+ return null;
884
+ }
885
+ function discAt(gray, w, h, ink, cx) {
886
+ const { comps, label } = components(gray, w, h, ink);
887
+ let charH = 0;
888
+ for (const c2 of comps) charH = Math.max(charH, c2.y1 - c2.y0);
889
+ if (charH < 10) return false;
890
+ const tall = comps.filter((c2) => c2.y1 - c2.y0 >= charH * 0.6);
891
+ const bh = Math.max(...tall.map((c2) => c2.y1)) - Math.min(...tall.map((c2) => c2.y0));
892
+ 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];
893
+ if (!c) return false;
894
+ const cw = c.x1 - c.x0, ch = c.y1 - c.y0;
895
+ if (ch < bh * 0.45 || ch > bh * 1.05 || cw < ch * 0.8 || cw > ch * 1.25) return false;
896
+ let n = 0;
897
+ 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++;
898
+ const fill = n / (cw * ch);
899
+ return fill >= 0.65 && fill <= 0.9;
900
+ }
901
+ function serifOne(gray, w, h, ink, cx) {
902
+ const { comps, label } = components(gray, w, h, ink);
903
+ let charH = 0;
904
+ for (const c2 of comps) charH = Math.max(charH, c2.y1 - c2.y0);
905
+ if (charH < 10) return false;
906
+ 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];
907
+ if (!c) return false;
908
+ const ch = c.y1 - c.y0;
909
+ const span = (a, b) => {
910
+ let mn = Infinity, mx = -1;
911
+ 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) {
912
+ mn = Math.min(mn, x);
913
+ mx = Math.max(mx, x);
914
+ }
915
+ return [mn, mx];
916
+ };
917
+ const [m0, m1] = span(0.4, 0.6), [t0, t1] = span(0, 0.2), [b0, b1] = span(0.8, 1);
918
+ const sw = m1 - m0 + 1;
919
+ const even = (l, r) => l >= sw * 0.4 && r >= sw * 0.4 && Math.abs(l - r) <= Math.max(l, r) * 0.35;
920
+ return sw >= 2 && sw <= ch * 0.3 && even(m0 - t0, t1 - m1) && even(m0 - b0, b1 - m1);
921
+ }
922
+ function starRun(gray, w, h, ink, cx) {
923
+ const { comps, label } = components(gray, w, h, ink);
924
+ let charH = 0;
925
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
926
+ if (charH < 10) return 0;
927
+ const tall = comps.filter((c) => c.y1 - c.y0 >= charH * 0.6);
928
+ const bandT = Math.min(...tall.map((c) => c.y0)), bh = Math.max(...tall.map((c) => c.y1)) - bandT;
929
+ const star = (c) => {
930
+ const cw = c.x1 - c.x0, ch = c.y1 - c.y0;
931
+ 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;
932
+ let n = 0;
933
+ 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++;
934
+ const fill = n / (cw * ch);
935
+ return fill >= 0.25 && fill <= 0.65;
936
+ };
937
+ const stars2 = comps.filter(star).sort((a, b) => a.x0 - b.x0);
938
+ const k = stars2.findIndex((c) => cx >= c.x0 - (c.x1 - c.x0) && cx <= c.x1 + (c.x1 - c.x0));
939
+ if (k < 0) return 0;
940
+ let lo = k, hi = k;
941
+ while (lo > 0 && stars2[lo].x0 - stars2[lo - 1].x1 <= stars2[lo].x1 - stars2[lo].x0) lo--;
942
+ while (hi + 1 < stars2.length && stars2[hi + 1].x0 - stars2[hi].x1 <= stars2[hi].x1 - stars2[hi].x0) hi++;
943
+ return hi - lo + 1;
944
+ }
945
+ var BRACKET_TAPER = 0.16;
946
+ var BRACKET_SOLID = 0.4;
947
+ var BRACKET_WIDE = 0.265;
948
+ var BRACKET_LENS = 0.35;
949
+ function bracketShape(f, close) {
950
+ const [near, far] = close ? [f.bottom, f.top] : [f.top, f.bottom];
951
+ const paren = close ? ")" : "(";
952
+ if (near >= 0.7 && far <= 0.5) return f.taper < BRACKET_TAPER ? close ? "\u300D" : "\u300C" : paren;
953
+ if (near >= 0.7 && far >= 0.7 && f.taper >= BRACKET_TAPER) {
954
+ return f.fill >= BRACKET_SOLID && (f.cw >= f.ch * BRACKET_WIDE || f.taper >= BRACKET_LENS) ? close ? "\u3011" : "\u3010" : paren;
955
+ }
956
+ return null;
957
+ }
958
+ function leadingBullet(gray, w, h, ink, firstX) {
959
+ const { comps, label } = components(gray, w, h, ink);
960
+ let charH = 0;
961
+ for (const c of comps) charH = Math.max(charH, c.y1 - c.y0);
962
+ if (charH < 10) return null;
963
+ const cand = comps.filter((c) => c.y1 - c.y0 >= charH * 0.15).sort((a, b) => a.x0 - b.x0);
964
+ const first = cand[0];
965
+ if (!first) return null;
966
+ const cl = cand.filter((c) => c.x0 < first.x1 && c.x1 > first.x0);
967
+ 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));
968
+ const cw = x1 - x0, ch = y1 - y0;
969
+ if (cw < ch * 0.8 || cw > ch * 1.25) return null;
970
+ const next = cand.find((c) => c.x0 >= x1);
971
+ if (next && next.x0 - x1 < ch * 0.3) return null;
972
+ let inkN = 0;
973
+ 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++;
974
+ const fill = inkN / (cw * ch);
975
+ const rel = ch / charH;
976
+ const ring2 = (c) => {
977
+ const a = c.x1 - c.x0, b = c.y1 - c.y0;
978
+ return a >= b * 0.75 && a <= b * 1.33;
979
+ };
980
+ 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 && ring2(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));
981
+ const span = (y) => {
982
+ let n = 0;
983
+ for (let x = x0; x < x1; x++) if (cl.some((c) => label[y * w + x] === c.id)) n++;
984
+ return n;
985
+ };
986
+ const band = Math.max(1, Math.round(ch * 0.15));
987
+ const edgeFull = (from, dir) => {
988
+ let m = 0;
989
+ for (let k = 0; k < band; k++) m = Math.max(m, span(from + dir * k));
990
+ return m >= cw * 0.8;
991
+ };
992
+ let midRuns = 0;
993
+ {
994
+ let prev = false;
995
+ const my = y0 + (ch >> 1);
996
+ for (let x = x0; x < x1; x++) {
997
+ const on = cl.some((c) => label[my * w + x] === c.id);
998
+ if (on && !prev) midRuns++;
999
+ prev = on;
1000
+ }
1001
+ }
1002
+ const hollowSquare = cl.length === 1 && fill < 0.5 && midRuns === 2 && edgeFull(y0, 1) && edgeFull(y1 - 1, -1);
1003
+ let mark = null;
1004
+ if (hollowSquare && rel >= 0.6 && rel <= 0.92) mark = "\u25A1";
1005
+ else if (nested && fill < 0.45 && rel >= 0.6) mark = "\u25CE";
1006
+ else if (cl.length === 1 && fill >= 0.9 && rel >= 0.28 && rel <= 0.6) mark = "\u25AA";
1007
+ else if (cl.length === 1 && fill >= 0.65 && fill <= 0.88 && rel >= 0.5 && rel <= 0.8) mark = "\u25CF";
1008
+ if (!mark) return null;
1009
+ return { mark, covers: firstX <= x1 + ch * 0.3 };
1010
+ }
1011
+
1012
+ // src/ocr/glyph-restore.ts
1013
+ function restoreGlyphs(rgba, pageW, box, text, steps, stepPx, note = {}) {
1014
+ const chars = [...text];
1015
+ if (chars.length !== steps.length) return text;
1016
+ const gray = grayCrop(rgba, pageW, box);
1017
+ const c = { chars, cx: (i) => (steps[i] + 0.5) * stepPx, gray, w: box.w, h: box.h, ink: inkStats(gray) };
1018
+ if (/[[\]]/.test(text)) brackets(c);
1019
+ if (/['"]/.test(text)) quotes(c);
1020
+ if (/(^|\D)[1-9](\D|$)/.test(text)) circled(c);
1021
+ if (text.includes("*")) stars(c);
1022
+ if (/[\u00b7\u2022]/.test(text)) discs(c);
1023
+ gaps(c);
1024
+ note.ringLead = ring(c);
1025
+ if (/[I1Ⅰ-Ⅲ]|^\s*\./.test(text)) roman(c);
1026
+ return chars.join("");
1027
+ }
1028
+ function brackets({ chars, cx, gray, w, h, ink }) {
1029
+ for (let i = 0; i < chars.length; i++) {
1030
+ if (chars[i] !== "[" && chars[i] !== "]") continue;
1031
+ const f = bracketFeatures(gray, w, h, ink, cx(i));
1032
+ const shape = f && bracketShape(f, chars[i] === "]");
1033
+ if (shape) chars[i] = shape;
1034
+ }
1035
+ }
1036
+ function quotes({ chars, cx, gray, w, h, ink }) {
1037
+ const year = (i) => chars[i] === "'" && !/[\p{L}\p{N}]/u.test(_nullishCoalesce(chars[i - 1], () => ( ""))) && /^\d{2}(?!\d)/.test(chars.slice(i + 1, i + 4).join(""));
1038
+ const single = { "'": chars.filter((c, i) => c === "'" && !year(i)).length === 1, '"': chars.filter((c) => c === '"').length === 1 };
1039
+ const open = { "'": false, '"': false };
1040
+ for (let i = 0; i < chars.length; i++) {
1041
+ const c = chars[i];
1042
+ const kind = c === "'" || c === "\u2018" || c === "\u2019" ? "'" : c === '"' || c === "\u201C" || c === "\u201D" ? '"' : null;
1043
+ if (!kind) continue;
1044
+ if (c !== kind) {
1045
+ open[kind] = c === "\u2018" || c === "\u201C";
1046
+ continue;
1047
+ }
1048
+ if (open[kind]) {
1049
+ open[kind] = false;
1050
+ continue;
1051
+ }
1052
+ if (!year(i) && !single[kind]) continue;
1053
+ const r = quoteHead(gray, w, h, ink, cx(i));
1054
+ if (r !== null && r >= 0.47) {
1055
+ chars[i] = kind === "'" ? "\u2018" : "\u201C";
1056
+ open[kind] = true;
1057
+ }
1058
+ }
1059
+ }
1060
+ function circled({ chars, cx, gray, w, h, ink }) {
1061
+ const at = chars.flatMap((c, i) => /^[1-9]$/.test(c) && !/\d/.test(_nullishCoalesce(chars[i - 1], () => ( ""))) && !/\d/.test(_nullishCoalesce(chars[i + 1], () => ( ""))) ? [i] : []);
1062
+ if (!at.length) return;
1063
+ const hit = circledAt(gray, w, h, ink, at.map(cx));
1064
+ at.forEach((i, k) => {
1065
+ if (hit[k]) chars[i] = String.fromCharCode(9311 + Number(chars[i]));
1066
+ });
1067
+ }
1068
+ function stars({ chars, cx, gray, w, h, ink }) {
1069
+ for (let i = 0; i < chars.length; i++) {
1070
+ if (chars[i] !== "*" || chars[i - 1] === "*") continue;
1071
+ let e = i;
1072
+ while (chars[e + 1] === "*") e++;
1073
+ const n = starRun(gray, w, h, ink, cx(i));
1074
+ if (n > e - i + 1 && n <= 4) chars[e] += "*".repeat(n - (e - i + 1));
1075
+ i = e;
1076
+ }
1077
+ }
1078
+ function discs({ chars, cx, gray, w, h, ink }) {
1079
+ for (let i = 0; i < chars.length; i++) if (/^[\u00b7\u2022]$/.test(chars[i]) && discAt(gray, w, h, ink, cx(i))) chars[i] = "\u25CF";
1080
+ }
1081
+ function gaps({ chars, cx, gray, w, h, ink }) {
1082
+ const { glyphs, bandH } = gapGlyphs(gray, w, h, ink);
1083
+ if (!glyphs.length) return;
1084
+ const live = chars.map((c, i) => i).filter((i) => chars[i].trim());
1085
+ if (!live.length) return;
1086
+ const d = [];
1087
+ for (let k = 0; k + 1 < live.length; k++) if (/[가-힣]$/.test(chars[live[k]]) && /^[가-힣]/.test(chars[live[k + 1]])) d.push(cx(live[k + 1]) - cx(live[k]));
1088
+ const pitch = d.length >= 2 ? d.sort((a, b) => a - b)[d.length >> 1] : bandH;
1089
+ const nearTo = (g) => (i) => Math.abs(cx(i) - (g.x0 + g.x1) / 2) <= Math.max(pitch * 0.45, (g.x1 - g.x0) / 2 + 1);
1090
+ const loose = glyphs.filter((g) => /^[\u00b7.]/.test(g.mark) && !live.some(nearTo(g)));
1091
+ for (const g of glyphs.sort((a, b) => a.x0 - b.x0)) {
1092
+ const mid = (g.x0 + g.x1) / 2;
1093
+ const near = nearTo(g);
1094
+ const cover = live.filter(near);
1095
+ if (g.mark === "\xB7") {
1096
+ const sp = chars.findIndex((c, i) => c === " " && near(i));
1097
+ const p = sp >= 0 ? chars.slice(0, sp).reverse().find((c) => c.trim()) : void 0, q = sp >= 0 ? chars.slice(sp + 1).find((c) => c.trim()) : void 0;
1098
+ if (p && q && /[가-힣]$/.test(p) && /^[가-힣]/.test(q) && !loose.some((o) => o !== g && Math.abs((o.x0 + o.x1) / 2 - mid) <= pitch * 1.5)) chars[sp] = "\xB7";
1099
+ continue;
1100
+ }
1101
+ if (cover.length) {
1102
+ if (g.mark.startsWith("\u25A1") && cover.length === 1 && /^[0Oo\u25cb]$/.test(chars[cover[0]])) chars[cover[0]] = "\u25A1";
1103
+ continue;
1104
+ }
1105
+ if (g.mark === "\u25A1\0") continue;
1106
+ const before = live.filter((i) => cx(i) < mid), after = live.filter((i) => cx(i) > mid);
1107
+ if (!before.length) {
1108
+ if (after.length && /^[\u3002\u00b7\u300c\u25b2]$/.test(g.mark) && /^[가-힣]/.test(chars[after[0]])) chars[after[0]] = g.mark + chars[after[0]];
1109
+ else if (g.mark === "-" && /^[\dⅰ-ⅹivx]+\s*-$/.test(chars.join("").trim())) chars[after[0]] = "-" + chars[after[0]];
1110
+ continue;
1111
+ }
1112
+ if (!after.length && g.mark !== ".") continue;
1113
+ if (g.mark === "/" && !(/\d$/.test(chars[before[before.length - 1]]) && /^\d/.test(chars[after[0]]))) continue;
1114
+ const prev = chars[before[before.length - 1]], next = after.length ? chars[after[0]] : "";
1115
+ if (g.mark === "\xB7" || g.mark === ".") {
1116
+ if (loose.some((o) => o !== g && Math.abs((o.x0 + o.x1) / 2 - mid) <= pitch * 1.5)) continue;
1117
+ if (/[\u2026.\u00b7]$/.test(prev) || /^[\u2026.\u00b7]/.test(next)) continue;
1118
+ }
1119
+ if (g.mark === "\xB7") {
1120
+ if (/^\d/.test(next)) continue;
1121
+ const sp = chars.findIndex((c, i) => c === " " && near(i));
1122
+ if (sp >= 0) {
1123
+ chars[sp] = g.mark;
1124
+ continue;
1125
+ }
1126
+ }
1127
+ chars[before[before.length - 1]] += g.mark;
1128
+ }
1129
+ }
1130
+ function ring({ chars, cx, gray, w, h, ink }) {
1131
+ const k = chars.findIndex((c) => c.trim());
1132
+ if (k < 0 || !/^[\u3147Oo0\u25cb]$/.test(chars[k])) return false;
1133
+ const rest = chars.slice(k + 1).join("").trimStart();
1134
+ if (!/^[(\[「【<]?[가-힣]/.test(rest)) return false;
1135
+ const r = ringBullet(gray, w, h, ink, cx(k));
1136
+ if (r) chars[k] = r;
1137
+ return r !== null;
1138
+ }
1139
+ function roman({ chars, cx, gray, w, h, ink }) {
1140
+ const ROMAN = /^[IⅠ-Ⅲ]$/, LATIN = /[A-Za-z]$/;
1141
+ for (let i = 0; i < chars.length; i++) {
1142
+ if (chars[i] !== "1" || /[\dA-Za-z]$/.test(_nullishCoalesce(chars[i - 1], () => ( ""))) || /^[\dA-Za-z,]/.test(_nullishCoalesce(chars[i + 1], () => ( "")))) continue;
1143
+ if (serifOne(gray, w, h, ink, cx(i))) chars[i] = "\u2160";
1144
+ }
1145
+ const inRoman = (i) => ROMAN.test(chars[i]) || chars[i] === "1" && (ROMAN.test(_nullishCoalesce(chars[i - 1], () => ( ""))) || ROMAN.test(_nullishCoalesce(chars[i + 1], () => ( ""))));
1146
+ const k = chars.findIndex((c) => c.trim());
1147
+ if (chars[k] === "." && chars.slice(k + 1).some((c) => /[가-힣A-Za-z0-9]/.test(c))) {
1148
+ const n = romanStems(gray, w, h, ink, -Infinity, cx(k));
1149
+ if (n) chars[k] = String.fromCharCode(8543 + n) + chars[k];
1150
+ }
1151
+ for (let s = 0; s < chars.length; ) {
1152
+ if (!inRoman(s) || s > 0 && LATIN.test(chars[s - 1])) {
1153
+ s++;
1154
+ continue;
1155
+ }
1156
+ let e = s;
1157
+ while (e < chars.length && inRoman(e)) e++;
1158
+ const tok = chars.slice(s, e).join("");
1159
+ if (e < chars.length && /^[A-Za-z]/.test(chars[e])) {
1160
+ s = e;
1161
+ continue;
1162
+ }
1163
+ let p = s - 1, q = e;
1164
+ while (p >= 0 && !chars[p].trim()) p--;
1165
+ while (q < chars.length && !chars[q].trim()) q++;
1166
+ const n = romanStems(gray, w, h, ink, p >= 0 ? cx(p) : -Infinity, q < chars.length ? cx(q) : Infinity);
1167
+ const canon = n ? [["\u2160", "I"], ["\u2161", "II"], ["\u2162", "III"]][n - 1] : null;
1168
+ if (canon && !canon.includes(tok)) {
1169
+ chars[s] = canon[0];
1170
+ for (let i = s + 1; i < e; i++) chars[i] = "";
1171
+ }
1172
+ s = e;
1173
+ }
1174
+ }
573
1175
 
574
1176
  // src/ocr/postprocess.ts
575
1177
  var HANGUL_START = /^[\uac00-\ud7a3]/;
576
- function restoreSymbols(text) {
1178
+ function restoreSymbols(text, ringDecided = false) {
577
1179
  let s = text;
578
- s = s.replace(/^([Oo])(\s?)(?=[\uac00-\ud7a3])/, "\u25CB$2");
1180
+ if (!ringDecided) s = s.replace(/^([Oo])(\s?)(?=[\uac00-\ud7a3])/, "\u25CB$2");
579
1181
  s = s.replace(/(?<![A-Za-z])O{2,}(?![A-Za-z])/g, (m, i) => /[\uac00-\ud7a3]$/.test(s.slice(Math.max(0, i - 2), i).trimEnd()) || /^\s?[\uac00-\ud7a3]/.test(s.slice(i + m.length)) ? "\u25CB".repeat(m.length) : m);
580
1182
  s = s.replace(/[\u2206\u0394]/g, "\u25B3");
581
1183
  s = s.replace(/\(cid:\d*\)/g, "");
@@ -695,7 +1297,7 @@ function bandBoxes(b, bands, pageH) {
695
1297
  }
696
1298
  return out;
697
1299
  }
698
- function lineCrop(rgba, pageW, box, rot) {
1300
+ function lineCrop(rgba, pageW, box, rot, keep) {
699
1301
  const srcW = rot === 0 ? box.w : box.h;
700
1302
  const srcH = rot === 0 ? box.h : box.w;
701
1303
  const rw = Math.min(REC_MAX_WIDTH, Math.max(16, Math.round(srcW * REC_HEIGHT / srcH)));
@@ -730,6 +1332,10 @@ function lineCrop(rgba, pageW, box, rot) {
730
1332
  const wx = sx - x0;
731
1333
  const i00 = at(x0, y0), i01 = at(x1, y0), i10 = at(x0, y1), i11 = at(x1, y1);
732
1334
  const o = (dy * rw + dx) * 3;
1335
+ if (keep && rot === 0 && (sx < keep.x0 || sx >= keep.x1 || sy < keep.y0 || sy >= keep.y1)) {
1336
+ rgb[o] = rgb[o + 1] = rgb[o + 2] = keep.bg;
1337
+ continue;
1338
+ }
733
1339
  for (let c = 0; c < 3; c++) {
734
1340
  const top = rgba[i00 + c] * (1 - wx) + rgba[i01 + c] * wx;
735
1341
  const bottom = rgba[i10 + c] * (1 - wx) + rgba[i11 + c] * wx;
@@ -763,6 +1369,7 @@ var DEFAULT_OCR_TUNING = Object.freeze({
763
1369
  textScore: 0.5,
764
1370
  recBatch: 1,
765
1371
  splitTall: true,
1372
+ trimEdges: true,
766
1373
  minInkContrast: 35,
767
1374
  postprocess: true,
768
1375
  tightBoxes: true,
@@ -774,6 +1381,7 @@ var REC_MIN_WIDTH = 320;
774
1381
  var REC_BATCH_MAX_PIXELS = 48 * 16e3;
775
1382
  var TALL_RATIO = 1.5;
776
1383
  var ROTATE_RATIO = 3;
1384
+ var KEEP_MARGIN = 0.06;
777
1385
  var LEADER_MIN_DOTS = 4;
778
1386
  var LEADER_CONTEXT = 0.5;
779
1387
  var MIN_INK_RATIO = 0.01;
@@ -802,7 +1410,7 @@ var OcrEngine = class _OcrEngine {
802
1410
  ]);
803
1411
  const sharpAny = sharpModRaw;
804
1412
  const sharpMod = typeof sharpAny === "function" ? sharpAny : _nullishCoalesce(sharpAny.default, () => ( sharpAny));
805
- const dir = _chunkMH6MAL64cjs.getOcrModelsDir.call(void 0, );
1413
+ const dir = _chunkWDQTFAXYcjs.getOcrModelsDir.call(void 0, );
806
1414
  const sessionOpts = {
807
1415
  graphOptimizationLevel: "all",
808
1416
  executionProviders: ["cpu"],
@@ -810,11 +1418,11 @@ var OcrEngine = class _OcrEngine {
810
1418
  // paddle2onnx 변환 잔여물 W 로그 폭주 억제
811
1419
  };
812
1420
  const [det, rec, dictYml] = await Promise.all([
813
- ortMod.InferenceSession.create(_path.join.call(void 0, dir, _chunkMH6MAL64cjs.OCR_DET_MODEL.filename), sessionOpts),
814
- ortMod.InferenceSession.create(_path.join.call(void 0, dir, _chunkMH6MAL64cjs.OCR_REC_MODEL.filename), sessionOpts),
815
- _promises.readFile.call(void 0, _path.join.call(void 0, dir, _chunkMH6MAL64cjs.OCR_REC_DICT.filename), "utf-8")
1421
+ ortMod.InferenceSession.create(_path.join.call(void 0, dir, _chunkWDQTFAXYcjs.OCR_DET_MODEL.filename), sessionOpts),
1422
+ ortMod.InferenceSession.create(_path.join.call(void 0, dir, _chunkWDQTFAXYcjs.OCR_REC_MODEL.filename), sessionOpts),
1423
+ _promises.readFile.call(void 0, _path.join.call(void 0, dir, _chunkWDQTFAXYcjs.OCR_REC_DICT.filename), "utf-8")
816
1424
  ]);
817
- const dict = _chunkMH6MAL64cjs.parseCharacterDict.call(void 0, dictYml);
1425
+ const dict = _chunkWDQTFAXYcjs.parseCharacterDict.call(void 0, dictYml);
818
1426
  if (dict.length === 0) throw new Error("OCR \uC0AC\uC804 \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uBAA8\uB378 \uCE90\uC2DC\uB97C \uC0AD\uC81C \uD6C4 \uC7AC\uB2E4\uC6B4\uB85C\uB4DC\uD558\uC138\uC694");
819
1427
  return new _OcrEngine({ det, rec, dict, ort: ortMod, sharp: sharpMod });
820
1428
  }
@@ -859,6 +1467,8 @@ var OcrEngine = class _OcrEngine {
859
1467
  continue;
860
1468
  }
861
1469
  }
1470
+ const trim = tuning.trimEdges ? edgeTrim(gray, b.w, b.h, ink) : null;
1471
+ const keep = trim ? { ...trim, bg: median(gray) } : void 0;
862
1472
  const leaders = tuning.splitLeaders ? leaderRuns(gray, b.w, b.h, ink, LEADER_MIN_DOTS) : [];
863
1473
  if (leaders.length) {
864
1474
  const join2 = joins.length;
@@ -877,20 +1487,37 @@ var OcrEngine = class _OcrEngine {
877
1487
  joins[join2].trailDots = dots;
878
1488
  continue;
879
1489
  }
1490
+ if (keep) jobs.push({ box: b, rot: 0, group, keep });
880
1491
  jobs.push({ box: b, rot: 0, group: group++ });
881
1492
  }
882
1493
  const results = await this.recognizeJobs(rgba, width, jobs, tuning.recBatch);
883
1494
  const best = /* @__PURE__ */ new Map();
1495
+ const score = (job, confidence) => confidence - (job.keep ? KEEP_MARGIN : 0);
884
1496
  jobs.forEach((job, i) => {
885
1497
  const r = results[i];
886
1498
  if (!r) return;
887
1499
  const cur = best.get(job.group);
888
- if (!cur || r.confidence > cur.confidence) best.set(job.group, { job, ...r });
1500
+ if (!cur || score(job, r.confidence) > score(cur.job, cur.confidence)) best.set(job.group, { job, ...r });
889
1501
  });
890
1502
  let items = [];
891
- for (const { job, text: raw, confidence } of best.values()) {
892
- let text = tuning.postprocess ? restoreSymbols(raw.trim()) : raw;
1503
+ for (const { job, text: read, confidence, steps, stepPx } of best.values()) {
1504
+ const note = {};
1505
+ const raw = tuning.postprocess && job.rot === 0 ? restoreGlyphs(rgba, width, job.box, read, steps, stepPx, note) : read;
1506
+ let text = tuning.postprocess ? restoreSymbols(raw.trim(), note.ringLead) : raw;
893
1507
  if (!text.trim()) continue;
1508
+ if (tuning.postprocess && job.rot === 0 && !/^[◎●▪□■○ㅇ]/.test(text)) {
1509
+ const chars = [...read], k = chars.findIndex((c) => c.trim());
1510
+ if (k >= 0 && steps.length === chars.length) {
1511
+ const g = grayCrop(rgba, width, job.box);
1512
+ const b = leadingBullet(g, job.box.w, job.box.h, inkStats(g), (steps[k] + 0.5) * stepPx);
1513
+ const body = (_nullishCoalesce(text.match(/[가-힣]/g), () => ( []))).length >= 4;
1514
+ const miss = /^[Oo0•·ㆍ∙‧○]/.test(text);
1515
+ if (_optionalChain([b, 'optionalAccess', _12 => _12.mark]) === "\u25CE" && b.covers && /^[Oo0○]/.test(text)) text = b.mark + " " + text.slice(1).trimStart();
1516
+ else if (_optionalChain([b, 'optionalAccess', _13 => _13.mark]) === "\u25A1") {
1517
+ if (!b.covers && body) text = b.mark + " " + text;
1518
+ } else if (b && b.mark !== "\u25CE" && body) text = b.covers ? miss ? b.mark + " " + text.slice(1).trimStart() : text : b.mark + " " + text;
1519
+ }
1520
+ }
894
1521
  if (tuning.postprocess && /^\d/.test(text) && job.rot === 0) {
895
1522
  const g = grayCrop(rgba, width, job.box);
896
1523
  const tri = leadingTriangle(g, job.box.w, job.box.h, inkStats(g));
@@ -971,7 +1598,7 @@ var OcrEngine = class _OcrEngine {
971
1598
  // ─── rec ─────────────────────────────────────────────
972
1599
  /** 라인 작업들을 폭 비율 순으로 배치 인식 — 결과는 jobs 순서 */
973
1600
  async recognizeJobs(rgba, pageW, jobs, batchSize) {
974
- const crops = jobs.map((j) => lineCrop(rgba, pageW, j.box, j.rot));
1601
+ const crops = jobs.map((j) => lineCrop(rgba, pageW, j.box, j.rot, j.keep));
975
1602
  const order = crops.map((_, i) => i).sort((a, b) => crops[a].w - crops[b].w);
976
1603
  const results = new Array(jobs.length).fill(null);
977
1604
  const plane = REC_HEIGHT;
@@ -1003,18 +1630,39 @@ var OcrEngine = class _OcrEngine {
1003
1630
  const [, T, C] = logits.dims;
1004
1631
  const data = logits.data;
1005
1632
  idx.forEach((ci, k) => {
1006
- results[ci] = ctcDecode(data.subarray(k * T * C, (k + 1) * T * C), T, C, this.dict);
1633
+ const r = ctcDecode(data.subarray(k * T * C, (k + 1) * T * C), T, C, this.dict);
1634
+ const job = jobs[ci], src = job.rot === 0 ? job.box.w : job.box.h;
1635
+ let kept = r;
1636
+ const last = crops[ci].w * T / bw + 1;
1637
+ const chars = r ? [...r.text] : [];
1638
+ if (r && job.rot === 0 && chars.length === r.steps.length && r.steps.some((t) => t > last) && !r.text.includes("(cid:")) {
1639
+ const tallCh = (c) => !/[\s.,:;\u00b7\u2026'"\u2018-\u201d`\-_~]/.test(c);
1640
+ const g = grayCrop(rgba, pageW, job.box);
1641
+ const inside = chars.filter((c, i) => r.steps[i] <= last && tallCh(c)).length;
1642
+ let budget = inside <= 2 ? tallInkCount(g, job.box.w, job.box.h, inkStats(g)) - inside : Infinity;
1643
+ const keep = chars.map((c, i) => r.steps[i] <= last || !tallCh(c) || budget-- > 0);
1644
+ if (!keep.every(Boolean)) kept = { ...r, text: chars.filter((_, i) => keep[i]).join(""), steps: r.steps.filter((_, i) => keep[i]) };
1645
+ }
1646
+ results[ci] = kept && { ...kept, stepPx: bw / T * (src / crops[ci].w) };
1007
1647
  });
1008
1648
  s = e;
1009
1649
  }
1010
1650
  return results;
1011
1651
  }
1012
1652
  };
1653
+ function median(gray) {
1654
+ const hist = new Uint32Array(256);
1655
+ for (let i = 0; i < gray.length; i++) hist[gray[i]]++;
1656
+ for (let v = 0, n = 0; v < 256; v++) if ((n += hist[v]) * 2 >= gray.length) return v;
1657
+ return 255;
1658
+ }
1013
1659
  function ctcDecode(data, T, C, dict) {
1014
1660
  let text = "";
1015
1661
  let confSum = 0;
1016
1662
  let confCount = 0;
1017
1663
  let prev = -1;
1664
+ const runs = [];
1665
+ let open = [];
1018
1666
  for (let t = 0; t < T; t++) {
1019
1667
  const off = t * C;
1020
1668
  let best = 0;
@@ -1028,7 +1676,12 @@ function ctcDecode(data, T, C, dict) {
1028
1676
  }
1029
1677
  const repeat = best === prev;
1030
1678
  prev = best;
1031
- if (best === 0 || repeat) continue;
1679
+ if (repeat && best !== 0) {
1680
+ for (const r of open) r[1] = t;
1681
+ continue;
1682
+ }
1683
+ open = [];
1684
+ if (best === 0) continue;
1032
1685
  let p = bestV;
1033
1686
  if (p > 1.0001 || p < 0) {
1034
1687
  let denom = 0;
@@ -1037,11 +1690,16 @@ function ctcDecode(data, T, C, dict) {
1037
1690
  }
1038
1691
  confSum += p;
1039
1692
  confCount++;
1040
- if (best >= 1 && best <= dict.length) text += dict[best - 1];
1041
- else if (best === dict.length + 1) text += " ";
1693
+ const tok = best >= 1 && best <= dict.length ? dict[best - 1] : best === dict.length + 1 ? " " : "";
1694
+ text += tok;
1695
+ for (const _ of tok) {
1696
+ const r = [t, t];
1697
+ runs.push(r);
1698
+ open.push(r);
1699
+ }
1042
1700
  }
1043
1701
  if (!text) return null;
1044
- return { text, confidence: confCount > 0 ? confSum / confCount : 0 };
1702
+ return { text, confidence: confCount > 0 ? confSum / confCount : 0, steps: runs.map(([a, b]) => (a + b) / 2) };
1045
1703
  }
1046
1704
  function componentBoxes(prob, w, h, thresh = DEFAULT_OCR_TUNING.detThresh, boxThresh = DEFAULT_OCR_TUNING.detBoxThresh) {
1047
1705
  const visited = new Uint8Array(w * h);
@@ -1092,7 +1750,7 @@ async function tryImport(name, loader) {
1092
1750
  return await loader();
1093
1751
  } catch (e) {
1094
1752
  throw new Error(
1095
- `\uB0B4\uC7A5 OCR \uC744 \uC0AC\uC6A9\uD558\uB824\uBA74 optional dependency '${name}' \uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \`npm install ${name}\` \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.${_chunkNWODPYBDcjs.OPTIONAL_DEP_INSTALL_HINT} \uC6D0\uC778: ${e.message}`
1753
+ `\uB0B4\uC7A5 OCR \uC744 \uC0AC\uC6A9\uD558\uB824\uBA74 optional dependency '${name}' \uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \`npm install ${name}\` \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.${_chunkSTM3ORY7cjs.OPTIONAL_DEP_INSTALL_HINT} \uC6D0\uC778: ${e.message}`
1096
1754
  );
1097
1755
  }
1098
1756
  }
@@ -1206,7 +1864,7 @@ async function runPdfOcr(buffer, targets, mode, warnings, onProgress, detectTabl
1206
1864
  () => Promise.resolve().then(() => _interopRequireWildcard(require("@hyzyla/pdfium")))
1207
1865
  );
1208
1866
  if (mode === "builtin") {
1209
- await _chunkMH6MAL64cjs.ensureOcrModels.call(void 0, (p) => {
1867
+ await _chunkWDQTFAXYcjs.ensureOcrModels.call(void 0, (p) => {
1210
1868
  if (p.phase === "download" && p.downloaded === 0) {
1211
1869
  process.stderr.write(`[kordoc-ocr] ${p.spec.name} \uB2E4\uC6B4\uB85C\uB4DC \uC911 (~${p.spec.sizeMb}MB)...
1212
1870
  `);
@@ -1221,10 +1879,10 @@ async function runPdfOcr(buffer, targets, mode, warnings, onProgress, detectTabl
1221
1879
  const count = doc.getPageCount();
1222
1880
  for (const pageNo of [...targets].filter((p) => p >= 1 && p <= count).sort((a, b) => a - b)) {
1223
1881
  const page = doc.getPage(pageNo - 1);
1224
- _optionalChain([onProgress, 'optionalCall', _12 => _12(++done, targets.size)]);
1882
+ _optionalChain([onProgress, 'optionalCall', _14 => _14(++done, targets.size)]);
1225
1883
  try {
1226
1884
  const blocks = await withTimeout(
1227
- ocrOnePage(page, pageNo, mode, engine, warnings, detectTables, _optionalChain([vectorOps, 'optionalAccess', _13 => _13.get, 'call', _14 => _14(pageNo)]), _optionalChain([imageRegions, 'optionalAccess', _15 => _15.get, 'call', _16 => _16(pageNo)])),
1885
+ ocrOnePage(page, pageNo, mode, engine, warnings, detectTables, _optionalChain([vectorOps, 'optionalAccess', _15 => _15.get, 'call', _16 => _16(pageNo)]), _optionalChain([imageRegions, 'optionalAccess', _17 => _17.get, 'call', _18 => _18(pageNo)])),
1228
1886
  PAGE_TIMEOUT_MS,
1229
1887
  `OCR \uD398\uC774\uC9C0 ${pageNo} \uD0C0\uC784\uC544\uC6C3 (${PAGE_TIMEOUT_MS / 1e3}\uCD08)`
1230
1888
  );
@@ -1260,7 +1918,7 @@ async function ocrOnePage(page, pageNo, mode, engine, warnings, detectTables, ve
1260
1918
  const upright = vectorOps ? rgba : deskewPage(rgba, rw, rh).rgba;
1261
1919
  const stats = { droppedLowConf: 0 };
1262
1920
  const ruling = vectorOps ? void 0 : detectRulingLines(upright, rw, rh, rh / pdfH);
1263
- const items = await engine.recognizePage(upright, rw, rh, stats, void 0, _optionalChain([ruling, 'optionalAccess', _17 => _17.cellDividers]));
1921
+ const items = await engine.recognizePage(upright, rw, rh, stats, regions ? REGION_TUNING : void 0, _optionalChain([ruling, 'optionalAccess', _19 => _19.cellDividers]));
1264
1922
  if (stats.droppedLowConf > 0) {
1265
1923
  warnings.push({
1266
1924
  page: pageNo,
@@ -1281,7 +1939,7 @@ async function ocrOnePage(page, pageNo, mode, engine, warnings, detectTables, ve
1281
1939
  return regions.flatMap((r, k) => {
1282
1940
  let own = items.filter((it) => inside(it, r));
1283
1941
  const near = reads[k];
1284
- if (_optionalChain([near, 'optionalAccess', _18 => _18.length]) && meanConfidence(near) > meanConfidence(own)) own = near;
1942
+ if (_optionalChain([near, 'optionalAccess', _20 => _20.length]) && meanConfidence(near) > meanConfidence(own)) own = near;
1285
1943
  return own.length ? ocrItemsToBlocks(own, pageNo, pdfW, pdfH, scale, ruling && rulingToPdfLines(ruling, scale, pdfH), detectTables) : [];
1286
1944
  });
1287
1945
  }
@@ -1312,6 +1970,7 @@ function halve(src, w, h) {
1312
1970
  }
1313
1971
  return { rgba: out, width: W, height: H };
1314
1972
  }
1973
+ var REGION_TUNING = Object.freeze({ ...DEFAULT_OCR_TUNING, trimEdges: false });
1315
1974
  function meanConfidence(items) {
1316
1975
  let n = 0, sum = 0;
1317
1976
  for (const it of items) {
@@ -1332,7 +1991,7 @@ async function closerReads(rgba, rw, rh, pdfH, hi, regions, engine) {
1332
1991
  }
1333
1992
  const crop = new Uint8Array(cw * ch * 4);
1334
1993
  for (let y = 0; y < ch; y++) crop.set(rgba.subarray(((y0 + y) * rw + x0) * 4, ((y0 + y) * rw + x0 + cw) * 4), y * cw * 4);
1335
- const items = await engine.recognizePage(crop, cw, ch);
1994
+ const items = await engine.recognizePage(crop, cw, ch, void 0, REGION_TUNING);
1336
1995
  out.push(items.map((it) => ({ ...it, x: (it.x + x0) / 2, y: (it.y + y0) / 2, w: it.w / 2, h: it.h / 2 })));
1337
1996
  }
1338
1997
  return out;
@@ -1353,14 +2012,14 @@ function ocrItemsToBlocks(items, pageNumber, pdfW, pdfH, scale, extraLines, dete
1353
2012
  isHidden: false
1354
2013
  };
1355
2014
  });
1356
- return _chunkMH6MAL64cjs.extractPageBlocksWithLines.call(void 0, norm, pageNumber, opList, pdfW, pdfH, extraLines, detectTables);
2015
+ return _chunkWDQTFAXYcjs.extractPageBlocksWithLines.call(void 0, norm, pageNumber, opList, pdfW, pdfH, extraLines, detectTables);
1357
2016
  }
1358
2017
  async function tryImport2(name, loader) {
1359
2018
  try {
1360
2019
  return await loader();
1361
2020
  } catch (e) {
1362
2021
  throw new Error(
1363
- `OCR \uC744 \uC0AC\uC6A9\uD558\uB824\uBA74 optional dependency '${name}' \uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \`npm install ${name}\` \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.${_chunkNWODPYBDcjs.OPTIONAL_DEP_INSTALL_HINT} \uC6D0\uC778: ${e.message}`
2022
+ `OCR \uC744 \uC0AC\uC6A9\uD558\uB824\uBA74 optional dependency '${name}' \uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \`npm install ${name}\` \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.${_chunkSTM3ORY7cjs.OPTIONAL_DEP_INSTALL_HINT} \uC6D0\uC778: ${e.message}`
1364
2023
  );
1365
2024
  }
1366
2025
  }
@@ -1399,4 +2058,4 @@ function bgraToRgba(bgra) {
1399
2058
 
1400
2059
 
1401
2060
  exports.getOcrEngine = getOcrEngine; exports.detectRulingLines = detectRulingLines; exports.rulingToPdfLines = rulingToPdfLines; exports.deskewPage = deskewPage; exports.MAX_OCR_PIXELS = MAX_OCR_PIXELS; exports.runPdfOcr = runPdfOcr; exports.ocrItemsToBlocks = ocrItemsToBlocks;
1402
- //# sourceMappingURL=chunk-B5NEGP65.cjs.map
2061
+ //# sourceMappingURL=chunk-FKUDAIJI.cjs.map