kordoc 4.15.2 → 4.15.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 (132) hide show
  1. package/README.md +8 -0
  2. package/dist/{-5HJYDZKM.js → -D6YMPGTL.js} +10 -10
  3. package/dist/{chunk-GYZCXVJX.js → chunk-2UV6BAIF.js} +2 -2
  4. package/dist/{chunk-B4LHTFLY.cjs → chunk-5GZ46MZC.cjs} +6 -6
  5. package/dist/chunk-5GZ46MZC.cjs.map +1 -0
  6. package/dist/{chunk-TCMEK4LF.js → chunk-6SRFRNYO.js} +5 -5
  7. package/dist/{chunk-MJHUOAAI.js → chunk-7DCD22JJ.js} +2 -2
  8. package/dist/{chunk-WQWWVZ6Y.js → chunk-7LTOY7WS.js} +3 -3
  9. package/dist/{chunk-2WERADM4.js → chunk-CY2BMAZ3.js} +9 -9
  10. package/dist/{chunk-GFBLDBL2.js → chunk-H3G7TK5V.js} +2 -2
  11. package/dist/{chunk-GS7T56RP.cjs → chunk-H4Q6UCNU.cjs} +2 -2
  12. package/dist/chunk-H4Q6UCNU.cjs.map +1 -0
  13. package/dist/{chunk-VTUADXSS.js → chunk-IHVWE3C5.js} +2 -2
  14. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  15. package/dist/{chunk-MRPDMIMN.cjs → chunk-JHGTA4RV.cjs} +3 -3
  16. package/dist/chunk-JHGTA4RV.cjs.map +1 -0
  17. package/dist/{chunk-PTN7YG24.js → chunk-JO3MKOBE.js} +177 -13
  18. package/dist/chunk-JO3MKOBE.js.map +1 -0
  19. package/dist/{chunk-ZVN2H7XL.js → chunk-KLNW5H4Q.js} +3 -3
  20. package/dist/{chunk-VRZEPD4K.js → chunk-KR2KUR2L.js} +2 -2
  21. package/dist/{chunk-SX2KSBMO.js → chunk-KYS6DOIU.js} +2 -2
  22. package/dist/{chunk-KY6HUZZR.js → chunk-NK2T4NTO.js} +2 -2
  23. package/dist/chunk-PY2M7YVN.cjs.map +1 -1
  24. package/dist/{chunk-JPJNZXCC.js → chunk-QLLCAEQJ.js} +2 -2
  25. package/dist/{chunk-3Y26ICMC.cjs → chunk-RWDDVTBH.cjs} +6 -6
  26. package/dist/chunk-RWDDVTBH.cjs.map +1 -0
  27. package/dist/{chunk-NYNJP45T.js → chunk-S7HVJQ6Y.js} +177 -13
  28. package/dist/chunk-S7HVJQ6Y.js.map +1 -0
  29. package/dist/{chunk-AS66S3HA.js → chunk-SIQAT7KT.js} +3 -3
  30. package/dist/{chunk-VSI5SRP2.cjs → chunk-SRITXAMQ.cjs} +202 -38
  31. package/dist/chunk-SRITXAMQ.cjs.map +1 -0
  32. package/dist/{chunk-CODOAMAV.js → chunk-UBB7BAFA.js} +2 -2
  33. package/dist/{chunk-73K6BXEQ.js → chunk-UEBFU2U3.js} +2 -2
  34. package/dist/{chunk-VGU4FLLD.cjs → chunk-UUNEKQUU.cjs} +2 -2
  35. package/dist/chunk-UUNEKQUU.cjs.map +1 -0
  36. package/dist/{chunk-KKQBQY32.js → chunk-VFNMPG22.js} +2 -2
  37. package/dist/{chunk-GWTN34UT.js → chunk-YIFFNFEO.js} +2 -2
  38. package/dist/{chunk-N4WZZ2PR.js → chunk-YNAIAEOA.js} +2 -2
  39. package/dist/chunks-AGZD5STK.js +10 -0
  40. package/dist/cli.js +25 -25
  41. package/dist/{formula-D4PUT5J3.js → formula-CJSQ7UOL.js} +3 -3
  42. package/dist/{formula-DYVJSHWN.cjs → formula-CZLVSDDH.cjs} +10 -10
  43. package/dist/formula-CZLVSDDH.cjs.map +1 -0
  44. package/dist/{formula-QVHAUNK6.js → formula-ZMFDYMTE.js} +4 -4
  45. package/dist/{image-ocr-5BHKFZL6.js → image-ocr-BPYZH6CJ.js} +7 -7
  46. package/dist/{image-ocr-SGANCWQ5.cjs → image-ocr-RE27IJJL.cjs} +14 -14
  47. package/dist/image-ocr-RE27IJJL.cjs.map +1 -0
  48. package/dist/{image-ocr-TNNFV5VQ.js → image-ocr-YFRXHIMH.js} +5 -5
  49. package/dist/index.cjs +236 -236
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.js +4 -4
  52. package/dist/mcp.js +21 -21
  53. package/dist/{model-bundle-ISGAMXQS.js → model-bundle-5ZXMHOCZ.js} +5 -5
  54. package/dist/{models-LI5INKZM.js → models-2SR5HYEU.js} +5 -5
  55. package/dist/{page-range-QHHFTL7X.cjs → page-range-WO7ADTXZ.cjs} +2 -2
  56. package/dist/page-range-WO7ADTXZ.cjs.map +1 -0
  57. package/dist/{parser-GW3KRTXY.js → parser-5CPM5VTB.js} +31 -8
  58. package/dist/parser-5CPM5VTB.js.map +1 -0
  59. package/dist/{parser-EYJID4YX.js → parser-M77PHHCS.js} +30 -7
  60. package/dist/parser-M77PHHCS.js.map +1 -0
  61. package/dist/{parser-XCXDSKQ3.cjs → parser-OJ4GRFTO.cjs} +115 -92
  62. package/dist/parser-OJ4GRFTO.cjs.map +1 -0
  63. package/dist/pdf-ocr-N7ZSSU4B.js +15 -0
  64. package/dist/pdf-ocr-PZMP333N.cjs +16 -0
  65. package/dist/pdf-ocr-PZMP333N.cjs.map +1 -0
  66. package/dist/pdf-ocr-W4V5AT24.js +18 -0
  67. package/dist/{profile-io-DBN56Y2Z.js → profile-io-JNHSNLKB.js} +3 -3
  68. package/dist/{rasterize-BYRCKQIW.js → rasterize-3L6X4SMV.js} +3 -3
  69. package/dist/{redact-doc-UP2J6RRP.js → redact-doc-YALOOA37.js} +10 -10
  70. package/dist/{render-WGD5DN4G.js → render-Q26GOZ2V.js} +6 -6
  71. package/dist/seal-GY2J6TFW.js +10 -0
  72. package/dist/{setup-E5JILUAN.js → setup-U4OZBOVP.js} +3 -3
  73. package/dist/visual-5AUY4EQJ.js +20 -0
  74. package/dist/{watch-LKWPCU42.js → watch-NXLLPCQG.js} +11 -11
  75. package/package.json +1 -1
  76. package/dist/chunk-3Y26ICMC.cjs.map +0 -1
  77. package/dist/chunk-B4LHTFLY.cjs.map +0 -1
  78. package/dist/chunk-GS7T56RP.cjs.map +0 -1
  79. package/dist/chunk-MRPDMIMN.cjs.map +0 -1
  80. package/dist/chunk-NYNJP45T.js.map +0 -1
  81. package/dist/chunk-PTN7YG24.js.map +0 -1
  82. package/dist/chunk-VGU4FLLD.cjs.map +0 -1
  83. package/dist/chunk-VSI5SRP2.cjs.map +0 -1
  84. package/dist/chunks-C3AW42QH.js +0 -10
  85. package/dist/formula-DYVJSHWN.cjs.map +0 -1
  86. package/dist/image-ocr-SGANCWQ5.cjs.map +0 -1
  87. package/dist/page-range-QHHFTL7X.cjs.map +0 -1
  88. package/dist/parser-EYJID4YX.js.map +0 -1
  89. package/dist/parser-GW3KRTXY.js.map +0 -1
  90. package/dist/parser-XCXDSKQ3.cjs.map +0 -1
  91. package/dist/pdf-ocr-JCJAME6Q.js +0 -15
  92. package/dist/pdf-ocr-K7MJFRBS.cjs +0 -16
  93. package/dist/pdf-ocr-K7MJFRBS.cjs.map +0 -1
  94. package/dist/pdf-ocr-TC3BN2MV.js +0 -18
  95. package/dist/seal-5GAFTYQO.js +0 -10
  96. package/dist/visual-FKV2TRYY.js +0 -20
  97. /package/dist/{-5HJYDZKM.js.map → -D6YMPGTL.js.map} +0 -0
  98. /package/dist/{chunk-GYZCXVJX.js.map → chunk-2UV6BAIF.js.map} +0 -0
  99. /package/dist/{chunk-TCMEK4LF.js.map → chunk-6SRFRNYO.js.map} +0 -0
  100. /package/dist/{chunk-MJHUOAAI.js.map → chunk-7DCD22JJ.js.map} +0 -0
  101. /package/dist/{chunk-WQWWVZ6Y.js.map → chunk-7LTOY7WS.js.map} +0 -0
  102. /package/dist/{chunk-2WERADM4.js.map → chunk-CY2BMAZ3.js.map} +0 -0
  103. /package/dist/{chunk-GFBLDBL2.js.map → chunk-H3G7TK5V.js.map} +0 -0
  104. /package/dist/{chunk-VTUADXSS.js.map → chunk-IHVWE3C5.js.map} +0 -0
  105. /package/dist/{chunk-ZVN2H7XL.js.map → chunk-KLNW5H4Q.js.map} +0 -0
  106. /package/dist/{chunk-VRZEPD4K.js.map → chunk-KR2KUR2L.js.map} +0 -0
  107. /package/dist/{chunk-SX2KSBMO.js.map → chunk-KYS6DOIU.js.map} +0 -0
  108. /package/dist/{chunk-KY6HUZZR.js.map → chunk-NK2T4NTO.js.map} +0 -0
  109. /package/dist/{chunk-JPJNZXCC.js.map → chunk-QLLCAEQJ.js.map} +0 -0
  110. /package/dist/{chunk-AS66S3HA.js.map → chunk-SIQAT7KT.js.map} +0 -0
  111. /package/dist/{chunk-CODOAMAV.js.map → chunk-UBB7BAFA.js.map} +0 -0
  112. /package/dist/{chunk-73K6BXEQ.js.map → chunk-UEBFU2U3.js.map} +0 -0
  113. /package/dist/{chunk-KKQBQY32.js.map → chunk-VFNMPG22.js.map} +0 -0
  114. /package/dist/{chunk-GWTN34UT.js.map → chunk-YIFFNFEO.js.map} +0 -0
  115. /package/dist/{chunk-N4WZZ2PR.js.map → chunk-YNAIAEOA.js.map} +0 -0
  116. /package/dist/{chunks-C3AW42QH.js.map → chunks-AGZD5STK.js.map} +0 -0
  117. /package/dist/{formula-D4PUT5J3.js.map → formula-CJSQ7UOL.js.map} +0 -0
  118. /package/dist/{formula-QVHAUNK6.js.map → formula-ZMFDYMTE.js.map} +0 -0
  119. /package/dist/{image-ocr-5BHKFZL6.js.map → image-ocr-BPYZH6CJ.js.map} +0 -0
  120. /package/dist/{image-ocr-TNNFV5VQ.js.map → image-ocr-YFRXHIMH.js.map} +0 -0
  121. /package/dist/{model-bundle-ISGAMXQS.js.map → model-bundle-5ZXMHOCZ.js.map} +0 -0
  122. /package/dist/{models-LI5INKZM.js.map → models-2SR5HYEU.js.map} +0 -0
  123. /package/dist/{pdf-ocr-JCJAME6Q.js.map → pdf-ocr-N7ZSSU4B.js.map} +0 -0
  124. /package/dist/{pdf-ocr-TC3BN2MV.js.map → pdf-ocr-W4V5AT24.js.map} +0 -0
  125. /package/dist/{profile-io-DBN56Y2Z.js.map → profile-io-JNHSNLKB.js.map} +0 -0
  126. /package/dist/{rasterize-BYRCKQIW.js.map → rasterize-3L6X4SMV.js.map} +0 -0
  127. /package/dist/{redact-doc-UP2J6RRP.js.map → redact-doc-YALOOA37.js.map} +0 -0
  128. /package/dist/{render-WGD5DN4G.js.map → render-Q26GOZ2V.js.map} +0 -0
  129. /package/dist/{seal-5GAFTYQO.js.map → seal-GY2J6TFW.js.map} +0 -0
  130. /package/dist/{setup-E5JILUAN.js.map → setup-U4OZBOVP.js.map} +0 -0
  131. /package/dist/{visual-FKV2TRYY.js.map → visual-5AUY4EQJ.js.map} +0 -0
  132. /package/dist/{watch-LKWPCU42.js.map → watch-NXLLPCQG.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  safeMax,
8
8
  safeMin
9
- } from "./chunk-CODOAMAV.js";
9
+ } from "./chunk-UBB7BAFA.js";
10
10
 
