kordoc 4.18.3 → 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 (145) hide show
  1. package/dist/{-WOKB25R2.js → -DDAVBMHD.js} +11 -11
  2. package/dist/{chunk-BGOR5GCF.js → chunk-274ZPMHK.js} +2 -2
  3. package/dist/{chunk-2ZBLQVAS.cjs → chunk-3WLZOJIE.cjs} +6 -6
  4. package/dist/chunk-3WLZOJIE.cjs.map +1 -0
  5. package/dist/{chunk-SHRRD2TD.cjs → chunk-3XSP7V5P.cjs} +2 -2
  6. package/dist/chunk-3XSP7V5P.cjs.map +1 -0
  7. package/dist/{chunk-6GLTGOP6.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-CAN5P6N6.js → chunk-7GR2GCJF.js} +2 -2
  11. package/dist/{chunk-HZV52V2X.cjs → chunk-AVIZ55NQ.cjs} +3 -3
  12. package/dist/chunk-AVIZ55NQ.cjs.map +1 -0
  13. package/dist/chunk-DC6CTQTK.js +9 -0
  14. package/dist/chunk-DC6CTQTK.js.map +1 -0
  15. package/dist/{chunk-NES5U4JK.js → chunk-DUV7Y6IQ.js} +3 -3
  16. package/dist/{chunk-WN5YLP7H.cjs → chunk-E2644XCO.cjs} +693 -187
  17. package/dist/chunk-E2644XCO.cjs.map +1 -0
  18. package/dist/{chunk-3FZGL3IL.js → chunk-ELOPDQES.js} +2 -2
  19. package/dist/{chunk-2I2RTJJ5.js → chunk-F2GDLA45.js} +25 -35
  20. package/dist/chunk-F2GDLA45.js.map +1 -0
  21. package/dist/{chunk-VGDTONTB.js → chunk-F5YVCJVF.js} +2 -2
  22. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  23. package/dist/{chunk-RDBTOE5X.js → chunk-HRJJDNRD.js} +2 -2
  24. package/dist/{chunk-KSJ7GKNQ.js → chunk-IAXGGQGD.js} +2 -2
  25. package/dist/{chunk-IXX3WHIE.js → chunk-IBQCK4F3.js} +7 -3
  26. package/dist/chunk-IBQCK4F3.js.map +1 -0
  27. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  28. package/dist/{chunk-J2A46VCA.js → chunk-KUUSP6OS.js} +576 -70
  29. package/dist/chunk-KUUSP6OS.js.map +1 -0
  30. package/dist/{chunk-RSDUH4XF.js → chunk-LGF2IO66.js} +2 -2
  31. package/dist/{chunk-BJ6RYYXV.js → chunk-MXA6YW2W.js} +577 -71
  32. package/dist/chunk-MXA6YW2W.js.map +1 -0
  33. package/dist/{chunk-ZD5WNQNC.cjs → chunk-OIUSF2VA.cjs} +16 -12
  34. package/dist/chunk-OIUSF2VA.cjs.map +1 -0
  35. package/dist/{chunk-AZQWESRH.js → chunk-QVCHFCT2.js} +2 -2
  36. package/dist/{chunk-W7P4GPOV.js → chunk-RCI7LY4N.js} +8 -4
  37. package/dist/chunk-RCI7LY4N.js.map +1 -0
  38. package/dist/{chunk-V4BKT65D.cjs → chunk-SVNBZEL6.cjs} +3 -3
  39. package/dist/chunk-SVNBZEL6.cjs.map +1 -0
  40. package/dist/{chunk-OOX2GZZ4.js → chunk-T2BJPT6D.js} +3 -3
  41. package/dist/{chunk-OOX2GZZ4.js.map → chunk-T2BJPT6D.js.map} +1 -1
  42. package/dist/chunk-TK3FDAEE.cjs +9 -0
  43. package/dist/chunk-TK3FDAEE.cjs.map +1 -0
  44. package/dist/{chunk-UVUKT3IQ.js → chunk-TMJVD6FW.js} +2 -2
  45. package/dist/{chunk-ZN3LHNJG.js → chunk-V5X4LLVM.js} +28 -27
  46. package/dist/chunk-V5X4LLVM.js.map +1 -0
  47. package/dist/{chunk-W3RADOLO.js → chunk-VTVK2JMI.js} +2 -2
  48. package/dist/{chunk-PSJLB3SZ.js → chunk-XG4SYOZL.js} +2 -2
  49. package/dist/{chunk-RRURZDEK.js → chunk-YCY2R4FM.js} +2 -2
  50. package/dist/{chunk-5X7XWYR2.js → chunk-Z2PPDQK2.js} +2 -2
  51. package/dist/chunks-5QTTAT5T.js +10 -0
  52. package/dist/cli.js +154 -115
  53. package/dist/cli.js.map +1 -1
  54. package/dist/{formula-QZAOKPBX.js → formula-7ZOZ5QJ6.js} +8 -4
  55. package/dist/{formula-QZAOKPBX.js.map → formula-7ZOZ5QJ6.js.map} +1 -1
  56. package/dist/{formula-GPCOJJXN.js → formula-HQFZEXFF.js} +7 -3
  57. package/dist/{formula-GPCOJJXN.js.map → formula-HQFZEXFF.js.map} +1 -1
  58. package/dist/{formula-BQ5RW5XD.cjs → formula-YYJQBKEN.cjs} +13 -9
  59. package/dist/formula-YYJQBKEN.cjs.map +1 -0
  60. package/dist/{image-ocr-POCME5I4.js → image-ocr-4BGX2EJJ.js} +7 -6
  61. package/dist/{image-ocr-POCME5I4.js.map → image-ocr-4BGX2EJJ.js.map} +1 -1
  62. package/dist/{image-ocr-VKIUBLBX.cjs → image-ocr-CC26Q5KG.cjs} +16 -15
  63. package/dist/image-ocr-CC26Q5KG.cjs.map +1 -0
  64. package/dist/{image-ocr-TWNNVTZS.js → image-ocr-RK3HJVCI.js} +10 -9
  65. package/dist/{image-ocr-TWNNVTZS.js.map → image-ocr-RK3HJVCI.js.map} +1 -1
  66. package/dist/index.cjs +338 -347
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.js +42 -51
  69. package/dist/index.js.map +1 -1
  70. package/dist/mcp.js +22 -22
  71. package/dist/{model-bundle-THYUL7GG.js → model-bundle-VHRXG63R.js} +5 -5
  72. package/dist/{models-U22N4RXX.js → models-G3WA24SY.js} +5 -5
  73. package/dist/page-range-QHHFTL7X.cjs.map +1 -1
  74. package/dist/{parser-JOMFOZLH.cjs → parser-BGORCLIU.cjs} +449 -244
  75. package/dist/parser-BGORCLIU.cjs.map +1 -0
  76. package/dist/{parser-SCXRLOW7.js → parser-GFDGAVLV.js} +242 -37
  77. package/dist/parser-GFDGAVLV.js.map +1 -0
  78. package/dist/{parser-NKP2NCSV.js → parser-SOWTUOMK.js} +245 -40
  79. package/dist/parser-SOWTUOMK.js.map +1 -0
  80. package/dist/pdf-ocr-7IOOVR2I.js +20 -0
  81. package/dist/pdf-ocr-EJ2HNZN6.cjs +17 -0
  82. package/dist/pdf-ocr-EJ2HNZN6.cjs.map +1 -0
  83. package/dist/pdf-ocr-RACAZU3N.js +16 -0
  84. package/dist/{profile-io-UI3TZBYH.js → profile-io-Q2T47RVH.js} +3 -3
  85. package/dist/{rasterize-XMBV5H2I.js → rasterize-OAVMUTE6.js} +3 -3
  86. package/dist/{redact-doc-7ZZMHDBO.js → redact-doc-ECFOVR4X.js} +11 -11
  87. package/dist/{render-SWRTUDAV.js → render-NSKWZVTD.js} +7 -7
  88. package/dist/seal-A4MG7MPQ.js +10 -0
  89. package/dist/{setup-43JJEEU7.js → setup-REH2KZP2.js} +3 -3
  90. package/dist/visual-B56WQAGW.js +20 -0
  91. package/dist/{watch-NRATQ472.js → watch-JXDUZYBY.js} +12 -12
  92. package/package.json +1 -1
  93. package/dist/chunk-2I2RTJJ5.js.map +0 -1
  94. package/dist/chunk-2ZBLQVAS.cjs.map +0 -1
  95. package/dist/chunk-BJ6RYYXV.js.map +0 -1
  96. package/dist/chunk-HZV52V2X.cjs.map +0 -1
  97. package/dist/chunk-IXX3WHIE.js.map +0 -1
  98. package/dist/chunk-J2A46VCA.js.map +0 -1
  99. package/dist/chunk-SHRRD2TD.cjs.map +0 -1
  100. package/dist/chunk-V4BKT65D.cjs.map +0 -1
  101. package/dist/chunk-W7P4GPOV.js.map +0 -1
  102. package/dist/chunk-WN5YLP7H.cjs.map +0 -1
  103. package/dist/chunk-ZD5WNQNC.cjs.map +0 -1
  104. package/dist/chunk-ZN3LHNJG.js.map +0 -1
  105. package/dist/chunks-OQDRDICD.js +0 -10
  106. package/dist/formula-BQ5RW5XD.cjs.map +0 -1
  107. package/dist/image-ocr-VKIUBLBX.cjs.map +0 -1
  108. package/dist/parser-JOMFOZLH.cjs.map +0 -1
  109. package/dist/parser-NKP2NCSV.js.map +0 -1
  110. package/dist/parser-SCXRLOW7.js.map +0 -1
  111. package/dist/pdf-ocr-NF4AY5MI.cjs +0 -16
  112. package/dist/pdf-ocr-NF4AY5MI.cjs.map +0 -1
  113. package/dist/pdf-ocr-OLII3WAG.js +0 -15
  114. package/dist/pdf-ocr-VJL7ON2Q.js +0 -19
  115. package/dist/seal-UKKJZHKE.js +0 -10
  116. package/dist/visual-D2563HCJ.js +0 -20
  117. /package/dist/{-WOKB25R2.js.map → -DDAVBMHD.js.map} +0 -0
  118. /package/dist/{chunk-BGOR5GCF.js.map → chunk-274ZPMHK.js.map} +0 -0
  119. /package/dist/{chunk-6GLTGOP6.js.map → chunk-4IBY5LTJ.js.map} +0 -0
  120. /package/dist/{chunk-CAN5P6N6.js.map → chunk-7GR2GCJF.js.map} +0 -0
  121. /package/dist/{chunk-NES5U4JK.js.map → chunk-DUV7Y6IQ.js.map} +0 -0
  122. /package/dist/{chunk-3FZGL3IL.js.map → chunk-ELOPDQES.js.map} +0 -0
  123. /package/dist/{chunk-VGDTONTB.js.map → chunk-F5YVCJVF.js.map} +0 -0
  124. /package/dist/{chunk-RDBTOE5X.js.map → chunk-HRJJDNRD.js.map} +0 -0
  125. /package/dist/{chunk-KSJ7GKNQ.js.map → chunk-IAXGGQGD.js.map} +0 -0
  126. /package/dist/{chunk-RSDUH4XF.js.map → chunk-LGF2IO66.js.map} +0 -0
  127. /package/dist/{chunk-AZQWESRH.js.map → chunk-QVCHFCT2.js.map} +0 -0
  128. /package/dist/{chunk-UVUKT3IQ.js.map → chunk-TMJVD6FW.js.map} +0 -0
  129. /package/dist/{chunk-W3RADOLO.js.map → chunk-VTVK2JMI.js.map} +0 -0
  130. /package/dist/{chunk-PSJLB3SZ.js.map → chunk-XG4SYOZL.js.map} +0 -0
  131. /package/dist/{chunk-RRURZDEK.js.map → chunk-YCY2R4FM.js.map} +0 -0
  132. /package/dist/{chunk-5X7XWYR2.js.map → chunk-Z2PPDQK2.js.map} +0 -0
  133. /package/dist/{chunks-OQDRDICD.js.map → chunks-5QTTAT5T.js.map} +0 -0
  134. /package/dist/{model-bundle-THYUL7GG.js.map → model-bundle-VHRXG63R.js.map} +0 -0
  135. /package/dist/{models-U22N4RXX.js.map → models-G3WA24SY.js.map} +0 -0
  136. /package/dist/{pdf-ocr-OLII3WAG.js.map → pdf-ocr-7IOOVR2I.js.map} +0 -0
  137. /package/dist/{pdf-ocr-VJL7ON2Q.js.map → pdf-ocr-RACAZU3N.js.map} +0 -0
  138. /package/dist/{profile-io-UI3TZBYH.js.map → profile-io-Q2T47RVH.js.map} +0 -0
  139. /package/dist/{rasterize-XMBV5H2I.js.map → rasterize-OAVMUTE6.js.map} +0 -0
  140. /package/dist/{redact-doc-7ZZMHDBO.js.map → redact-doc-ECFOVR4X.js.map} +0 -0
  141. /package/dist/{render-SWRTUDAV.js.map → render-NSKWZVTD.js.map} +0 -0
  142. /package/dist/{seal-UKKJZHKE.js.map → seal-A4MG7MPQ.js.map} +0 -0
  143. /package/dist/{setup-43JJEEU7.js.map → setup-REH2KZP2.js.map} +0 -0
  144. /package/dist/{visual-D2563HCJ.js.map → visual-B56WQAGW.js.map} +0 -0
  145. /package/dist/{watch-NRATQ472.js.map → watch-JXDUZYBY.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-UVUKT3IQ.js";
8
+ } from "./chunk-TMJVD6FW.js";
9
9
  import {
10
10
  safeMax,
11
11
  safeMin
12
- } from "./chunk-VGDTONTB.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,15 +4825,39 @@ 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;
4566
4831
  if (col.filter((g) => /[\p{L}\p{N}]/u.test(g.text)).length < col.length * 0.8) continue;
