kordoc 4.18.2 → 4.18.4

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 (152) hide show
  1. package/dist/{-I3CP33G4.js → -DDAVBMHD.js} +11 -11
  2. package/dist/{chunk-O2DI4GHD.js → chunk-274ZPMHK.js} +2 -2
  3. package/dist/{chunk-QGFZU3JE.cjs → chunk-3WLZOJIE.cjs} +10 -8
  4. package/dist/chunk-3WLZOJIE.cjs.map +1 -0
  5. package/dist/{chunk-UZGZRGYM.cjs → chunk-3XSP7V5P.cjs} +10 -5
  6. package/dist/chunk-3XSP7V5P.cjs.map +1 -0
  7. package/dist/{chunk-GM7XIZF2.js → chunk-4IBY5LTJ.js} +2 -2
  8. package/dist/chunk-6H25SXDI.js +11 -0
  9. package/dist/chunk-6H25SXDI.js.map +1 -0
  10. package/dist/{chunk-N5DQFA7L.js → chunk-7GR2GCJF.js} +10 -5
  11. package/dist/chunk-7GR2GCJF.js.map +1 -0
  12. package/dist/{chunk-YNLW6EGU.cjs → chunk-AVIZ55NQ.cjs} +3 -3
  13. package/dist/chunk-AVIZ55NQ.cjs.map +1 -0
  14. package/dist/chunk-DC6CTQTK.js +9 -0
  15. package/dist/chunk-DC6CTQTK.js.map +1 -0
  16. package/dist/{chunk-L5RLJPI3.js → chunk-DUV7Y6IQ.js} +3 -3
  17. package/dist/{chunk-KDMAYOWH.cjs → chunk-E2644XCO.cjs} +698 -189
  18. package/dist/chunk-E2644XCO.cjs.map +1 -0
  19. package/dist/{chunk-LSOD2A6N.js → chunk-ELOPDQES.js} +2 -2
  20. package/dist/{chunk-M4P7YS2N.js → chunk-F2GDLA45.js} +81 -61
  21. package/dist/chunk-F2GDLA45.js.map +1 -0
  22. package/dist/{chunk-OBILAVWC.js → chunk-F5YVCJVF.js} +10 -5
  23. package/dist/chunk-F5YVCJVF.js.map +1 -0
  24. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  25. package/dist/{chunk-4B2EXELH.js → chunk-HRJJDNRD.js} +2 -2
  26. package/dist/{chunk-CKUIFTXJ.js → chunk-IAXGGQGD.js} +2 -2
  27. package/dist/{chunk-BL7KRE6X.js → chunk-IBQCK4F3.js} +7 -3
  28. package/dist/chunk-IBQCK4F3.js.map +1 -0
  29. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  30. package/dist/{chunk-OGAYGUJF.js → chunk-KUUSP6OS.js} +581 -72
  31. package/dist/chunk-KUUSP6OS.js.map +1 -0
  32. package/dist/{chunk-LACO375L.js → chunk-LGF2IO66.js} +4 -3
  33. package/dist/chunk-LGF2IO66.js.map +1 -0
  34. package/dist/{chunk-L64QRUMK.js → chunk-MXA6YW2W.js} +582 -73
  35. package/dist/chunk-MXA6YW2W.js.map +1 -0
  36. package/dist/{chunk-OI5ZSSIC.cjs → chunk-OIUSF2VA.cjs} +16 -12
  37. package/dist/chunk-OIUSF2VA.cjs.map +1 -0
  38. package/dist/{chunk-6OKBTY72.js → chunk-QVCHFCT2.js} +2 -2
  39. package/dist/{chunk-C2CVOC5K.js → chunk-RCI7LY4N.js} +8 -4
  40. package/dist/chunk-RCI7LY4N.js.map +1 -0
  41. package/dist/{chunk-W2YYBPW5.cjs → chunk-SVNBZEL6.cjs} +3 -3
  42. package/dist/chunk-SVNBZEL6.cjs.map +1 -0
  43. package/dist/{chunk-VSY5PAJ5.js → chunk-T2BJPT6D.js} +3 -3
  44. package/dist/{chunk-VSY5PAJ5.js.map → chunk-T2BJPT6D.js.map} +1 -1
  45. package/dist/chunk-TK3FDAEE.cjs +9 -0
  46. package/dist/chunk-TK3FDAEE.cjs.map +1 -0
  47. package/dist/{chunk-F5HING4Y.js → chunk-TMJVD6FW.js} +5 -4
  48. package/dist/chunk-TMJVD6FW.js.map +1 -0
  49. package/dist/{chunk-T4MXCSBP.js → chunk-V5X4LLVM.js} +71 -47
  50. package/dist/chunk-V5X4LLVM.js.map +1 -0
  51. package/dist/{chunk-IA3UKDHA.js → chunk-VTVK2JMI.js} +3 -2
  52. package/dist/{chunk-IA3UKDHA.js.map → chunk-VTVK2JMI.js.map} +1 -1
  53. package/dist/{chunk-B2E6C2LH.js → chunk-XG4SYOZL.js} +6 -4
  54. package/dist/chunk-XG4SYOZL.js.map +1 -0
  55. package/dist/{chunk-QP7IXF7R.js → chunk-YCY2R4FM.js} +2 -2
  56. package/dist/{chunk-NZM2SSI4.js → chunk-Z2PPDQK2.js} +2 -2
  57. package/dist/chunks-5QTTAT5T.js +10 -0
  58. package/dist/cli.js +179 -119
  59. package/dist/cli.js.map +1 -1
  60. package/dist/{formula-E3N7KDDG.js → formula-7ZOZ5QJ6.js} +8 -4
  61. package/dist/{formula-E3N7KDDG.js.map → formula-7ZOZ5QJ6.js.map} +1 -1
  62. package/dist/{formula-AQZODKZF.js → formula-HQFZEXFF.js} +7 -3
  63. package/dist/{formula-AQZODKZF.js.map → formula-HQFZEXFF.js.map} +1 -1
  64. package/dist/{formula-VY4ITOMN.cjs → formula-YYJQBKEN.cjs} +13 -9
  65. package/dist/formula-YYJQBKEN.cjs.map +1 -0
  66. package/dist/{image-ocr-IMYXOHHV.js → image-ocr-4BGX2EJJ.js} +7 -6
  67. package/dist/{image-ocr-IMYXOHHV.js.map → image-ocr-4BGX2EJJ.js.map} +1 -1
  68. package/dist/{image-ocr-6LYEBMVR.cjs → image-ocr-CC26Q5KG.cjs} +16 -15
  69. package/dist/image-ocr-CC26Q5KG.cjs.map +1 -0
  70. package/dist/{image-ocr-LN3GSQPW.js → image-ocr-RK3HJVCI.js} +10 -9
  71. package/dist/{image-ocr-LN3GSQPW.js.map → image-ocr-RK3HJVCI.js.map} +1 -1
  72. package/dist/index.cjs +860 -817
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.js +141 -98
  75. package/dist/index.js.map +1 -1
  76. package/dist/mcp.js +26 -26
  77. package/dist/mcp.js.map +1 -1
  78. package/dist/{model-bundle-IHKNDLOT.js → model-bundle-VHRXG63R.js} +5 -5
  79. package/dist/{models-YXITRAMR.js → models-G3WA24SY.js} +5 -5
  80. package/dist/page-range-QHHFTL7X.cjs.map +1 -1
  81. package/dist/{parser-7KECZTTP.cjs → parser-BGORCLIU.cjs} +461 -243
  82. package/dist/parser-BGORCLIU.cjs.map +1 -0
  83. package/dist/{parser-M3T2EWUN.js → parser-GFDGAVLV.js} +255 -37
  84. package/dist/parser-GFDGAVLV.js.map +1 -0
  85. package/dist/{parser-KB4BYQBZ.js → parser-SOWTUOMK.js} +258 -40
  86. package/dist/parser-SOWTUOMK.js.map +1 -0
  87. package/dist/pdf-ocr-7IOOVR2I.js +20 -0
  88. package/dist/pdf-ocr-EJ2HNZN6.cjs +17 -0
  89. package/dist/pdf-ocr-EJ2HNZN6.cjs.map +1 -0
  90. package/dist/pdf-ocr-RACAZU3N.js +16 -0
  91. package/dist/{profile-io-GQWQR7YQ.js → profile-io-Q2T47RVH.js} +3 -3
  92. package/dist/{rasterize-Y6TVFQ3M.js → rasterize-OAVMUTE6.js} +3 -3
  93. package/dist/{redact-doc-TRPZ3HNW.js → redact-doc-ECFOVR4X.js} +11 -11
  94. package/dist/{render-GHT5HKBO.js → render-NSKWZVTD.js} +7 -7
  95. package/dist/seal-A4MG7MPQ.js +10 -0
  96. package/dist/{setup-AGSCEHDB.js → setup-REH2KZP2.js} +3 -3
  97. package/dist/visual-B56WQAGW.js +20 -0
  98. package/dist/{watch-ALSEW4GB.js → watch-JXDUZYBY.js} +13 -13
  99. package/dist/watch-JXDUZYBY.js.map +1 -0
  100. package/package.json +1 -1
  101. package/dist/chunk-B2E6C2LH.js.map +0 -1
  102. package/dist/chunk-BL7KRE6X.js.map +0 -1
  103. package/dist/chunk-C2CVOC5K.js.map +0 -1
  104. package/dist/chunk-F5HING4Y.js.map +0 -1
  105. package/dist/chunk-KDMAYOWH.cjs.map +0 -1
  106. package/dist/chunk-L64QRUMK.js.map +0 -1
  107. package/dist/chunk-LACO375L.js.map +0 -1
  108. package/dist/chunk-M4P7YS2N.js.map +0 -1
  109. package/dist/chunk-N5DQFA7L.js.map +0 -1
  110. package/dist/chunk-OBILAVWC.js.map +0 -1
  111. package/dist/chunk-OGAYGUJF.js.map +0 -1
  112. package/dist/chunk-OI5ZSSIC.cjs.map +0 -1
  113. package/dist/chunk-QGFZU3JE.cjs.map +0 -1
  114. package/dist/chunk-T4MXCSBP.js.map +0 -1
  115. package/dist/chunk-UZGZRGYM.cjs.map +0 -1
  116. package/dist/chunk-W2YYBPW5.cjs.map +0 -1
  117. package/dist/chunk-YNLW6EGU.cjs.map +0 -1
  118. package/dist/chunks-IFFRC5IQ.js +0 -10
  119. package/dist/formula-VY4ITOMN.cjs.map +0 -1
  120. package/dist/image-ocr-6LYEBMVR.cjs.map +0 -1
  121. package/dist/parser-7KECZTTP.cjs.map +0 -1
  122. package/dist/parser-KB4BYQBZ.js.map +0 -1
  123. package/dist/parser-M3T2EWUN.js.map +0 -1
  124. package/dist/pdf-ocr-4S2OAHUX.js +0 -15
  125. package/dist/pdf-ocr-IGYCZ7G2.cjs +0 -16
  126. package/dist/pdf-ocr-IGYCZ7G2.cjs.map +0 -1
  127. package/dist/pdf-ocr-YEBFWU2K.js +0 -19
  128. package/dist/seal-3YTXSABW.js +0 -10
  129. package/dist/visual-R3E35Q2K.js +0 -20
  130. package/dist/watch-ALSEW4GB.js.map +0 -1
  131. /package/dist/{-I3CP33G4.js.map → -DDAVBMHD.js.map} +0 -0
  132. /package/dist/{chunk-O2DI4GHD.js.map → chunk-274ZPMHK.js.map} +0 -0
  133. /package/dist/{chunk-GM7XIZF2.js.map → chunk-4IBY5LTJ.js.map} +0 -0
  134. /package/dist/{chunk-L5RLJPI3.js.map → chunk-DUV7Y6IQ.js.map} +0 -0
  135. /package/dist/{chunk-LSOD2A6N.js.map → chunk-ELOPDQES.js.map} +0 -0
  136. /package/dist/{chunk-4B2EXELH.js.map → chunk-HRJJDNRD.js.map} +0 -0
  137. /package/dist/{chunk-CKUIFTXJ.js.map → chunk-IAXGGQGD.js.map} +0 -0
  138. /package/dist/{chunk-6OKBTY72.js.map → chunk-QVCHFCT2.js.map} +0 -0
  139. /package/dist/{chunk-QP7IXF7R.js.map → chunk-YCY2R4FM.js.map} +0 -0
  140. /package/dist/{chunk-NZM2SSI4.js.map → chunk-Z2PPDQK2.js.map} +0 -0
  141. /package/dist/{chunks-IFFRC5IQ.js.map → chunks-5QTTAT5T.js.map} +0 -0
  142. /package/dist/{model-bundle-IHKNDLOT.js.map → model-bundle-VHRXG63R.js.map} +0 -0
  143. /package/dist/{models-YXITRAMR.js.map → models-G3WA24SY.js.map} +0 -0
  144. /package/dist/{pdf-ocr-4S2OAHUX.js.map → pdf-ocr-7IOOVR2I.js.map} +0 -0
  145. /package/dist/{pdf-ocr-YEBFWU2K.js.map → pdf-ocr-RACAZU3N.js.map} +0 -0
  146. /package/dist/{profile-io-GQWQR7YQ.js.map → profile-io-Q2T47RVH.js.map} +0 -0
  147. /package/dist/{rasterize-Y6TVFQ3M.js.map → rasterize-OAVMUTE6.js.map} +0 -0
  148. /package/dist/{redact-doc-TRPZ3HNW.js.map → redact-doc-ECFOVR4X.js.map} +0 -0
  149. /package/dist/{render-GHT5HKBO.js.map → render-NSKWZVTD.js.map} +0 -0
  150. /package/dist/{seal-3YTXSABW.js.map → seal-A4MG7MPQ.js.map} +0 -0
  151. /package/dist/{setup-AGSCEHDB.js.map → setup-REH2KZP2.js.map} +0 -0
  152. /package/dist/{visual-R3E35Q2K.js.map → visual-B56WQAGW.js.map} +0 -0