11
11
  // src/pdf/image-regions.ts
12
12
  import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
@@ -824,9 +824,20 @@ function buildTableGrids(horizontals, verticals) {
824
824
  }
825
825
  const groupRadius = groupVertices.length > 0 ? groupVertices.reduce((max, v) => Math.max(max, v.radius), 1) : globalRadius;
826
826
  const coordMergeTol = Math.max(VERTEX_MERGE_FACTOR * groupRadius, MIN_COORD_MERGE_TOL);
827
+ const leftEdge = Math.min(...vLines.map((v) => v.x1));
828
+ const rightEdge = Math.max(...vLines.map((v) => v.x1));
829
+ const underlines = hLines.filter((h) => {
830
+ const crossings = /* @__PURE__ */ new Set();
831
+ for (const v of vLines) {
832
+ if (v.x1 >= h.x1 - CONNECT_TOL && v.x1 <= h.x2 + CONNECT_TOL && h.y1 >= v.y1 - CONNECT_TOL && h.y1 <= v.y2 + CONNECT_TOL) crossings.add(v.x1);
833
+ }
834
+ return crossings.size === 1 && Math.abs(h.x2 - rightEdge) <= CONNECT_TOL && h.x1 > (leftEdge + rightEdge) / 2 && h.x2 - h.x1 < (rightEdge - leftEdge) * 0.6;
835
+ });
836
+ const rowLines = underlines.length >= 3 && hLines.length - underlines.length <= 4 ? hLines.filter((h) => !underlines.includes(h)) : hLines;
837
+ const rowVertices = rowLines.length === hLines.length ? groupVertices : groupVertices.filter((v) => rowLines.some((h) => Math.abs(v.y - h.y1) <= CONNECT_TOL));
827
838
  const rawYs = [
828
- ...hLines.map((l) => l.y1),
829
- ...groupVertices.map((v) => v.y)
839
+ ...rowLines.map((l) => l.y1),
840
+ ...rowVertices.map((v) => v.y)
830
841
  ];