4567
4832
  col.sort((a, b) => b.y - a.y);
4568
4833
  const fs = [...col.map((i) => i.fontSize)].sort((a, b) => a - b)[col.length >> 1];
4569
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);
4570
4857
  const members = new Set(col);
4571
4858
  const top = col[0].y + fs, bottom = col[col.length - 1].y - fs;
4572
- 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;
4573
4861
  const left = Math.min(...col.map((i) => i.x)), right = Math.max(...col.map((i) => i.x + i.w));
4574
4862
  const rest = items.filter((i) => !members.has(i) && i.text.trim());
4575
4863
  const outside = rest.every((i) => i.x >= right + fs) || rest.every((i) => i.x + i.w <= left - fs);
@@ -5830,13 +6118,44 @@ var UNDER_SPAN_OVERLAP_RATIO = 0.8;
5830
6118
  var UNDER_BOX_PAIR_MIN_EM = 0.5;
5831
6119
  var UNDER_BOX_PAIR_MAX_EM = 2.2;
5832
6120
  var UNDER_BOX_PAIR_OVERLAP = 0.8;
5833
- function markUnderlineItems(items, horizontals, verticals) {
6121
+ function markUnderlineItems(items, horizontals, verticals, nonRules) {
5834
6122
  const underlines = [];
6123
+ if (nonRules?.size) horizontals = horizontals.filter((line) => !nonRules.has(line));
5835
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
+ };
5836
6153
  for (const line of horizontals) {
5837
6154
  if (line.lineWidth > UNDER_MAX_THICKNESS) continue;
5838
6155
  const enclosed = insideTallCell(line, verticals, 0);
5839
- 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;
5840
6159
  const matches = [];
5841
6160
  for (const item of items) {
5842
6161
  const h = item.h > 0 ? item.h : item.fontSize;
@@ -5856,11 +6175,11 @@ function markUnderlineItems(items, horizontals, verticals) {
5856
6175
  covered += m.w;
5857
6176
  }
5858
6177
  const inset = enclosed && insideTallCell(line, verticals, maxH);
5859
- if (enclosed && !inset && (touchesVertical(line, verticals) || isRepeatedSpanRule(line, horizontals))) continue;
6178
+ if (enclosed && !inset && (touchesVertical(line, verticals) || repeated && !textSeries)) continue;
5860
6179
  const pad = Math.max(maxH * UNDER_OWNER_PAD_EM, UNDER_OWNER_PAD_MIN_PT);
5861
6180
  if (line.x1 < x1 - pad || line.x2 > x2 + pad) continue;
5862
6181
  if (covered < (line.x2 - line.x1) * UNDER_MIN_COVERAGE) continue;
5863
- if (hasBoxTopPair(line, maxH, horizontals, inset ? items : void 0)) continue;
6182
+ if (hasBoxTopPair(line, maxH, horizontals, inset || textSeries || tightlyOwned(line) ? items : void 0)) continue;
5864
6183
  matches.sort((a, b) => a.x - b.x);
5865
6184
  let hole = false;
5866
6185
  for (let i = 1; i < matches.length; i++) {
@@ -5891,6 +6210,7 @@ function touchesVertical(line, verticals) {
5891
6210
  for (const v of verticals) {
5892
6211
  if (v.x1 < line.x1 - UNDER_GRID_EPS || v.x1 > line.x2 + UNDER_GRID_EPS) continue;
5893
6212
  const lo = Math.min(v.y1, v.y2), hi = Math.max(v.y1, v.y2);
6213
+ if (hi - lo <= UNDER_GRID_EPS) continue;
5894
6214
  if (lo <= line.y1 + UNDER_GRID_EPS && hi >= line.y1 - UNDER_GRID_EPS) return true;
5895
6215
  }
5896
6216
  return false;
@@ -6100,6 +6420,57 @@ function isPageFrameGrid(grid, cells, pageWidth, pageHeight, items = []) {
6100
6420
  return inner.length === 0 || crossing >= 1 && crossing >= inBig.length * 0.01;
6101
6421
  }
6102
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
+
6103
6474
  // src/pdf/open-table-ends.ts
6104
6475
  var ALIGN_TOL = 3;
6105
6476
  var MIN_RULES = 3;
@@ -6435,6 +6806,140 @@ function detectTextBoxTables(boxes, items, pageNum) {
6435
6806
  }];
6436
6807
  }
6437
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
+
6438
6943
  // src/pdf/page-regions.ts
6439
6944
  function splitTwoColumnProse(items, cutX) {
6440
6945
  const left = [];
@@ -6762,7 +7267,7 @@ function figureColumnBands(items, figures) {
6762
7267
  }
6763
7268
 
6764
7269
  // src/pdf/page-blocks.ts
6765
- 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 = []) {
6766
7271
  if (items.length === 0) {
6767
7272
  if (carry) carry.clip = void 0;
6768
7273
  return [];
@@ -6770,7 +7275,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6770
7275
  const tab = sideTabGlyphs(items);
6771
7276
  if (tab.size) {
6772
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]) }));
6773
- 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)];
6774
7279
  }
