kordoc 4.17.1 → 4.17.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 (133) hide show
  1. package/README.md +3 -0
  2. package/dist/{-UNMTWAEI.js → -2UA2JHVA.js} +10 -10
  3. package/dist/{chunk-B4PUQ4FP.js → chunk-2XHXABCX.js} +2 -2
  4. package/dist/{chunk-MPAN6S5O.js → chunk-3N272C5L.js} +243 -114
  5. package/dist/chunk-3N272C5L.js.map +1 -0
  6. package/dist/{chunk-EULS3PDF.js → chunk-4HRJISJF.js} +3 -3
  7. package/dist/{chunk-KBIQGGNY.js → chunk-4TSYMJDL.js} +242 -113
  8. package/dist/chunk-4TSYMJDL.js.map +1 -0
  9. package/dist/{chunk-3M2BMQJY.js → chunk-5JZEY5IL.js} +119 -77
  10. package/dist/chunk-5JZEY5IL.js.map +1 -0
  11. package/dist/{chunk-5FCLQVT5.cjs → chunk-6DOCSYEM.cjs} +113 -71
  12. package/dist/chunk-6DOCSYEM.cjs.map +1 -0
  13. package/dist/{chunk-N6MPDM55.js → chunk-6OCKAXIS.js} +2 -2
  14. package/dist/{chunk-QXNQ2HNB.js → chunk-A2W2JQ3S.js} +2 -2
  15. package/dist/{chunk-66YTQ3NQ.js → chunk-C4MVZXB5.js} +2 -2
  16. package/dist/{chunk-GYHY2YLV.js → chunk-FGL4IGOT.js} +4 -4
  17. package/dist/{chunk-BEJYCCDN.cjs → chunk-GPC72VSF.cjs} +6 -6
  18. package/dist/chunk-GPC72VSF.cjs.map +1 -0
  19. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  20. package/dist/{chunk-FMP4POMD.js → chunk-GYTL6WT3.js} +3 -3
  21. package/dist/{chunk-4NH45EEB.cjs → chunk-HAW2VL62.cjs} +12 -12
  22. package/dist/chunk-HAW2VL62.cjs.map +1 -0
  23. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  24. package/dist/{chunk-ELNYQRBN.js → chunk-JY2NBFIG.js} +14 -12
  25. package/dist/chunk-JY2NBFIG.js.map +1 -0
  26. package/dist/{chunk-BQAJHAWR.js → chunk-KQIYC2ZU.js} +2 -2
  27. package/dist/{chunk-GRHSVGSB.cjs → chunk-KZVQK5NS.cjs} +2 -2
  28. package/dist/chunk-KZVQK5NS.cjs.map +1 -0
  29. package/dist/{chunk-AUFO5LVN.js → chunk-MDCTITUG.js} +2 -2
  30. package/dist/{chunk-OVSCNCT6.js → chunk-O4CB7D2Q.js} +2 -2
  31. package/dist/{chunk-3J3BJIOZ.js → chunk-SDRKHEVT.js} +2 -2
  32. package/dist/{chunk-HAPCN7BD.js → chunk-VU26WGE5.js} +37 -15
  33. package/dist/chunk-VU26WGE5.js.map +1 -0
  34. package/dist/{chunk-BHUYJBSQ.js → chunk-WJLICIYV.js} +2 -2
  35. package/dist/{chunk-ZRXQDJPM.cjs → chunk-WWMKIHF7.cjs} +336 -207
  36. package/dist/chunk-WWMKIHF7.cjs.map +1 -0
  37. package/dist/{chunk-MY6Q2HFW.js → chunk-XZA6X34R.js} +121 -77
  38. package/dist/chunk-XZA6X34R.js.map +1 -0
  39. package/dist/{chunk-4CSY3QUO.js → chunk-YGKC6E6A.js} +2 -2
  40. package/dist/chunk-YIANW5QX.cjs.map +1 -1
  41. package/dist/{chunk-E6TJNN2U.js → chunk-ZDX5PIWR.js} +2 -2
  42. package/dist/chunks-EBWJR55V.js +10 -0
  43. package/dist/cli.js +25 -25
  44. package/dist/{formula-K7UF5MBF.cjs → formula-5RETDCIC.cjs} +9 -9
  45. package/dist/formula-5RETDCIC.cjs.map +1 -0
  46. package/dist/{formula-VH545WNH.js → formula-MBLN2DG4.js} +4 -4
  47. package/dist/{formula-3GB47YTH.js → formula-QZZDW5GW.js} +3 -3
  48. package/dist/{image-ocr-6AKQWAU2.js → image-ocr-CJSO3QSR.js} +7 -7
  49. package/dist/{image-ocr-VJFZCFAI.js → image-ocr-EREKAS47.js} +5 -5
  50. package/dist/{image-ocr-ZPQTFYSK.cjs → image-ocr-PSPVRLB5.cjs} +14 -14
  51. package/dist/image-ocr-PSPVRLB5.cjs.map +1 -0
  52. package/dist/index.cjs +612 -588
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.js +41 -17
  55. package/dist/index.js.map +1 -1
  56. package/dist/mcp.js +21 -21
  57. package/dist/{model-bundle-P2XOZ76Q.js → model-bundle-MA4MMCRJ.js} +5 -5
  58. package/dist/{models-GPHBC2YJ.js → models-SEJ7LNSN.js} +5 -5
  59. package/dist/page-range-QHHFTL7X.cjs.map +1 -1
  60. package/dist/{parser-YIIRSBZS.js → parser-M5WHRX25.js} +40 -22
  61. package/dist/parser-M5WHRX25.js.map +1 -0
  62. package/dist/{parser-6SPYH3RU.cjs → parser-QILYPWSI.cjs} +211 -193
  63. package/dist/parser-QILYPWSI.cjs.map +1 -0
  64. package/dist/{parser-BVUFTU2O.js → parser-VH3F4EQU.js} +37 -19
  65. package/dist/parser-VH3F4EQU.js.map +1 -0
  66. package/dist/pdf-ocr-3SZUM3CN.cjs +16 -0
  67. package/dist/pdf-ocr-3SZUM3CN.cjs.map +1 -0
  68. package/dist/pdf-ocr-QB2TB5W5.js +18 -0
  69. package/dist/pdf-ocr-XC33L43D.js +15 -0
  70. package/dist/{profile-io-GU45HTYP.js → profile-io-5YB627EO.js} +3 -3
  71. package/dist/{rasterize-OWHFWG6R.js → rasterize-V2XHHS2I.js} +3 -3
  72. package/dist/{redact-doc-YTP6AAUP.js → redact-doc-5MK5L5IQ.js} +10 -10
  73. package/dist/{render-525LXDMV.js → render-SYZMHYK6.js} +6 -6
  74. package/dist/seal-IPKIQHYB.js +10 -0
  75. package/dist/{setup-NOXZLS7G.js → setup-ZPLJENSK.js} +3 -3
  76. package/dist/visual-RTTGNOAE.js +20 -0
  77. package/dist/{watch-NDRTCLVT.js → watch-FNWHVKK3.js} +11 -11
  78. package/package.json +1 -1
  79. package/dist/chunk-3M2BMQJY.js.map +0 -1
  80. package/dist/chunk-4NH45EEB.cjs.map +0 -1
  81. package/dist/chunk-5FCLQVT5.cjs.map +0 -1
  82. package/dist/chunk-BEJYCCDN.cjs.map +0 -1
  83. package/dist/chunk-ELNYQRBN.js.map +0 -1
  84. package/dist/chunk-GRHSVGSB.cjs.map +0 -1
  85. package/dist/chunk-HAPCN7BD.js.map +0 -1
  86. package/dist/chunk-KBIQGGNY.js.map +0 -1
  87. package/dist/chunk-MPAN6S5O.js.map +0 -1
  88. package/dist/chunk-MY6Q2HFW.js.map +0 -1
  89. package/dist/chunk-ZRXQDJPM.cjs.map +0 -1
  90. package/dist/chunks-P5Y7V73K.js +0 -10
  91. package/dist/formula-K7UF5MBF.cjs.map +0 -1
  92. package/dist/image-ocr-ZPQTFYSK.cjs.map +0 -1
  93. package/dist/parser-6SPYH3RU.cjs.map +0 -1
  94. package/dist/parser-BVUFTU2O.js.map +0 -1
  95. package/dist/parser-YIIRSBZS.js.map +0 -1
  96. package/dist/pdf-ocr-5YBMXW54.js +0 -18
  97. package/dist/pdf-ocr-EAVNHK3V.js +0 -15
  98. package/dist/pdf-ocr-J5GB5BW6.cjs +0 -16
  99. package/dist/pdf-ocr-J5GB5BW6.cjs.map +0 -1
  100. package/dist/seal-S4KJGZUI.js +0 -10
  101. package/dist/visual-AU3NH6EO.js +0 -20
  102. /package/dist/{-UNMTWAEI.js.map → -2UA2JHVA.js.map} +0 -0
  103. /package/dist/{chunk-B4PUQ4FP.js.map → chunk-2XHXABCX.js.map} +0 -0
  104. /package/dist/{chunk-EULS3PDF.js.map → chunk-4HRJISJF.js.map} +0 -0
  105. /package/dist/{chunk-N6MPDM55.js.map → chunk-6OCKAXIS.js.map} +0 -0
  106. /package/dist/{chunk-QXNQ2HNB.js.map → chunk-A2W2JQ3S.js.map} +0 -0
  107. /package/dist/{chunk-66YTQ3NQ.js.map → chunk-C4MVZXB5.js.map} +0 -0
  108. /package/dist/{chunk-GYHY2YLV.js.map → chunk-FGL4IGOT.js.map} +0 -0
  109. /package/dist/{chunk-FMP4POMD.js.map → chunk-GYTL6WT3.js.map} +0 -0
  110. /package/dist/{chunk-BQAJHAWR.js.map → chunk-KQIYC2ZU.js.map} +0 -0
  111. /package/dist/{chunk-AUFO5LVN.js.map → chunk-MDCTITUG.js.map} +0 -0
  112. /package/dist/{chunk-OVSCNCT6.js.map → chunk-O4CB7D2Q.js.map} +0 -0
  113. /package/dist/{chunk-3J3BJIOZ.js.map → chunk-SDRKHEVT.js.map} +0 -0
  114. /package/dist/{chunk-BHUYJBSQ.js.map → chunk-WJLICIYV.js.map} +0 -0
  115. /package/dist/{chunk-4CSY3QUO.js.map → chunk-YGKC6E6A.js.map} +0 -0
  116. /package/dist/{chunk-E6TJNN2U.js.map → chunk-ZDX5PIWR.js.map} +0 -0
  117. /package/dist/{chunks-P5Y7V73K.js.map → chunks-EBWJR55V.js.map} +0 -0
  118. /package/dist/{formula-VH545WNH.js.map → formula-MBLN2DG4.js.map} +0 -0
  119. /package/dist/{formula-3GB47YTH.js.map → formula-QZZDW5GW.js.map} +0 -0
  120. /package/dist/{image-ocr-6AKQWAU2.js.map → image-ocr-CJSO3QSR.js.map} +0 -0
  121. /package/dist/{image-ocr-VJFZCFAI.js.map → image-ocr-EREKAS47.js.map} +0 -0
  122. /package/dist/{model-bundle-P2XOZ76Q.js.map → model-bundle-MA4MMCRJ.js.map} +0 -0
  123. /package/dist/{models-GPHBC2YJ.js.map → models-SEJ7LNSN.js.map} +0 -0
  124. /package/dist/{pdf-ocr-5YBMXW54.js.map → pdf-ocr-QB2TB5W5.js.map} +0 -0
  125. /package/dist/{pdf-ocr-EAVNHK3V.js.map → pdf-ocr-XC33L43D.js.map} +0 -0
  126. /package/dist/{profile-io-GU45HTYP.js.map → profile-io-5YB627EO.js.map} +0 -0
  127. /package/dist/{rasterize-OWHFWG6R.js.map → rasterize-V2XHHS2I.js.map} +0 -0
  128. /package/dist/{redact-doc-YTP6AAUP.js.map → redact-doc-5MK5L5IQ.js.map} +0 -0
  129. /package/dist/{render-525LXDMV.js.map → render-SYZMHYK6.js.map} +0 -0
  130. /package/dist/{seal-S4KJGZUI.js.map → seal-IPKIQHYB.js.map} +0 -0
  131. /package/dist/{setup-NOXZLS7G.js.map → setup-ZPLJENSK.js.map} +0 -0
  132. /package/dist/{visual-AU3NH6EO.js.map → visual-RTTGNOAE.js.map} +0 -0
  133. /package/dist/{watch-NDRTCLVT.js.map → watch-FNWHVKK3.js.map} +0 -0