831
842
  const rowYs = clusterCoordinates(rawYs, coordMergeTol).sort((a, b) => b - a);
832
843
  const rawXs = [
@@ -861,7 +872,7 @@ function dropShadingClipGrids(clipGrids, lineGrids, fillRects, verticals = []) {
861
872
  if (!shaded) return true;
862
873
  const ruled = c.cells.every((cell) => cell.bbox.x2 >= c.bbox.x2 - SHADE_CLIP_TOL || verticals.some((v) => Math.abs(v.x1 - cell.bbox.x2) <= SHADE_CLIP_TOL * 2 && Math.min(v.y2, cell.bbox.y2) - Math.max(v.y1, cell.bbox.y1) >= (cell.bbox.y2 - cell.bbox.y1) * 0.75));
863
874
  if (!ruled) return true;
864
- const host = lineGrids.find((l) => c.bbox.x1 >= l.bbox.x1 - SHADE_CLIP_TOL && c.bbox.x2 <= l.bbox.x2 + SHADE_CLIP_TOL && c.bbox.y1 >= l.bbox.y1 - SHADE_CLIP_TOL && c.bbox.y2 <= l.bbox.y2 + SHADE_CLIP_TOL && c.colXs.every((x) => l.colXs.some((lx) => near(lx, x))) && clipGrids.reduce((s, o) => s + inter(o.bbox, l.bbox), 0) <= area(l.bbox) * SHADE_CLIP_MAX_AREA);
875
+ const host = lineGrids.find((l) => c.bbox.x1 >= l.bbox.x1 - SHADE_CLIP_TOL && c.bbox.x2 <= l.bbox.x2 + SHADE_CLIP_TOL && c.bbox.y1 >= l.bbox.y1 - SHADE_CLIP_TOL && c.bbox.y2 <= l.bbox.y2 + SHADE_CLIP_TOL && c.colXs.every((x) => l.colXs.some((lx) => near(lx, x))) && (clipGrids.reduce((s, o) => s + inter(o.bbox, l.bbox), 0) <= area(l.bbox) * SHADE_CLIP_MAX_AREA || l.rowYs.length === c.rowYs.length + 1 && c.rowYs.every((y, i) => near(y, l.rowYs[i])) && l.colXs.length === c.colXs.length && c.colXs.every((x, i) => near(x, l.colXs[i])) && !clipGrids.some((o) => o !== c && inter(o.bbox, l.bbox) > 0)));
865
876
  return !host;
866
877
  });