6775
7280
  const lex = lexicon ?? new WrapLexicon();
6776
7281
  for (const line of streamLines(items)) lex.addLine(mergeLineSimple(line));
@@ -6798,19 +7303,28 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6798
7303
  }
6799
7304
  ;
6800
7305
  ({ horizontals, verticals } = filterPageBorderLines(horizontals, verticals, pageWidth, pageHeight));
6801
- ({ 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);
6802
7312
  horizontals = closeOpenTableEnds(horizontals, verticals, items.every((it) => it.seq !== void 0));
6803
7313
  verticals = closeOpenTableEdges(horizontals, verticals);
6804
7314
  verticals = bridgeSplitColumnVerticals(horizontals, verticals);
6805
7315
  if (detectTables && clipGrids.length === 0) verticals = bridgeSkippedRowVerticals(horizontals, verticals, items);
6806
7316
  if (detectTables && clipGrids.length === 0) ({ horizontals, verticals } = extendHeaderBoxRows(horizontals, verticals, items));
6807
- markStrikethroughItems(items, horizontals);
6808
- wrapStrikethroughRuns(items);
6809
- const underlines = new Set(markUnderlineItems(items, horizontals, verticals));
6810
- if (underlines.size) horizontals = horizontals.filter((l) => !underlines.has(l));
6811
- wrapUnderlineRuns(items);
7317
+ const shadedEdges = detectTables ? closeShadedTableEdges(clipGrids, horizontals, verticals, extracted.fillRects) : void 0;
7318
+ if (shadedEdges) ({ horizontals, verticals } = shadedEdges);
6812
7319
  const lineGrids = detectTables ? buildTableGrids(horizontals, verticals) : [];
7320
+ if (shadedEdges) nestRestoredShadedGrids(lineGrids, shadedEdges.restored, horizontals, verticals);
6813
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
+ );
6814
7328
  const grids = [...tableClipGrids, ...dropGridsInside(lineGrids, tableClipGrids, clipResult.containers)];