@@ -8,11 +8,11 @@ import {
8
8
  import {
9
9
  ensureModelsIn,
10
10
  getModelsDir
11
- } from "./chunk-3M2BMQJY.js";
11
+ } from "./chunk-5JZEY5IL.js";
12
12
  import {
13
13
  safeMax,
14
14
  safeMin
15
- } from "./chunk-4CSY3QUO.js";
15
+ } from "./chunk-YGKC6E6A.js";
16
16
 
17
17
  // src/ocr/models.ts
18
18
  import { join } from "path";
@@ -728,19 +728,34 @@ function multiplyTransform(m, t) {
728
728
  m[1] * t[4] + m[3] * t[5] + m[5]
729
729
  ];
730
730
  }
731
- function extractImageRegions(fnArray, argsArray) {
731
+ function transformedRect(ctm, x, y, w, h) {
732
+ const corners = [[x, y], [x + w, y], [x, y + h], [x + w, y + h]];
733
+ const xs = corners.map(([u, v]) => ctm[0] * u + ctm[2] * v + ctm[4]);
734
+ const ys = corners.map(([u, v]) => ctm[1] * u + ctm[3] * v + ctm[5]);
735
+ return { x1: Math.min(...xs), y1: Math.min(...ys), x2: Math.max(...xs), y2: Math.max(...ys) };
736
+ }
737
+ function intersect(a, b) {
738
+ return b ? { x1: Math.max(a.x1, b.x1), y1: Math.max(a.y1, b.y1), x2: Math.min(a.x2, b.x2), y2: Math.min(a.y2, b.y2) } : a;
739
+ }
740
+ function extractImageRegions(fnArray, argsArray, respectClip = false) {
732
741
  const regions = [];
733
742
  let ctm = [1, 0, 0, 1, 0, 0];
743
+ let clip = null;
734
744
  const stack = [];
745
+ let pathRect;
746
+ let pendingClip = false;
735
747
  for (let i = 0; i < fnArray.length; i++) {
736
748
  const op = fnArray[i];
737
749
  switch (op) {
738
750
  case OPS2.save:
739
- stack.push(ctm);
751
+ stack.push({ ctm, clip });
740
752
  break;
741
- case OPS2.restore:
742
- ctm = stack.pop() || [1, 0, 0, 1, 0, 0];
753
+ case OPS2.restore: {
754
+ const state = stack.pop();
755
+ ctm = state?.ctm ?? [1, 0, 0, 1, 0, 0];
756
+ clip = state?.clip ?? null;
743
757
  break;
758
+ }
744
759
  case OPS2.transform: {
745
760
  const t = argsArray[i];
746
761
  if (Array.isArray(t) && t.length >= 6) ctm = multiplyTransform(ctm, t);
@@ -748,28 +763,44 @@ function extractImageRegions(fnArray, argsArray) {
748
763
  }
749
764
  // Form XObject 는 /Matrix 공간에서 그린다 — pdfjs 인자 [matrix, bbox], End 에서 복원 (line-extract.ts 와 같음)
750
765
  case OPS2.paintFormXObjectBegin: {
751
- stack.push(ctm);
766
+ stack.push({ ctm, clip });
752
767
  const m = argsArray[i]?.[0];
753
768
  if (Array.isArray(m) && m.length >= 6) ctm = multiplyTransform(ctm, m);
754
769
  break;
755
770
  }
756
- case OPS2.paintFormXObjectEnd:
757
- ctm = stack.pop() || [1, 0, 0, 1, 0, 0];
771
+ case OPS2.paintFormXObjectEnd: {
772
+ const state = stack.pop();
773
+ ctm = state?.ctm ?? [1, 0, 0, 1, 0, 0];
774
+ clip = state?.clip ?? null;
775
+ break;
776
+ }
777
+ case OPS2.constructPath: {
778
+ const [ops, coords] = argsArray[i];
779
+ pathRect = pathRect === void 0 && ops?.length === 1 && ops[0] === OPS2.rectangle && coords?.length >= 4 ? transformedRect(ctm, coords[0], coords[1], coords[2], coords[3]) : null;
780
+ break;
781
+ }
782
+ case OPS2.clip:
783
+ case OPS2.eoClip:
784
+ pendingClip = true;
785
+ break;
786
+ case OPS2.endPath:
787
+ case OPS2.stroke:
788
+ case OPS2.closeStroke:
789
+ case OPS2.fill:
790
+ case OPS2.eoFill:
791
+ case OPS2.fillStroke:
792
+ case OPS2.eoFillStroke:
793
+ case OPS2.closeFillStroke:
794
+ case OPS2.closeEOFillStroke:
795
+ if (respectClip && pendingClip && pathRect) clip = intersect(pathRect, clip);
796
+ pathRect = void 0;
797
+ pendingClip = false;
758
798
  break;
759
799
  case OPS2.paintImageXObject:
760
800
  case OPS2.paintInlineImageXObject:
761
801
  case OPS2.paintImageMaskXObject:
762
802
  case OPS2.paintImageXObjectRepeat: {
763
- const corners = [[0, 0], [1, 0], [0, 1], [1, 1]];
764
- let x1 = Infinity, y1 = Infinity, x2 = -Infinity, y2 = -Infinity;
765
- for (const [u, v] of corners) {
766
- const x = ctm[0] * u + ctm[2] * v + ctm[4];
767
- const y = ctm[1] * u + ctm[3] * v + ctm[5];
768
- if (x < x1) x1 = x;
769
- if (x > x2) x2 = x;
770
- if (y < y1) y1 = y;
771
- if (y > y2) y2 = y;
772
- }
803
+ const { x1, y1, x2, y2 } = intersect(transformedRect(ctm, 0, 0, 1, 1), clip);
773
804
  if (x2 - x1 > 0 && y2 - y1 > 0) regions.push({ x1, y1, x2, y2 });
774
805
  break;
775
806
  }
@@ -2278,6 +2309,10 @@ function cellLineFills(box, contentLeft, cellRight, lineRight, fontSize) {
2278
2309
  // src/pdf/cell-text.ts
2279
2310
  var REGION_LABELS = /* @__PURE__ */ new Set(["\uC11C\uC6B8", "\uBD80\uC0B0", "\uB300\uAD6C", "\uC778\uCC9C", "\uAD11\uC8FC", "\uB300\uC804", "\uC6B8\uC0B0", "\uC138\uC885", "\uACBD\uAE30", "\uAC15\uC6D0", "\uCDA9\uBD81", "\uCDA9\uB0A8", "\uC804\uBD81", "\uC804\uB0A8", "\uACBD\uBD81", "\uACBD\uB0A8", "\uC81C\uC8FC", "\uC804\uAD6D"]);
2280
2311
  var CELL_PADDING = 2;
2312
+ function cleanCellText(text) {
2313
+ const stripped = text.replace(/^[\s]*[-–—]\s*\d+\s*[-–—][\s]*$/gm, "").trim();
2314
+ return stripped.split("\n").map((line) => collapseEvenSpacing(line)).join("\n");
2315
+ }
2281
2316
  var SPACE_GAP_RATIO = 0.17;
2282
2317
  function spaceGapThreshold(fontSize) {
2283
2318
  return Math.max(fontSize * SPACE_GAP_RATIO, 1);
@@ -2287,11 +2322,12 @@ function mapTextToCells(items, cells) {
2287
2322
  for (const cell of cells) {
2288
2323
  result.set(cell, []);
2289
2324
  }
2325
+ const candidates = items.length * cells.length >= 5e3 ? cellBandLookup(cells) : () => cells;
2290
2326
  for (const item of items) {
2291
2327
  const pad = CELL_PADDING;
2292
2328
  let bestCell = null;
2293
2329
  let bestScore = 0;
2294
- for (const cell of cells) {
2330
+ for (const cell of candidates(item.y - pad, item.y + (item.h || item.fontSize) + pad)) {
2295
2331
  const ix1 = Math.max(item.x, cell.bbox.x1 - pad);
2296
2332
  const ix2 = Math.min(item.x + item.w, cell.bbox.x2 + pad);
2297
2333
  const iy1 = Math.max(item.y, cell.bbox.y1 - pad);
@@ -2312,6 +2348,39 @@ function mapTextToCells(items, cells) {
2312
2348
  keepWordsInOneCell(result);
2313
2349
  return result;
2314
2350
  }
2351
+ function cellBandLookup(cells) {
2352
+ const sorted = cells.map((cell, index) => ({ cell, index })).sort((a, b) => a.cell.bbox.y1 - b.cell.bbox.y1);
2353
+ const build = (start, end) => {
2354
+ let lo = Infinity, hi = -Infinity;
2355
+ for (let i = start; i < end; i++) {
2356
+ lo = Math.min(lo, sorted[i].cell.bbox.y1);
2357
+ hi = Math.max(hi, sorted[i].cell.bbox.y2);
2358
+ }
2359
+ const band = { start, end, lo, hi };
2360
+ if (end - start > 8) {
2361
+ const mid = start + end >> 1;
2362
+ band.left = build(start, mid);
2363
+ band.right = build(mid, end);
2364
+ }
2365
+ return band;
2366
+ };
2367
+ const root = build(0, sorted.length);
2368
+ return (lo, hi) => {
2369
+ const found = [];
2370
+ const visit = (band) => {
2371
+ if (band.hi <= lo || band.lo >= hi) return;
2372
+ if (band.left && band.right) {
2373
+ visit(band.left);
2374
+ visit(band.right);
2375
+ } else for (let i = band.start; i < band.end; i++) {
2376
+ const entry = sorted[i];
2377
+ if (entry.cell.bbox.y2 > lo && entry.cell.bbox.y1 < hi) found.push(entry);
2378
+ }
2379
+ };
2380
+ visit(root);
2381
+ return found.sort((a, b) => a.index - b.index).map((entry) => entry.cell);
2382
+ };
2383
+ }
2315
2384
  function keepWordsInOneCell(result) {
2316
2385
  const owner = /* @__PURE__ */ new Map();
2317
2386
  for (const [cell, arr] of result) for (const it of arr) owner.set(it, cell);
@@ -2367,9 +2436,8 @@ function cellTextToString(items, wrap) {
2367
2436
  }
2368
2437
  }
2369
2438
  lines.push(curLine);
2370
- const merged = mergeSuperscriptRows(lines);
2371
- const textLines = merged.map((line) => {
2372
- const s = sortLineByX(line);
2439
+ const merged = mergeSuperscriptRows(lines).map((line) => sortLineByX(line));
2440
+ const textLines = merged.map((s) => {
2373
2441
  if (s.length === 1) return s[0].text;
2374
2442
  if (s.length === 2 && /^[가-힣]$/.test(s[0].text) && /^[가-힣]$/.test(s[1].text) && !!wrap?.lex && (wrap.lex.isWord(s[0].text + s[1].text) || wrap.lex.evidence(s[0].text, s[1].text) === "" || REGION_LABELS.has(s[0].text + s[1].text)) && s[1].x - (s[0].x + s[0].w) >= Math.max(s[0].fontSize, s[1].fontSize) * 1.5 && s[1].x + s[1].w - s[0].x >= wrap.box.x2 - wrap.box.x1 - Math.max(s[0].fontSize, s[1].fontSize) * 2) return s[0].text + s[1].text;
2375
2443
  const evenSpaced = detectEvenSpacedItems(s, true);
@@ -2393,16 +2461,16 @@ function cellTextToString(items, wrap) {
2393
2461
  }
2394
2462
  return result;
2395
2463
  });
2396
- const scripted = (s) => tagScripts(s, merged.map((l) => sortLineByX([...l])));
2464
+ const scripted = (s) => tagScripts(s, merged);
2397
2465
  if (!wrap) return scripted(mergeCellTextLines(textLines));
2398
2466
  let contentLeft = Infinity;
2399
2467
  for (const it of items) if (it.x < contentLeft) contentLeft = it.x;
2400
- const lineEnds = merged.map((line) => {
2401
- const s = sortLineByX(line);
2468
+ const lineEnds = merged.map((s) => {
2402
2469
  let right = -Infinity;
2403
2470
  for (const it of s) if (it.x + it.w > right) right = it.x + it.w;
2404
2471
  const first = s[0];
2405
- return { right, fontSize: first.fontSize, firstCharW: first.w / Math.max(1, [...first.text].length) };
2472
+ const visible = first.text.replace(/<\/?u>|~~/g, "");
2473
+ return { right, fontSize: first.fontSize, firstCharW: first.w / Math.max(1, [...visible].length) };
2406
2474
  });
2407
2475
  let cellRight = -Infinity;
2408
2476
  for (const e of lineEnds) if (e.right > cellRight) cellRight = e.right;
@@ -2446,15 +2514,16 @@ function mergeSuperscriptRows(lines) {
2446
2514
  function detectEvenSpacedItems(items, cellLine = false) {
2447
2515
  const result = new Array(items.length).fill(false);
2448
2516
  if (items.length < 3) return result;
2449
- if (cellLine && items.every((it) => /^[가-힣]$/.test(it.text)) && items.slice(1).every((it) => !it.hasSpaceBefore || it.syntheticSpace) && items.slice(1).every((it, k) => it.x - (items[k].x + items[k].w) >= it.fontSize * 0.1)) {
2450
- markEvenRun(items, result, 0, items.length);
2517
+ const visible = items.map((it) => it.text.replace(/<\/?u>|~~/g, ""));
2518
+ if (cellLine && visible.every((text) => /^[가-힣]$/.test(text)) && items.slice(1).every((it) => !it.hasSpaceBefore || it.syntheticSpace) && items.slice(1).every((it, k) => it.x - (items[k].x + items[k].w) >= it.fontSize * 0.1)) {
2519
+ markEvenRun(items, visible, result, 0, items.length);
2451
2520
  if (result.some(Boolean)) return result;
2452
2521
  }
2453
2522
  let runStart = -1;
2454
2523
  for (let i = 0; i < items.length; i++) {
2455
- const isShortKorean = /^[가-힣]{1}$/.test(items[i].text) || /^[\d]{1}$/.test(items[i].text);
2524
+ const isShortKorean = /^[가-힣]{1}$/.test(visible[i]) || /^[\d]{1}$/.test(visible[i]);
2456
2525
  if (isShortKorean && runStart >= 0 && items[i].hasSpaceBefore) {
2457
- if (i - runStart >= 3) markEvenRun(items, result, runStart, i);
2526
+ if (i - runStart >= 3) markEvenRun(items, visible, result, runStart, i);
2458
2527
  runStart = i;
2459
2528
  continue;
2460
2529
  }
@@ -2462,7 +2531,7 @@ function detectEvenSpacedItems(items, cellLine = false) {
2462
2531
  const gap = items[i].x - (items[i - 1].x + items[i - 1].w);
2463
2532
  const maxRunGap = Math.max(items[i].fontSize * 3, 30);
2464
2533
  if (gap > maxRunGap) {
2465
- if (i - runStart >= 3) markEvenRun(items, result, runStart, i);
2534
+ if (i - runStart >= 3) markEvenRun(items, visible, result, runStart, i);
2466
2535
  runStart = i;
2467
2536
  continue;
2468
2537
  }
@@ -2471,19 +2540,19 @@ function detectEvenSpacedItems(items, cellLine = false) {
2471
2540
  if (runStart < 0) runStart = i;
2472
2541
  } else {
2473
2542
  if (runStart >= 0 && i - runStart >= 3) {
2474
- markEvenRun(items, result, runStart, i);
2543
+ markEvenRun(items, visible, result, runStart, i);
2475
2544
  }
2476
2545
  runStart = -1;
2477
2546
  }
2478
2547
  }
2479
2548
  if (runStart >= 0 && items.length - runStart >= 3) {
2480
- markEvenRun(items, result, runStart, items.length);
2549
+ markEvenRun(items, visible, result, runStart, items.length);
2481
2550
  }
2482
2551
  return result;
2483
2552
  }
2484
- function markEvenRun(items, result, start, end) {
2553
+ function markEvenRun(items, visible, result, start, end) {
2485
2554
  let dateOnly = true;
2486
- for (let i = start; i < end; i++) if (!/^[년월일시분초]$/.test(items[i].text)) {
2555
+ for (let i = start; i < end; i++) if (!/^[년월일시분초]$/.test(visible[i])) {
2487
2556
  dateOnly = false;
2488
2557
  break;
2489
2558
  }
@@ -2705,14 +2774,20 @@ function filterHiddenText(items, pageWidth, pageHeight, originX = 0, originY = 0
2705
2774
  return { visible, hiddenCount };
2706
2775
  }
2707
2776
  function collapseEvenSpacing(text, whole = true) {
2708
- const tokens = text.split(" ");
2709
- const singleCharCount = tokens.filter((t) => t.length === 1).length;
2710
- if (whole && tokens.length >= 3 && singleCharCount / tokens.length >= 0.7 && !isDateUnitBlank(tokens)) {
2711
- return tokens.join("");
2777
+ if (whole) {
2778
+ const tokens = text.split(" ");
2779
+ const visible = tokens.map((t) => t.replace(/<\/?u>|~~/g, ""));
2780
+ const singleCharCount = visible.filter((t) => t.length === 1).length;
2781
+ if (tokens.length >= 3 && singleCharCount / tokens.length >= 0.7 && !isDateUnitBlank(visible)) return tokens.join("");
2712
2782
  }
2713
2783
  return text.replace(
2714
2784
  /(?<![^\s>])[가-힣](?: [가-힣\d]){2,}(?![^\s<])/g,
2715
- (match) => isDateUnitBlank(match.split(" ")) ? match : match.replace(/ /g, "")
2785
+ (match, offset) => {
2786
+ const before = text.slice(0, offset).replace(/(?:<\/?u>|~~)+$/, "");
2787
+ const after = text.slice(offset + match.length).replace(/^(?:<\/?u>|~~)+/, "");
2788
+ if (/\S$/.test(before) || /^\S/.test(after)) return match;
2789
+ return isDateUnitBlank(match.split(" ")) ? match : match.replace(/ /g, "");
2790
+ }
2716
2791
  );
2717
2792
  }
2718
2793
  function isDateUnitBlank(tokens) {
@@ -5075,27 +5150,6 @@ function splitEdgeSpannedColumns(items, gapThreshold, depth) {
5075
5150
  ];
5076
5151
  }
5077
5152
 
5078
- // src/pdf/blank-fills.ts
5079
- var FILL_EDGE_GAP = 6;
5080
- var FILL_MAX_THICKNESS = 2;
5081
- function fillBlanks(items, horizontals, verticals = []) {
5082
- const widened = /* @__PURE__ */ new Map();
5083
- const used = /* @__PURE__ */ new Set();
5084
- for (const h of horizontals) {
5085
- if (h.lineWidth > FILL_MAX_THICKNESS || Math.abs(h.y1 - h.y2) > 1) continue;
5086
- const x1 = Math.min(h.x1, h.x2), x2 = Math.max(h.x1, h.x2);
5087
- const row = items.filter((i) => i.y >= h.y1 && i.y - h.y1 <= Math.max(3, i.fontSize * 0.5));
5088
- const left = row.find((i) => i.x + i.w <= x1 + 1 && x1 - (i.x + i.w) <= FILL_EDGE_GAP);
5089
- const right = row.find((i) => i.x >= x2 - 1 && i.x - x2 <= FILL_EDGE_GAP);
5090
- if (!left || !right || widened.has(left) || row.some((i) => i.x < x2 && i.x + i.w > x1) || x2 - x1 < 2 * left.fontSize) continue;
5091
- if (verticals.some((v) => (Math.abs(v.x1 - x1) <= 3 || Math.abs(v.x1 - x2) <= 3) && Math.min(v.y1, v.y2) - 3 <= h.y1 && Math.max(v.y1, v.y2) + 3 >= h.y1)) continue;
5092
- widened.set(left, { ...left, text: left.text.trimEnd() + " ", w: x2 - left.x });
5093
- used.add(h);
5094
- }
5095
- if (!used.size) return { items, horizontals };
5096
- return { items: items.map((i) => widened.get(i) ?? i), horizontals: horizontals.filter((h) => !used.has(h)) };
5097
- }
5098
-
5099
5153
  // src/pdf/two-column.ts
5100
5154
  var GUTTER_SCAN_LO = 0.3;
5101
5155
  var GUTTER_SCAN_HI = 0.7;
@@ -5330,6 +5384,55 @@ function orderByPanels(units, rectOf, gutters) {
5330
5384
  return ordered;
5331
5385
  }
5332
5386
 
5387
+ // src/pdf/image-panels.ts
5388
+ function splitImagePanels(items, figures) {
5389
+ if (figures.length === 0) return null;
5390
+ const rects = items.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }));
5391
+ const cuts = detectPanelGutters([...rects, ...figures]);
5392
+ if (!cuts || cuts.length !== 2 || items.some((i) => cuts.some((x) => i.x < x && i.x + i.w > x))) return null;
5393
+ const groups = [
5394
+ items.filter((i) => i.x + i.w <= cuts[0]),
5395
+ items.filter((i) => i.x >= cuts[0] && i.x + i.w <= cuts[1]),
5396
+ items.filter((i) => i.x >= cuts[1])
5397
+ ];
5398
+ const rows = groupByY(items);
5399
+ const sharedRows = rows.filter((row) => new Set(row.map((i) => i.x >= cuts[1] ? 2 : i.x >= cuts[0] ? 1 : 0)).size === 3).length;
5400
+ if (sharedRows / Math.min(...groups.map((group) => groupByY(group).length)) > 0.65) return null;
5401
+ for (const row of rows) {
5402
+ const ordered = [...row].sort((a, b) => a.x - b.x);
5403
+ let start = ordered[0].x, end = start + ordered[0].w, height = ordered[0].h || ordered[0].fontSize;
5404
+ for (const i of ordered.slice(1)) {
5405
+ const h = i.h || i.fontSize;
5406
+ if (i.x - end > Math.max(height, h) * 2) start = i.x;
5407
+ end = Math.max(end, i.x + i.w);
5408
+ height = h;
5409
+ if (cuts.some((x) => start < x && end > x)) return null;
5410
+ }
5411
+ }
5412
+ return groups;
5413
+ }
5414
+
5415
+ // src/pdf/blank-fills.ts
5416
+ var FILL_EDGE_GAP = 6;
5417
+ var FILL_MAX_THICKNESS = 2;
5418
+ function fillBlanks(items, horizontals, verticals = []) {
5419
+ const widened = /* @__PURE__ */ new Map();
5420
+ const used = /* @__PURE__ */ new Set();
5421
+ for (const h of horizontals) {
5422
+ if (h.lineWidth > FILL_MAX_THICKNESS || Math.abs(h.y1 - h.y2) > 1) continue;
5423
+ const x1 = Math.min(h.x1, h.x2), x2 = Math.max(h.x1, h.x2);
5424
+ const row = items.filter((i) => i.y >= h.y1 && i.y - h.y1 <= Math.max(3, i.fontSize * 0.5));
5425
+ const left = row.find((i) => i.x + i.w <= x1 + 1 && x1 - (i.x + i.w) <= FILL_EDGE_GAP);
5426
+ const right = row.find((i) => i.x >= x2 - 1 && i.x - x2 <= FILL_EDGE_GAP);
5427
+ if (!left || !right || widened.has(left) || row.some((i) => i.x < x2 && i.x + i.w > x1) || x2 - x1 < 2 * left.fontSize) continue;
5428
+ if (verticals.some((v) => (Math.abs(v.x1 - x1) <= 3 || Math.abs(v.x1 - x2) <= 3) && Math.min(v.y1, v.y2) - 3 <= h.y1 && Math.max(v.y1, v.y2) + 3 >= h.y1)) continue;
5429
+ widened.set(left, { ...left, text: left.text.trimEnd() + " ", w: x2 - left.x });
5430
+ used.add(h);
5431
+ }
5432
+ if (!used.size) return { items, horizontals };
5433
+ return { items: items.map((i) => widened.get(i) ?? i), horizontals: horizontals.filter((h) => !used.has(h)) };
5434
+ }
5435
+
5333
5436
  // src/pdf/columns.ts
5334
5437
  function isProseSpread(items) {
5335
5438
  if (items.length < 3) return false;
@@ -5575,8 +5678,8 @@ function markUnderlineItems(items, horizontals, verticals) {
5575
5678
  if (items.length === 0 || horizontals.length === 0) return underlines;
5576
5679
  for (const line of horizontals) {
5577
5680
  if (line.lineWidth > UNDER_MAX_THICKNESS) continue;
5578
- if (touchesVertical(line, verticals)) continue;
5579
- if (isRepeatedSpanRule(line, horizontals)) continue;
5681
+ const enclosed = insideTallCell(line, verticals, 0);
5682
+ if (!enclosed && (touchesVertical(line, verticals) || isRepeatedSpanRule(line, horizontals))) continue;
5580
5683
  const matches = [];
5581
5684
  for (const item of items) {
5582
5685
  const h = item.h > 0 ? item.h : item.fontSize;
@@ -5595,10 +5698,12 @@ function markUnderlineItems(items, horizontals, verticals) {
5595
5698
  maxH = Math.max(maxH, m.h > 0 ? m.h : m.fontSize);
5596
5699
  covered += m.w;
5597
5700
  }
5701
+ const inset = enclosed && insideTallCell(line, verticals, maxH);
5702
+ if (enclosed && !inset && (touchesVertical(line, verticals) || isRepeatedSpanRule(line, horizontals))) continue;
5598
5703
  const pad = Math.max(maxH * UNDER_OWNER_PAD_EM, UNDER_OWNER_PAD_MIN_PT);
5599
5704
  if (line.x1 < x1 - pad || line.x2 > x2 + pad) continue;
5600
5705
  if (covered < (line.x2 - line.x1) * UNDER_MIN_COVERAGE) continue;
5601
- if (hasBoxTopPair(line, maxH, horizontals)) continue;
5706
+ if (hasBoxTopPair(line, maxH, horizontals, inset ? items : void 0)) continue;
5602
5707
  matches.sort((a, b) => a.x - b.x);
5603
5708
  let hole = false;
5604
5709
  for (let i = 1; i < matches.length; i++) {
@@ -5613,6 +5718,18 @@ function markUnderlineItems(items, horizontals, verticals) {
5613
5718
  }
5614
5719
  return underlines;
5615
5720
  }
5721
+ function insideTallCell(line, verticals, em) {
5722
+ let left = false, right = false;
5723
+ for (const v of verticals) {
5724
+ const lo = Math.min(v.y1, v.y2), hi = Math.max(v.y1, v.y2);
5725
+ if (lo > line.y1 || hi < line.y1) continue;
5726
+ if (v.x1 >= line.x1 - 0.5 && v.x1 <= line.x2 + 0.5) return false;
5727
+ if (hi - lo <= em * UNDER_BOX_PAIR_MAX_EM) continue;
5728
+ if (v.x1 < line.x1 - 0.5) left = true;
5729
+ if (v.x1 > line.x2 + 0.5) right = true;
5730
+ }
5731
+ return left && right;
5732
+ }
5616
5733
  function touchesVertical(line, verticals) {
5617
5734
  for (const v of verticals) {
5618
5735
  if (v.x1 < line.x1 - UNDER_GRID_EPS || v.x1 > line.x2 + UNDER_GRID_EPS) continue;
@@ -5621,7 +5738,7 @@ function touchesVertical(line, verticals) {
5621
5738
  }
5622
5739
  return false;
5623
5740
  }
5624
- function hasBoxTopPair(line, maxH, horizontals) {
5741
+ function hasBoxTopPair(line, maxH, horizontals, cellItems) {
5625
5742
  const w = line.x2 - line.x1;
5626
5743
  if (w <= 0) return false;
5627
5744
  for (const o of horizontals) {
@@ -5631,7 +5748,13 @@ function hasBoxTopPair(line, maxH, horizontals) {
5631
5748
  const ow = o.x2 - o.x1;
5632
5749
  if (ow <= 0) continue;
5633
5750
  const overlap2 = Math.min(line.x2, o.x2) - Math.max(line.x1, o.x1);
5634
- if (overlap2 / Math.max(w, ow) >= UNDER_BOX_PAIR_OVERLAP) return true;
5751
+ if (overlap2 / Math.max(w, ow) < UNDER_BOX_PAIR_OVERLAP) continue;
5752
+ const covered = cellItems?.reduce((sum, item) => {
5753
+ const below = item.y - o.y1;
5754
+ if (below < 0.5 || below > 3 || !item.text.trim()) return sum;
5755
+ return sum + Math.max(0, Math.min(o.x2, item.x + item.w) - Math.max(o.x1, item.x));
5756
+ }, 0) ?? 0;
5757
+ if (covered < ow * UNDER_MIN_COVERAGE) return true;
5635
5758
  }
5636
5759
  return false;
5637
5760
  }
@@ -5712,21 +5835,21 @@ function rules(lines, dir) {
5712
5835
  const sorted = [...lines].sort((a, b) => dir === "h" ? a.y1 - b.y1 : a.x1 - b.x1);
5713
5836
  return { dir, pos: sorted.map((l) => dir === "h" ? l.y1 : l.x1), lines: sorted };
5714
5837
  }
5715
- function seen(r, at, a1, a2) {
5838
+ function seen(r, at, a1, a2, near = EDGE_NEAR2) {
5716
5839
  const len = a2 - a1;
5717
5840
  if (len <= 0) return false;
5718
- const covered = coverage(r, at, a1, a2);
5841
+ const covered = coverage(r, at, a1, a2, near);
5719
5842
  return covered >= len * EDGE_COVER && len - covered <= EDGE_SLACK;
5720
5843
  }
5721
- function coverage(r, at, a1, a2) {
5844
+ function coverage(r, at, a1, a2, near) {
5722
5845
  let i = 0, j = r.pos.length;
5723
5846
  while (i < j) {
5724
5847
  const m = i + j >> 1;
5725
- if (r.pos[m] < at - EDGE_NEAR2) i = m + 1;
5848
+ if (r.pos[m] < at - near) i = m + 1;
5726
5849
  else j = m;
5727
5850
  }
5728
5851
  const spans = [];
5729
- for (; i < r.pos.length && r.pos[i] <= at + EDGE_NEAR2; i++) {
5852
+ for (; i < r.pos.length && r.pos[i] <= at + near; i++) {
5730
5853
  const l = r.lines[i];
5731
5854
  const l1 = r.dir === "h" ? Math.min(l.x1, l.x2) : Math.min(l.y1, l.y2), l2 = r.dir === "h" ? Math.max(l.x1, l.x2) : Math.max(l.y1, l.y2);
5732
5855
  const lo = Math.max(a1, l1), hi = Math.min(a2, l2);
@@ -5746,10 +5869,10 @@ function coverage(r, at, a1, a2) {
5746
5869
  }
5747
5870
  return covered + e - s;
5748
5871
  }
5749
- function edgesOf(box, h, v) {
5872
+ function edgesOf(box, h, v, hNear = EDGE_NEAR2) {
5750
5873
  return {
5751
- t: seen(h, box.y2, box.x1, box.x2),
5752
- b: seen(h, box.y1, box.x1, box.x2),
5874
+ t: seen(h, box.y2, box.x1, box.x2, hNear),
5875
+ b: seen(h, box.y1, box.x1, box.x2, hNear),
5753
5876
  l: seen(v, box.x1, box.y1, box.y2),
5754
5877
  r: seen(v, box.x2, box.y1, box.y2)
5755
5878
  };
@@ -5758,7 +5881,10 @@ function recordClipCellEdges(grids, horizontals, verticals, nonRules) {
5758
5881
  if (!grids.some((g) => g.cells)) return;
5759
5882
  const h = rules(nonRules?.size ? horizontals.filter((l) => !nonRules.has(l)) : horizontals, "h");
5760
5883
  const v = rules(nonRules?.size ? verticals.filter((l) => !nonRules.has(l)) : verticals, "v");
5761
- for (const g of grids) for (const c of g.cells ?? []) CLIP_CELL_EDGES.set(c, edgesOf(c.bbox, h, v));
5884
+ for (const g of grids) for (const c of g.cells ?? []) {
5885
+ const near = c.filler ? Math.min(EDGE_NEAR2, (c.bbox.y2 - c.bbox.y1) / 3) : EDGE_NEAR2;
5886
+ CLIP_CELL_EDGES.set(c, edgesOf(c.bbox, h, v, near));
5887
+ }
5762
5888
  }
5763
5889
  function takeClipCellEdges(from, to) {
5764
5890
  const e = CLIP_CELL_EDGES.get(from);
@@ -5770,6 +5896,36 @@ function joinCellEdges(a, b) {
5770
5896
  CELL_EDGES.set(a, { t: u.t, b: d.b, l: u.l || d.l, r: u.r || d.r });
5771
5897
  }
5772
5898
 
5899
+ // src/pdf/table-unit-row.ts
5900
+ var UNIT = /^\s*\(\s*단위\s*[::]/;
5901
+ function rebuildUnitLine(items, grid) {
5902
+ const nearby = items.filter((item) => item.y >= grid.bbox.y2 && item.y - grid.bbox.y2 <= 18 && item.x >= grid.bbox.x1 - 3 && item.x + item.w <= grid.bbox.x2 + 3);
5903
+ for (const y of [...new Set(nearby.map((item) => Math.round(item.y)))].sort((a, b) => a - b)) {
5904
+ const line = nearby.filter((item) => Math.abs(item.y - y) <= 1).sort((a, b) => a.x - b.x);
5905
+ if (UNIT.test(line.map((item) => item.text).join(""))) return line;
5906
+ }
5907
+ return [];
5908
+ }
5909
+ function prependUnitRow(cells, cols, line, usedItems) {
5910
+ const text = cleanCellText(line.map((item) => item.text).join(""));
5911
+ cells.unshift(Array.from({ length: cols }, (_, c) => ({ text: c === 0 ? text : "", colSpan: c === 0 ? cols : 1, rowSpan: 1 })));
5912
+ for (const cell of cells[0]) CELL_EDGES.set(cell, NO_EDGES);
5913
+ for (const item of line) usedItems.add(item);
5914
+ }
5915
+ function attachUnitRow(items, grid, cells, cols, usedItems) {
5916
+ const all = cells.flat();
5917
+ if (all.filter((c) => c.text.trim()).length * 2 < all.length || UNIT.test(cells[0]?.[0]?.text ?? "")) return false;
5918
+ const above = items.filter((it) => !usedItems.has(it) && it.y >= grid.bbox.y2 && it.y - grid.bbox.y2 <= 8 && it.x >= grid.bbox.x1 - 3 && it.x + it.w <= grid.bbox.x2 + 3);
5919
+ let nearest = Infinity;
5920
+ for (const it of above) nearest = Math.min(nearest, it.y);
5921
+ const line = above.filter((it) => Math.abs(it.y - nearest) <= 1).sort((a, b) => a.x - b.x);
5922
+ const text = line.map((it) => it.text).join("");
5923
+ const inset = line.length ? grid.bbox.x2 - (line[line.length - 1].x + line[line.length - 1].w) : 0;
5924
+ if (!line.length || !UNIT.test(text) || inset < 4.6 || inset > 5.6) return false;
5925
+ prependUnitRow(cells, cols, line, usedItems);
5926
+ return true;
5927
+ }
5928
+
5773
5929
  // src/pdf/page-frame.ts
5774
5930
  function isPageFrameGrid(grid, cells, pageWidth, pageHeight, items = []) {
5775
5931
  const b = grid.bbox;
@@ -6494,8 +6650,9 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6494
6650
  const lineGrids = detectTables ? buildTableGrids(horizontals, verticals) : [];
6495
6651
  const tableClipGrids = dropHeadBandClipGrids(dropInsetClipGrids(dropShadingClipGrids(clipGrids, lineGrids, extracted.fillRects, verticals), lineGrids), lineGrids);
6496
6652
  const grids = [...tableClipGrids, ...dropGridsInside(lineGrids, tableClipGrids, clipResult.containers)];
6653
+ const figures = () => extractImageRegions(opList.fnArray, opList.argsArray, true).filter((r) => r.x2 - r.x1 >= 40 && r.y2 - r.y1 >= 40).map((r) => ({ x: r.x1, y: r.y1, w: r.x2 - r.x1, h: r.y2 - r.y1 }));
6497
6654
  if (grids.length === 1 && grids[0].rowYs.length === 2 && grids[0].colXs.length === 2 && grids[0].bbox.x2 - grids[0].bbox.x1 > pageWidth * 1.2 && grids[0].bbox.y2 - grids[0].bbox.y1 > pageHeight * 1.2) {
6498
- return extractPageBlocksFallback(items, pageNum, true, detectTables, lex);
6655
+ return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
6499
6656
  }
6500
6657
  const ruled = detectTables && clipGrids.length === 0 ? detectRuledBandTables(horizontals, verticals, items, pageNum) : [];
6501
6658
  if (detectTables && clipGrids.length === 0 && ruled.length === 0 && lineGrids.length === 0) ruled.push(...detectTextBoxTables(extracted.hiddenBoxes, items, pageNum));
@@ -6504,7 +6661,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6504
6661
  return extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions, lex, ruled, rawRules);
6505
6662
  }
6506
6663
  if (detectTables && stackedTableBands(items)) {
6507
- return extractPageBlocksFallback(items, pageNum, true, detectTables, lex);
6664
+ return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
6508
6665
  }
6509
6666
  const sidebar = splitSidebarTitleRegion(items);
6510
6667
  if (sidebar && grids.every((grid) => grid.rowYs.length === 2 && grid.colXs.length === 2 && !items.some((item) => item.x + item.w / 2 >= grid.bbox.x1 && item.x + item.w / 2 <= grid.bbox.x2 && item.y + item.h / 2 >= grid.bbox.y1 && item.y + item.h / 2 <= grid.bbox.y2))) {
@@ -6514,8 +6671,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6514
6671
  const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
6515
6672
  return extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions, lex, [], rawRules);
6516
6673
  }
6517
- const figures = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 40 && r.y2 - r.y1 >= 40).map((r) => ({ x: r.x1, y: r.y1, w: r.x2 - r.x1, h: r.y2 - r.y1 }));
6518
- return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures);
6674
+ return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
6519
6675
  }
6520
6676
  var STRIKE_MAX_THICKNESS = 2;
6521
6677
  var STRIKE_MAX_THICKNESS_RATIO = 0.25;
@@ -6618,10 +6774,6 @@ function isSparseProseGrid(table) {
6618
6774
  const prose = filled.filter((t) => t.length >= 60 && /[.!?。]/.test(t)).reduce((n, t) => n + t.length, 0);
6619
6775
  return prose >= chars * 0.6;
6620
6776
  }
6621
- function cleanCellText(text) {
6622
- const stripped = text.replace(/^[\s]*[-–—]\s*\d+\s*[-–—][\s]*$/gm, "").trim();
6623
- return stripped.split("\n").map((line) => collapseEvenSpacing(line)).join("\n");
6624
- }
6625
6777
  var FRAME_RECT_TOL = 1.5;
6626
6778
  function takePendingNested(pending, cellBox, clipCell) {
6627
6779
  const out = [];
@@ -6734,25 +6886,13 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
6734
6886
  markImageCell(irGrid[cell.row][cell.col]);
6735
6887
  }
6736
6888
  if (cell.filler && !cellItems.length) FILLER_CELLS.add(irGrid[cell.row][cell.col]);
6737
- if (grid.cells && cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
6889
+ if (cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
6738
6890
  takeClipCellEdges(cell, irGrid[cell.row][cell.col]);
6739
6891
  }
6740
6892
  let finalGrid = irGrid;
6741
6893
  let finalRows = numRows;
6742
6894
  let rebuiltUsed = false;
6743
- let unitLine = [];
6744
- let unitText = "";
6745
- if (!grid.cells && numRows >= 3 && numRows <= 5 && numCols >= 3) {
6746
- const nearby = items.filter((item) => item.y >= grid.bbox.y2 && item.y - grid.bbox.y2 <= 18 && item.x >= grid.bbox.x1 - 3 && item.x + item.w <= grid.bbox.x2 + 3);
6747
- for (const y of [...new Set(nearby.map((item) => Math.round(item.y)))].sort((a, b) => a - b)) {
6748
- const line = nearby.filter((item) => Math.abs(item.y - y) <= 1).sort((a, b) => a.x - b.x);
6749
- const text = line.map((item) => item.text).join("");
6750
- if (!/^\s*\(\s*단위\s*[::]/.test(text)) continue;
6751
- unitLine = line;
6752
- unitText = text;
6753
- break;
6754
- }
6755
- }
6895
+ const unitLine = !grid.cells && numRows >= 3 && numRows <= 5 && numCols >= 3 ? rebuildUnitLine(items, grid) : [];
6756
6896
  if (!grid.cells && numRows <= 5 && numCols >= 3 && !nestedAttached && (numRows <= 2 || unitLine.length > 0)) {
6757
6897
  const rebuilt = normalizeUndersegmentedTable(irGrid, grid.colXs, textItems, grid.rowYs);
6758
6898
  if (rebuilt) {
@@ -6767,23 +6907,10 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
6767
6907
  }
6768
6908
  }
6769
6909
  if (unitLine.length > 0 && rebuiltUsed && !/^\s*\(\s*단위\s*[::]/.test(finalGrid[0]?.[0]?.text ?? "")) {
6770
- finalGrid.unshift(Array.from({ length: numCols }, (_, c) => ({ text: c === 0 ? cleanCellText(unitText) : "", colSpan: c === 0 ? numCols : 1, rowSpan: 1 })));
6910
+ prependUnitRow(finalGrid, numCols, unitLine, usedItems);
6771
6911
  finalRows++;
6772
- for (const item of unitLine) usedItems.add(item);
6773
- }
6774
- const filledCells = finalGrid.flat().filter((c) => c.text.trim()).length;
6775
- if (!rebuiltUsed && filledCells * 2 >= finalGrid.flat().length && !/^\s*\(\s*단위\s*[::]/.test(finalGrid[0]?.[0]?.text ?? "")) {
6776
- const above = items.filter((it) => !usedItems.has(it) && it.y >= grid.bbox.y2 && it.y - grid.bbox.y2 <= 8 && it.x >= grid.bbox.x1 - 3 && it.x + it.w <= grid.bbox.x2 + 3);
6777
- const line = above.filter((it) => Math.abs(it.y - Math.min(...above.map((a) => a.y))) <= 1).sort((a, b) => a.x - b.x);
6778
- const text = line.map((it) => it.text).join("");
6779
- const inset = line.length ? grid.bbox.x2 - (line[line.length - 1].x + line[line.length - 1].w) : 0;
6780
- if (line.length && /^\s*\(\s*단위\s*[::]/.test(text) && inset >= 4.6 && inset <= 5.6) {
6781
- finalGrid.unshift(Array.from({ length: numCols }, (_, c) => ({ text: c === 0 ? cleanCellText(text) : "", colSpan: c === 0 ? numCols : 1, rowSpan: 1 })));
6782
- if (grid.cells) for (const u of finalGrid[0]) CELL_EDGES.set(u, NO_EDGES);
6783
- finalRows++;
6784
- for (const item of line) usedItems.add(item);
6785
- }
6786
6912
  }
6913
+ if (!rebuiltUsed && attachUnitRow(items, grid, finalGrid, numCols, usedItems)) finalRows++;
6787
6914
  let semanticOneColumn = false;
6788
6915
  if (!grid.cells && numCols === 1 && numGridRows >= 2) {
6789
6916
  const populatedRows = finalGrid.filter((row) => row[0]?.text.trim());
@@ -7232,6 +7359,8 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
7232
7359
  syntheticSpace: i.syntheticSpace
7233
7360
  }));
7234
7361
  const textRects = items.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }));
7362
+ const imagePanels = fullPage ? splitImagePanels(items, figures) : null;
7363
+ if (imagePanels) return imagePanels.flatMap((group) => extractPageBlocksFallback(group, pageNum, false, detectTables, lex));
7235
7364
  const earlyProseCut = fullPage && detectTables ? findTwoColumnProseCutX(clusterItems) ?? persistentGutter(textRects) ?? (figures.length > 0 ? detectColumnGutter([...textRects, ...figures]) : null) : null;
7236
7365
  if (earlyProseCut !== null) {
7237
7366
  const band = topTableBand(items);
@@ -7355,4 +7484,4 @@ export {
7355
7484
  joinCellEdges,
7356
7485
  extractPageBlocksWithLines
7357
7486
  };
7358
- //# sourceMappingURL=chunk-MPAN6S5O.js.map
7487
+ //# sourceMappingURL=chunk-3N272C5L.js.map