867
878
  }
@@ -1148,10 +1159,28 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1148
1159
  const pageArea = pageWidth * pageHeight;
1149
1160
  const sameRect = (a, b) => Math.abs(a.x1 - b.x1) <= CLIP_EDGE_TOL && Math.abs(a.x2 - b.x2) <= CLIP_EDGE_TOL && Math.abs(a.y1 - b.y1) <= CLIP_EDGE_TOL && Math.abs(a.y2 - b.y2) <= CLIP_EDGE_TOL;
1150
1161
  const cells = [];
1162
+ const buckets = /* @__PURE__ */ new Map();
1151
1163
  for (const r of rects) {
1152
1164
  if (r.x2 - r.x1 < CLIP_MIN_W2 || r.y2 - r.y1 < CLIP_MIN_H2) continue;
1153
1165
  if (pageArea > 0 && (r.x2 - r.x1) * (r.y2 - r.y1) >= pageArea * CLIP_MAX_PAGE_FRAC) continue;
1154
- if (!cells.some((c) => sameRect(c, r))) cells.push(r);
1166
+ const bx = Math.floor(r.x1 / CLIP_EDGE_TOL), by = Math.floor(r.y1 / CLIP_EDGE_TOL);
1167
+ let duplicate = false;
1168
+ for (let dx = -1; dx <= 1 && !duplicate; dx++) {
1169
+ const ys2 = buckets.get(bx + dx);
1170
+ for (let dy = -1; dy <= 1 && !duplicate; dy++) {
1171
+ duplicate = ys2?.get(by + dy)?.some((c) => sameRect(c, r)) ?? false;
1172
+ }
1173
+ }
1174
+ if (duplicate) continue;
1175
+ cells.push(r);
1176
+ let ys = buckets.get(bx);
1177
+ if (!ys) {
1178
+ ys = /* @__PURE__ */ new Map();
1179
+ buckets.set(bx, ys);
1180
+ }
1181
+ const row = ys.get(by);
1182
+ if (row) row.push(r);
1183
+ else ys.set(by, [r]);
1155
1184
  }