6815
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 }));
6816
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) {
@@ -6820,7 +7334,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6820
7334
  if (detectTables && clipGrids.length === 0 && ruled.length === 0 && lineGrids.length === 0) ruled.push(...detectTextBoxTables(extracted.hiddenBoxes, items, pageNum));
6821
7335
  if (ruled.length > 0) {
6822
7336
  const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
6823
- 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);
6824
7338
  }
6825
7339
  if (detectTables && stackedTableBands(items)) {
6826
7340
  return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
@@ -6831,7 +7345,7 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
6831
7345
  }
6832
7346
  if (grids.length > 0) {
6833
7347
  const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
6834
- 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);
6835
7349
  }
6836
7350
  return extractPageBlocksFallback(items, pageNum, true, detectTables, lex, figures());
6837
7351
  }
@@ -6936,42 +7450,10 @@ function isSparseProseGrid(table) {
6936
7450
  const prose = filled.filter((t) => t.length >= 60 && /[.!?。]/.test(t)).reduce((n, t) => n + t.length, 0);
6937
7451
  return prose >= chars * 0.6;
6938
7452
  }
6939
- var FRAME_RECT_TOL = 1.5;
6940
- function takePendingNested(pending, cellBox, clipCell) {
6941
- const out = [];
6942
- for (let i = pending.length - 1; i >= 0; i--) {
6943
- const p = pending[i].parent;
6944
- 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) {
6945
- out.push(pending[i].block);
6946
- pending.splice(i, 1);
6947
- }
6948
- }
6949
- return out;
6950
- }
6951
- function buildFrameCellBlocks(cellItems, nested, pageNum, wrap) {
6952
- const tables = [...nested].sort((a, b) => b.bbox.y + b.bbox.height - (a.bbox.y + a.bbox.height));
6953
- const blocks = [];
6954
- let rest = [...cellItems];
6955
- const pushParagraphs = (items) => {
6956
- if (items.length === 0) return;
6957
- for (const line of cleanCellText(cellTextToString(items, wrap)).split("\n")) {
6958
- const t = line.trim();
6959
- if (t) blocks.push({ type: "paragraph", text: t, pageNumber: pageNum });
6960
- }
6961
- };
6962
- for (const tb of tables) {
6963
- const bottom = tb.bbox.y;
6964
- pushParagraphs(rest.filter((it) => it.y >= bottom));
6965
- rest = rest.filter((it) => it.y < bottom);
6966
- blocks.push(tb);
6967
- }
6968
- pushParagraphs(rest);
6969
- 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");
6970
- return { blocks, text };
6971
- }
6972
- 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 = []) {
6973
7454
  const ocrPage = items.length > 0 && items.every((i) => i.fontName === "ocr");
6974
7455
  const blocks = [];
7456
+ const frameParagraphUnits = [];
6975
7457
  const usedItems = /* @__PURE__ */ new Set();
6976
7458
  for (const r of ruled) {
6977
7459
  for (const it of r.items) usedItems.add(it);
@@ -7028,7 +7510,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7028
7510
  const nested = pendingNested.length ? takePendingNested(pendingNested, cell.bbox, !!grid.cells) : [];
7029
7511
  if (nested.length > 0) {
7030
7512
  nestedAttached = true;
7031
- const built = buildFrameCellBlocks(cellItems, nested, pageNum, { box: cell.bbox, lex });
7513
+ const built = buildFrameCellBlocks(cellItems, nested, pageNum, lex);
7032
7514
  irGrid[cell.row][cell.col] = { text: built.text, colSpan: cell.colSpan, rowSpan: cell.rowSpan, blocks: built.blocks };
7033
7515
  if (cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
7034
7516
  takeClipCellEdges(cell, irGrid[cell.row][cell.col]);
@@ -7039,6 +7521,9 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7039
7521
  colSpan: cell.colSpan,
7040
7522
  rowSpan: cell.rowSpan
7041
7523
  };
7524
+ if (numRows === 1 && numCols === 1 && cellItems.length) {
7525
+ irGrid[cell.row][cell.col].blocks = buildFrameCellBlocks(cellItems, [], pageNum, lex).blocks;
7526
+ }
7042
7527
  const b = cell.bbox;
7043
7528
  const isBackdrop = (r) => r.x1 <= b.x1 + 1 && r.x2 >= b.x2 - 1 && r.y1 <= b.y1 + 1 && r.y2 >= b.y2 - 1;
7044
7529
  if (imageRegions.some((r) => {
@@ -7074,7 +7559,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7074
7559
  }
7075
7560
  if (!rebuiltUsed && attachUnitRow(items, grid, finalGrid, numCols, usedItems)) finalRows++;
7076
7561
  let semanticOneColumn = false;
7077
- if (!grid.cells && numCols === 1 && numGridRows >= 2) {
7562
+ if (!grid.cells && !nestedAttached && numCols === 1 && numGridRows >= 2) {
7078
7563
  const populatedRows = finalGrid.filter((row) => row[0]?.text.trim());
7079
7564
  if (populatedRows.length >= 4) {
7080
7565
  const fontSizes = tableItems.map((item) => item.fontSize).filter((size) => size > 0).sort((a, b) => a - b);
@@ -7127,6 +7612,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7127
7612
  recordRowRules(irTable, grid.cells, grid.bbox, rawRules);
7128
7613
  }
7129
7614
  TABLE_COLXS.set(irTable, grid.colXs);
7615
+ if (grid.cells) recordFrameTitle(irTable, tableItems, pageNum);
7130
7616
  if (grid.cells && finalRows === numRows) TABLE_ROWYS.set(irTable, grid.rowYs);
7131
7617
  if (grid.continues) CONT_PARTS.set(irTable, grid.continues);
7132
7618
  const hasContent = finalGrid.some((row) => row.some((cell) => cell.text.trim() !== ""));
@@ -7178,7 +7664,14 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7178
7664
  continue;
7179
7665
  }
7180
7666
  }
7181
- 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
+ }
7182
7675
  const demoted = demoteTableToText(irTable);
7183
7676
  if (demoted) {
7184
7677
  const text = numGridRows === 1 ? "\n" + demoted + "\n" : demoted;
@@ -7232,9 +7725,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7232
7725
  }
7233
7726
  {
7234
7727
  const rects = remaining.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }));
7235
- for (const b of blocks) {
7236
- 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 });
7237
- }
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 });
7238
7729
  gutterX = detectColumnGutter(rects) ?? findRuledColumnDivider(
7239
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 })),
7240
7731
  horizontals,