@@ -5,11 +5,11 @@ import {
5
5
  HEADING_RATIO_H2,
6
6
  HEADING_RATIO_H3,
7
7
  tidyScriptTags
8
- } from "./chunk-F5HING4Y.js";
8
+ } from "./chunk-TMJVD6FW.js";
9
9
  import {
10
10
  safeMax,
11
11
  safeMin
12
- } from "./chunk-OBILAVWC.js";
12
+ } from "./chunk-F5YVCJVF.js";
13
13
 
14
14
  // src/pdf/line-extract.ts
15
15
  import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
@@ -415,13 +415,13 @@ function classifyAndAdd(seg, lineWidth, horizontals, verticals, fromFill = false
415
415
  verticals.push({ x1: x, y1, x2: x, y2, lineWidth, fromFill });
416
416
  }
417
417
  }
418
- function preprocessLines(horizontals, verticals) {
418
+ function preprocessLines(horizontals, verticals, nonRules) {
419
419
  let h = horizontals.filter((l) => l.lineWidth <= MAX_LINE_WIDTH);
420
420
  let v = verticals.filter((l) => l.lineWidth <= MAX_LINE_WIDTH);
421
421
  h = dropShadingStacks(h, "h");
422
422
  v = dropShadingStacks(v, "v");
423
- h = mergeParallelLines(h, "h");
424
- v = mergeParallelLines(v, "v");
423
+ h = mergeParallelLines(h, "h", nonRules);
424
+ v = mergeParallelLines(v, "v", nonRules);
425
425
  return { horizontals: h, verticals: v };
426
426
  }
427
427
  var STACK_GAP = 2;
@@ -581,15 +581,15 @@ function closeOpenTableEdges(horizontals, verticals) {
581
581
  const hasInterior = verticals.some((v) => v.x1 > x1 + EDGE_INSET && v.x1 < x2 - EDGE_INSET && crossCount(v) >= 2);
582
582
  if (!hasInterior) continue;
583
583
  for (const edgeX of [x1, x2]) {
584
- const closed = verticals.some((v) => Math.abs(v.x1 - edgeX) <= EDGE_NEAR && v.y1 <= yMax + EDGE_CONNECT_TOL && v.y2 >= yMin - EDGE_CONNECT_TOL);
585
- if (!closed) {
584
+ const closed2 = verticals.some((v) => Math.abs(v.x1 - edgeX) <= EDGE_NEAR && v.y1 <= yMax + EDGE_CONNECT_TOL && v.y2 >= yMin - EDGE_CONNECT_TOL);
585
+ if (!closed2) {
586
586
  synthesized.push({ x1: edgeX, y1: yMin, x2: edgeX, y2: yMax, lineWidth: 0.5 });
587
587
  }
588
588
  }
589
589
  }
590
590
  return synthesized.length ? [...verticals, ...synthesized] : verticals;
591
591
  }
592
- function mergeParallelLines(lines, dir) {
592
+ function mergeParallelLines(lines, dir, nonRules) {
593
593
  if (lines.length <= 1) return lines;
594
594
  const sorted = [...lines].sort((a, b) => {
595
595
  const posA = dir === "h" ? a.y1 : a.x1;
@@ -612,6 +612,7 @@ function mergeParallelLines(lines, dir) {
612
612
  const overlap2 = Math.min(prevEnd, currEnd) - Math.max(prevStart, currStart);
613
613
  const minLen = Math.min(prevEnd - prevStart, currEnd - currStart);
614
614
  if (overlap2 > minLen * 0.3) {
615
+ if (nonRules?.has(prev) && !nonRules.has(curr)) nonRules.delete(prev);
615
616
  if (dir === "h") {
616
617
  prev.x1 = Math.min(prev.x1, curr.x1);
617
618
  prev.x2 = Math.max(prev.x2, curr.x2);
@@ -4124,7 +4125,12 @@ function detectSpecialKoreanTables(blocks) {
4124
4125
  flushKvTable();
4125
4126
  return result;
4126
4127
  }
4127
- function removeHeaderFooterBlocks(blocks, pageHeights, warnings, notes, tables = false) {
4128
+ function removeHeaderFooterBlocks(blocks, pageHeights, warnings, notes, tables = false, context = []) {
4129
+ if (context.length && !tables) {
4130
+ const removed = removeHeaderFooterBlocks([...blocks, ...context], pageHeights, [], notes).filter((index) => index < blocks.length);
4131
+ if (removed.length) warnings.push({ message: `${removed.length}\uAC1C \uBA38\uB9AC\uAE00/\uBC14\uB2E5\uAE00 \uC694\uC18C \uC81C\uAC70\uB428`, code: "HIDDEN_TEXT_FILTERED" });
4132
+ return removed;
4133
+ }
4128
4134
  const ZONE_RATIO = 0.12;
4129
4135
  const MIN_REPEAT2 = 3;
4130
4136
  const topEntries = [];
@@ -4204,6 +4210,7 @@ function removeHeaderFooterBlocks(blocks, pageHeights, warnings, notes, tables =
4204
4210
 
4205
4211
  // src/pdf/table-meta.ts
4206
4212
  var CLIP_TABLES = /* @__PURE__ */ new WeakSet();
4213
+ var FRAME_TITLE_BLOCKS = /* @__PURE__ */ new WeakMap();
4207
4214
  var TABLE_COLXS = /* @__PURE__ */ new WeakMap();
4208
4215
  var TABLE_ROWYS = /* @__PURE__ */ new WeakMap();
4209
4216
  var TABLE_TAIL = /* @__PURE__ */ new WeakMap();
@@ -4311,6 +4318,262 @@ function mergeSliverColumns(grid, colXs, maxWidth) {
4311
4318
  return merged;
4312
4319
  }
4313
4320
 
4321
+ // src/pdf/image-bullets.ts
4322
+ import { OPS as OPS3, ImageKind } from "pdfjs-dist/legacy/build/pdf.mjs";
4323
+ var multiply = (m, t) => [
4324
+ m[0] * t[0] + m[2] * t[1],
4325
+ m[1] * t[0] + m[3] * t[1],
4326
+ m[0] * t[2] + m[2] * t[3],
4327
+ m[1] * t[2] + m[3] * t[3],
4328
+ m[0] * t[4] + m[2] * t[5] + m[4],
4329
+ m[1] * t[4] + m[3] * t[5] + m[5]
4330
+ ];
4331
+ function hollowCircle(image) {
4332
+ if (!image?.data || image.width < 16 || image.width > 128 || image.height < 16 || image.height > 128 || Math.abs(image.width - image.height) > Math.min(image.width, image.height) * 0.1) return false;
4333
+ const stride = image.kind === ImageKind.RGB_24BPP ? 3 : image.kind === ImageKind.RGBA_32BPP ? 4 : 0;
4334
+ const { width, height, data } = image;
4335
+ if (!stride || data.length < width * height * stride) return false;
4336
+ const dark = [];
4337
+ let white = 0;
4338
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
4339
+ const i = (y * width + x) * stride;
4340
+ if (stride === 4 && data[i + 3] < 240) return false;
4341
+ const min = Math.min(data[i], data[i + 1], data[i + 2]), max = Math.max(data[i], data[i + 1], data[i + 2]);
4342
+ if (max - min > 15) return false;
4343
+ if (min >= 235) white++;
4344
+ if (max < 180) dark.push({ x, y });
4345
+ }
4346
+ if (white < width * height * 0.65 || dark.length < width * height * 0.025 || dark.length > width * height * 0.3) return false;
4347
+ const x0 = Math.min(...dark.map((p) => p.x)), x1 = Math.max(...dark.map((p) => p.x));
4348
+ const y0 = Math.min(...dark.map((p) => p.y)), y1 = Math.max(...dark.map((p) => p.y));
4349
+ const w = x1 - x0 + 1, h = y1 - y0 + 1, radius = (w + h) / 4;
4350
+ const cx = (x0 + x1) / 2, cy = (y0 + y1) / 2;
4351
+ if (Math.abs(w - h) > Math.min(w, h) * 0.15 || w < width * 0.4 || w > width * 0.85 || h < height * 0.4 || h > height * 0.85 || x0 < width * 0.04 || y0 < height * 0.04 || x1 >= width * 0.96 || y1 >= height * 0.96 || Math.abs(cx - width / 2) > width * 0.15 || Math.abs(cy - height / 2) > height * 0.15) return false;
4352
+ const sectors = /* @__PURE__ */ new Set();
4353
+ for (const point of dark) {
4354
+ const r = Math.hypot(point.x - cx, point.y - cy) / radius;
4355
+ if (r < 0.65 || r > 1.15) return false;
4356
+ sectors.add(Math.floor((Math.atan2(point.y - cy, point.x - cx) + Math.PI) / (Math.PI * 2) * 8) % 8);
4357
+ }
4358
+ return sectors.size === 8;
4359
+ }
4360
+ async function resolveImagePixels(page, id) {
4361
+ const objects = id.startsWith("g_") ? page.commonObjs : page.objs;
4362
+ return new Promise((resolve) => {
4363
+ const timer = setTimeout(() => resolve(null), 5e3);
4364
+ try {
4365
+ objects.get(id, (value) => {
4366
+ clearTimeout(timer);
4367
+ resolve(value ?? null);
4368
+ });
4369
+ } catch {
4370
+ clearTimeout(timer);
4371
+ resolve(null);
4372
+ }
4373
+ });
4374
+ }
4375
+ function smallVisibleImagePaints(fnArray, argsArray) {
4376
+ const paints = [];
4377
+ let ctm = [1, 0, 0, 1, 0, 0];
4378
+ let alpha = 1, clip = null, unknownClip = false;
4379
+ const stack = [];
4380
+ let pathRect, pendingClip = false;
4381
+ const intersectClip = (box) => {
4382
+ clip = clip ? {
4383
+ x1: Math.max(clip.x1, box.x1),
4384
+ y1: Math.max(clip.y1, box.y1),
4385
+ x2: Math.min(clip.x2, box.x2),
4386
+ y2: Math.min(clip.y2, box.y2)
4387
+ } : box;
4388
+ };
4389
+ const rectangle = (x, y, w, h) => extractImageRegions(
4390
+ [OPS3.transform, OPS3.transform, OPS3.paintImageXObject],
4391
+ [ctm, [w, 0, 0, h, x, y], ["bounds"]]
4392
+ )[0];
4393
+ for (let i = 0; i < fnArray.length; i++) {
4394
+ const op = fnArray[i], args = argsArray[i];
4395
+ if (op === OPS3.save || op === OPS3.paintFormXObjectBegin) {
4396
+ stack.push({ ctm, alpha, clip, unknownClip });
4397
+ const matrix = op === OPS3.paintFormXObjectBegin ? args?.[0] : void 0;
4398
+ if (Array.isArray(matrix) && matrix.length >= 6) ctm = multiply(ctm, matrix);
4399
+ const bounds = op === OPS3.paintFormXObjectBegin ? args?.[1] : void 0;
4400
+ if (Array.isArray(bounds) && bounds.length === 4) {
4401
+ const box = rectangle(bounds[0], bounds[1], bounds[2] - bounds[0], bounds[3] - bounds[1]);
4402
+ if (box && ctm[1] === 0 && ctm[2] === 0) intersectClip(box);
4403
+ else unknownClip = true;
4404
+ }
4405
+ } else if (op === OPS3.restore || op === OPS3.paintFormXObjectEnd) {
4406
+ const state = stack.pop();
4407
+ ctm = state?.ctm ?? [1, 0, 0, 1, 0, 0];
4408
+ alpha = state?.alpha ?? 1;
4409
+ clip = state?.clip ?? null;
4410
+ unknownClip = state?.unknownClip ?? false;
4411
+ } else if (op === OPS3.setGState) {
4412
+ for (const [key, value] of args?.[0] ?? []) {
4413
+ if (key === "ca") alpha = typeof value === "number" ? value : 0;
4414
+ }
4415
+ } else if (op === OPS3.constructPath) {
4416
+ const [operations, coordinates] = args;
4417
+ pathRect = pathRect === void 0 && operations?.length === 1 && operations[0] === OPS3.rectangle && coordinates?.length >= 4 && ctm[1] === 0 && ctm[2] === 0 ? rectangle(coordinates[0], coordinates[1], coordinates[2], coordinates[3]) : null;
4418
+ } else if (op === OPS3.clip || op === OPS3.eoClip) pendingClip = true;
4419
+ else if ([
4420
+ OPS3.endPath,
4421
+ OPS3.stroke,
4422
+ OPS3.closeStroke,
4423
+ OPS3.fill,
4424
+ OPS3.eoFill,
4425
+ OPS3.fillStroke,
4426
+ OPS3.eoFillStroke,
4427
+ OPS3.closeFillStroke,
4428
+ OPS3.closeEOFillStroke
4429
+ ].includes(op)) {
4430
+ if (pendingClip) {
4431
+ if (pathRect) intersectClip(pathRect);
4432
+ else unknownClip = true;
4433
+ }
4434
+ pendingClip = false;
4435
+ pathRect = void 0;
4436
+ } else if (op === OPS3.transform && Array.isArray(args) && args.length >= 6) ctm = multiply(ctm, args);
4437
+ else if (op === OPS3.paintImageXObject && typeof args?.[0] === "string") {
4438
+ if (typeof args[1] !== "number" || typeof args[2] !== "number" || args[1] < 16 || args[1] > 128 || args[2] < 16 || args[2] > 128) continue;
4439
+ const box = extractImageRegions([OPS3.transform, OPS3.paintImageXObject], [ctm, args])[0];
4440
+ if (!box || ctm[1] !== 0 || ctm[2] !== 0 || alpha < 0.99 || unknownClip || clip && (box.x1 + (box.x2 - box.x1) * 0.03 < clip.x1 || box.y1 + (box.y2 - box.y1) * 0.03 < clip.y1 || box.x2 - (box.x2 - box.x1) * 0.03 > clip.x2 || box.y2 - (box.y2 - box.y1) * 0.03 > clip.y2)) continue;
4441
+ const width = box.x2 - box.x1, height = box.y2 - box.y1;
4442
+ if (width < 3 || width > 24 || height < 3 || height > 24 || width / height < 0.5 || width / height > 2) continue;
4443
+ paints.push({ id: args[0], box, mirrored: ctm[0] < 0 });
4444
+ }
4445
+ }
4446
+ return paints;
4447
+ }
4448
+ async function restoreImageBullets(items, page, fnArray, argsArray, shapes = /* @__PURE__ */ new Map()) {
4449
+ const candidates = [];
4450
+ for (const { id, box } of smallVisibleImagePaints(fnArray, argsArray)) {
4451
+ const width = box.x2 - box.x1, height = box.y2 - box.y1;
4452
+ if (Math.abs(width - height) > Math.min(width, height) * 0.1) continue;
4453
+ const line = items.filter((item) => !item.isHidden && item.fontSize > 0 && item.x >= box.x2 && item.x - box.x2 <= item.fontSize * 0.7 && Math.abs(item.y + item.h / 2 - (box.y1 + box.y2) / 2) <= item.fontSize * 0.4 && height >= item.fontSize * 0.5 && height <= item.fontSize * 1.2).sort((a, b) => a.x - b.x)[0];
4454
+ if (!line || /^[○●•◦□■∙]/.test(line.text.trim()) || !/\p{L}/u.test(line.text)) continue;
4455
+ const row = items.filter((item) => Math.abs(item.y - line.y) <= 2 && item.x >= line.x);
4456
+ if (row.reduce((n, item) => n + (item.text.match(/\p{L}/gu)?.length ?? 0), 0) < 12 || items.some((item) => item !== line && Math.abs(item.y - line.y) <= 2 && item.x < line.x)) continue;
4457
+ candidates.push({ id, x: box.x1, width, line });
4458
+ }
4459
+ let restored = 0;
4460
+ const used = /* @__PURE__ */ new Set();
4461
+ for (const candidate of candidates) {
4462
+ const same = candidates.filter((other) => other.id === candidate.id && other.line !== candidate.line && Math.abs(other.line.x - candidate.line.x) <= 1 && Math.abs(other.x - candidate.x) <= 1 && Math.abs(other.width - candidate.width) <= 1 && other.line.fontSize === candidate.line.fontSize && Math.abs(other.line.y - candidate.line.y) >= candidate.line.fontSize * 2);
4463
+ const numbered = items.filter((item) => !item.isHidden && /^[①-⑮]\s*/.test(item.text.trim()) && item.fontName === candidate.line.fontName && item.fontSize === candidate.line.fontSize && Math.abs(item.x - candidate.x) <= 1 && (item.text.match(/\p{L}/gu)?.length ?? 0) >= 12).sort((a, b) => b.y - a.y);
4464
+ const numberedList = numbered.some((item, index) => index > 0 && item.text.trim().charCodeAt(0) === numbered[index - 1].text.trim().charCodeAt(0) + 1);
4465
+ if (!same.length && !numberedList || used.has(candidate.line)) continue;
4466
+ if (!shapes.has(candidate.id)) shapes.set(candidate.id, hollowCircle(await resolveImagePixels(page, candidate.id)));
4467
+ if (!shapes.get(candidate.id)) continue;
4468
+ used.add(candidate.line);
4469
+ items.push({ ...candidate.line, text: "\u25CB", x: candidate.x, w: candidate.width, strike: false, underline: false, hasSpaceBefore: false, syntheticSpace: false });
4470
+ restored++;
4471
+ }
4472
+ return restored;
4473
+ }
4474
+
4475
+ // src/pdf/flow-boxes.ts
4476
+ import { ImageKind as ImageKind2 } from "pdfjs-dist/legacy/build/pdf.mjs";
4477
+ var contains2 = (outer, inner, tolerance = 1) => inner.x1 >= outer.x1 - tolerance && inner.x2 <= outer.x2 + tolerance && inner.y1 >= outer.y1 - tolerance && inner.y2 <= outer.y2 + tolerance;
4478
+ function closed(grid, hs, vs) {
4479
+ const b = grid.bbox;
4480
+ return [b.y1, b.y2].every((y) => hs.some((h) => Math.abs(h.y1 - y) <= 1 && h.x1 <= b.x1 + 1 && h.x2 >= b.x2 - 1)) && [b.x1, b.x2].every((x) => vs.some((v) => Math.abs(v.x1 - x) <= 1 && v.y1 <= b.y1 + 1 && v.y2 >= b.y2 - 1));
4481
+ }
4482
+ function groupFlowBoxUnits(blocks, grids, hs, vs, images) {
4483
+ const candidates = grids.filter((g) => g.bbox.y2 - g.bbox.y1 >= 30 && closed(g, hs, vs) && !grids.some((other) => other !== g && contains2(other.bbox, g.bbox, 0) && (other.bbox.x2 - other.bbox.x1) * (other.bbox.y2 - other.bbox.y1) > (g.bbox.x2 - g.bbox.x1) * (g.bbox.y2 - g.bbox.y1) + 1));
4484
+ const bands = [], visited = /* @__PURE__ */ new Set();
4485
+ for (const first of candidates) {
4486
+ if (visited.has(first)) continue;
4487
+ const row = candidates.filter((g) => Math.abs(g.bbox.y1 - first.bbox.y1) <= 1 && Math.abs(g.bbox.y2 - first.bbox.y2) <= 1).sort((a, b) => a.bbox.x1 - b.bbox.x1);
4488
+ row.forEach((g) => visited.add(g));
4489
+ if (row.length < 4 || row.filter((g) => g.rowYs.length === 2 && g.colXs.length === 2).length < 3) continue;
4490
+ const connectors = [];
4491
+ for (let i = 1; i < row.length; i++) {
4492
+ const left = row[i - 1].bbox, right = row[i].bbox, gap = right.x1 - left.x2;
4493
+ if (gap < 4 || gap > Math.min(left.x2 - left.x1, right.x2 - right.x1) * 0.6) break;
4494
+ const centerX = (left.x2 + right.x1) / 2, centerY = (left.y1 + left.y2) / 2;
4495
+ const connector = images.find((p) => {
4496
+ const w = p.x2 - p.x1, h = p.y2 - p.y1;
4497
+ return p.x1 >= left.x2 && p.x2 <= right.x1 && w >= gap * 0.4 && w <= gap * 0.9 && h >= 3 && h <= (left.y2 - left.y1) * 0.2 && w / h >= 0.8 && w / h <= 1.6 && Math.abs((p.x1 + p.x2) / 2 - centerX) <= gap * 0.15 && Math.abs((p.y1 + p.y2) / 2 - centerY) <= (left.y2 - left.y1) * 0.1;
4498
+ });
4499
+ if (!connector) break;
4500
+ connectors.push(connector);
4501
+ }
4502
+ if (connectors.length !== row.length - 1 || connectors.some((p) => Math.abs(p.x2 - p.x1 - (connectors[0].x2 - connectors[0].x1)) > (connectors[0].x2 - connectors[0].x1) * 0.2 || Math.abs(p.y2 - p.y1 - (connectors[0].y2 - connectors[0].y1)) > (connectors[0].y2 - connectors[0].y1) * 0.2)) continue;
4503
+ bands.push(row);
4504
+ }
4505
+ const members = /* @__PURE__ */ new Map();
4506
+ for (const band of bands) {
4507
+ const columns = band.map((g) => blocks.filter((b) => b.bbox && contains2(g.bbox, {
4508
+ x1: b.bbox.x,
4509
+ y1: b.bbox.y,
4510
+ x2: b.bbox.x + b.bbox.width,
4511
+ y2: b.bbox.y + b.bbox.height
4512
+ })).sort((a, b) => b.bbox.y + b.bbox.height - a.bbox.y - a.bbox.height || a.bbox.x - b.bbox.x));
4513
+ if (columns.some((column) => !column.length)) continue;
4514
+ const unit = columns.flat();
4515
+ for (const block of unit) members.set(block, unit);
4516
+ }
4517
+ const units = [], emitted = /* @__PURE__ */ new Set();
4518
+ for (const block of blocks) {
4519
+ const unit = members.get(block);
4520
+ if (!unit) units.push([block]);
4521
+ else if (!emitted.has(unit)) {
4522
+ units.push(unit);
4523
+ emitted.add(unit);
4524
+ }
4525
+ }
4526
+ return units;
4527
+ }
4528
+ function arrowDirection(image) {
4529
+ if (!image?.data || image.width < 16 || image.width > 128 || image.height < 16 || image.height > 128) return 0;
4530
+ const { width, height, data } = image;
4531
+ const stride = image.kind === ImageKind2.RGB_24BPP ? 3 : image.kind === ImageKind2.RGBA_32BPP ? 4 : 0;
4532
+ if (!stride || data.length < width * height * stride) return 0;
4533
+ const ink = [];
4534
+ let white = 0;
4535
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
4536
+ const offset = (y * width + x) * stride;
4537
+ if (stride === 4 && data[offset + 3] < 240) return 0;
4538
+ const min = Math.min(data[offset], data[offset + 1], data[offset + 2]);
4539
+ if (min >= 235) white++;
4540
+ if (min < 230) ink.push({ x, y });
4541
+ }
4542
+ if (white < width * height * 0.55 || !ink.length) return 0;
4543
+ const x0 = Math.min(...ink.map((p) => p.x)), x1 = Math.max(...ink.map((p) => p.x));
4544
+ const y0 = Math.min(...ink.map((p) => p.y)), y1 = Math.max(...ink.map((p) => p.y));
4545
+ const w = x1 - x0 + 1, h = y1 - y0 + 1, centerY = (y0 + y1) / 2;
4546
+ if (w < width * 0.5 || h < height * 0.5 || x0 < width * 0.04 || y0 < height * 0.04 || x1 >= width * 0.96 || y1 >= height * 0.96) return 0;
4547
+ const pointsRight = (mirror) => {
4548
+ const points = ink.map((p) => ({ x: mirror ? x0 + x1 - p.x : p.x, y: p.y }));
4549
+ const tail = points.filter((p) => p.x <= x0 + w * 0.25), tip = points.filter((p) => p.x >= x1 - w * 0.1);
4550
+ if (!tail.length || !tip.length) return false;
4551
+ const span = (points2) => Math.max(...points2.map((p) => p.y)) - Math.min(...points2.map((p) => p.y)) + 1;
4552
+ if (span(tail) > h * 0.4 || span(tip) > h * 0.45 || Math.abs((Math.max(...tail.map((p) => p.y)) + Math.min(...tail.map((p) => p.y))) / 2 - centerY) > h * 0.1 || Math.abs((Math.max(...tip.map((p) => p.y)) + Math.min(...tip.map((p) => p.y))) / 2 - centerY) > h * 0.1) return false;
4553
+ let head = 0;
4554
+ for (let x = Math.ceil(x0 + w * 0.45); x <= x0 + w * 0.75; x++) {
4555
+ const column = points.filter((p) => p.x === x);
4556
+ if (column.length) head = Math.max(head, span(column));
4557
+ }
4558
+ const middle = new Set(points.filter((p) => Math.abs(p.y - centerY) <= 1).map((p) => p.x));
4559
+ return head >= h * 0.8 && middle.size >= w * 0.9;
4560
+ };
4561
+ return pointsRight(false) ? 1 : pointsRight(true) ? -1 : 0;
4562
+ }
4563
+ async function detectRightArrowRegions(page, fnArray, argsArray, directions = /* @__PURE__ */ new Map()) {
4564
+ const regions = [];
4565
+ const paints = smallVisibleImagePaints(fnArray, argsArray);
4566
+ for (const paint of paints) {
4567
+ const row = paints.filter((other) => other.id === paint.id && Math.abs(other.box.y1 - paint.box.y1) <= 1 && Math.abs(other.box.y2 - paint.box.y2) <= 1 && Math.abs(other.box.x2 - other.box.x1 - (paint.box.x2 - paint.box.x1)) <= 1);
4568
+ const positions = new Set(row.map((other) => Math.round(other.box.x1)));
4569
+ if (positions.size < 3) continue;
4570
+ if (!directions.has(paint.id)) directions.set(paint.id, arrowDirection(await resolveImagePixels(page, paint.id)));
4571
+ const direction = directions.get(paint.id) ?? 0;
4572
+ if (direction && (paint.mirrored ? -direction : direction) === 1) regions.push(paint.box);
4573
+ }
4574
+ return regions;
4575
+ }
4576
+
4314
4577
  // src/pdf/side-tabs.ts
4315
4578
  var BAND = 0.12;
4316
4579
  var MAX_CHARS = 10;
@@ -4325,7 +4588,8 @@ function isSideTabTable(box, table, others, pageWidth, pageHeight) {
4325
4588
  if (!side) return false;
4326
4589
  const texts = table.cells.flat().map((c) => c.text.replace(/\s+/g, ""));
4327
4590
  if (!texts.some(Boolean) || texts.some((t) => [...t].length > TAB_CELL_CHARS)) return false;
4328
- return others.every((it) => it.y < box.y1 || it.y > box.y2 || (side === "R" ? it.x + it.w <= box.x1 + 1 : it.x >= box.x2 - 1));
4591
+ const overlap2 = others.filter((it) => it.y >= box.y1 && it.y <= box.y2);
4592
+ return overlap2.length > 0 && overlap2.every((it) => side === "R" ? it.x + it.w <= box.x1 + 1 : it.x >= box.x2 - 1);
4329
4593
  }
4330
4594
  function removeSideTabs(blocks, pageWidths) {
4331
4595
  const edges = /* @__PURE__ */ new Map();
@@ -4354,7 +4618,7 @@ function removeSideTabs(blocks, pageWidths) {
4354
4618
  }
4355
4619
  if (!w || !area || !b.bbox || !text || [...text].length > MAX_CHARS) return null;
4356
4620
  const side = b.bbox.x + b.bbox.width <= Math.min(w * BAND, area.left - 1) ? "L" : b.bbox.x >= Math.max(w * (1 - BAND), area.right + 1) ? "R" : null;
4357
- return side && text.replace(/\s+/g, "");
4621
+ return side && `${side}\0${text.replace(/\s+/g, "")}`;
4358
4622
  };
4359
4623
  const pages = /* @__PURE__ */ new Map();
4360
4624
  for (const b of blocks) {
@@ -4561,16 +4825,42 @@ function sideTabGlyphs(items) {
4561
4825
  if (c) c.push(it);
4562
4826
  else cols.push([it]);
4563
4827
  }
4828
+ const cands = [];
4564
4829
  for (const col of cols) {
4565
- if (col.length < SIDE_TAB_MIN) continue;
4830
+ if (col.length < 2) continue;
4831
+ if (col.filter((g) => /[\p{L}\p{N}]/u.test(g.text)).length < col.length * 0.8) continue;
4566
4832
  col.sort((a, b) => b.y - a.y);
4567
4833
  const fs = [...col.map((i) => i.fontSize)].sort((a, b) => a - b)[col.length >> 1];
4568
4834
  if (col.slice(1).some((it, k) => col[k].y - it.y > fs * 3)) continue;
4835
+ cands.push({ col, fs });
4836
+ }
4837
+ const bands = [];
4838
+ for (const c of cands.sort((a, b) => a.col[0].x - b.col[0].x)) {
4839
+ const last = bands[bands.length - 1];
4840
+ if (last) {
4841
+ const lastRight = Math.max(...last.col.map((i) => i.x + i.w));
4842
+ const gap = Math.min(...c.col.map((i) => i.x)) - lastRight;
4843
+ const lTop = Math.max(...last.col.map((i) => i.y)), lBot = Math.min(...last.col.map((i) => i.y));
4844
+ const cTop = c.col[0].y, cBot = c.col[c.col.length - 1].y;
4845
+ if (gap <= Math.max(last.fs, c.fs) * 1.5 && Math.min(lTop, cTop) >= Math.max(lBot, cBot)) {
4846
+ last.col.push(...c.col);
4847
+ last.fs = Math.max(last.fs, c.fs);
4848
+ last.anchored ||= c.col.length >= SIDE_TAB_MIN;
4849
+ continue;
4850
+ }
4851
+ }
4852
+ bands.push({ col: [...c.col], fs: c.fs, anchored: c.col.length >= SIDE_TAB_MIN });
4853
+ }
4854
+ for (const { col, fs, anchored } of bands) {
4855
+ if (!anchored) continue;
4856
+ col.sort((a, b) => b.y - a.y);
4569
4857
  const members = new Set(col);
4570
4858
  const top = col[0].y + fs, bottom = col[col.length - 1].y - fs;
4571
- const others = items.filter((i) => !members.has(i) && i.y <= top && i.y >= bottom);
4859
+ const others = items.filter((i) => !members.has(i) && i.text.trim() && i.y <= top && i.y >= bottom);
4860
+ if (!others.length) continue;
4572
4861
  const left = Math.min(...col.map((i) => i.x)), right = Math.max(...col.map((i) => i.x + i.w));
4573
- const outside = others.every((i) => i.x >= right + fs) || others.every((i) => i.x + i.w <= left - fs);
4862
+ const rest = items.filter((i) => !members.has(i) && i.text.trim());
4863
+ const outside = rest.every((i) => i.x >= right + fs) || rest.every((i) => i.x + i.w <= left - fs);
4574
4864
  if (!outside) continue;
4575
4865
  const alone = col.filter((g) => !others.some((i) => Math.abs(i.y - g.y) <= Y_TOL)).length;
4576
4866
  if (alone < col.length * 0.4) continue;
@@ -5828,13 +6118,44 @@ var UNDER_SPAN_OVERLAP_RATIO = 0.8;
5828
6118
  var UNDER_BOX_PAIR_MIN_EM = 0.5;
5829
6119
  var UNDER_BOX_PAIR_MAX_EM = 2.2;
5830
6120
  var UNDER_BOX_PAIR_OVERLAP = 0.8;
5831
- function markUnderlineItems(items, horizontals, verticals) {
6121
+ function markUnderlineItems(items, horizontals, verticals, nonRules) {
5832
6122
  const underlines = [];
6123
+ if (nonRules?.size) horizontals = horizontals.filter((line) => !nonRules.has(line));
5833
6124
  if (items.length === 0 || horizontals.length === 0) return underlines;
6125
+ const tightOwners = /* @__PURE__ */ new Map();
6126
+ const tightlyOwned = (line) => {
6127
+ const cached = tightOwners.get(line);
6128
+ if (cached !== void 0) return cached;
6129
+ const owners = items.filter((item) => {
6130
+ const below = item.y - line.y1;
6131
+ return item.w > 0 && item.text.trim() && below >= 0.5 && below <= 3 && Math.min(line.x2, item.x + item.w) > Math.max(line.x1, item.x);
6132
+ }).sort((a, b) => a.x - b.x);
6133
+ let owned = owners.length > 0 && (Math.abs(owners[0].x - line.x1) <= UNDER_GRID_EPS || owners.length === 1 && line.x1 > owners[0].x && line.x1 - owners[0].x <= Math.max(owners[0].h, owners[0].fontSize, 1) * 3) && Math.abs(owners[owners.length - 1].x + owners[owners.length - 1].w - line.x2) <= UNDER_GRID_EPS;
6134
+ for (let i = 1; owned && i < owners.length; i++) {
6135
+ const em = Math.max(owners[i].h, owners[i].fontSize, 1);
6136
+ if (owners[i].x - (owners[i - 1].x + owners[i - 1].w) > em * UNDER_COLUMN_GAP_EM) owned = false;
6137
+ }
6138
+ tightOwners.set(line, owned);
6139
+ return owned;
6140
+ };
6141
+ const paragraphUnderline = (line) => {
6142
+ if (!tightlyOwned(line)) return false;
6143
+ const width = line.x2 - line.x1;
6144
+ const ys = [];
6145
+ for (const other of horizontals) {
6146
+ const overlap2 = Math.min(line.x2, other.x2) - Math.max(line.x1, other.x1);
6147
+ if (other.lineWidth > UNDER_MAX_THICKNESS || overlap2 / Math.max(width, other.x2 - other.x1) < UNDER_SPAN_OVERLAP_RATIO || !tightlyOwned(other)) continue;
6148
+ if (!ys.some((y) => Math.abs(y - other.y1) < UNDER_GRID_EPS)) ys.push(other.y1);
6149
+ if (ys.length >= UNDER_REPEATED_SPAN_LEVELS) return true;
6150
+ }
6151
+ return false;
6152
+ };
5834
6153
  for (const line of horizontals) {
5835
6154
  if (line.lineWidth > UNDER_MAX_THICKNESS) continue;
5836
6155
  const enclosed = insideTallCell(line, verticals, 0);
5837
- if (!enclosed && (touchesVertical(line, verticals) || isRepeatedSpanRule(line, horizontals))) continue;
6156
+ const repeated = isRepeatedSpanRule(line, horizontals);
6157
+ const textSeries = repeated && paragraphUnderline(line);
6158
+ if (!enclosed && (touchesVertical(line, verticals) || repeated && !textSeries)) continue;
5838
6159
  const matches = [];
5839
6160
  for (const item of items) {
5840
6161
  const h = item.h > 0 ? item.h : item.fontSize;
@@ -5854,11 +6175,11 @@ function markUnderlineItems(items, horizontals, verticals) {
5854
6175
  covered += m.w;
5855
6176
  }
5856
6177
  const inset = enclosed && insideTallCell(line, verticals, maxH);
5857
- if (enclosed && !inset && (touchesVertical(line, verticals) || isRepeatedSpanRule(line, horizontals))) continue;
6178
+ if (enclosed && !inset && (touchesVertical(line, verticals) || repeated && !textSeries)) continue;
5858
6179
  const pad = Math.max(maxH * UNDER_OWNER_PAD_EM, UNDER_OWNER_PAD_MIN_PT);
5859
6180
  if (line.x1 < x1 - pad || line.x2 > x2 + pad) continue;
5860
6181
  if (covered < (line.x2 - line.x1) * UNDER_MIN_COVERAGE) continue;
5861
- if (hasBoxTopPair(line, maxH, horizontals, inset ? items : void 0)) continue;
6182
+ if (hasBoxTopPair(line, maxH, horizontals, inset || textSeries || tightlyOwned(line) ? items : void 0)) continue;
5862
6183
  matches.sort((a, b) => a.x - b.x);
5863
6184
  let hole = false;
5864
6185
  for (let i = 1; i < matches.length; i++) {
@@ -5889,6 +6210,7 @@ function touchesVertical(line, verticals) {
5889
6210
  for (const v of verticals) {
5890
6211
  if (v.x1 < line.x1 - UNDER_GRID_EPS || v.x1 > line.x2 + UNDER_GRID_EPS) continue;
5891
6212
  const lo = Math.min(v.y1, v.y2), hi = Math.max(v.y1, v.y2);
6213
+ if (hi - lo <= UNDER_GRID_EPS) continue;
5892
6214
  if (lo <= line.y1 + UNDER_GRID_EPS && hi >= line.y1 - UNDER_GRID_EPS) return true;
5893
6215
  }
5894
6216
  return false;
@@ -6098,6 +6420,57 @@ function isPageFrameGrid(grid, cells, pageWidth, pageHeight, items = []) {
6098
6420
  return inner.length === 0 || crossing >= 1 && crossing >= inBig.length * 0.01;
6099
6421
  }
6100
6422
 
6423
+ // src/pdf/shaded-open-table.ts
6424
+ function closeShadedTableEdges(clipGrids, horizontals, verticals, fillRects) {
6425
+ const near = (a, b) => Math.abs(a - b) <= 1.5;
6426
+ let hs = horizontals, vs = verticals;
6427
+ const restored = [];
6428
+ for (const grid of clipGrids) {
6429
+ if (grid.clipParent || grid.rowYs.length !== 2 || grid.colXs.length < 3 || !grid.cells?.length || grid.cells.some((c) => c.filler || c.rowSpan !== 1 || c.colSpan !== 1)) continue;
6430
+ const { x1, x2, y1: mid, y2: top } = grid.bbox;
6431
+ if (!grid.cells.every((c) => fillRects.some((f) => near(f.x1, c.bbox.x1) && near(f.x2, c.bbox.x2) && near(f.y1, c.bbox.y1) && near(f.y2, c.bbox.y2)))) continue;
6432
+ const topRule = hs.find((h) => near(h.y1, top) && near(h.x1, x1) && near(h.x2, x2));
6433
+ if (!topRule) continue;
6434
+ const dividers = grid.colXs.slice(1, -1).map((x) => vs.find((v) => near(v.x1, x) && near(v.y2, top) && v.y1 <= mid - 6));
6435
+ if (dividers.some((v) => !v)) continue;
6436
+ const bottom = dividers[0].y1;
6437
+ if (!dividers.every((v) => near(v.y1, bottom))) continue;
6438
+ const bottomRule = hs.find((h) => near(h.y1, bottom) && near(h.x1, x1) && near(h.x2, x2));
6439
+ if (!bottomRule) continue;
6440
+ const missing = [x1, x2].filter((x) => !vs.some((v) => near(v.x1, x) && v.y1 <= bottom + 1.5 && v.y2 >= top - 1.5));
6441
+ if (!missing.length) continue;
6442
+ restored.push({ x1, x2, y1: bottom, y2: top });
6443
+ vs = vs.concat(missing.map((x) => ({ x1: x, x2: x, y1: bottom, y2: top, lineWidth: topRule.lineWidth })));
6444
+ if (!hs.some((h) => near(h.y1, mid) && near(h.x1, x1) && near(h.x2, x2))) {
6445
+ hs = hs.concat({ x1, x2, y1: mid, y2: mid, lineWidth: topRule.lineWidth });
6446
+ }
6447
+ }
6448
+ return { horizontals: hs, verticals: vs, restored };
6449
+ }
6450
+ function nestRestoredShadedGrids(grids, restored, horizontals, verticals) {
6451
+ const near = (a, b) => Math.abs(a - b) <= 1.5;
6452
+ if (restored.length === 0) return;
6453
+ const nested = /* @__PURE__ */ new Set();
6454
+ for (const grid of grids) {
6455
+ const b = grid.bbox;
6456
+ if (!restored.some((r) => near(r.x1, b.x1) && near(r.x2, b.x2) && near(r.y1, b.y1) && near(r.y2, b.y2))) continue;
6457
+ if (grids.some((g) => g !== grid && b.x1 - g.bbox.x1 >= 0.8 && g.bbox.x2 - b.x2 >= 0.8 && b.y1 - g.bbox.y1 >= 0.8 && g.bbox.y2 - b.y2 >= 0.8)) {
6458
+ grid.lineNested = true;
6459
+ nested.add(grid);
6460
+ }
6461
+ }
6462
+ for (const frame of grids) {
6463
+ const b = frame.bbox;
6464
+ if (frame.lineNested || ![...nested].some((g) => g.bbox.x1 > b.x1 && g.bbox.x2 < b.x2 && g.bbox.y1 > b.y1 && g.bbox.y2 < b.y2)) continue;
6465
+ const fullH = horizontals.filter((h) => near(h.x1, b.x1) && near(h.x2, b.x2) && h.y1 >= b.y1 - 1.5 && h.y1 <= b.y2 + 1.5);
6466
+ if (!fullH.some((h) => near(h.y1, b.y1)) || !fullH.some((h) => near(h.y1, b.y2)) || fullH.some((h) => h.y1 > b.y1 + 1.5 && h.y1 < b.y2 - 1.5)) continue;
6467
+ const fullV = verticals.filter((v) => v.y1 <= b.y1 + 1.5 && v.y2 >= b.y2 - 1.5 && v.x1 >= b.x1 - 1.5 && v.x1 <= b.x2 + 1.5);
6468
+ if (!fullV.some((v) => near(v.x1, b.x1)) || !fullV.some((v) => near(v.x1, b.x2)) || fullV.some((v) => v.x1 > b.x1 + 1.5 && v.x1 < b.x2 - 1.5)) continue;
6469
+ frame.colXs = [b.x1, b.x2];
6470
+ frame.rowYs = [b.y2, b.y1];
6471
+ }
6472
+ }
6473
+
6101
6474
  // src/pdf/open-table-ends.ts
6102
6475
  var ALIGN_TOL = 3;
6103
6476
  var MIN_RULES = 3;
@@ -6433,6 +6806,140 @@ function detectTextBoxTables(boxes, items, pageNum) {
6433
6806
  }];
6434
6807
  }
6435
6808
 
6809
+ // src/pdf/frame-cell-blocks.ts
6810
+ var FRAME_READING_UNITS = /* @__PURE__ */ new WeakMap();
6811
+ var FRAME_SOURCE_BOUNDS = /* @__PURE__ */ new WeakMap();
6812
+ function recordFrameReadingUnit(blocks, bbox) {
6813
+ for (const block of blocks) FRAME_SOURCE_BOUNDS.set(block, bbox);
6814
+ if (blocks.length < 2) return;
6815
+ const unit = { blocks, bbox };
6816
+ for (const block of blocks) FRAME_READING_UNITS.set(block, unit);
6817
+ }
6818
+ function frameLayoutBoxes(blocks) {
6819
+ const boxes = [], seen2 = /* @__PURE__ */ new Set();
6820
+ for (const block of blocks) {
6821
+ const frame = FRAME_SOURCE_BOUNDS.get(block);
6822
+ if (frame) {
6823
+ if (!seen2.has(frame)) {
6824
+ boxes.push(frame);
6825
+ seen2.add(frame);
6826
+ }
6827
+ } else if (block.bbox) boxes.push(block.bbox);
6828
+ }
6829
+ return boxes;
6830
+ }
6831
+ function groupFrameParagraphUnits(units, frames) {
6832
+ const source = /* @__PURE__ */ new Map();
6833
+ for (const unit of units) for (const block of unit) source.set(block, unit);
6834
+ const members = /* @__PURE__ */ new Map(), emitted = /* @__PURE__ */ new Set();
6835
+ for (const frame of frames) {
6836
+ if (frame.length < 2 || frame.some((block) => block.type !== "paragraph" || source.get(block)?.length !== 1)) continue;
6837
+ for (const block of frame) members.set(block, frame);
6838
+ }
6839
+ const out = [];
6840
+ for (const unit of units) {
6841
+ const frame = unit.length === 1 ? members.get(unit[0]) : void 0;
6842
+ if (!frame) out.push(unit);
6843
+ else if (!emitted.has(frame)) {
6844
+ out.push(frame);
6845
+ emitted.add(frame);
6846
+ }
6847
+ }
6848
+ for (const unit of out) {
6849
+ if (unit.length < 2 || FRAME_READING_UNITS.get(unit[0])?.blocks === unit) continue;
6850
+ const bs = unit.map((b) => b.bbox).filter((b) => !!b);
6851
+ if (bs.length !== unit.length) continue;
6852
+ const x = Math.min(...bs.map((b) => b.x)), y = Math.min(...bs.map((b) => b.y));
6853
+ recordFrameReadingUnit(unit, {
6854
+ page: bs[0].page,
6855
+ x,
6856
+ y,
6857
+ width: Math.max(...bs.map((b) => b.x + b.width)) - x,
6858
+ height: Math.max(...bs.map((b) => b.y + b.height)) - y
6859
+ });
6860
+ }
6861
+ return out;
6862
+ }
6863
+ function recordFrameTitle(table, source, pageNum) {
6864
+ if (!CLIP_TABLES.has(table) || table.rows !== 1 || table.cols !== 3 || source.length < 2) return;
6865
+ const cells = table.cells[0];
6866
+ if (cells.length !== 3 || cells.some((c) => c.colSpan !== 1 || c.rowSpan !== 1 || c.blocks?.length || c.text.includes("\n"))) return;
6867
+ const [label, spacer, title] = cells;
6868
+ if (!/^(?:붙임|별첨|첨부|부록)\s*\d{1,3}$/.test(label.text.trim()) || spacer.text.trim() || !title.text.trim()) return;
6869
+ if (cells.some((c) => {
6870
+ const e = CELL_EDGES.get(c);
6871
+ return !e || e.t || e.b || e.l || e.r;
6872
+ })) return;
6873
+ const xs = TABLE_COLXS.get(table), style = dominantStyle(source), fs = style?.fontSize ?? 0;
6874
+ if (!xs || xs.length !== 4 || fs <= 0) return;
6875
+ const width = xs[3] - xs[0], gap = xs[2] - xs[1];
6876
+ if (width < fs * 12 || gap <= 0 || gap > fs * 1.5 || xs[1] - xs[0] > width * 0.25 || xs[3] - xs[2] < width * 0.6) return;
6877
+ if (source.some((i) => i.fontName !== source[0].fontName || Math.abs(i.fontSize - fs) > fs * 0.15)) return;
6878
+ if (groupByY([...source].sort((a, b) => b.y - a.y)).length !== 1) return;
6879
+ FRAME_TITLE_BLOCKS.set(table, {
6880
+ type: "paragraph",
6881
+ text: `${label.text.trim()} ${title.text.trim()}`,
6882
+ pageNumber: pageNum,
6883
+ bbox: computeBBox(source, pageNum),
6884
+ style
6885
+ });
6886
+ }
6887
+ var FRAME_RECT_TOL = 1.5;
6888
+ function takePendingNested(pending, cellBox, clipCell) {
6889
+ const out = [];
6890
+ for (let i = pending.length - 1; i >= 0; i--) {
6891
+ const p = pending[i].parent;
6892
+ if (pending[i].contained ? p.x1 >= cellBox.x1 - FRAME_RECT_TOL && p.x2 <= cellBox.x2 + FRAME_RECT_TOL && p.y1 >= cellBox.y1 - FRAME_RECT_TOL && p.y2 <= cellBox.y2 + FRAME_RECT_TOL : clipCell && Math.abs(p.x1 - cellBox.x1) <= FRAME_RECT_TOL && Math.abs(p.x2 - cellBox.x2) <= FRAME_RECT_TOL && Math.abs(p.y1 - cellBox.y1) <= FRAME_RECT_TOL && Math.abs(p.y2 - cellBox.y2) <= FRAME_RECT_TOL) {
6893
+ out.push(pending[i].block);
6894
+ pending.splice(i, 1);
6895
+ }
6896
+ }
6897
+ return out;
6898
+ }
6899
+ function buildFrameCellBlocks(cellItems, nested, pageNum, lex) {
6900
+ const tables = [...nested].sort((a, b) => b.bbox.y + b.bbox.height - (a.bbox.y + a.bbox.height));
6901
+ const blocks = [];
6902
+ let rest = [...cellItems];
6903
+ const pushParagraphs = (items) => {
6904
+ if (items.length === 0) return;
6905
+ const source = items.map((it) => ({ ...it, isHidden: false })).sort((a, b) => b.y - a.y || a.x - b.x);
6906
+ const paragraphs = [];
6907
+ pushLineParagraphs(paragraphs, mergeSuperscriptLines(groupByY(source)), pageNum, lex);
6908
+ for (const block of paragraphs) {
6909
+ block.text = cleanCellText(block.text ?? "");
6910
+ if (block.text) blocks.push(block);
6911
+ }
6912
+ };
6913
+ for (const tb of tables) {
6914
+ const bottom = tb.bbox.y;
6915
+ pushParagraphs(rest.filter((it) => it.y >= bottom));
6916
+ rest = rest.filter((it) => it.y < bottom);
6917
+ blocks.push(tb);
6918
+ }
6919
+ pushParagraphs(rest);
6920
+ const text = blocks.map((b) => b.type === "table" && b.table ? b.table.cells.flat().map((c) => c.text).filter(Boolean).join("\n") : b.text ?? "").filter(Boolean).join("\n");
6921
+ return { blocks, text };
6922
+ }
6923
+
6924
+ // src/pdf/nested-shaded-headers.ts
6925
+ function extendNestedShadedHeaders(clipGrids, lineGrids, horizontals, verticals, fills) {
6926
+ const near = (a, b) => Math.abs(a - b) <= 1.5;
6927
+ const contains3 = (a, b) => b.x1 >= a.x1 - 1.5 && b.x2 <= a.x2 + 1.5 && b.y1 >= a.y1 - 1.5 && b.y2 <= a.y2 + 1.5;
6928
+ const extended = [];
6929
+ for (let i = 0; i < clipGrids.length; i++) {
6930
+ const c = clipGrids[i], parent = c.clipParent;
6931
+ if (!parent || c.rowYs.length !== 2 || c.colXs.length < 3 || !c.cells?.length || c.cells.some((cell) => cell.filler || cell.rowSpan !== 1 || cell.colSpan !== 1)) continue;
6932
+ if (!clipGrids.some((g) => g.rowYs.length === 2 && g.colXs.length === 2 && near(g.bbox.x1, parent.x1) && near(g.bbox.x2, parent.x2) && near(g.bbox.y1, parent.y1) && near(g.bbox.y2, parent.y2))) continue;
6933
+ if (!c.cells.every((cell) => fills.some((f) => near(f.x1, cell.bbox.x1) && near(f.x2, cell.bbox.x2) && near(f.y1, cell.bbox.y1) && near(f.y2, cell.bbox.y2)))) continue;
6934
+ const host = lineGrids.find((l) => l.rowYs.length > 2 && l.colXs.length === c.colXs.length && c.colXs.every((x, k) => near(x, l.colXs[k])) && c.rowYs.every((y, k) => near(y, l.rowYs[k])) && contains3(parent, l.bbox) && l.rowYs.every((y) => horizontals.some((h) => near(h.y1, y) && near(h.x1, l.bbox.x1) && near(h.x2, l.bbox.x2))) && l.colXs.every((x) => verticals.some((v) => near(v.x1, x) && v.y1 <= l.bbox.y1 + 1.5 && v.y2 >= l.bbox.y2 - 1.5)) && !clipGrids.some((o) => o !== c && !contains3(o.bbox, l.bbox) && Math.min(o.bbox.x2, l.bbox.x2) > Math.max(o.bbox.x1, l.bbox.x1) && Math.min(o.bbox.y2, l.bbox.y2) > Math.max(o.bbox.y1, l.bbox.y1)));
6935
+ if (!host) continue;
6936
+ const grid = { ...host, clipParent: parent, cells: extractCells(host, horizontals, verticals) };
6937
+ clipGrids[i] = grid;
6938
+ extended.push(grid);
6939
+ }
6940
+ return extended;
6941
+ }
6942
+
6436
6943
  // src/pdf/page-regions.ts
6437
6944
  function splitTwoColumnProse(items, cutX) {
6438
6945
  const left = [];
@@ -6760,7 +7267,7 @@ function figureColumnBands(items, figures) {
6760
7267
  }
6761
7268
 
6762
7269
  // src/pdf/page-blocks.ts
6763
- function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true, carry, lexicon) {
7270
+ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true, carry, lexicon, verifiedRightArrows = []) {
6764
7271
  if (items.length === 0) {
6765
7272
  if (carry) carry.clip = void 0;
6766
7273
  return [];
@@ -6768,7 +7275,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6768
7275
  const tab = sideTabGlyphs(items);
6769
7276
  if (tab.size) {
6770
7277
  const glyphs = [...tab].sort((a, b) => b.y - a.y).map((g) => ({ type: "paragraph", text: g.text.trim(), pageNumber: pageNum, bbox: computeBBox([g], pageNum), style: dominantStyle([g]) }));
6771
- return [...glyphs, ...extractPageBlocksWithLines(items.filter((i) => !tab.has(i)), pageNum, opList, pageWidth, pageHeight, extraLines, detectTables, carry, lexicon)];
7278
+ return [...glyphs, ...extractPageBlocksWithLines(items.filter((i) => !tab.has(i)), pageNum, opList, pageWidth, pageHeight, extraLines, detectTables, carry, lexicon, verifiedRightArrows)];
6772
7279
  }
6773
7280
  const lex = lexicon ?? new WrapLexicon();
6774
7281
  for (const line of streamLines(items)) lex.addLine(mergeLineSimple(line));
@@ -6796,19 +7303,28 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6796
7303
  }
6797
7304
  ;
6798
7305
  ({ horizontals, verticals } = filterPageBorderLines(horizontals, verticals, pageWidth, pageHeight));
6799
- ({ horizontals, verticals } = preprocessLines(horizontals, verticals));
7306
+ ({ horizontals, verticals } = preprocessLines(horizontals, verticals, extracted.nonRules));
7307
+ markStrikethroughItems(items, horizontals);
7308
+ wrapStrikethroughRuns(items);
7309
+ const underlines = new Set(markUnderlineItems(items, horizontals, verticals, extracted.nonRules));
7310
+ if (underlines.size) horizontals = horizontals.filter((l) => !underlines.has(l));
7311
+ wrapUnderlineRuns(items);
6800
7312
  horizontals = closeOpenTableEnds(horizontals, verticals, items.every((it) => it.seq !== void 0));
6801
7313
  verticals = closeOpenTableEdges(horizontals, verticals);
6802
7314
  verticals = bridgeSplitColumnVerticals(horizontals, verticals);
6803
7315
  if (detectTables && clipGrids.length === 0) verticals = bridgeSkippedRowVerticals(horizontals, verticals, items);
6804
7316
  if (detectTables && clipGrids.length === 0) ({ horizontals, verticals } = extendHeaderBoxRows(horizontals, verticals, items));
6805
- markStrikethroughItems(items, horizontals);
6806
- wrapStrikethroughRuns(items);
6807
- const underlines = new Set(markUnderlineItems(items, horizontals, verticals));
6808
- if (underlines.size) horizontals = horizontals.filter((l) => !underlines.has(l));
6809
- wrapUnderlineRuns(items);
7317
+ const shadedEdges = detectTables ? closeShadedTableEdges(clipGrids, horizontals, verticals, extracted.fillRects) : void 0;
7318
+ if (shadedEdges) ({ horizontals, verticals } = shadedEdges);
6810
7319
  const lineGrids = detectTables ? buildTableGrids(horizontals, verticals) : [];
7320
+ if (shadedEdges) nestRestoredShadedGrids(lineGrids, shadedEdges.restored, horizontals, verticals);
6811
7321
  const tableClipGrids = dropHeadBandClipGrids(dropInsetClipGrids(dropShadingClipGrids(clipGrids, lineGrids, extracted.fillRects, verticals), lineGrids), lineGrids);
7322
+ recordClipCellEdges(
7323
+ extendNestedShadedHeaders(tableClipGrids, lineGrids, horizontals, verticals, extracted.fillRects),
7324
+ extracted.horizontals.concat(extracted.shortH),
7325
+ extracted.verticals.concat(extracted.shortV),
7326
+ extracted.nonRules
7327
+ );
6812
7328
  const grids = [...tableClipGrids, ...dropGridsInside(lineGrids, tableClipGrids, clipResult.containers)];
6813
7329
  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 }));
6814
7330
  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) {
@@ -6818,7 +7334,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6818
7334
  if (detectTables && clipGrids.length === 0 && ruled.length === 0 && lineGrids.length === 0) ruled.push(...detectTextBoxTables(extracted.hiddenBoxes, items, pageNum));
6819
7335
  if (ruled.length > 0) {
6820
7336
  const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
6821
- return extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions, lex, ruled, rawRules);
7337
+ return extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions, lex, ruled, rawRules, verifiedRightArrows);
6822
7338
  }
6823
7339
  if (detectTables && stackedTableBands(items)) {
6824
7340
  return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
@@ -6829,7 +7345,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6829
7345
  }
6830
7346
  if (grids.length > 0) {
6831
7347
  const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
6832
- return extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions, lex, [], rawRules);
7348
+ return extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions, lex, [], rawRules, verifiedRightArrows);
6833
7349
  }
6834
7350
  return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
6835
7351
  }
@@ -6934,42 +7450,10 @@ function isSparseProseGrid(table) {
6934
7450
  const prose = filled.filter((t) => t.length >= 60 && /[.!?。]/.test(t)).reduce((n, t) => n + t.length, 0);
6935
7451
  return prose >= chars * 0.6;
6936
7452
  }
6937
- var FRAME_RECT_TOL = 1.5;
6938
- function takePendingNested(pending, cellBox, clipCell) {
6939
- const out = [];
6940
- for (let i = pending.length - 1; i >= 0; i--) {
6941
- const p = pending[i].parent;
6942
- if (pending[i].contained ? p.x1 >= cellBox.x1 - FRAME_RECT_TOL && p.x2 <= cellBox.x2 + FRAME_RECT_TOL && p.y1 >= cellBox.y1 - FRAME_RECT_TOL && p.y2 <= cellBox.y2 + FRAME_RECT_TOL : clipCell && Math.abs(p.x1 - cellBox.x1) <= FRAME_RECT_TOL && Math.abs(p.x2 - cellBox.x2) <= FRAME_RECT_TOL && Math.abs(p.y1 - cellBox.y1) <= FRAME_RECT_TOL && Math.abs(p.y2 - cellBox.y2) <= FRAME_RECT_TOL) {
6943
- out.push(pending[i].block);
6944
- pending.splice(i, 1);
6945
- }
6946
- }
6947
- return out;
6948
- }
6949
- function buildFrameCellBlocks(cellItems, nested, pageNum, wrap) {
6950
- const tables = [...nested].sort((a, b) => b.bbox.y + b.bbox.height - (a.bbox.y + a.bbox.height));
6951
- const blocks = [];
6952
- let rest = [...cellItems];
6953
- const pushParagraphs = (items) => {
6954
- if (items.length === 0) return;
6955
- for (const line of cleanCellText(cellTextToString(items, wrap)).split("\n")) {
6956
- const t = line.trim();
6957
- if (t) blocks.push({ type: "paragraph", text: t, pageNumber: pageNum });
6958
- }
6959
- };
6960
- for (const tb of tables) {
6961
- const bottom = tb.bbox.y;
6962
- pushParagraphs(rest.filter((it) => it.y >= bottom));
6963
- rest = rest.filter((it) => it.y < bottom);
6964
- blocks.push(tb);
6965
- }
6966
- pushParagraphs(rest);
6967
- const text = blocks.map((b) => b.type === "table" && b.table ? b.table.cells.flat().map((c) => c.text).filter(Boolean).join("\n") : b.text ?? "").filter(Boolean).join("\n");
6968
- return { blocks, text };
6969
- }
6970
- function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions = [], lex, ruled = [], rawRules = horizontals) {
7453
+ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, horizontals, verticals, imageRegions = [], lex, ruled = [], rawRules = horizontals, verifiedRightArrows = []) {
6971
7454
  const ocrPage = items.length > 0 && items.every((i) => i.fontName === "ocr");
6972
7455
  const blocks = [];
7456
+ const frameParagraphUnits = [];
6973
7457
  const usedItems = /* @__PURE__ */ new Set();
6974
7458
  for (const r of ruled) {
6975
7459
  for (const it of r.items) usedItems.add(it);
@@ -7026,7 +7510,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7026
7510
  const nested = pendingNested.length ? takePendingNested(pendingNested, cell.bbox, !!grid.cells) : [];
7027
7511
  if (nested.length > 0) {
7028
7512
  nestedAttached = true;
7029
- const built = buildFrameCellBlocks(cellItems, nested, pageNum, { box: cell.bbox, lex });
7513
+ const built = buildFrameCellBlocks(cellItems, nested, pageNum, lex);
7030
7514
  irGrid[cell.row][cell.col] = { text: built.text, colSpan: cell.colSpan, rowSpan: cell.rowSpan, blocks: built.blocks };
7031
7515
  if (cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
7032
7516
  takeClipCellEdges(cell, irGrid[cell.row][cell.col]);
@@ -7037,6 +7521,9 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7037
7521
  colSpan: cell.colSpan,
7038
7522
  rowSpan: cell.rowSpan
7039
7523
  };
7524
+ if (numRows === 1 && numCols === 1 && cellItems.length) {
7525
+ irGrid[cell.row][cell.col].blocks = buildFrameCellBlocks(cellItems, [], pageNum, lex).blocks;
7526
+ }
7040
7527
  const b = cell.bbox;
7041
7528
  const isBackdrop = (r) => r.x1 <= b.x1 + 1 && r.x2 >= b.x2 - 1 && r.y1 <= b.y1 + 1 && r.y2 >= b.y2 - 1;
7042
7529
  if (imageRegions.some((r) => {
@@ -7072,7 +7559,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7072
7559
  }
7073
7560
  if (!rebuiltUsed && attachUnitRow(items, grid, finalGrid, numCols, usedItems)) finalRows++;
7074
7561
  let semanticOneColumn = false;
7075
- if (!grid.cells && numCols === 1 && numGridRows >= 2) {
7562
+ if (!grid.cells && !nestedAttached && numCols === 1 && numGridRows >= 2) {
7076
7563
  const populatedRows = finalGrid.filter((row) => row[0]?.text.trim());
7077
7564
  if (populatedRows.length >= 4) {
7078
7565
  const fontSizes = tableItems.map((item) => item.fontSize).filter((size) => size > 0).sort((a, b) => a - b);
@@ -7125,13 +7612,15 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7125
7612
  recordRowRules(irTable, grid.cells, grid.bbox, rawRules);
7126
7613
  }
7127
7614
  TABLE_COLXS.set(irTable, grid.colXs);
7615
+ if (grid.cells) recordFrameTitle(irTable, tableItems, pageNum);
7128
7616
  if (grid.cells && finalRows === numRows) TABLE_ROWYS.set(irTable, grid.rowYs);
7129
7617
  if (grid.continues) CONT_PARTS.set(irTable, grid.continues);
7130
7618
  const hasContent = finalGrid.some((row) => row.some((cell) => cell.text.trim() !== ""));
7131
7619
  const emptyPart = !hasContent && !!grid.cells && !grid.clipParent && !nestedAttached;
7132
7620
  if (!hasContent && !emptyPart) continue;
7133
7621
  if (emptyPart) EMPTY_PARTS.add(irTable);
7134
- if (numCols === 1 && isSideTabTable(grid.bbox, irTable, items.filter((it) => !tableItems.includes(it)), pageWidth, pageHeight)) {
7622
+ const ownItems = numCols === 1 ? new Set(tableItems) : null;
7623
+ if (ownItems && isSideTabTable(grid.bbox, irTable, items.filter((it) => !ownItems.has(it)), pageWidth, pageHeight)) {
7135
7624
  SIDE_TAB_TABLES.add(irTable);
7136
7625
  blocks.push({ type: "table", table: irTable, pageNumber: pageNum, bbox: { page: pageNum, x: grid.bbox.x1, y: grid.bbox.y1, width: gridW, height: grid.bbox.y2 - grid.bbox.y1 } });
7137
7626
  continue;
@@ -7175,7 +7664,14 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7175
7664
  continue;
7176
7665
  }
7177
7666
  }
7178
- if (!grid.cells && shouldDemoteTable(irTable)) {
7667
+ if (!grid.cells && !nestedAttached && shouldDemoteTable(irTable)) {
7668
+ const paragraphs = numRows === 1 && numCols === 1 ? irTable.cells[0]?.[0]?.blocks : void 0;
7669
+ if (paragraphs?.length) {
7670
+ blocks.push(...paragraphs);
7671
+ frameParagraphUnits.push(paragraphs);
7672
+ recordFrameReadingUnit(paragraphs, tableBbox);
7673
+ continue;
7674
+ }
7179
7675
  const demoted = demoteTableToText(irTable);
7180
7676
  if (demoted) {
7181
7677
  const text = numGridRows === 1 ? "\n" + demoted + "\n" : demoted;
@@ -7229,9 +7725,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7229
7725
  }
7230
7726
  {
7231
7727
  const rects = remaining.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }));
7232
- for (const b of blocks) {
7233
- if (b.bbox && !(b.table && EMPTY_PARTS.has(b.table))) rects.push({ x: b.bbox.x, y: b.bbox.y, w: b.bbox.width, h: b.bbox.height });
7234
- }
7728
+ for (const b of frameLayoutBoxes(blocks.filter((b2) => !(b2.table && EMPTY_PARTS.has(b2.table))))) rects.push({ x: b.x, y: b.y, w: b.width, h: b.height });
7235
7729
  gutterX = detectColumnGutter(rects) ?? findRuledColumnDivider(
7236
7730
  blocks.filter((b) => b.type === "table" && b.bbox && b.table && !EMPTY_PARTS.has(b.table)).map((b) => ({ x: b.bbox.x, y: b.bbox.y, w: b.bbox.width, h: b.bbox.height })),
7237
7731
  horizontals,
@@ -7278,7 +7772,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7278
7772
  finalTextBlocks = detectListBlocks(textBlocks);
7279
7773
  }
7280
7774
  }
7281
- const units = blocks.map((b) => [b]);
7775
+ const units = groupFrameParagraphUnits(groupFlowBoxUnits(blocks, grids, horizontals, verticals, verifiedRightArrows), frameParagraphUnits);
7282
7776
  let off = 0;
7283
7777
  for (const size of groupSizes) {
7284
7778
  const unit = finalTextBlocks.slice(off, off + size);
@@ -7417,7 +7911,7 @@ function mergeAdjacentTableBlocks(blocks) {
7417
7911
  table: merged,
7418
7912
  bbox: { ...pb, y: cb.y, height: pb.y + pb.height - cb.y }
7419
7913
  };
7420
- } else if (prev.type === "table" && curr.type === "table" && prev.table && curr.table && prev.table.cols === curr.table.cols && prev.table.renderAsTable === curr.table.renderAsTable && !CLIP_TABLES.has(prev.table) && !CLIP_TABLES.has(curr.table)) {
7914
+ } else if (prev.type === "table" && curr.type === "table" && prev.table && curr.table && prev.table.cols === curr.table.cols && prev.table.renderAsTable === curr.table.renderAsTable && !CLIP_TABLES.has(prev.table) && !CLIP_TABLES.has(curr.table) && !separateRepeatedHeadings(prev, curr)) {
7421
7915
  const merged = {
7422
7916
  rows: prev.table.rows + curr.table.rows,
7423
7917
  cols: prev.table.cols,
@@ -7432,6 +7926,12 @@ function mergeAdjacentTableBlocks(blocks) {
7432
7926
  }
7433
7927
  return result;
7434
7928
  }
7929
+ function separateRepeatedHeadings(upper, lower) {
7930
+ const u = upper.table, d = lower.table, ub = upper.bbox, db = lower.bbox;
7931
+ if (!ub || !db || ub.page !== db.page || u.rows < 2 || d.rows < 2 || !u.hasHeader || !d.hasHeader || ub.y - (db.y + db.height) <= 1 || Math.abs(ub.x - db.x) > 1 || Math.abs(ub.width - db.width) > 1) return false;
7932
+ const a = u.cells[0], b = d.cells[0], norm = (text) => text.replace(/<\/?u>|~~|\s+/g, "");
7933
+ return a.filter((cell) => norm(cell.text)).length >= 2 && a.length === b.length && a.every((cell, c) => cell.colSpan === b[c].colSpan && cell.rowSpan === b[c].rowSpan && norm(cell.text) === norm(b[c].text));
7934
+ }
7435
7935
  function streamLines(items) {
7436
7936
  if (items.some((i) => i.seq === void 0)) return groupByY([...items].sort((a, b) => b.y - a.y || a.x - b.x));
7437
7937
  const lines = [];
@@ -7609,7 +8109,11 @@ export {
7609
8109
  extractImageRegions,
7610
8110
  filterHiddenText,
7611
8111
  collapseEvenSpacing,
8112
+ computeBBox,
8113
+ dominantStyle,
7612
8114
  normalizeItems,
8115
+ groupByY,
8116
+ mergeLineSimple,
7613
8117
  WrapLexicon,
7614
8118
  startsNewItem,
7615
8119
  wrapJoiner,
@@ -7630,6 +8134,7 @@ export {
7630
8134
  detectKoreanListBlocks,
7631
8135
  removeHeaderFooterBlocks,
7632
8136
  CLIP_TABLES,
8137
+ FRAME_TITLE_BLOCKS,
7633
8138
  TABLE_COLXS,
7634
8139
  TABLE_ROWYS,
7635
8140
  TABLE_TAIL,
@@ -7643,7 +8148,11 @@ export {
7643
8148
  trimTrailingEmptyTableCols,
7644
8149
  NO_EDGES,
7645
8150
  joinCellEdges,
8151
+ FRAME_READING_UNITS,
8152
+ recordFrameReadingUnit,
8153
+ restoreImageBullets,
8154
+ detectRightArrowRegions,
7646
8155
  removeSideTabs,
7647
8156
  extractPageBlocksWithLines
7648
8157
  };
7649
- //# sourceMappingURL=chunk-OGAYGUJF.js.map
8158
+ //# sourceMappingURL=chunk-KUUSP6OS.js.map