1156
1185
  if (cells.length < 1) return { grids: [], containers: [], page: { lastCells: [], clips: [] } };
1157
1186
  const parent = new Array(cells.length).fill(-1);
@@ -1210,9 +1239,32 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1210
1239
  }
1211
1240
  return i;
1212
1241
  };
1242
+ const edgeBin = (v) => Math.floor(v / CLIP_ADJ_GAP);
1243
+ const startsX = /* @__PURE__ */ new Map(), endsX = /* @__PURE__ */ new Map();
1244
+ const startsY = /* @__PURE__ */ new Map(), endsY = /* @__PURE__ */ new Map();
1245
+ const addEdge = (map, v, i) => {
1246
+ const k = edgeBin(v), row = map.get(k);
1247
+ if (row) row.push(i);
1248
+ else map.set(k, [i]);
1249
+ };
1250
+ for (let i = 0; i < cells.length; i++) {
1251
+ addEdge(startsX, cells[i].x1, i);
1252
+ addEdge(endsX, cells[i].x2, i);
1253
+ addEdge(startsY, cells[i].y1, i);
1254
+ addEdge(endsY, cells[i].y2, i);
1255
+ }
1213
1256
  for (let i = 0; i < cells.length; i++) {
1214
1257
  if (tableClip[i]) continue;
1215
- for (let j = i + 1; j < cells.length; j++) {
1258
+ const candidates = /* @__PURE__ */ new Set();
1259
+ const near = (map, v) => {
1260
+ const k = edgeBin(v);
1261
+ for (let d = -1; d <= 1; d++) for (const j of map.get(k + d) ?? []) if (j > i) candidates.add(j);
1262
+ };
1263
+ near(startsX, cells[i].x2);
1264
+ near(endsX, cells[i].x1);
1265
+ near(startsY, cells[i].y2);
1266
+ near(endsY, cells[i].y1);
1267
+ for (const j of [...candidates].sort((a, b) => a - b)) {
1216
1268
  if (tableClip[j] || parent[i] !== parent[j]) continue;
1217
1269
  if (adjacent(cells[i], cells[j])) {
1218
1270
  const ra = find(i), rb = find(j);
@@ -2051,7 +2103,7 @@ function mergeCellTextLines(textLines, wrap) {
2051
2103
  }
2052
2104
 
2053
2105
  // src/pdf/undersegmented.ts
2054
- var MAX_UNDERSEGMENTED_ROWS = 2;
2106
+ var MAX_UNDERSEGMENTED_ROWS = 5;
2055
2107
  var MIN_UNDERSEGMENTED_COLUMNS = 3;
2056
2108
  var MIN_UNDERSEGMENTED_TEXT_LINES = 8;
2057
2109
  var MIN_ROW_BAND_MISMATCH = 2;
@@ -2099,11 +2151,30 @@ function groupItemsToVisualLines(items) {
2099
2151
  lines.push(cur);
2100
2152
  return lines;
2101
2153
  }
2102
- function normalizeUndersegmentedTable(originalCells, colXs, items) {
2154
+ function normalizeUndersegmentedTable(originalCells, colXs, items, rowYs) {
2103
2155
  const numRows = originalCells.length;
2104
2156
  const numCols = colXs.length - 1;
2105
2157
  if (numRows > MAX_UNDERSEGMENTED_ROWS || numCols < MIN_UNDERSEGMENTED_COLUMNS) return null;
2106
2158
  if (items.length === 0) return null;
2159
+ if (numRows >= 3) {
2160
+ if (rowYs?.length !== numRows + 1) return null;
2161
+ const rebuilt2 = [];
2162
+ let expanded = 0;
2163
+ for (let r = 0; r < numRows; r++) {
2164
+ const row = originalCells[r].map((cell) => cell.text);
2165
+ if (r === 0 || numRows === 3 && r === 1) {
2166
+ rebuilt2.push(row);
2167
+ continue;
2168
+ }
2169
+ const rowItems = items.filter((item) => itemCenterY(item) <= rowYs[r] + 1 && itemCenterY(item) >= rowYs[r + 1] - 1);
2170
+ const body = normalizeUndersegmentedTable([originalCells[r]], colXs, rowItems);
2171
+ if (body) {
2172
+ rebuilt2.push(...body);
2173
+ expanded++;
2174
+ } else rebuilt2.push(row);
2175
+ }
2176
+ return expanded ? rebuilt2 : null;
2177
+ }
2107
2178
  const itemsByCol = Array.from({ length: numCols }, () => []);
2108
2179
  for (const item of items) {
2109
2180
  if (!item.text.trim()) continue;
@@ -2522,6 +2593,43 @@ function detectHeadings(blocks, medianFontSize) {
2522
2593
  }
2523
2594
  }
2524
2595
  }
2596
+ function detectTypographyHeadings(blocks) {
2597
+ const byPage = /* @__PURE__ */ new Map();
2598
+ for (const block of blocks) {
2599
+ if (!block.pageNumber) continue;
2600
+ const page = byPage.get(block.pageNumber) ?? [];
2601
+ page.push(block);
2602
+ byPage.set(block.pageNumber, page);
2603
+ }
2604
+ for (const page of byPage.values()) {
2605
+ if (page.some((block) => block.type === "heading")) continue;
2606
+ const faceWeight = /* @__PURE__ */ new Map();
2607
+ for (const block of page) {
2608
+ if (block.type !== "paragraph" || !block.text || !block.style?.fontName) continue;
2609
+ const face = block.style.fontName;
2610
+ faceWeight.set(face, (faceWeight.get(face) ?? 0) + block.text.length);
2611
+ }
2612
+ const bodyFace = [...faceWeight].sort((a, b) => b[1] - a[1])[0]?.[0];
2613
+ if (!bodyFace || (faceWeight.get(bodyFace) ?? 0) < 250) continue;
2614
+ for (let i = 0; i < page.length; i++) {
2615
+ const block = page[i];
2616
+ const { text, bbox, style } = block;
2617
+ if (block.type !== "paragraph" || !text || !bbox || !style?.fontName || !style.fontSize) continue;
2618
+ const title = text.trim();
2619
+ const numbered = /^\d+(?:\.\d+)*\.?\s+[A-Z가-힣]/.test(title);
2620
+ if (style.fontName === bodyFace || title.length < 3 || title.length > 120 || /^\d+$/.test(title) || /^(?:table|figure|fig\.?|표|그림)\s*\d/i.test(title) || /^(?:doi:|https?:|[•●○▪▫])/i.test(title) || /^(?:over|under)\s+\d+$/i.test(title) || /^[\d\s.,:%+\-–]+$/.test(title) || bbox.height > style.fontSize * (numbered ? 2.4 : 1.6) || title.includes("\n")) continue;
2621
+ const centerX = bbox.x + bbox.width / 2;
2622
+ const nearby = page.filter((other) => other !== block && other.bbox && other.type !== "image" && other.type !== "separator" && other.bbox.x < centerX && centerX < other.bbox.x + other.bbox.width);
2623
+ const above = nearby.filter((other) => other.bbox.y >= bbox.y + bbox.height).sort((a, b) => a.bbox.y - b.bbox.y)[0];
2624
+ const below = nearby.filter((other) => other.bbox.y + other.bbox.height <= bbox.y).sort((a, b) => b.bbox.y - a.bbox.y)[0];
2625
+ const gapAbove = above ? above.bbox.y - bbox.y - bbox.height : Infinity;
2626
+ const gapBelow = below ? bbox.y - below.bbox.y - below.bbox.height : Infinity;
2627
+ if (Math.max(gapAbove, gapBelow) < style.fontSize * (numbered ? 0.7 : 1.2)) continue;
2628
+ block.type = "heading";
2629
+ block.level = 2;
2630
+ }
2631
+ }
2632
+ }
2525
2633
  function shouldDemoteTable(table) {
2526
2634
  const allCells = table.cells.flatMap((row) => row.map((c) => c.text.trim())).filter(Boolean);
2527
2635
  const allText = allCells.join(" ");
@@ -4422,8 +4530,21 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, i
4422
4530
  let finalGrid = irGrid;
4423
4531
  let finalRows = numRows;
4424
4532
  let rebuiltUsed = false;
4425
- if (!grid.cells && numRows <= 2 && numCols >= 3 && !nestedAttached) {
4426
- const rebuilt = normalizeUndersegmentedTable(irGrid, grid.colXs, textItems);
4533
+ let unitLine = [];
4534
+ let unitText = "";
4535
+ if (!grid.cells && numRows >= 3 && numRows <= 5 && numCols >= 3) {
4536
+ const nearby = items.filter((item) => item.y >= grid.bbox.y2 && item.y - grid.bbox.y2 <= 18 && item.x >= grid.bbox.x1 - 3 && item.x + item.w <= grid.bbox.x2 + 3);
4537
+ for (const y of [...new Set(nearby.map((item) => Math.round(item.y)))].sort((a, b) => a - b)) {
4538
+ const line = nearby.filter((item) => Math.abs(item.y - y) <= 1).sort((a, b) => a.x - b.x);
4539
+ const text = line.map((item) => item.text).join("");
4540
+ if (!/^\s*\(\s*단위\s*[::]/.test(text)) continue;
4541
+ unitLine = line;
4542
+ unitText = text;
4543
+ break;
4544
+ }
4545
+ }
4546
+ if (!grid.cells && numRows <= 5 && numCols >= 3 && !nestedAttached && (numRows <= 2 || unitLine.length > 0)) {
4547
+ const rebuilt = normalizeUndersegmentedTable(irGrid, grid.colXs, textItems, grid.rowYs);
4427
4548
  if (rebuilt) {
4428
4549
  rebuiltUsed = true;
4429
4550
  finalGrid = rebuilt.map((row) => row.map((rawText) => ({ text: cleanCellText(rawText), colSpan: 1, rowSpan: 1 })));
@@ -4435,6 +4556,11 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, i
4435
4556
  if (!assignedItems.has(textItems[ti])) usedItems.delete(tableItems[ti]);
4436
4557
  }
4437
4558
  }
4559
+ if (unitLine.length > 0 && rebuiltUsed && !/^\s*\(\s*단위\s*[::]/.test(finalGrid[0]?.[0]?.text ?? "")) {
4560
+ finalGrid.unshift(Array.from({ length: numCols }, (_, c) => ({ text: c === 0 ? cleanCellText(unitText) : "", colSpan: c === 0 ? numCols : 1, rowSpan: 1 })));
4561
+ finalRows++;
4562
+ for (const item of unitLine) usedItems.add(item);
4563
+ }
4438
4564
  const irTable = {
4439
4565
  rows: finalRows,
4440
4566
  cols: numCols,
@@ -4687,6 +4813,43 @@ function pushLineParagraphs(out, yLines, pageNum, lex) {
4687
4813
  i++;
4688
4814
  }
4689
4815
  }
4816
+ function columnTextToBlocks(text, pageNum, bbox, style) {
4817
+ const lines = text.split("\n");
4818
+ const blocks = [];
4819
+ const prose = [];
4820
+ const flushProse = () => {
4821
+ const content = prose.join("\n").trim();
4822
+ if (content) blocks.push({ type: "paragraph", text: content, pageNumber: pageNum, bbox, style });
4823
+ prose.length = 0;
4824
+ };
4825
+ const cells = (line) => line.trim().slice(1, -1).split(/(?<!\\)\|/).map((c) => c.trim().replace(/\\\|/g, "|"));
4826
+ const tableLine = (line) => /^\|.*\|\s*$/.test(line.trim());
4827
+ for (let i = 0; i < lines.length; ) {
4828
+ if (tableLine(lines[i]) && i + 2 < lines.length && tableLine(lines[i + 1]) && cells(lines[i + 1]).every((c) => /^:?-{3,}:?$/.test(c))) {
4829
+ let end = i + 2;
4830
+ while (end < lines.length && tableLine(lines[end])) end++;
4831
+ const rows = [cells(lines[i]), ...lines.slice(i + 2, end).map(cells)];
4832
+ const cols = rows[0].length;
4833
+ const values = rows.slice(1).flat().filter(Boolean);
4834
+ const numeric = values.filter((value) => value.length <= 24 && /\d/.test(value)).length;
4835
+ if (cols >= 3 && rows.length >= 3 && rows.every((row) => row.length === cols) && rows[0].every((value) => value.length <= 80) && numeric >= 3 && numeric / values.length >= 0.25) {
4836
+ flushProse();
4837
+ blocks.push({ type: "table", pageNumber: pageNum, bbox, table: {
4838
+ rows: rows.length,
4839
+ cols,
4840
+ hasHeader: true,
4841
+ cells: rows.map((row) => row.map((value) => ({ text: value, rowSpan: 1, colSpan: 1 })))
4842
+ } });
4843
+ i = end;
4844
+ continue;
4845
+ }
4846
+ }
4847
+ prose.push(lines[i]);
4848
+ i++;
4849
+ }
4850
+ flushProse();
4851
+ return blocks;
4852
+ }
4690
4853
  function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTables = true, lex) {
4691
4854
  if (items.length === 0) return [];
4692
4855
  const blocks = [];
@@ -4729,7 +4892,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
4729
4892
  if (columns && columns.length >= 3) {
4730
4893
  const tableText = extractWithColumns(allYLines, columns);
4731
4894
  const bbox = computeBBox(items, pageNum);
4732
- blocks.push({ type: "paragraph", text: tableText, pageNumber: pageNum, bbox, style: dominantStyle(items) });
4895
+ blocks.push(...columnTextToBlocks(tableText, pageNum, bbox, dominantStyle(items)));
4733
4896
  } else {
4734
4897
  const allY = items.map((i) => i.y);
4735
4898
  const pageHeight = safeMax(allY) - safeMin(allY);
@@ -4742,7 +4905,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
4742
4905
  if (groupColumns && groupColumns.length >= 3) {
4743
4906
  const tableText = extractWithColumns(yLines, groupColumns);
4744
4907
  const bbox = computeBBox(group, pageNum);
4745
- blocks.push({ type: "paragraph", text: tableText, pageNumber: pageNum, bbox, style: dominantStyle(group) });
4908
+ blocks.push(...columnTextToBlocks(tableText, pageNum, bbox, dominantStyle(group)));
4746
4909
  } else {
4747
4910
  pushLineParagraphs(blocks, yLines, pageNum, lex);
4748
4911
  }
@@ -4762,6 +4925,7 @@ export {
4762
4925
  joinPageBreakWraps,
4763
4926
  computeMedianFontSizeFromFreq,
4764
4927
  detectHeadings,
4928
+ detectTypographyHeadings,
4765
4929
  detectMarkerHeadings,
4766
4930
  detectTableCaptions,
4767
4931
  detectKoreanListBlocks,
@@ -4776,4 +4940,4 @@ export {
4776
4940
  trimTrailingEmptyTableCols,
4777
4941
  extractPageBlocksWithLines
4778
4942
  };
4779
- //# sourceMappingURL=chunk-NYNJP45T.js.map
4943
+ //# sourceMappingURL=chunk-S7HVJQ6Y.js.map