@@ -7281,7 +7772,7 @@ function extractBlocksWithGrids(items, pageNum, pageWidth, pageHeight, grids, ho
7281
7772
  finalTextBlocks = detectListBlocks(textBlocks);
7282
7773
  }
7283
7774
  }
7284
- const units = blocks.map((b) => [b]);
7775
+ const units = groupFrameParagraphUnits(groupFlowBoxUnits(blocks, grids, horizontals, verticals, verifiedRightArrows), frameParagraphUnits);
7285
7776
  let off = 0;
7286
7777
  for (const size of groupSizes) {
7287
7778
  const unit = finalTextBlocks.slice(off, off + size);
@@ -7420,7 +7911,7 @@ function mergeAdjacentTableBlocks(blocks) {
7420
7911
  table: merged,
7421
7912
  bbox: { ...pb, y: cb.y, height: pb.y + pb.height - cb.y }
7422
7913
  };
7423
- } 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)) {
7424
7915
  const merged = {
7425
7916
  rows: prev.table.rows + curr.table.rows,
7426
7917
  cols: prev.table.cols,
@@ -7435,6 +7926,12 @@ function mergeAdjacentTableBlocks(blocks) {
7435
7926
  }
7436
7927
  return result;
7437
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
+ }
7438
7935
  function streamLines(items) {
7439
7936
  if (items.some((i) => i.seq === void 0)) return groupByY([...items].sort((a, b) => b.y - a.y || a.x - b.x));
7440
7937
  const lines = [];
@@ -7612,7 +8109,11 @@ export {
7612
8109
  extractImageRegions,
7613
8110
  filterHiddenText,
7614
8111
  collapseEvenSpacing,
8112
+ computeBBox,
8113
+ dominantStyle,
7615
8114
  normalizeItems,
8115
+ groupByY,
8116
+ mergeLineSimple,
7616
8117
  WrapLexicon,
7617
8118
  startsNewItem,
7618
8119
  wrapJoiner,
@@ -7633,6 +8134,7 @@ export {
7633
8134
  detectKoreanListBlocks,
7634
8135
  removeHeaderFooterBlocks,
7635
8136
  CLIP_TABLES,
8137
+ FRAME_TITLE_BLOCKS,
7636
8138
  TABLE_COLXS,
7637
8139
  TABLE_ROWYS,
7638
8140
  TABLE_TAIL,
@@ -7646,7 +8148,11 @@ export {
7646
8148
  trimTrailingEmptyTableCols,
7647
8149
  NO_EDGES,
7648
8150
  joinCellEdges,
8151
+ FRAME_READING_UNITS,
8152
+ recordFrameReadingUnit,
8153
+ restoreImageBullets,
8154
+ detectRightArrowRegions,
7649
8155
  removeSideTabs,
7650
8156
  extractPageBlocksWithLines
7651
8157
  };
7652
- //# sourceMappingURL=chunk-J2A46VCA.js.map
8158
+ //# sourceMappingURL=chunk-KUUSP6OS.js.map