kordoc 4.14.2 → 4.14.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 (151) hide show
  1. package/README.md +51 -6
  2. package/dist/{-AQKXLFIM.js → -Z4TJ3QGK.js} +13 -11
  3. package/dist/{chunk-QOMNEWCQ.cjs → chunk-5LAZQCUJ.cjs} +305 -31
  4. package/dist/chunk-5LAZQCUJ.cjs.map +1 -0
  5. package/dist/{chunk-7IAQ4HOK.js → chunk-6BWU6SQX.js} +1 -1
  6. package/dist/chunk-6BWU6SQX.js.map +1 -0
  7. package/dist/{chunk-FOJZNMMK.js → chunk-6CUIXJOC.js} +1 -1
  8. package/dist/chunk-6CUIXJOC.js.map +1 -0
  9. package/dist/{chunk-ZWD734EY.js → chunk-7SE4EZ45.js} +2 -2
  10. package/dist/{chunk-TSFS5VKD.js → chunk-D4U2TXDC.js} +332 -141
  11. package/dist/chunk-D4U2TXDC.js.map +1 -0
  12. package/dist/chunk-DPWWXUOG.js +2222 -0
  13. package/dist/chunk-DPWWXUOG.js.map +1 -0
  14. package/dist/{chunk-GDW4YP7K.js → chunk-GLSBEEZJ.js} +2 -2
  15. package/dist/{chunk-JQEFP45L.js → chunk-GUJ4Q73I.js} +2 -2
  16. package/dist/{chunk-ZWOFQRIB.js → chunk-HIJUKZHM.js} +3 -3
  17. package/dist/chunk-IMAAFSAX.cjs +1352 -0
  18. package/dist/chunk-IMAAFSAX.cjs.map +1 -0
  19. package/dist/{chunk-TNCKOL2O.js → chunk-LCW7SEPG.js} +2 -2
  20. package/dist/chunk-LFSQAMSM.js +1352 -0
  21. package/dist/chunk-LFSQAMSM.js.map +1 -0
  22. package/dist/chunk-MG6DJEX4.js +1299 -0
  23. package/dist/chunk-MG6DJEX4.js.map +1 -0
  24. package/dist/{chunk-QC5T6D4J.js → chunk-NLU6LBLY.js} +299 -25
  25. package/dist/chunk-NLU6LBLY.js.map +1 -0
  26. package/dist/{chunk-EQZI77PP.cjs → chunk-QEJUY6SK.cjs} +3 -3
  27. package/dist/{chunk-EQZI77PP.cjs.map → chunk-QEJUY6SK.cjs.map} +1 -1
  28. package/dist/{chunk-VQ5W4MYO.js → chunk-QIF2VXBQ.js} +298 -24
  29. package/dist/chunk-QIF2VXBQ.js.map +1 -0
  30. package/dist/{chunk-UET462HB.js → chunk-TITDJWVZ.js} +2 -2
  31. package/dist/{chunk-247ANM72.cjs → chunk-USHQCF5T.cjs} +870 -147
  32. package/dist/chunk-USHQCF5T.cjs.map +1 -0
  33. package/dist/{chunk-C7FQBSON.js → chunk-V3DX4DUF.js} +2 -2
  34. package/dist/{chunk-DOGPOY4B.js → chunk-VDN7LMPL.js} +2 -2
  35. package/dist/{chunk-EDNJLUSE.js → chunk-VJIL6PIL.js} +2 -2
  36. package/dist/{chunk-L7DMSMAG.cjs → chunk-VWUYVEFN.cjs} +1 -1
  37. package/dist/chunk-VWUYVEFN.cjs.map +1 -0
  38. package/dist/{chunk-HR2XNERB.cjs → chunk-WJHAU73B.cjs} +2 -2
  39. package/dist/{chunk-HR2XNERB.cjs.map → chunk-WJHAU73B.cjs.map} +1 -1
  40. package/dist/{chunk-NS55SM7A.js → chunk-WJJGW5PL.js} +2 -2
  41. package/dist/{chunk-EISO3POT.js → chunk-XVXHXQIO.js} +788 -194
  42. package/dist/chunk-XVXHXQIO.js.map +1 -0
  43. package/dist/{chunk-ILU76UF3.js → chunk-YAKC46HZ.js} +2 -2
  44. package/dist/chunk-YAKC46HZ.js.map +1 -0
  45. package/dist/{chunk-3DUADAY2.js → chunk-YAVHVFRY.js} +851 -128
  46. package/dist/chunk-YAVHVFRY.js.map +1 -0
  47. package/dist/{chunk-A7QC2ZVP.js → chunk-YT3GC62E.js} +851 -128
  48. package/dist/chunk-YT3GC62E.js.map +1 -0
  49. package/dist/chunks-CCMRL4MJ.js +10 -0
  50. package/dist/cli.js +228 -97
  51. package/dist/cli.js.map +1 -1
  52. package/dist/{formula-PJJ6S3Z2.cjs → formula-5LOVHAKE.cjs} +9 -9
  53. package/dist/{formula-PJJ6S3Z2.cjs.map → formula-5LOVHAKE.cjs.map} +1 -1
  54. package/dist/{formula-JGFTXWYT.js → formula-7RANCW5L.js} +3 -3
  55. package/dist/{formula-5S7WWXGH.js → formula-7UYJOVTB.js} +4 -4
  56. package/dist/{image-ocr-WCAYXLOA.js → image-ocr-I6JOCATS.js} +34 -16
  57. package/dist/image-ocr-I6JOCATS.js.map +1 -0
  58. package/dist/{image-ocr-ZTYAGV3W.cjs → image-ocr-NYBJEOH2.cjs} +36 -18
  59. package/dist/image-ocr-NYBJEOH2.cjs.map +1 -0
  60. package/dist/{image-ocr-FLIL7MGN.js → image-ocr-ZV6PDBUQ.js} +32 -14
  61. package/dist/image-ocr-ZV6PDBUQ.js.map +1 -0
  62. package/dist/index.cjs +4048 -1216
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +66 -28
  65. package/dist/index.d.ts +66 -28
  66. package/dist/index.js +3487 -655
  67. package/dist/index.js.map +1 -1
  68. package/dist/mcp.js +74 -53
  69. package/dist/mcp.js.map +1 -1
  70. package/dist/{model-bundle-FSPZZ2EM.js → model-bundle-2C5IYDLE.js} +5 -5
  71. package/dist/{models-CJPAQK5R.js → models-SEQZHZFT.js} +5 -5
  72. package/dist/{parser-SCUTZAIJ.js → parser-3FNXEP6T.js} +708 -250
  73. package/dist/parser-3FNXEP6T.js.map +1 -0
  74. package/dist/{parser-A4TMPJAM.js → parser-FJBJXRJK.js} +708 -250
  75. package/dist/parser-FJBJXRJK.js.map +1 -0
  76. package/dist/{parser-VCXE4I5A.cjs → parser-HVEKF73B.cjs} +733 -275
  77. package/dist/parser-HVEKF73B.cjs.map +1 -0
  78. package/dist/pdf-ocr-JRDBJ6TA.cjs +14 -0
  79. package/dist/{pdf-ocr-MBZZ57LD.cjs.map → pdf-ocr-JRDBJ6TA.cjs.map} +1 -1
  80. package/dist/pdf-ocr-UUBWBEHL.js +16 -0
  81. package/dist/pdf-ocr-VJSF2SQC.js +13 -0
  82. package/dist/{profile-io-LBM263K7.js → profile-io-RUFZOZO6.js} +3 -3
  83. package/dist/{rasterize-46RG4ZF6.js → rasterize-IOYXR5SI.js} +3 -3
  84. package/dist/redact-NMIKVHMP.js +18 -0
  85. package/dist/redact-doc-QBHNJ2BU.js +1284 -0
  86. package/dist/redact-doc-QBHNJ2BU.js.map +1 -0
  87. package/dist/{render-NGP5T24S.js → render-YZ2TEC3N.js} +7 -7
  88. package/dist/seal-ECV44TUH.js +10 -0
  89. package/dist/{setup-AU4OCHNH.js → setup-MPIEP6XT.js} +3 -3
  90. package/dist/visual-ICMOWUCR.js +19 -0
  91. package/dist/{watch-MKPUCPXM.js → watch-RYO7MN5B.js} +12 -12
  92. package/package.json +2 -2
  93. package/dist/chunk-247ANM72.cjs.map +0 -1
  94. package/dist/chunk-3DUADAY2.js.map +0 -1
  95. package/dist/chunk-422427XJ.cjs +0 -571
  96. package/dist/chunk-422427XJ.cjs.map +0 -1
  97. package/dist/chunk-7IAQ4HOK.js.map +0 -1
  98. package/dist/chunk-A7QC2ZVP.js.map +0 -1
  99. package/dist/chunk-DZIXKL7E.js +0 -145
  100. package/dist/chunk-DZIXKL7E.js.map +0 -1
  101. package/dist/chunk-EISO3POT.js.map +0 -1
  102. package/dist/chunk-EP7KOI2V.js +0 -571
  103. package/dist/chunk-EP7KOI2V.js.map +0 -1
  104. package/dist/chunk-FOJZNMMK.js.map +0 -1
  105. package/dist/chunk-ILU76UF3.js.map +0 -1
  106. package/dist/chunk-KVWV4ULP.js +0 -516
  107. package/dist/chunk-KVWV4ULP.js.map +0 -1
  108. package/dist/chunk-L7DMSMAG.cjs.map +0 -1
  109. package/dist/chunk-QC5T6D4J.js.map +0 -1
  110. package/dist/chunk-QOMNEWCQ.cjs.map +0 -1
  111. package/dist/chunk-TSFS5VKD.js.map +0 -1
  112. package/dist/chunk-VQ5W4MYO.js.map +0 -1
  113. package/dist/chunks-SX77P5CV.js +0 -10
  114. package/dist/image-ocr-FLIL7MGN.js.map +0 -1
  115. package/dist/image-ocr-WCAYXLOA.js.map +0 -1
  116. package/dist/image-ocr-ZTYAGV3W.cjs.map +0 -1
  117. package/dist/parser-A4TMPJAM.js.map +0 -1
  118. package/dist/parser-SCUTZAIJ.js.map +0 -1
  119. package/dist/parser-VCXE4I5A.cjs.map +0 -1
  120. package/dist/pdf-ocr-76SAUXMZ.js +0 -13
  121. package/dist/pdf-ocr-CPU55CVF.js +0 -16
  122. package/dist/pdf-ocr-MBZZ57LD.cjs +0 -14
  123. package/dist/redact-7ILEAUTS.js +0 -12
  124. package/dist/seal-RGUPOLXD.js +0 -10
  125. package/dist/visual-NZLYIYEM.js +0 -19
  126. /package/dist/{-AQKXLFIM.js.map → -Z4TJ3QGK.js.map} +0 -0
  127. /package/dist/{chunk-ZWD734EY.js.map → chunk-7SE4EZ45.js.map} +0 -0
  128. /package/dist/{chunk-GDW4YP7K.js.map → chunk-GLSBEEZJ.js.map} +0 -0
  129. /package/dist/{chunk-JQEFP45L.js.map → chunk-GUJ4Q73I.js.map} +0 -0
  130. /package/dist/{chunk-ZWOFQRIB.js.map → chunk-HIJUKZHM.js.map} +0 -0
  131. /package/dist/{chunk-TNCKOL2O.js.map → chunk-LCW7SEPG.js.map} +0 -0
  132. /package/dist/{chunk-UET462HB.js.map → chunk-TITDJWVZ.js.map} +0 -0
  133. /package/dist/{chunk-C7FQBSON.js.map → chunk-V3DX4DUF.js.map} +0 -0
  134. /package/dist/{chunk-DOGPOY4B.js.map → chunk-VDN7LMPL.js.map} +0 -0
  135. /package/dist/{chunk-EDNJLUSE.js.map → chunk-VJIL6PIL.js.map} +0 -0
  136. /package/dist/{chunk-NS55SM7A.js.map → chunk-WJJGW5PL.js.map} +0 -0
  137. /package/dist/{chunks-SX77P5CV.js.map → chunks-CCMRL4MJ.js.map} +0 -0
  138. /package/dist/{formula-JGFTXWYT.js.map → formula-7RANCW5L.js.map} +0 -0
  139. /package/dist/{formula-5S7WWXGH.js.map → formula-7UYJOVTB.js.map} +0 -0
  140. /package/dist/{model-bundle-FSPZZ2EM.js.map → model-bundle-2C5IYDLE.js.map} +0 -0
  141. /package/dist/{models-CJPAQK5R.js.map → models-SEQZHZFT.js.map} +0 -0
  142. /package/dist/{pdf-ocr-76SAUXMZ.js.map → pdf-ocr-UUBWBEHL.js.map} +0 -0
  143. /package/dist/{pdf-ocr-CPU55CVF.js.map → pdf-ocr-VJSF2SQC.js.map} +0 -0
  144. /package/dist/{profile-io-LBM263K7.js.map → profile-io-RUFZOZO6.js.map} +0 -0
  145. /package/dist/{rasterize-46RG4ZF6.js.map → rasterize-IOYXR5SI.js.map} +0 -0
  146. /package/dist/{redact-7ILEAUTS.js.map → redact-NMIKVHMP.js.map} +0 -0
  147. /package/dist/{render-NGP5T24S.js.map → render-YZ2TEC3N.js.map} +0 -0
  148. /package/dist/{seal-RGUPOLXD.js.map → seal-ECV44TUH.js.map} +0 -0
  149. /package/dist/{setup-AU4OCHNH.js.map → setup-MPIEP6XT.js.map} +0 -0
  150. /package/dist/{visual-NZLYIYEM.js.map → visual-ICMOWUCR.js.map} +0 -0
  151. /package/dist/{watch-MKPUCPXM.js.map → watch-RYO7MN5B.js.map} +0 -0
@@ -3,11 +3,11 @@ import {
3
3
  HEADING_RATIO_H1,
4
4
  HEADING_RATIO_H2,
5
5
  HEADING_RATIO_H3
6
- } from "./chunk-7IAQ4HOK.js";
6
+ } from "./chunk-6BWU6SQX.js";
7
7
  import {
8
8
  safeMax,
9
9
  safeMin
10
- } from "./chunk-UET462HB.js";
10
+ } from "./chunk-TITDJWVZ.js";
11
11
 
12
12
  // src/pdf/image-regions.ts
13
13
  import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
@@ -69,7 +69,11 @@ var MAX_LINE_WIDTH = 5;
69
69
  function extractLines(fnArray, argsArray) {
70
70
  const horizontals = [];
71
71
  const verticals = [];
72
+ const shortH = [];
73
+ const shortV = [];
72
74
  const clipRects = [];
75
+ const fillRects = [];
76
+ let pathRects = [];
73
77
  let pendingClip = false;
74
78
  let lineWidth = 1;
75
79
  let ctm = [1, 0, 0, 1, 0, 0];
@@ -85,6 +89,8 @@ function extractLines(fnArray, argsArray) {
85
89
  currentPath.push({ x1: tx1, y1: ty1, x2: tx2, y2: ty2 });
86
90
  }
87
91
  function pushRectangle(rx, ry, rw, rh) {
92
+ const [ax, ay] = applyCtm(rx, ry), [bx, by] = applyCtm(rx + rw, ry + rh);
93
+ pathRects.push({ x1: Math.min(ax, bx), y1: Math.min(ay, by), x2: Math.max(ax, bx), y2: Math.max(ay, by) });
88
94
  const effH = Math.abs(rh) * Math.hypot(ctm[2], ctm[3]);
89
95
  const effW = Math.abs(rw) * Math.hypot(ctm[0], ctm[1]);
90
96
  if (effH < ORIENTATION_TOL * 2) {
@@ -98,7 +104,12 @@ function extractLines(fnArray, argsArray) {
98
104
  pushSeg(rx, ry + rh, rx, ry);
99
105
  }
100
106
  }
101
- function flushPath(isStroke, fromFill = false) {
107
+ function flushPath(isStroke, fromFill = false, filled = fromFill) {
108
+ if (filled) {
109
+ if (pathRects.length) for (const r of pathRects) fillRects.push(r);
110
+ else captureClipRect(currentPath, fillRects, 0.3, 0.3);
111
+ }
112
+ pathRects = [];
102
113
  if (!isStroke) {
103
114
  if (pendingClip) captureClipRect(currentPath, clipRects);
104
115
  pendingClip = false;
@@ -108,7 +119,7 @@ function extractLines(fnArray, argsArray) {
108
119
  pendingClip = false;
109
120
  const effWidth = lineWidth * ctmScale();
110
121
  for (const seg of currentPath) {
111
- classifyAndAdd(seg, effWidth, horizontals, verticals, fromFill);
122
+ classifyAndAdd(seg, effWidth, horizontals, verticals, fromFill, fromFill ? void 0 : { h: shortH, v: shortV });
112
123
  }
113
124
  currentPath = [];
114
125
  }
@@ -209,7 +220,7 @@ function extractLines(fnArray, argsArray) {
209
220
  } else if (afterOp === OPS2.fill || afterOp === OPS2.eoFill) {
210
221
  flushPath(true, true);
211
222
  } else if (afterOp === OPS2.fillStroke || afterOp === OPS2.eoFillStroke || afterOp === OPS2.closeFillStroke || afterOp === OPS2.closeEOFillStroke) {
212
- flushPath(true);
223
+ flushPath(true, false, true);
213
224
  } else if (afterOp === OPS2.endPath) {
214
225
  flushPath(false);
215
226
  }
@@ -228,7 +239,7 @@ function extractLines(fnArray, argsArray) {
228
239
  case OPS2.eoFillStroke:
229
240
  case OPS2.closeFillStroke:
230
241
  case OPS2.closeEOFillStroke:
231
- flushPath(true);
242
+ flushPath(true, false, true);
232
243
  break;
233
244
  case OPS2.endPath:
234
245
  flushPath(false);
@@ -239,11 +250,60 @@ function extractLines(fnArray, argsArray) {
239
250
  break;
240
251
  }
241
252
  }
242
- return { horizontals, verticals, clipRects };
253
+ return { horizontals, verticals, clipRects, fillRects, shortH, shortV };
254
+ }
255
+ var SHORT_CHAIN_POS_TOL = 0.5;
256
+ var SHORT_CHAIN_GAP = 0.2;
257
+ function chainShortSegments(longs, shorts, dir) {
258
+ if (shorts.length === 0) return longs;
259
+ const pos = (l) => dir === "h" ? l.y1 : l.x1;
260
+ const lo = (l) => dir === "h" ? l.x1 : l.y1;
261
+ const hi = (l) => dir === "h" ? l.x2 : l.y2;
262
+ const shortSet = new Set(shorts);
263
+ const all = [...longs.filter((l) => !l.fromFill), ...shorts].sort((a, b) => pos(a) - pos(b) || lo(a) - lo(b));
264
+ const absorbed = /* @__PURE__ */ new Set();
265
+ const chained = [];
266
+ const flush = (chain) => {
267
+ if (!chain.some((l) => shortSet.has(l))) return;
268
+ let s = Infinity, e = -Infinity, p = 0, w = 0;
269
+ for (const l of chain) {
270
+ s = Math.min(s, lo(l));
271
+ e = Math.max(e, hi(l));
272
+ p += pos(l);
273
+ w = Math.max(w, l.lineWidth);
274
+ }
275
+ if (e - s < MIN_LINE_LENGTH) return;
276
+ p /= chain.length;
277
+ for (const l of chain) absorbed.add(l);
278
+ chained.push(dir === "h" ? { x1: s, y1: p, x2: e, y2: p, lineWidth: w } : { x1: p, y1: s, x2: p, y2: e, lineWidth: w });
279
+ };
280
+ let band = [];
281
+ const flushBand = () => {
282
+ band.sort((a, b) => lo(a) - lo(b));
283
+ let chain = [];
284
+ let end = -Infinity;
285
+ for (const l of band) {
286
+ if (chain.length && lo(l) > end + SHORT_CHAIN_GAP) {
287
+ flush(chain);
288
+ chain = [];
289
+ }
290
+ chain.push(l);
291
+ end = chain.length === 1 ? hi(l) : Math.max(end, hi(l));
292
+ }
293
+ if (chain.length) flush(chain);
294
+ band = [];
295
+ };
296
+ for (const l of all) {
297
+ if (band.length && pos(l) - pos(band[0]) > SHORT_CHAIN_POS_TOL) flushBand();
298
+ band.push(l);
299
+ }
300
+ if (band.length) flushBand();
301
+ if (chained.length === 0) return longs;
302
+ return [...longs.filter((l) => !absorbed.has(l)), ...chained];
243
303
  }
244
304
  var CLIP_MIN_W = 4;
245
305
  var CLIP_MIN_H = 2;
246
- function captureClipRect(path, out) {
306
+ function captureClipRect(path, out, minW = CLIP_MIN_W, minH = CLIP_MIN_H) {
247
307
  if (path.length < 3 || path.length > 5) return;
248
308
  let x1 = Infinity, y1 = Infinity, x2 = -Infinity, y2 = -Infinity;
249
309
  for (const s of path) {
@@ -254,14 +314,24 @@ function captureClipRect(path, out) {
254
314
  y1 = Math.min(y1, s.y1, s.y2);
255
315
  y2 = Math.max(y2, s.y1, s.y2);
256
316
  }
257
- if (x2 - x1 < CLIP_MIN_W || y2 - y1 < CLIP_MIN_H) return;
317
+ if (x2 - x1 < minW || y2 - y1 < minH) return;
258
318
  out.push({ x1, y1, x2, y2 });
259
319
  }
260
- function classifyAndAdd(seg, lineWidth, horizontals, verticals, fromFill = false) {
320
+ function classifyAndAdd(seg, lineWidth, horizontals, verticals, fromFill = false, short) {
261
321
  const dx = Math.abs(seg.x2 - seg.x1);
262
322
  const dy = Math.abs(seg.y2 - seg.y1);
263
323
  const length = Math.sqrt(dx * dx + dy * dy);
264
- if (length < MIN_LINE_LENGTH) return;
324
+ if (length < MIN_LINE_LENGTH) {
325
+ if (!short || length <= 0) return;
326
+ if (dy <= ORIENTATION_TOL && dx > dy) {
327
+ const y = (seg.y1 + seg.y2) / 2;
328
+ short.h.push({ x1: Math.min(seg.x1, seg.x2), y1: y, x2: Math.max(seg.x1, seg.x2), y2: y, lineWidth });
329
+ } else if (dx <= ORIENTATION_TOL && dy > dx) {
330
+ const x = (seg.x1 + seg.x2) / 2;
331
+ short.v.push({ x1: x, y1: Math.min(seg.y1, seg.y2), x2: x, y2: Math.max(seg.y1, seg.y2), lineWidth });
332
+ }
333
+ return;
334
+ }
265
335
  if (dy <= ORIENTATION_TOL) {
266
336
  const y = (seg.y1 + seg.y2) / 2;
267
337
  const x1 = Math.min(seg.x1, seg.x2);
@@ -742,6 +812,23 @@ function buildTableGrids(horizontals, verticals) {
742
812
  }
743
813
  return mergeAdjacentGrids(grids);
744
814
  }
815
+ var SHADE_CLIP_TOL = 1.5;
816
+ var SHADE_CLIP_MAX_AREA = 0.5;
817
+ function dropShadingClipGrids(clipGrids, lineGrids, fillRects, verticals = []) {
818
+ if (clipGrids.length === 0 || lineGrids.length === 0 || fillRects.length === 0) return clipGrids;
819
+ const near = (a, b) => Math.abs(a - b) <= SHADE_CLIP_TOL;
820
+ const area = (b) => Math.max(0, b.x2 - b.x1) * Math.max(0, b.y2 - b.y1);
821
+ const inter = (a, b) => area({ x1: Math.max(a.x1, b.x1), y1: Math.max(a.y1, b.y1), x2: Math.min(a.x2, b.x2), y2: Math.min(a.y2, b.y2) });
822
+ return clipGrids.filter((c) => {
823
+ if (!c.cells?.length || c.clipParent) return true;
824
+ const shaded = c.cells.every((cell) => fillRects.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)));
825
+ if (!shaded) return true;
826
+ 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));
827
+ if (!ruled) return true;
828
+ 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);
829
+ return !host;
830
+ });
831
+ }
745
832
  function enforceMinWidth(colXs, minWidth) {
746
833
  if (colXs.length <= 2) return colXs;
747
834
  const result = [colXs[0]];
@@ -961,20 +1048,25 @@ function linesIntersect(a, b) {
961
1048
 
962
1049
  // src/pdf/clip-cells.ts
963
1050
  var CLIP_EDGE_TOL = 1.5;
1051
+ var CLIP_ADJ_GAP = 0.15;
964
1052
  var CLIP_MIN_GROUP = 2;
965
1053
  var CLIP_MAX_PAGE_FRAC = 0.75;
1054
+ var STRADDLE_MIN = 0.1;
966
1055
  var TITLED_FRAME_MIN_TOP = 0.2;
967
1056
  var TITLED_FRAME_MIN_WIDTH = 0.6;
968
1057
  var HEADER_BAND = 0.08;
969
1058
  var CLIP_MIN_W2 = 4;
1059
+ var NARROW_FILL_MIN_W = 1;
970
1060
  var CLIP_MIN_H2 = 2;
971
1061
  var STROKE_NEAR = 2;
972
1062
  var STROKE_COVER = 0.5;
973
1063
  var CLIP_MIN_INVISIBLE = 0;
1064
+ var CLIP_COORD_TOL = 0.3;
974
1065
  var overlap = (a1, a2, b1, b2) => Math.min(a2, b2) - Math.max(a1, b1);
975
1066
  function adjacent(a, b) {
976
- if ((Math.abs(a.x2 - b.x1) <= CLIP_EDGE_TOL || Math.abs(b.x2 - a.x1) <= CLIP_EDGE_TOL) && overlap(a.y1, a.y2, b.y1, b.y2) > CLIP_EDGE_TOL) return true;
977
- if ((Math.abs(a.y2 - b.y1) <= CLIP_EDGE_TOL || Math.abs(b.y2 - a.y1) <= CLIP_EDGE_TOL) && overlap(a.x1, a.x2, b.x1, b.x2) > CLIP_EDGE_TOL) return true;
1067
+ const al = (u, v) => Math.abs(u - v) <= CLIP_COORD_TOL;
1068
+ if ((Math.abs(a.x2 - b.x1) <= CLIP_ADJ_GAP || Math.abs(b.x2 - a.x1) <= CLIP_ADJ_GAP) && overlap(a.y1, a.y2, b.y1, b.y2) > CLIP_EDGE_TOL && (al(a.y1, b.y1) || al(a.y2, b.y2))) return true;
1069
+ if ((Math.abs(a.y2 - b.y1) <= CLIP_ADJ_GAP || Math.abs(b.y2 - a.y1) <= CLIP_ADJ_GAP) && overlap(a.x1, a.x2, b.x1, b.x2) > CLIP_EDGE_TOL && (al(a.x1, b.x1) || al(a.x2, b.x2))) return true;
978
1070
  return false;
979
1071
  }
980
1072
  function contains(a, b) {
@@ -985,7 +1077,7 @@ function clusterCoords(values) {
985
1077
  const out = [];
986
1078
  let run = [];
987
1079
  for (const v of sorted) {
988
- if (run.length > 0 && v - run[run.length - 1] > CLIP_EDGE_TOL) {
1080
+ if (run.length > 0 && v - run[run.length - 1] > CLIP_COORD_TOL) {
989
1081
  out.push(run.reduce((s, x) => s + x, 0) / run.length);
990
1082
  run = [];
991
1083
  }
@@ -1011,7 +1103,8 @@ function edgeStroked(lines, dir, pos, a1, a2) {
1011
1103
  }
1012
1104
  return false;
1013
1105
  }
1014
- function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, textPoints = []) {
1106
+ var CONT_X_TOL = 0.1;
1107
+ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, textPoints = [], fillRects = [], prev = { lastCells: [], clips: [] }) {
1015
1108
  const pageArea = pageWidth * pageHeight;
1016
1109
  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;
1017
1110
  const cells = [];
@@ -1020,17 +1113,55 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1020
1113
  if (pageArea > 0 && (r.x2 - r.x1) * (r.y2 - r.y1) >= pageArea * CLIP_MAX_PAGE_FRAC) continue;
1021
1114
  if (!cells.some((c) => sameRect(c, r))) cells.push(r);
1022
1115
  }
1023
- if (cells.length < 1) return { grids: [], containers: [] };
1116
+ if (cells.length < 1) return { grids: [], containers: [], page: { lastCells: [], clips: [] } };
1024
1117
  const parent = new Array(cells.length).fill(-1);
1118
+ const area = (r) => (r.x2 - r.x1) * (r.y2 - r.y1);
1119
+ const straddles = (a, b) => {
1120
+ const ix = overlap(a.x1, a.x2, b.x1, b.x2), iy = overlap(a.y1, a.y2, b.y1, b.y2);
1121
+ if (ix <= CLIP_EDGE_TOL || iy <= CLIP_EDGE_TOL || contains(a, b) || contains(b, a)) return false;
1122
+ const inside = ix * iy / area(b);
1123
+ return inside > STRADDLE_MIN && inside < 1 - STRADDLE_MIN;
1124
+ };
1125
+ const canParent = cells.map((a, i) => !cells.some((b, j) => i !== j && straddles(a, b)));
1025
1126
  for (let i = 0; i < cells.length; i++) {
1026
1127
  for (let j = 0; j < cells.length; j++) {
1027
- if (i === j || !contains(cells[j], cells[i])) continue;
1128
+ if (i === j || !canParent[j] || area(cells[j]) <= area(cells[i]) || !contains(cells[j], cells[i])) continue;
1028
1129
  const cur = parent[i];
1029
1130
  if (cur < 0 || contains(cells[cur], cells[j])) parent[i] = j;
1030
1131
  }
1031
1132
  }
1133
+ const ruledGaps = findRuledGaps(cells, parent, strokedH, strokedV);
1032
1134
  const isContainer = new Array(cells.length).fill(false);
1033
1135
  for (const p of parent) if (p >= 0) isContainer[p] = true;
1136
+ const tileParent = new Array(cells.length).fill(false);
1137
+ const tableClip = new Array(cells.length).fill(false);
1138
+ for (let p = 0; p < cells.length; p++) {
1139
+ if (!isContainer[p]) continue;
1140
+ let n = 0, x1 = Infinity, y1 = Infinity, x2 = -Infinity, y2 = -Infinity;
1141
+ for (let i = 0; i < cells.length; i++) {
1142
+ if (parent[i] !== p) continue;
1143
+ const k = cells[i];
1144
+ n++;
1145
+ x1 = Math.min(x1, k.x1);
1146
+ y1 = Math.min(y1, k.y1);
1147
+ x2 = Math.max(x2, k.x2);
1148
+ y2 = Math.max(y2, k.y2);
1149
+ }
1150
+ if (n < CLIP_MIN_GROUP) continue;
1151
+ const c = cells[p];
1152
+ if (Math.max(Math.abs(x1 - c.x1), Math.abs(x2 - c.x2), Math.abs(y1 - c.y1), Math.abs(y2 - c.y2)) <= CLIP_COORD_TOL) tileParent[p] = true;
1153
+ }
1154
+ for (let p = 0; p < cells.length; p++) {
1155
+ if (tileParent[p]) tableClip[p] = !cells.some((q, i) => i !== p && parent[i] === parent[p] && !tileParent[i] && adjacent(q, cells[p]));
1156
+ }
1157
+ const effParent = (i) => {
1158
+ let p = parent[i];
1159
+ for (let steps = 0; p >= 0 && steps <= cells.length; steps++) {
1160
+ if (!tableClip[p] && (isGridMember(p) || loneFrame(p) || continues(p))) return p;
1161
+ p = parent[p];
1162
+ }
1163
+ return -1;
1164
+ };
1034
1165
  const root = cells.map((_, i) => i);
1035
1166
  const find = (i) => {
1036
1167
  while (root[i] !== i) {
@@ -1040,14 +1171,20 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1040
1171
  return i;
1041
1172
  };
1042
1173
  for (let i = 0; i < cells.length; i++) {
1174
+ if (tableClip[i]) continue;
1043
1175
  for (let j = i + 1; j < cells.length; j++) {
1044
- if (parent[i] !== parent[j]) continue;
1176
+ if (tableClip[j] || parent[i] !== parent[j]) continue;
1045
1177
  if (adjacent(cells[i], cells[j])) {
1046
1178
  const ra = find(i), rb = find(j);
1047
1179
  if (ra !== rb) root[ra] = rb;
1048
1180
  }
1049
1181
  }
1050
1182
  }
1183
+ for (const g of ruledGaps) {
1184
+ if (tableClip[g.i] || tableClip[g.j]) continue;
1185
+ const ra = find(g.i), rb = find(g.j);
1186
+ if (ra !== rb) root[ra] = rb;
1187
+ }
1051
1188
  const groups = /* @__PURE__ */ new Map();
1052
1189
  for (let i = 0; i < cells.length; i++) {
1053
1190
  const r = find(i);
@@ -1061,14 +1198,27 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1061
1198
  const framed = (r) => edgeStroked(strokedH, "h", r.y1, r.x1, r.x2) && edgeStroked(strokedH, "h", r.y2, r.x1, r.x2) && edgeStroked(strokedV, "v", r.x1, r.y1, r.y2) && edgeStroked(strokedV, "v", r.x2, r.y1, r.y2);
1062
1199
  const titledFrame = (r) => r.y1 >= pageHeight * TITLED_FRAME_MIN_TOP && r.x2 - r.x1 >= pageWidth * TITLED_FRAME_MIN_WIDTH && textPoints.some((p) => p.y < r.y1 && p.y > pageHeight * HEADER_BAND && p.x >= r.x1 - CLIP_EDGE_TOL && p.x <= r.x2 + CLIP_EDGE_TOL) && textPoints.some((p) => p.x > r.x1 && p.x < r.x2 && p.y > r.y1 && p.y < r.y2);
1063
1200
  const loneFrame = (i) => isContainer[i] && !isGridMember(i) && (framed(cells[i]) || titledFrame(cells[i]));
1064
- const parentAttachable = (i) => parent[i] >= 0 && (isGridMember(parent[i]) || loneFrame(parent[i]));
1201
+ const headY = pageHeight * (1 - HEADER_BAND);
1202
+ const continues = (i) => {
1203
+ if (!prev.lastCells.length || parent[i] >= 0 || tableClip[i] || isGridMember(i)) return void 0;
1204
+ const c = cells[i];
1205
+ const from = prev.lastCells.find((b) => Math.abs(b.x1 - c.x1) <= CONT_X_TOL && Math.abs(b.x2 - c.x2) <= CONT_X_TOL);
1206
+ if (!from) return void 0;
1207
+ if (prev.clips.some((k) => sameRect(k, c) && !sameRect(k, from))) return void 0;
1208
+ if (textPoints.some((p) => p.y > c.y2 && p.y < headY)) return void 0;
1209
+ if (cells.some((o) => o.y1 >= c.y2 - CLIP_EDGE_TOL && (o.y1 + o.y2) / 2 < headY)) return void 0;
1210
+ return from;
1211
+ };
1212
+ const parentAttachable = (i) => effParent(i) >= 0;
1065
1213
  const onlyChild = (i) => parent[i] >= 0 && parent.filter((p) => p === parent[i]).length === 1;
1066
1214
  const grids = [];
1067
1215
  for (const idxs of groups.values()) {
1068
1216
  const first = idxs[0];
1069
- const parentRect = parentAttachable(first) ? cells[parent[first]] : void 0;
1217
+ if (tableClip[first]) continue;
1218
+ const parentRect = parentAttachable(first) ? cells[effParent(first)] : void 0;
1070
1219
  if (idxs.length < CLIP_MIN_GROUP) {
1071
- if (!loneFrame(first) && !((parentRect || onlyChild(first)) && framed(cells[first]))) continue;
1220
+ const from = continues(first);
1221
+ if (!from && !loneFrame(first) && !((parentRect || onlyChild(first)) && framed(cells[first]))) continue;
1072
1222
  const r = cells[first];
1073
1223
  grids.push({
1074
1224
  rowYs: [r.y2, r.y1],
@@ -1076,11 +1226,13 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1076
1226
  bbox: { x1: r.x1, y1: r.y1, x2: r.x2, y2: r.y2 },
1077
1227
  vertexRadius: 1,
1078
1228
  cells: [{ row: 0, col: 0, rowSpan: 1, colSpan: 1, bbox: { x1: r.x1, y1: r.y1, x2: r.x2, y2: r.y2 } }],
1079
- ...parentRect ? { clipParent: parentRect } : {}
1229
+ ...parentRect ? { clipParent: parentRect } : {},
1230
+ ...from ? { continues: from } : {}
1080
1231
  });
1081
1232
  continue;
1082
1233
  }
1083
- const members = idxs.map((i) => cells[i]);
1234
+ const gs = ruledGaps.filter((g) => find(g.i) === find(first));
1235
+ const members = gs.length ? closeGaps(idxs.map((i) => cells[i]), gs) : idxs.map((i) => cells[i]);
1084
1236
  let edges = 0, invisible = 0;
1085
1237
  for (const r of members) {
1086
1238
  edges += 4;
@@ -1112,19 +1264,178 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
1112
1264
  for (let rr = 0; rr < numRows; rr++) {
1113
1265
  for (let cc = 0; cc < numCols; cc++) {
1114
1266
  if (occupied[rr][cc]) continue;
1115
- out.push({ row: rr, col: cc, rowSpan: 1, colSpan: 1, bbox: { x1: colXs[cc], y1: rowYs[rr + 1], x2: colXs[cc + 1], y2: rowYs[rr] } });
1267
+ out.push({ row: rr, col: cc, rowSpan: 1, colSpan: 1, bbox: { x1: colXs[cc], y1: rowYs[rr + 1], x2: colXs[cc + 1], y2: rowYs[rr] }, filler: true });
1116
1268
  }
1117
1269
  }
1270
+ addNarrowEdgeCols(colXs, rowYs, out, fillRects);
1118
1271
  grids.push({
1119
1272
  rowYs,
1120
1273
  colXs,
1121
- bbox: { x1: colXs[0], y1: rowYs[numRows], x2: colXs[numCols], y2: rowYs[0] },
1274
+ bbox: { x1: colXs[0], y1: rowYs[numRows], x2: colXs[colXs.length - 1], y2: rowYs[0] },
1122
1275
  vertexRadius: 1,
1123
1276
  cells: out,
1124
1277
  ...parentRect ? { clipParent: parentRect } : {}
1125
1278
  });
1126
1279
  }
1127
- return { grids, containers };
1280
+ const tops = grids.filter((g) => !g.clipParent).flatMap((g) => g.cells.filter((c) => !c.filler).map((c) => c.bbox));
1281
+ let lastCells = [];
1282
+ if (tops.length) {
1283
+ const bottom = Math.min(...tops.map((b) => b.y1));
1284
+ const footY = pageHeight * HEADER_BAND;
1285
+ const below = (y) => y < bottom - CLIP_EDGE_TOL && y > footY;
1286
+ if (!textPoints.some((p) => below(p.y)) && !cells.some((o) => o.y2 < bottom && below((o.y1 + o.y2) / 2))) {
1287
+ lastCells = tops.filter((b) => Math.abs(b.y1 - bottom) <= CLIP_COORD_TOL && !cells.some((k) => Math.abs(k.x1 - b.x1) <= CONT_X_TOL && Math.abs(k.x2 - b.x2) <= CONT_X_TOL && k.y2 > b.y2 + CLIP_EDGE_TOL && contains(k, b)));
1288
+ }
1289
+ }
1290
+ return { grids, containers, page: { lastCells, clips: cells } };
1291
+ }
1292
+ var CLIP_SPACING_MAX = 3;
1293
+ function findRuledGaps(cells, parent, strokedH, strokedV) {
1294
+ const al = (u, v) => Math.abs(u - v) <= CLIP_COORD_TOL;
1295
+ const occupied = (i, j, x1, y1, x2, y2) => cells.some((k, n) => n !== i && n !== j && parent[n] === parent[i] && overlap(k.x1, k.x2, x1, x2) > CLIP_ADJ_GAP && overlap(k.y1, k.y2, y1, y2) > CLIP_ADJ_GAP);
1296
+ const gapBetween = (i, j) => {
1297
+ const a = cells[i], b = cells[j];
1298
+ const gv = a.y1 - b.y2;
1299
+ if (gv > CLIP_ADJ_GAP && gv <= CLIP_SPACING_MAX && overlap(a.x1, a.x2, b.x1, b.x2) > CLIP_EDGE_TOL && (al(a.x1, b.x1) || al(a.x2, b.x2)) && !occupied(i, j, Math.max(a.x1, b.x1), b.y2, Math.min(a.x2, b.x2), a.y1)) {
1300
+ const e1 = Math.max(a.x1, b.x1), e2 = Math.min(a.x2, b.x2);
1301
+ return { i, j, axis: "y", lo: b.y2, hi: a.y1, e1, e2, to: ruleEnd(strokedH, "h", b.y2, a.y1, e1, e2) };
1302
+ }
1303
+ const gh = b.x1 - a.x2;
1304
+ if (gh > CLIP_ADJ_GAP && gh <= CLIP_SPACING_MAX && overlap(a.y1, a.y2, b.y1, b.y2) > CLIP_EDGE_TOL && (al(a.y1, b.y1) || al(a.y2, b.y2)) && !occupied(i, j, a.x2, Math.max(a.y1, b.y1), b.x1, Math.min(a.y2, b.y2))) {
1305
+ const e1 = Math.max(a.y1, b.y1), e2 = Math.min(a.y2, b.y2);
1306
+ return { i, j, axis: "x", lo: a.x2, hi: b.x1, e1, e2, to: ruleEnd(strokedV, "v", a.x2, b.x1, e1, e2) };
1307
+ }
1308
+ return void 0;
1309
+ };
1310
+ const ruled = (g) => (g.axis === "y" ? strokedH : strokedV).some((l) => {
1311
+ const pos = g.axis === "y" ? l.y1 : l.x1;
1312
+ return (Math.abs(pos - g.lo) <= 0.5 || Math.abs(pos - g.hi) <= 0.5) && (g.axis === "y" ? overlap(l.x1, l.x2, g.e1, g.e2) : overlap(l.y1, l.y2, g.e1, g.e2)) >= (g.e2 - g.e1) * STROKE_COVER;
1313
+ });
1314
+ const root = cells.map((_, i) => i);
1315
+ const find = (i) => {
1316
+ while (root[i] !== i) {
1317
+ root[i] = root[root[i]];
1318
+ i = root[i];
1319
+ }
1320
+ return i;
1321
+ };
1322
+ const found = [];
1323
+ const widths = /* @__PURE__ */ new Map();
1324
+ const link = (g) => {
1325
+ found.push(g);
1326
+ const ri = find(g.i), rj = find(g.j);
1327
+ const w = [...widths.get(rj) ?? [], ...ri !== rj ? widths.get(ri) ?? [] : [], { axis: g.axis, w: g.hi - g.lo }];
1328
+ root[ri] = rj;
1329
+ widths.set(rj, w);
1330
+ };
1331
+ const cand = [];
1332
+ for (let i = 0; i < cells.length; i++) {
1333
+ for (let j = 0; j < cells.length; j++) {
1334
+ if (i === j || parent[i] !== parent[j]) continue;
1335
+ const g = gapBetween(i, j);
1336
+ if (!g) continue;
1337
+ const a = cells[i], b = cells[j];
1338
+ const both = g.axis === "y" ? al(a.x1, b.x1) && al(a.x2, b.x2) : al(a.y1, b.y1) && al(a.y2, b.y2);
1339
+ const isRuled = ruled(g);
1340
+ if (both && isRuled) link(g);
1341
+ else if (both || isRuled) cand.push([g, both]);
1342
+ }
1343
+ }
1344
+ if (!found.length) return [];
1345
+ for (let grew = true; grew; ) {
1346
+ grew = false;
1347
+ for (let k = cand.length - 1; k >= 0; k--) {
1348
+ const [g, both] = cand[k], w = g.hi - g.lo;
1349
+ if (![find(g.i), find(g.j)].some((r) => (widths.get(r) ?? []).some((s) => (both || s.axis === g.axis) && Math.abs(s.w - w) <= CLIP_COORD_TOL))) continue;
1350
+ link(g);
1351
+ cand.splice(k, 1);
1352
+ grew = true;
1353
+ }
1354
+ }
1355
+ return found;
1356
+ }
1357
+ function ruleEnd(lines, dir, lo, hi, e1, e2) {
1358
+ const at = (p) => lines.some((l) => Math.abs((dir === "h" ? l.y1 : l.x1) - p) <= 0.5 && (dir === "h" ? overlap(l.x1, l.x2, e1, e2) : overlap(l.y1, l.y2, e1, e2)) >= (e2 - e1) * STROKE_COVER);
1359
+ const atLo = at(lo), atHi = at(hi);
1360
+ return atLo && !atHi ? lo : atHi && !atLo ? hi : (lo + hi) / 2;
1361
+ }
1362
+ function closeGaps(members, gaps) {
1363
+ const al = (u, v) => Math.abs(u - v) <= CLIP_COORD_TOL;
1364
+ const lines = [];
1365
+ for (const g of gaps) {
1366
+ const l = lines.find((k) => k.axis === g.axis && al(k.lo, g.lo) && al(k.hi, g.hi));
1367
+ if (l) {
1368
+ l.e1 = Math.min(l.e1, g.e1);
1369
+ l.e2 = Math.max(l.e2, g.e2);
1370
+ } else lines.push({ ...g });
1371
+ }
1372
+ const out = members.map((r) => ({ ...r }));
1373
+ for (const l of lines) {
1374
+ const shared = (p) => members.some((u) => members.some((v) => u !== v && (l.axis === "y" ? Math.abs(u.y1 - v.y2) <= CLIP_ADJ_GAP && al(u.y1, p) && overlap(u.x1, u.x2, v.x1, v.x2) > CLIP_EDGE_TOL : Math.abs(u.x2 - v.x1) <= CLIP_ADJ_GAP && al(u.x2, p) && overlap(u.y1, u.y2, v.y1, v.y2) > CLIP_EDGE_TOL)));
1375
+ const atLo = shared(l.lo), atHi = shared(l.hi);
1376
+ const to = atLo && !atHi ? l.lo : atHi && !atLo ? l.hi : l.to;
1377
+ const onEdge = (r) => l.axis === "y" ? al(r.y2, l.lo) || al(r.y1, l.hi) : al(r.x2, l.lo) || al(r.x1, l.hi);
1378
+ const span = (r) => l.axis === "y" ? [r.x1, r.x2] : [r.y1, r.y2];
1379
+ const take = /* @__PURE__ */ new Set();
1380
+ for (let grew = true; grew; ) {
1381
+ grew = false;
1382
+ for (let n = 0; n < members.length; n++) {
1383
+ if (take.has(n) || !onEdge(members[n])) continue;
1384
+ const [a1, a2] = span(members[n]);
1385
+ if (overlap(a1, a2, l.e1, l.e2) <= -(CLIP_SPACING_MAX + CLIP_COORD_TOL)) continue;
1386
+ take.add(n);
1387
+ l.e1 = Math.min(l.e1, a1);
1388
+ l.e2 = Math.max(l.e2, a2);
1389
+ grew = true;
1390
+ }
1391
+ }
1392
+ for (const n of take) {
1393
+ const r = members[n], s = out[n];
1394
+ if (l.axis === "y") {
1395
+ if (al(r.y2, l.lo)) s.y2 = to;
1396
+ if (al(r.y1, l.hi)) s.y1 = to;
1397
+ } else {
1398
+ if (al(r.x2, l.lo)) s.x2 = to;
1399
+ if (al(r.x1, l.hi)) s.x1 = to;
1400
+ }
1401
+ }
1402
+ }
1403
+ return out;
1404
+ }
1405
+ function addNarrowEdgeCols(colXs, rowYs, out, fills) {
1406
+ if (!fills.length) return;
1407
+ const rowIdx = (y) => {
1408
+ const i = nearestIndex(rowYs, y);
1409
+ return Math.abs(rowYs[i] - y) <= CLIP_COORD_TOL ? i : -1;
1410
+ };
1411
+ for (const side of ["l", "r"]) {
1412
+ const edge = side === "l" ? colXs[0] : colXs[colXs.length - 1];
1413
+ const hits = fills.filter((f) => {
1414
+ const w = f.x2 - f.x1;
1415
+ if (w < NARROW_FILL_MIN_W || w >= CLIP_MIN_W2 || Math.abs((side === "l" ? f.x2 : f.x1) - edge) > CLIP_COORD_TOL) return false;
1416
+ const r0 = rowIdx(f.y2), r1 = rowIdx(f.y1);
1417
+ return r0 >= 0 && r1 > r0;
1418
+ });
1419
+ if (!hits.length) continue;
1420
+ let x = side === "l" ? Infinity : -Infinity;
1421
+ for (const f of hits) x = side === "l" ? Math.min(x, f.x1) : Math.max(x, f.x2);
1422
+ if (side === "l") {
1423
+ colXs.unshift(x);
1424
+ for (const c of out) c.col++;
1425
+ } else colXs.push(x);
1426
+ const col = side === "l" ? 0 : colXs.length - 2;
1427
+ const [cx1, cx2] = [colXs[col], colXs[col + 1]];
1428
+ const taken = new Array(rowYs.length - 1).fill(false);
1429
+ for (const f of hits) {
1430
+ const r0 = rowIdx(f.y2), r1 = rowIdx(f.y1);
1431
+ if (taken.slice(r0, r1).some(Boolean)) continue;
1432
+ taken.fill(true, r0, r1);
1433
+ out.push({ row: r0, col, rowSpan: r1 - r0, colSpan: 1, bbox: { x1: cx1, y1: rowYs[r1], x2: cx2, y2: rowYs[r0] } });
1434
+ }
1435
+ for (let r = 0; r < taken.length; r++) {
1436
+ if (!taken[r]) out.push({ row: r, col, rowSpan: 1, colSpan: 1, bbox: { x1: cx1, y1: rowYs[r + 1], x2: cx2, y2: rowYs[r] }, filler: true });
1437
+ }
1438
+ }
1128
1439
  }
1129
1440
  function dropGridsInside(lineGrids, clipGrids, containers = []) {
1130
1441
  if (clipGrids.length === 0 && containers.length === 0) return lineGrids;
@@ -1242,6 +1553,165 @@ function hasHorizontalLine(horizontals, y, leftX, rightX, vertexRadius) {
1242
1553
  return false;
1243
1554
  }
1244
1555
 
1556
+ // src/pdf/line-wrap.ts
1557
+ var MARKUP = /<\/?u>|~~/g;
1558
+ var bump = (m, k) => {
1559
+ m.set(k, (m.get(k) ?? 0) + 1);
1560
+ };
1561
+ var pairKey = (a, b) => a * 65536 + b;
1562
+ var bump2 = (m, k1, k2) => {
1563
+ let inner = m.get(k1);
1564
+ if (!inner) m.set(k1, inner = /* @__PURE__ */ new Map());
1565
+ bump(inner, k2);
1566
+ };
1567
+ var WrapLexicon = class {
1568
+ joined1 = /* @__PURE__ */ new Map();
1569
+ joined2 = /* @__PURE__ */ new Map();
1570
+ spaced1 = /* @__PURE__ */ new Map();
1571
+ spaced2 = /* @__PURE__ */ new Map();
1572
+ /** 줄 안(줄 첫·끝 어절 제외 — 꺾인 조각일 수 있다) 홀로 선 어절 수 — 한 음절 조각이 낱말("등"·"및"·"그")인지 가른다 */
1573
+ words = /* @__PURE__ */ new Map();
1574
+ /** 줄 글 한 줄을 증거로 더한다 — 탭(큰 갭)으로 나뉜 조각은 다른 칸·단이라 조각 사이는 어절 경계 증거로 쓰지 않는다 */
1575
+ addLine(text) {
1576
+ for (const seg of text.replace(MARKUP, "").split(" ")) {
1577
+ const toks = seg.split(" ").filter((t) => t && !t.includes("]("));
1578
+ for (let i = 0; i < toks.length; i++) {
1579
+ const t = toks[i], n = t.length;
1580
+ if (i > 0 && i < toks.length - 1) bump(this.words, t);
1581
+ for (let a = 0; a + 1 < n; a++) {
1582
+ const c1 = t.charCodeAt(a), c2 = t.charCodeAt(a + 1);
1583
+ bump(this.joined1, pairKey(c1, c2));
1584
+ if (a >= 1 && a + 2 < n) bump2(this.joined2, pairKey(t.charCodeAt(a - 1), c1), pairKey(c2, t.charCodeAt(a + 2)));
1585
+ }
1586
+ if (i + 1 < toks.length) {
1587
+ const u = toks[i + 1];
1588
+ bump(this.spaced1, pairKey(t.charCodeAt(n - 1), u.charCodeAt(0)));
1589
+ if (n >= 2 && u.length >= 2) bump2(this.spaced2, pairKey(t.charCodeAt(n - 2), t.charCodeAt(n - 1)), pairKey(u.charCodeAt(0), u.charCodeAt(1)));
1590
+ }
1591
+ }
1592
+ }
1593
+ }
1594
+ /** 문서 줄 안에서 홀로 선 어절로 나온 적 있나 */
1595
+ isWord(word) {
1596
+ return (this.words.get(word) ?? 0) > 0;
1597
+ }
1598
+ /** 꺾인 자리 증거: "" 붙음 · " " 띄움 · null 모름 (두 글자 증거가 갈리거나 없으면 한 글자) */
1599
+ evidence(left, right) {
1600
+ const n = left.length;
1601
+ if (n >= 2 && right.length >= 2) {
1602
+ const k1 = pairKey(left.charCodeAt(n - 2), left.charCodeAt(n - 1)), k2 = pairKey(right.charCodeAt(0), right.charCodeAt(1));
1603
+ const v = decideCounts(this.joined2.get(k1)?.get(k2) ?? 0, this.spaced2.get(k1)?.get(k2) ?? 0);
1604
+ if (v !== null) return v;
1605
+ }
1606
+ if (n && right.length) {
1607
+ const k = pairKey(left.charCodeAt(n - 1), right.charCodeAt(0));
1608
+ return decideCounts(this.joined1.get(k) ?? 0, this.spaced1.get(k) ?? 0);
1609
+ }
1610
+ return null;
1611
+ }
1612
+ };
1613
+ var decideCounts = (joined, spaced) => joined && !spaced ? "" : spaced && !joined ? " " : null;
1614
+ var hasBatchim = (c) => {
1615
+ const k = c.charCodeAt(0) - 44032;
1616
+ return k >= 0 && k < 11172 && k % 28 !== 0;
1617
+ };
1618
+ var CLOSE_TAIL = /[’”」』)\]〉》>]+$/;
1619
+ var TAIL = "[.,)\u300D\u300F\u2019\u201D]*";
1620
+ var PARTICLE = new RegExp(`^(?:\uB2E4\\.|\uC758|\uC5D0|\uC5D0\uC11C|\uC5D0\uB294|\uC5D0\uAC8C|\uC5D0\uB3C4|\uC5D0\uC11C\uB294|\uAE4C\uC9C0|\uBD80\uD130|\uCC98\uB7FC|\uB9C8\uB2E4|\uB9CC|\uB3C4|\uB4E4|\uB4E4\uC774|\uB4E4\uC758|\uB4E4\uC740|\uB4E4\uC744|\uB4E4\uC5D0\uAC8C|\uC11C\uB294|\uC11C\uB3C4|\uB77C\uACE0|\uB77C\uBA70|\uC740|\uB294|\uC744|\uB97C|\uC73C\uB85C|\uB85C|\uC73C\uB85C\uC11C|\uB85C\uC11C|\uC73C\uB85C\uC368|\uB85C\uC368)${TAIL}$`);
1621
+ var PARTICLE_AFTER_CONS = new RegExp(`^(?:\uC774|\uACFC|\uC774\uB2E4|\uC774\uBA70|\uC774\uACE0|\uC774\uB098|\uC774\uB77C\uB294|\uC774\uB77C\uACE0|\uC774\uB77C\uBA70)${TAIL}$`);
1622
+ var PARTICLE_AFTER_VOWEL = new RegExp(`^(?:\uAC00|\uC640|\uB77C\uB294)${TAIL}$`);
1623
+ var VERB_FORM = new RegExp(`^(?:\uD558\uB294|\uD558\uC5EC|\uD558\uACE0|\uD558\uBA70|\uD558\uC600\uB2E4|\uD558\uC600\uC73C\uBA70|\uD558\uC600\uACE0|\uD558\uC600\uB2E4\uACE0|\uD588\uB2E4|\uD588\uC73C\uBA70|\uD588\uACE0|\uD588\uB2E4\uACE0|\uD588\uB2E4\uBA70|\uD55C\uB2E4|\uD55C\uB2E4\uACE0|\uD55C\uB2E4\uBA70|\uD55C\uB2E4\uB294|\uD560|\uD568|\uD558\uACA0\uB2E4|\uD558\uACA0\uB2E4\uACE0|\uD558\uACA0\uC2B5\uB2C8\uB2E4|\uD558\uAE30\uB85C|\uD558\uBA74|\uD558\uBA74\uC11C|\uD558\uB3C4\uB85D|\uD574\uC57C|\uB418\uB294|\uB418\uC5B4|\uB41C|\uB41C\uB2E4|\uB41C\uB2E4\uACE0|\uB41C\uB2E4\uB294|\uB418\uC5C8\uB2E4|\uB418\uC5C8\uC73C\uBA70|\uB418\uC5C8\uACE0|\uB410\uB2E4|\uB410\uC73C\uBA70|\uB418\uBA70|\uB418\uACE0|\uB418\uBA74|\uB428|\uB3FC)${TAIL}$`);
1624
+ var AUX_BEFORE = /(?:야|록|로|게|도|히|자|를|을|면|서|고|며|지|라)$/;
1625
+ var BECOME = /^[되된됐됨돼]/;
1626
+ var STANDALONE_SYLLABLE = /^(?:및|등|수|것|그|이|저|더|또|각|약|총|중|간|때|뿐|듯|채|전|후|내|외|한|두|세|네|몇|새|첫|온|본|곧|꼭|잘|못|안|좀|늘|다|왜|뭐|시|할|데|뒤|된)$/;
1627
+ var DATE_DAY_END = /(?:^|\s)\d*월\s+\d*일$/;
1628
+ var COUNTER = /^(?:명|일|월|년|개|원|건|회|차|호|조|항|층|톤|대|곳|시|분|초|배|위|점|주|종|억|만|천|%|퍼센트)/;
1629
+ function endsAsWord(word) {
1630
+ const h = word.replace(CLOSE_TAIL, "");
1631
+ if (!/^[가-힣]{2,}$/.test(h)) return false;
1632
+ if (/(?:을|를|는|은|히|른|할|야|게|의)$/.test(h)) return true;
1633
+ const prev = h[h.length - 2];
1634
+ if (/(?:이|과)$/.test(h)) return hasBatchim(prev);
1635
+ if (/(?:가|와)$/.test(h)) return !hasBatchim(prev);
1636
+ return false;
1637
+ }
1638
+ function particleContinues(left, right) {
1639
+ const h = left.replace(CLOSE_TAIL, "");
1640
+ const last = h[h.length - 1];
1641
+ if (!last || !/[가-힣A-Za-z0-9]/.test(last)) return false;
1642
+ if (PARTICLE.test(right)) return true;
1643
+ if (!/[가-힣]/.test(last)) return false;
1644
+ if (PARTICLE_AFTER_CONS.test(right)) return hasBatchim(last);
1645
+ if (PARTICLE_AFTER_VOWEL.test(right)) return !hasBatchim(last);
1646
+ return VERB_FORM.test(right) && !AUX_BEFORE.test(h) && !(last === "\uC774" && BECOME.test(right));
1647
+ }
1648
+ var ITEM_HEAD = /^(?:[□■◆◇○●◎◦▪▫•※▶▷►❍❏❑✓✔➢➤☞]|[①-⑳]|[-–·∙ㆍ*](?=\s)|\(?\d{1,2}[.)](?!\d)|\(?[가-하][.)]|\([가-하\d]{1,2}\)|[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]|제\d+[조항호장절])/;
1649
+ function startsNewItem(prevText, nextText) {
1650
+ const t = nextText.replace(MARKUP, "").trimStart();
1651
+ return ITEM_HEAD.test(t) && !(/^다\./.test(t) && /[가-힣]$/.test(prevText.replace(MARKUP, "").trimEnd()));
1652
+ }
1653
+ function wrapJoiner(prevText, nextText, lex) {
1654
+ const prev = prevText.replace(MARKUP, "").trimEnd(), next = nextText.replace(MARKUP, "").trimStart();
1655
+ const a = prev[prev.length - 1], b = next[0];
1656
+ if (!a || !b || /[,;:!?]/.test(a) || DATE_DAY_END.test(prev) && b !== "(") return " ";
1657
+ let s = prev.length;
1658
+ while (s > 0 && !/\s/.test(prev[s - 1])) s--;
1659
+ const left = prev.slice(s), right = next.match(/^\S+/)[0];
1660
+ if (particleContinues(left, right)) return "";
1661
+ if (endsAsWord(left)) return " ";
1662
+ if (/[·ㆍ‧-]/.test(a) && /[가-힣A-Za-z0-9]/.test(b)) return "";
1663
+ if (/[A-Za-z]/.test(a) && /[A-Za-z]/.test(b) || /\d/.test(a) && /\d/.test(b)) return " ";
1664
+ const ev = lex?.evidence(left, right);
1665
+ if (ev != null) return ev;
1666
+ if (/\d$/.test(left) && COUNTER.test(right)) return "";
1667
+ if (lex && /^[가-힣]$/.test(left) && /^[가-힣]/.test(right) && !STANDALONE_SYLLABLE.test(left) && ([...right].length === 1 || !lex.isWord(left))) return "";
1668
+ const r1 = right.replace(/[.,)」』’”]+$/, "");
1669
+ if (lex && /[가-힣]$/.test(left) && /^[가-힣]$/.test(r1) && !STANDALONE_SYLLABLE.test(r1) && !lex.isWord(r1) && !lex.isWord(right)) return "";
1670
+ return " ";
1671
+ }
1672
+ var PARA_LAST_LINE = /* @__PURE__ */ new WeakMap();
1673
+ function joinPageBreakWraps(blocks, lex) {
1674
+ const pageRight = /* @__PURE__ */ new Map();
1675
+ for (const b of blocks) {
1676
+ if (b.type !== "paragraph" || !b.bbox || !b.pageNumber) continue;
1677
+ pageRight.set(b.pageNumber, Math.max(pageRight.get(b.pageNumber) ?? -Infinity, b.bbox.x + b.bbox.width));
1678
+ }
1679
+ for (let i = blocks.length - 1; i > 0; i--) {
1680
+ const a = blocks[i - 1], b = blocks[i];
1681
+ if (!a.pageNumber || b.pageNumber !== a.pageNumber + 1 || !a.text || !b.text) continue;
1682
+ if (a.type !== "paragraph" && a.type !== "list" || b.type !== "paragraph") continue;
1683
+ const last = a.bbox && PARA_LAST_LINE.get(a.bbox), fs = last ? last.fontSize : 0;
1684
+ if (!last || fs <= 0 || (pageRight.get(a.pageNumber) ?? Infinity) - last.right >= BODY_FULL_TOL * fs) continue;
1685
+ if (last.width < BODY_MIN_WIDTH_EM * fs || Math.abs((b.style?.fontSize ?? 0) - fs) > 0.15 * fs) continue;
1686
+ if (startsNewItem(a.text, b.text)) continue;
1687
+ a.text += wrapJoiner(a.text, b.text, lex) + b.text;
1688
+ const bl = b.bbox && PARA_LAST_LINE.get(b.bbox);
1689
+ if (bl) PARA_LAST_LINE.set(a.bbox, bl);
1690
+ else PARA_LAST_LINE.delete(a.bbox);
1691
+ blocks.splice(i, 1);
1692
+ }
1693
+ }
1694
+ var BODY_FULL_TOL = 0.25;
1695
+ var BODY_MIN_WIDTH_EM = 12;
1696
+ var BODY_MAX_PITCH_EM = 2;
1697
+ function bodyLineJoins(lines, lex) {
1698
+ let right = -Infinity;
1699
+ for (const l of lines) if (l.right > right) right = l.right;
1700
+ const out = [];
1701
+ for (let i = 0; i + 1 < lines.length; i++) {
1702
+ const a = lines[i], b = lines[i + 1];
1703
+ const fs = a.fontSize;
1704
+ const wraps = fs > 0 && right - a.right < BODY_FULL_TOL * fs && a.right - a.left >= BODY_MIN_WIDTH_EM * fs && a.y - b.y > 0 && a.y - b.y < BODY_MAX_PITCH_EM * fs && Math.abs(b.fontSize - fs) <= 0.15 * fs && !startsNewItem(a.text, b.text);
1705
+ out.push(wraps ? wrapJoiner(a.text, b.text, lex) : "\n");
1706
+ }
1707
+ return out;
1708
+ }
1709
+ var CELL_PAD_MAX = 6;
1710
+ function cellLineWraps(box, contentLeft, prevRight, fontSize, nextFirstCharW) {
1711
+ const pad = Math.min(CELL_PAD_MAX, Math.max(0, contentLeft - box.x1));
1712
+ return fontSize > 0 && prevRight + nextFirstCharW - (box.x2 - pad) > 0.5 * fontSize;
1713
+ }
1714
+
1245
1715
  // src/pdf/cell-text.ts
1246
1716
  var CELL_PADDING = 2;
1247
1717
  var SPACE_GAP_RATIO = 0.17;
@@ -1275,9 +1745,47 @@ function mapTextToCells(items, cells) {
1275
1745
  result.get(bestCell).push(item);
1276
1746
  }
1277
1747
  }
1748
+ keepWordsInOneCell(result);
1278
1749
  return result;
1279
1750
  }
1280
- function cellTextToString(items) {
1751
+ function keepWordsInOneCell(result) {
1752
+ const owner = /* @__PURE__ */ new Map();
1753
+ for (const [cell, arr] of result) for (const it of arr) owner.set(it, cell);
1754
+ if (result.size < 2 || owner.size < 2) return;
1755
+ const all = [...owner.keys()].sort((a, b) => b.y - a.y || a.x - b.x);
1756
+ for (let i = 0; i < all.length; ) {
1757
+ let j = i + 1;
1758
+ while (j < all.length && Math.abs(all[j].y - all[i].y) <= 1) j++;
1759
+ const line = all.slice(i, j).sort((a, b) => a.x - b.x);
1760
+ for (let k = 0; k < line.length; ) {
1761
+ let e = k + 1;
1762
+ while (e < line.length && !line[e].hasSpaceBefore && line[e].x - (line[e - 1].x + line[e - 1].w) <= spaceGapThreshold((line[e].fontSize + line[e - 1].fontSize) / 2)) e++;
1763
+ const word = line.slice(k, e);
1764
+ const width = /* @__PURE__ */ new Map();
1765
+ for (const it of word) {
1766
+ const c = owner.get(it);
1767
+ width.set(c, (width.get(c) ?? 0) + it.w);
1768
+ }
1769
+ const crosses = word.some((it) => {
1770
+ const b = owner.get(it).bbox;
1771
+ return it.x < b.x1 - 1 || it.x + it.w > b.x2 + 1;
1772
+ });
1773
+ if (width.size > 1 && crosses) {
1774
+ const target = [...width].sort((a, b) => b[1] - a[1])[0][0];
1775
+ for (const it of word) {
1776
+ const c = owner.get(it);
1777
+ if (c === target) continue;
1778
+ result.set(c, result.get(c).filter((x) => x !== it));
1779
+ result.get(target).push(it);
1780
+ owner.set(it, target);
1781
+ }
1782
+ }
1783
+ k = e;
1784
+ }
1785
+ i = j;
1786
+ }
1787
+ }
1788
+ function cellTextToString(items, wrap) {
1281
1789
  if (items.length === 0) return "";
1282
1790
  if (items.length === 1) return items[0].text;
1283
1791
  const sorted = [...items].sort((a, b) => b.y - a.y || a.x - b.x);
@@ -1297,7 +1805,7 @@ function cellTextToString(items) {
1297
1805
  lines.push(curLine);
1298
1806
  const merged = mergeSuperscriptRows(lines);
1299
1807
  const textLines = merged.map((line) => {
1300
- const s = line.sort((a, b) => a.x - b.x);
1808
+ const s = sortLineByX(line);
1301
1809
  if (s.length === 1) return s[0].text;
1302
1810
  const evenSpaced = detectEvenSpacedItems(s);
1303
1811
  let result = s[0].text;
@@ -1318,7 +1826,18 @@ function cellTextToString(items) {
1318
1826
  }
1319
1827
  return result;
1320
1828
  });
1321
- return mergeCellTextLines(textLines);
1829
+ if (!wrap) return mergeCellTextLines(textLines);
1830
+ let contentLeft = Infinity;
1831
+ for (const it of items) if (it.x < contentLeft) contentLeft = it.x;
1832
+ const lineEnds = merged.map((line) => {
1833
+ const s = sortLineByX(line);
1834
+ let right = -Infinity;
1835
+ for (const it of s) if (it.x + it.w > right) right = it.x + it.w;
1836
+ const first = s[0];
1837
+ return { right, fontSize: first.fontSize, firstCharW: first.w / Math.max(1, [...first.text].length) };
1838
+ });
1839
+ const wraps = lineEnds.slice(0, -1).map((a, i) => cellLineWraps(wrap.box, contentLeft, a.right, a.fontSize, lineEnds[i + 1].firstCharW));
1840
+ return mergeCellTextLines(textLines, { wraps, lex: wrap.lex });
1322
1841
  }
1323
1842
  function mergeSuperscriptRows(lines) {
1324
1843
  if (lines.length <= 1) return lines;
@@ -1407,19 +1926,19 @@ function markEvenRun(items, result, start, end) {
1407
1926
  }
1408
1927
  }
1409
1928
  }
1410
- function mergeCellTextLines(textLines) {
1929
+ function mergeCellTextLines(textLines, wrap) {
1411
1930
  if (textLines.length <= 1) return textLines[0] || "";
1412
1931
  const merged = [textLines[0]];
1413
1932
  for (let i = 1; i < textLines.length; i++) {
1414
1933
  const prev = merged[merged.length - 1];
1415
1934
  const curr = textLines[i];
1416
- if (/[가-힣]$/.test(prev) && /^[가-힣]+$/.test(curr) && curr.length <= 8 && !curr.includes(" ")) {
1935
+ if (wrap ? wrap.wraps[i - 1] && !startsNewItem(prev, curr) && wrapJoiner(prev, curr, wrap.lex) === "" : /[가-힣]$/.test(prev) && /^[가-힣]+$/.test(curr) && curr.length <= 8 && !curr.includes(" ")) {
1417
1936
  merged[merged.length - 1] = prev + curr;
1418
1937
  } else if (curr.trim().length <= 3 && /^[)\]%}]/.test(curr.trim())) {
1419
1938
  merged[merged.length - 1] = prev + curr.trim();
1420
- } else if (/[,(]$/.test(prev.trim()) && curr.trim().length <= 15) {
1939
+ } else if (!wrap && /[,(]$/.test(prev.trim()) && curr.trim().length <= 15) {
1421
1940
  merged[merged.length - 1] = prev + curr.trim();
1422
- } else if (/[\d,]$/.test(prev) && /^[\d,]+[)\]]?$/.test(curr.trim()) && curr.trim().length <= 10) {
1941
+ } else if (/[\d,]$/.test(prev) && /^[\d,]+[)\]]?$/.test(curr.trim()) && curr.trim().length <= 10 && !(/\d,\d{3}$/.test(prev) && /^\d/.test(curr.trim())) && !(/(?:^|[^\d,])\d{1,3}$/.test(prev) && /^(\d{1,3}(,\d{3})+|\d{1,3})$/.test(curr.trim()))) {
1423
1942
  merged[merged.length - 1] = prev + curr.trim();
1424
1943
  } else {
1425
1944
  merged.push(curr);
@@ -1539,11 +2058,24 @@ function normalizeUndersegmentedTable(originalCells, colXs, items) {
1539
2058
  return n;
1540
2059
  };
1541
2060
  if (countNonEmptyRows(rebuilt) <= countNonEmptyRows(originalCells)) return null;
1542
- if (countNonEmptyCols(rebuilt, numCols) < countNonEmptyCols(originalCells, numCols)) return null;
2061
+ const origBody = numRows > 1 ? originalCells.slice(1) : originalCells;
2062
+ if (countNonEmptyCols(rebuilt, numCols) < countNonEmptyCols(origBody, numCols)) return null;
1543
2063
  return rebuilt;
1544
2064
  }
1545
2065
 
1546
2066
  // src/pdf/text-line.ts
2067
+ function sortLineByX(items) {
2068
+ items.sort((a, b) => a.x - b.x);
2069
+ for (let i = 1; i < items.length; i++) {
2070
+ for (let j = i; j > 0; j--) {
2071
+ const a = items[j - 1], b = items[j];
2072
+ if (b.x - a.x > 1 || a.seq === void 0 || b.seq === void 0 || b.seq >= a.seq) break;
2073
+ items[j - 1] = b;
2074
+ items[j] = a;
2075
+ }
2076
+ }
2077
+ return items;
2078
+ }
1547
2079
  function filterHiddenText(items, pageWidth, pageHeight) {
1548
2080
  let hiddenCount = 0;
1549
2081
  const visible = [];
@@ -1606,7 +2138,9 @@ function dominantStyle(items) {
1606
2138
  function normalizeItems(rawItems) {
1607
2139
  const items = [];
1608
2140
  const spacePositions = [];
2141
+ let seq = 0;
1609
2142
  for (const i of rawItems) {
2143
+ seq++;
1610
2144
  if (typeof i.str !== "string") continue;
1611
2145
  const x = Math.round(i.transform[4]);
1612
2146
  const y = Math.round(i.transform[5]);
@@ -1626,11 +2160,11 @@ function normalizeItems(rawItems) {
1626
2160
  }
1627
2161
  const split = splitEvenSpacedItem(text, x, w, fontSize);
1628
2162
  if (split) {
1629
- for (const s of split) {
1630
- items.push({ text: s.text, x: s.x, y, w: s.w, h, fontSize, fontName: i.fontName || "", isHidden });
1631
- }
2163
+ split.forEach((s, k) => {
2164
+ items.push({ text: s.text, x: s.x, y, w: s.w, h, fontSize, fontName: i.fontName || "", isHidden, seq: seq + k / 1e3 });
2165
+ });
1632
2166
  } else {
1633
- items.push({ text, x, y, w, h, fontSize, fontName: i.fontName || "", isHidden });
2167
+ items.push({ text, x, y, w, h, fontSize, fontName: i.fontName || "", isHidden, seq });
1634
2168
  }
1635
2169
  }
1636
2170
  const sorted = items.sort((a, b) => b.y - a.y || a.x - b.x);
@@ -1648,6 +2182,7 @@ function normalizeItems(rawItems) {
1648
2182
  }
1649
2183
  if (!isDup) deduped.push(sorted[i]);
1650
2184
  }
2185
+ splitOverlaidRuns(deduped);
1651
2186
  if (spacePositions.length > 0) {
1652
2187
  for (const sp of spacePositions) {
1653
2188
  let nearest = null;
@@ -1663,6 +2198,98 @@ function normalizeItems(rawItems) {
1663
2198
  }
1664
2199
  return deduped;
1665
2200
  }
2201
+ var WORD_SPACE_EM = 0.3;
2202
+ var OVERLAY_DUP_EM = 0.5;
2203
+ var OVERLAY_FIT_EM = 0.6;
2204
+ function glyphUnits(s) {
2205
+ let u = 0;
2206
+ for (const ch of s) u += (ch.codePointAt(0) ?? 0) >= 8592 ? 1 : 0.5;
2207
+ return u;
2208
+ }
2209
+ function splitOverlaidRuns(items) {
2210
+ let replaced = null;
2211
+ for (let ci = 0; ci < items.length; ci++) {
2212
+ const c = items[ci];
2213
+ if (c.w <= 0 || !/\S\s+\S/.test(c.text)) continue;
2214
+ const overlays = [];
2215
+ for (let j = ci - 1; j >= 0 && items[j].y - c.y <= 1; j--) if (isInside(items[j], c)) overlays.push(items[j]);
2216
+ for (let j = ci + 1; j < items.length && c.y - items[j].y <= 1; j++) if (isInside(items[j], c)) overlays.push(items[j]);
2217
+ if (overlays.length === 0) continue;
2218
+ const pieces = splitAroundOverlays(c, overlays);
2219
+ if (pieces) (replaced ??= /* @__PURE__ */ new Map()).set(c, pieces);
2220
+ }
2221
+ if (!replaced) return;
2222
+ const out = [];
2223
+ for (const it of items) {
2224
+ const p = replaced.get(it);
2225
+ if (p) out.push(...p);
2226
+ else out.push(it);
2227
+ }
2228
+ items.length = 0;
2229
+ items.push(...out.sort((a, b) => b.y - a.y || a.x - b.x));
2230
+ }
2231
+ function isInside(o, c) {
2232
+ return o.x >= c.x + 1 && o.x + o.w <= c.x + c.w + 1 && o.w < c.w;
2233
+ }
2234
+ function splitAroundOverlays(c, overlays) {
2235
+ const words = c.text.split(/\s+/);
2236
+ const fs = c.fontSize > 0 ? c.fontSize : c.h > 0 ? c.h : 10;
2237
+ const ws = WORD_SPACE_EM * fs;
2238
+ const totalU = words.reduce((s, w) => s + glyphUnits(w), 0);
2239
+ if (totalU <= 0) return null;
2240
+ const a0 = (c.w - (words.length - 1) * ws) / totalU;
2241
+ const ov = overlays.filter((o) => {
2242
+ for (let at = c.text.indexOf(o.text); at >= 0; at = c.text.indexOf(o.text, at + 1)) {
2243
+ const prefix = c.text.slice(0, at);
2244
+ const est = c.x + glyphUnits(prefix.replace(/\s+/g, "")) * a0 + (prefix.match(/\s+/g)?.length ?? 0) * ws;
2245
+ if (Math.abs(est - o.x) <= OVERLAY_DUP_EM * fs) return false;
2246
+ }
2247
+ return true;
2248
+ }).sort((p2, q) => p2.x - q.x);
2249
+ if (ov.length === 0) return null;
2250
+ const holes = Math.min(ov.length, words.length - 1);
2251
+ let ovW = 0;
2252
+ for (const o of ov) ovW += o.w;
2253
+ const a = (c.w - ovW - (words.length - 1 - holes) * ws) / totalU;
2254
+ if (!(a > 0)) return null;
2255
+ const starts = [], ends = [], holeAfter = [];
2256
+ let p = c.x;
2257
+ let k = 0;
2258
+ let assigned = 0;
2259
+ for (let i = 0; i < words.length; i++) {
2260
+ starts.push(p);
2261
+ p += glyphUnits(words[i]) * a;
2262
+ ends.push(p);
2263
+ if (i === words.length - 1) break;
2264
+ while (k < ov.length && ov[k].x < p - a * 0.6) k++;
2265
+ const half = glyphUnits(words[i + 1]) * a / 2;
2266
+ let hole = false;
2267
+ while (k < ov.length && ov[k].x < p + half) {
2268
+ hole = true;
2269
+ p = Math.max(p, ov[k].x + ov[k].w);
2270
+ k++;
2271
+ }
2272
+ holeAfter.push(hole);
2273
+ if (hole) assigned++;
2274
+ else p += ws;
2275
+ }
2276
+ if (assigned === 0 || Math.abs(p - (c.x + c.w)) > Math.max(3, OVERLAY_FIT_EM * fs)) return null;
2277
+ const pieces = [];
2278
+ for (let s = 0; s < words.length; ) {
2279
+ let e = s;
2280
+ while (e < words.length - 1 && !holeAfter[e]) e++;
2281
+ pieces.push({
2282
+ ...c,
2283
+ text: words.slice(s, e + 1).join(" "),
2284
+ x: Math.round(starts[s]),
2285
+ w: Math.max(1, Math.round(ends[e] - starts[s])),
2286
+ hasSpaceBefore: s === 0 ? c.hasSpaceBefore : false,
2287
+ seq: c.seq === void 0 ? void 0 : c.seq + pieces.length / 1e4
2288
+ });
2289
+ s = e + 1;
2290
+ }
2291
+ return pieces;
2292
+ }
1666
2293
  function splitEvenSpacedItem(text, itemX, itemW, fontSize) {
1667
2294
  if (!/^[가-힣\d](?: [가-힣\d]){2,}$/.test(text)) return null;
1668
2295
  const chars = text.split(" ");
@@ -1728,7 +2355,7 @@ function mergeSuperscriptLines(lines) {
1728
2355
  }
1729
2356
  function mergeLineSimple(items) {
1730
2357
  if (items.length <= 1) return items[0]?.text || "";
1731
- const sorted = [...items].sort((a, b) => a.x - b.x);
2358
+ const sorted = sortLineByX([...items]);
1732
2359
  const isEvenSpaced = detectEvenSpacedItems(sorted);
1733
2360
  let result = sorted[0].text;
1734
2361
  for (let i = 1; i < sorted.length; i++) {
@@ -2146,6 +2773,53 @@ function removeHeaderFooterBlocks(blocks, pageHeights, warnings) {
2146
2773
  return [...removeSet].sort((a, b) => a - b);
2147
2774
  }
2148
2775
 
2776
+ // src/pdf/table-meta.ts
2777
+ var CLIP_TABLES = /* @__PURE__ */ new WeakSet();
2778
+ var TABLE_COLXS = /* @__PURE__ */ new WeakMap();
2779
+ var EMPTY_PARTS = /* @__PURE__ */ new WeakSet();
2780
+ var IMAGE_CELLS = /* @__PURE__ */ new WeakSet();
2781
+ var FILLER_CELLS = /* @__PURE__ */ new WeakSet();
2782
+ var CONT_PARTS = /* @__PURE__ */ new WeakMap();
2783
+ var CELL_LINES = /* @__PURE__ */ new WeakMap();
2784
+ function recordCellLines(cell, items) {
2785
+ const lines = [];
2786
+ for (const it of [...items].sort((a, b) => b.y - a.y)) {
2787
+ const fs = it.fontSize || it.h;
2788
+ const last = lines[lines.length - 1];
2789
+ if (last && Math.abs(last.y - it.y) <= Math.max(3, Math.min(fs, last.h) * 0.6)) {
2790
+ last.l = Math.min(last.l, it.x);
2791
+ last.r = Math.max(last.r, it.x + it.w);
2792
+ last.h = Math.max(last.h, fs);
2793
+ } else lines.push({ y: it.y, l: it.x, r: it.x + it.w, h: fs });
2794
+ }
2795
+ CELL_LINES.set(cell, lines);
2796
+ }
2797
+
2798
+ // src/pdf/table-trim.ts
2799
+ function markImageCell(cell) {
2800
+ IMAGE_CELLS.add(cell);
2801
+ }
2802
+ var emptyCell = (cell) => !cell || !cell.text?.trim() && !cell.blocks?.length && !IMAGE_CELLS.has(cell);
2803
+ function trimTable(table) {
2804
+ let cols = table.cols;
2805
+ while (cols > 0 && table.cells.every((row) => emptyCell(row[cols - 1]))) cols--;
2806
+ if (cols === table.cols || cols === 0) return;
2807
+ table.cells = table.cells.map((row) => row.slice(0, cols));
2808
+ for (const row of table.cells) {
2809
+ for (let c = 0; c < row.length; c++) {
2810
+ if (c + row[c].colSpan > cols) row[c].colSpan = cols - c;
2811
+ }
2812
+ }
2813
+ table.cols = cols;
2814
+ }
2815
+ function trimTrailingEmptyTableCols(blocks) {
2816
+ for (const b of blocks) {
2817
+ if (b.type !== "table" || !b.table) continue;
2818
+ for (const row of b.table.cells) for (const cell of row) if (cell.blocks?.length) trimTrailingEmptyTableCols(cell.blocks);
2819
+ trimTable(b.table);
2820
+ }
2821
+ }
2822
+
2149
2823
  // src/pdf/cluster-detector.ts
2150
2824
  var Y_TOL = 3;
2151
2825
  var COL_CLUSTER_TOL = 15;
@@ -2242,18 +2916,16 @@ function findTwoColumnProseCutX(items) {
2242
2916
  const right = [];
2243
2917
  let twoSide = 0;
2244
2918
  for (const line of lines) {
2245
- let hasL = false;
2246
- let hasR = false;
2919
+ const L = [], R = [];
2247
2920
  for (const i of line.items) {
2248
- if (i.x + i.w <= cutX) {
2249
- left.push(i);
2250
- hasL = true;
2251
- } else if (i.x >= cutX) {
2252
- right.push(i);
2253
- hasR = true;
2254
- }
2921
+ if (i.x + i.w <= cutX) L.push(i);
2922
+ else if (i.x >= cutX) R.push(i);
2923
+ }
2924
+ if (L.length && R.length) {
2925
+ twoSide++;
2926
+ left.push(...L);
2927
+ right.push(...R);
2255
2928
  }
2256
- if (hasL && hasR) twoSide++;
2257
2929
  }
2258
2930
  if (twoSide / lines.length < 0.55) return null;
2259
2931
  if (left.length < 5 || right.length < 5) return null;
@@ -2663,7 +3335,7 @@ function buildClusterTable(rows, columns, pageNum) {
2663
3335
  }
2664
3336
  const assignments = assignRowItems(row.items, columns, numCols);
2665
3337
  for (const { col, items } of assignments) {
2666
- const text = items.map((i) => i.text).join(" ");
3338
+ const text = joinCellItems(items);
2667
3339
  const existing = cells[r][col].text;
2668
3340
  cells[r][col].text = existing ? existing + " " + text : text;
2669
3341
  for (const item of items) usedItems.add(item);
@@ -2686,6 +3358,7 @@ function buildClusterTable(rows, columns, pageNum) {
2686
3358
  if (/^[○●▶\-·]/.test(contentText)) continue;
2687
3359
  for (let pr = r - 1; pr >= 0; pr--) {
2688
3360
  if (cells[pr].some((c) => c.text.trim())) {
3361
+ if (cells[pr][0].colSpan > 1) break;
2689
3362
  for (let c = 0; c < numCols; c++) {
2690
3363
  const prev = cells[pr][c].text.trim();
2691
3364
  const curr = cells[r][c].text.trim();
@@ -2736,6 +3409,29 @@ function buildClusterTable(rows, columns, pageNum) {
2736
3409
  usedItems
2737
3410
  };
2738
3411
  }
3412
+ function joinCellItems(items) {
3413
+ const hOf = (i) => i.h > 0 ? i.h : i.fontSize;
3414
+ const lines = [];
3415
+ for (const it of [...items].sort((a, b) => b.y - a.y || a.x - b.x)) {
3416
+ const bottom = it.y, top = it.y + hOf(it);
3417
+ const line = lines.find((l) => Math.min(l.top, top) - Math.max(l.bottom, bottom) >= Math.min(l.top - l.bottom, top - bottom) * 0.5);
3418
+ if (line) {
3419
+ line.items.push(it);
3420
+ line.bottom = Math.min(line.bottom, bottom);
3421
+ line.top = Math.max(line.top, top);
3422
+ } else lines.push({ bottom, top, items: [it] });
3423
+ }
3424
+ return lines.sort((a, b) => b.top - a.top).map(({ items: line }) => {
3425
+ line.sort((a, b) => a.x - b.x);
3426
+ let s = line[0].text;
3427
+ for (let i = 1; i < line.length; i++) {
3428
+ const gap = line[i].x - (line[i - 1].x + line[i - 1].w);
3429
+ const fs = (line[i].fontSize + line[i - 1].fontSize) / 2;
3430
+ s += (line[i].hasSpaceBefore && gap >= fs * 0.05 || gap > spaceGapThreshold(fs) ? " " : "") + line[i].text;
3431
+ }
3432
+ return s;
3433
+ }).join(" ");
3434
+ }
2739
3435
 
2740
3436
  // src/pdf/xy-cut.ts
2741
3437
  var MAX_XYCUT_DEPTH = 50;
@@ -3068,7 +3764,10 @@ function detectColumns(yLines) {
3068
3764
  const xClusters = [];
3069
3765
  for (const line of tableYLines) {
3070
3766
  if (isProseSpread(line)) continue;
3071
- for (const item of line) {
3767
+ const byX = [...line].sort((a, b) => a.x - b.x);
3768
+ for (let k = 0; k < byX.length; k++) {
3769
+ const item = byX[k];
3770
+ if (k > 0 && item.x - (byX[k - 1].x + byX[k - 1].w) < Math.max(item.fontSize, byX[k - 1].fontSize)) continue;
3072
3771
  let found = false;
3073
3772
  for (const c of xClusters) {
3074
3773
  if (Math.abs(item.x - c.center) <= CLUSTER_TOL) {
@@ -3373,12 +4072,26 @@ function wrapUnderlineRuns(items) {
3373
4072
  }
3374
4073
 
3375
4074
  // src/pdf/page-blocks.ts
3376
- function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true) {
3377
- if (items.length === 0) return [];
4075
+ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true, carry, lexicon) {
4076
+ if (items.length === 0) {
4077
+ if (carry) carry.clip = void 0;
4078
+ return [];
4079
+ }
4080
+ const lex = lexicon ?? new WrapLexicon();
4081
+ for (const line of streamLines(items)) lex.addLine(mergeLineSimple(line));
3378
4082
  const extracted = extractLines(opList.fnArray, opList.argsArray);
3379
4083
  let { horizontals, verticals } = extracted;
3380
- const clipResult = detectTables ? buildClipCellGrids(extracted.clipRects, horizontals, verticals, pageWidth, pageHeight, items.map((it) => ({ x: it.x + it.w / 2, y: it.y + it.h / 2 }))) : { grids: [], containers: [] };
4084
+ const prevPage = carry?.page === pageNum - 1 ? carry.clip : void 0;
4085
+ const clipResult = detectTables ? buildClipCellGrids(extracted.clipRects, horizontals, verticals, pageWidth, pageHeight, items.map((it) => ({ x: it.x + it.w / 2, y: it.y + it.h / 2 })), extracted.fillRects, prevPage) : { grids: [], containers: [], page: void 0 };
4086
+ if (carry) {
4087
+ carry.page = pageNum;
4088
+ carry.clip = clipResult.page;
4089
+ }
3381
4090
  const clipGrids = clipResult.grids;
4091
+ if (clipGrids.length === 0) {
4092
+ horizontals = chainShortSegments(horizontals, extracted.shortH, "h");
4093
+ verticals = chainShortSegments(verticals, extracted.shortV, "v");
4094
+ }
3382
4095
  if (extraLines) {
3383
4096
  horizontals = horizontals.concat(extraLines.horizontals);
3384
4097
  verticals = verticals.concat(extraLines.verticals);
@@ -3393,11 +4106,13 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
3393
4106
  markUnderlineItems(items, horizontals, verticals);
3394
4107
  wrapUnderlineRuns(items);
3395
4108
  const lineGrids = detectTables ? buildTableGrids(horizontals, verticals) : [];
3396
- const grids = [...clipGrids, ...dropGridsInside(lineGrids, clipGrids, clipResult.containers)];
4109
+ const tableClipGrids = dropShadingClipGrids(clipGrids, lineGrids, extracted.fillRects, verticals);
4110
+ const grids = [...tableClipGrids, ...dropGridsInside(lineGrids, tableClipGrids, clipResult.containers)];
3397
4111
  if (grids.length > 0) {
3398
- return extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals);
4112
+ const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
4113
+ return extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, imageRegions, lex);
3399
4114
  }
3400
- return extractPageBlocksFallback(items, pageNum, true, detectTables);
4115
+ return extractPageBlocksFallback(items, pageNum, true, detectTables, lex);
3401
4116
  }
3402
4117
  var STRIKE_MAX_THICKNESS = 2;
3403
4118
  var STRIKE_MAX_THICKNESS_RATIO = 0.25;
@@ -3506,13 +4221,13 @@ function takePendingNested(pending, cellBox) {
3506
4221
  }
3507
4222
  return out;
3508
4223
  }
3509
- function buildFrameCellBlocks(cellItems, nested, pageNum) {
4224
+ function buildFrameCellBlocks(cellItems, nested, pageNum, wrap) {
3510
4225
  const tables = [...nested].sort((a, b) => b.bbox.y + b.bbox.height - (a.bbox.y + a.bbox.height));
3511
4226
  const blocks = [];
3512
4227
  let rest = [...cellItems];
3513
4228
  const pushParagraphs = (items) => {
3514
4229
  if (items.length === 0) return;
3515
- for (const line of cleanCellText(cellTextToString(items)).split("\n")) {
4230
+ for (const line of cleanCellText(cellTextToString(items, wrap)).split("\n")) {
3516
4231
  const t = line.trim();
3517
4232
  if (t) blocks.push({ type: "paragraph", text: t, pageNumber: pageNum });
3518
4233
  }
@@ -3527,7 +4242,7 @@ function buildFrameCellBlocks(cellItems, nested, pageNum) {
3527
4242
  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");
3528
4243
  return { blocks, text };
3529
4244
  }
3530
- function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
4245
+ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, imageRegions = [], lex) {
3531
4246
  const blocks = [];
3532
4247
  const usedItems = /* @__PURE__ */ new Set();
3533
4248
  const pendingNested = [];
@@ -3559,7 +4274,8 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3559
4274
  h: i.h,
3560
4275
  fontSize: i.fontSize,
3561
4276
  fontName: i.fontName,
3562
- hasSpaceBefore: i.hasSpaceBefore
4277
+ hasSpaceBefore: i.hasSpaceBefore,
4278
+ seq: i.seq
3563
4279
  }));
3564
4280
  const cellTextMap = mapTextToCells(textItems, cells);
3565
4281
  const assignedItems = /* @__PURE__ */ new Set();
@@ -3576,20 +4292,31 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3576
4292
  const nested = grid.cells ? takePendingNested(pendingNested, cell.bbox) : [];
3577
4293
  if (nested.length > 0) {
3578
4294
  nestedAttached = true;
3579
- const built = buildFrameCellBlocks(cellItems, nested, pageNum);
4295
+ const built = buildFrameCellBlocks(cellItems, nested, pageNum, { box: cell.bbox, lex });
3580
4296
  irGrid[cell.row][cell.col] = { text: built.text, colSpan: cell.colSpan, rowSpan: cell.rowSpan, blocks: built.blocks };
4297
+ if (cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
3581
4298
  continue;
3582
4299
  }
3583
4300
  irGrid[cell.row][cell.col] = {
3584
- text: cleanCellText(cellTextToString(cellItems)),
4301
+ text: cleanCellText(cellTextToString(cellItems, { box: cell.bbox, lex })),
3585
4302
  colSpan: cell.colSpan,
3586
4303
  rowSpan: cell.rowSpan
3587
4304
  };
4305
+ const b = cell.bbox;
4306
+ const isBackdrop = (r) => r.x1 <= b.x1 + 1 && r.x2 >= b.x2 - 1 && r.y1 <= b.y1 + 1 && r.y2 >= b.y2 - 1;
4307
+ if (imageRegions.some((r) => {
4308
+ const cx = (r.x1 + r.x2) / 2, cy = (r.y1 + r.y2) / 2;
4309
+ return cx > b.x1 && cx < b.x2 && cy > b.y1 && cy < b.y2 && !isBackdrop(r);
4310
+ })) {
4311
+ markImageCell(irGrid[cell.row][cell.col]);
4312
+ }
4313
+ if (cell.filler && !cellItems.length) FILLER_CELLS.add(irGrid[cell.row][cell.col]);
4314
+ if (grid.cells && cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
3588
4315
  }
3589
4316
  let finalGrid = irGrid;
3590
4317
  let finalRows = numRows;
3591
4318
  let rebuiltUsed = false;
3592
- if (numRows <= 2 && numCols >= 3 && !nestedAttached) {
4319
+ if (!grid.cells && numRows <= 2 && numCols >= 3 && !nestedAttached) {
3593
4320
  const rebuilt = normalizeUndersegmentedTable(irGrid, grid.colXs, textItems);
3594
4321
  if (rebuilt) {
3595
4322
  rebuiltUsed = true;
@@ -3608,8 +4335,12 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3608
4335
  cells: finalGrid,
3609
4336
  hasHeader: finalRows > 1
3610
4337
  };
4338
+ TABLE_COLXS.set(irTable, grid.colXs);
4339
+ if (grid.continues) CONT_PARTS.set(irTable, grid.continues);
3611
4340
  const hasContent = finalGrid.some((row) => row.some((cell) => cell.text.trim() !== ""));
3612
- if (!hasContent) continue;
4341
+ const emptyPart = !hasContent && !!grid.cells && !grid.clipParent && !nestedAttached;
4342
+ if (!hasContent && !emptyPart) continue;
4343
+ if (emptyPart) EMPTY_PARTS.add(irTable);
3613
4344
  if (grid.clipParent) {
3614
4345
  const nb = { page: pageNum, x: grid.bbox.x1, y: grid.bbox.y1, width: grid.bbox.x2 - grid.bbox.x1, height: grid.bbox.y2 - grid.bbox.y1 };
3615
4346
  pendingNested.push({ parent: grid.clipParent, block: { type: "table", table: irTable, pageNumber: pageNum, bbox: nb } });
@@ -3634,6 +4365,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3634
4365
  }
3635
4366
  continue;
3636
4367
  }
4368
+ if (grid.cells) CLIP_TABLES.add(irTable);
3637
4369
  blocks.push({ type: "table", table: irTable, pageNumber: pageNum, bbox: tableBbox });
3638
4370
  }
3639
4371
  for (const p of pendingNested) blocks.push(p.block);
@@ -3671,7 +4403,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3671
4403
  {
3672
4404
  const rects = remaining.map((i) => ({ x: i.x, y: i.y, w: i.w, h: i.h > 0 ? i.h : i.fontSize }));
3673
4405
  for (const b of blocks) {
3674
- if (b.bbox) rects.push({ x: b.bbox.x, y: b.bbox.y, w: b.bbox.width, h: b.bbox.height });
4406
+ 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 });
3675
4407
  }
3676
4408
  gutterX = detectColumnGutter(rects);
3677
4409
  }
@@ -3691,7 +4423,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3691
4423
  if (side.length === 0) continue;
3692
4424
  for (const group of xyCutOrder(side, gapThreshold)) {
3693
4425
  if (group.length === 0) continue;
3694
- const groupBlocks = extractPageBlocksFallback(group, pageNum);
4426
+ const groupBlocks = extractPageBlocksFallback(group, pageNum, false, true, lex);
3695
4427
  for (const b of groupBlocks) textBlocks.push(b);
3696
4428
  groupSizes.push(groupBlocks.length);
3697
4429
  }
@@ -3704,7 +4436,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3704
4436
  const textBlocks = [];
3705
4437
  for (const group of groups) {
3706
4438
  if (group.length === 0) continue;
3707
- const groupBlocks = extractPageBlocksFallback(group, pageNum);
4439
+ const groupBlocks = extractPageBlocksFallback(group, pageNum, false, true, lex);
3708
4440
  for (const b of groupBlocks) textBlocks.push(b);
3709
4441
  groupSizes.push(groupBlocks.length);
3710
4442
  }
@@ -3748,53 +4480,13 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
3748
4480
  for (const u of units) for (const b of u) ordered.push(b);
3749
4481
  return mergeAdjacentTableBlocks(ordered);
3750
4482
  }
3751
- var NEIGHBOR_TABLE_EPSILON = 0.2;
3752
- function mergeCrossPageTables(blocks) {
3753
- for (let i = blocks.length - 2; i >= 0; i--) {
3754
- const prev = blocks[i];
3755
- const curr = blocks[i + 1];
3756
- if (prev.type !== "table" || curr.type !== "table" || !prev.table || !curr.table) continue;
3757
- if (!prev.pageNumber || !curr.pageNumber || curr.pageNumber !== prev.pageNumber + 1) continue;
3758
- if (prev.table.cols !== curr.table.cols) continue;
3759
- if (!prev.bbox || !curr.bbox) continue;
3760
- const width = Math.max(prev.bbox.width, curr.bbox.width, 1);
3761
- const leftDiff = Math.abs(prev.bbox.x - curr.bbox.x);
3762
- const rightDiff = Math.abs(prev.bbox.x + prev.bbox.width - (curr.bbox.x + curr.bbox.width));
3763
- if (leftDiff > width * NEIGHBOR_TABLE_EPSILON || rightDiff > width * NEIGHBOR_TABLE_EPSILON) continue;
3764
- let currCells = curr.table.cells;
3765
- if (currCells.length > 1 && prev.table.cells.length > 0 && rowTextsEqual(prev.table.cells[0], currCells[0])) {
3766
- currCells = currCells.slice(1);
3767
- }
3768
- if (currCells.length === 0) {
3769
- blocks.splice(i + 1, 1);
3770
- continue;
3771
- }
3772
- const merged = {
3773
- rows: prev.table.rows + currCells.length,
3774
- cols: prev.table.cols,
3775
- cells: [...prev.table.cells, ...currCells],
3776
- hasHeader: prev.table.hasHeader,
3777
- caption: prev.table.caption
3778
- };
3779
- blocks[i] = { ...prev, table: merged };
3780
- blocks.splice(i + 1, 1);
3781
- }
3782
- }
3783
- function rowTextsEqual(a, b) {
3784
- if (a.length !== b.length) return false;
3785
- const norm = (t) => t.replace(/\s+/g, "");
3786
- for (let i = 0; i < a.length; i++) {
3787
- if (norm(a[i].text) !== norm(b[i].text)) return false;
3788
- }
3789
- return a.some((c) => c.text.trim() !== "");
3790
- }
3791
4483
  function mergeAdjacentTableBlocks(blocks) {
3792
4484
  if (blocks.length <= 1) return blocks;
3793
4485
  const result = [blocks[0]];
3794
4486
  for (let i = 1; i < blocks.length; i++) {
3795
4487
  const prev = result[result.length - 1];
3796
4488
  const curr = blocks[i];
3797
- if (prev.type === "table" && curr.type === "table" && prev.table && curr.table && prev.table.cols === curr.table.cols) {
4489
+ if (prev.type === "table" && curr.type === "table" && prev.table && curr.table && prev.table.cols === curr.table.cols && !CLIP_TABLES.has(prev.table) && !CLIP_TABLES.has(curr.table)) {
3798
4490
  const merged = {
3799
4491
  rows: prev.table.rows + curr.table.rows,
3800
4492
  cols: prev.table.cols,
@@ -3854,7 +4546,42 @@ function splitTwoColumnProse(items, cutX) {
3854
4546
  }
3855
4547
  return groups;
3856
4548
  }
3857
- function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTables = true) {
4549
+ function streamLines(items) {
4550
+ if (items.some((i) => i.seq === void 0)) return groupByY([...items].sort((a, b) => b.y - a.y || a.x - b.x));
4551
+ const lines = [];
4552
+ let cur = [];
4553
+ for (const it of [...items].sort((a, b) => a.seq - b.seq)) {
4554
+ const last = cur[cur.length - 1];
4555
+ if (last && (Math.abs(it.y - last.y) > 3 || it.x < last.x - 1 || it.x - (last.x + last.w) > Math.max(2 * it.fontSize, 30))) {
4556
+ lines.push(cur);
4557
+ cur = [];
4558
+ }
4559
+ cur.push(it);
4560
+ }
4561
+ if (cur.length) lines.push(cur);
4562
+ return lines;
4563
+ }
4564
+ function pushLineParagraphs(out, yLines, pageNum, lex) {
4565
+ const lines = yLines.map((items) => ({ items, text: mergeLineSimple(items) })).filter((l) => l.text.trim());
4566
+ const geo = lines.map((l) => {
4567
+ const b = computeBBox(l.items, pageNum);
4568
+ return { text: l.text, left: b.x, right: b.x + b.width, y: l.items.reduce((s, i) => s + i.y, 0) / l.items.length, fontSize: dominantStyle(l.items)?.fontSize ?? 0 };
4569
+ });
4570
+ const joins = bodyLineJoins(geo, lex);
4571
+ for (let i = 0; i < lines.length; ) {
4572
+ let text = lines[i].text;
4573
+ const items = [...lines[i].items];
4574
+ for (; i + 1 < lines.length && joins[i] !== "\n"; i++) {
4575
+ text += joins[i] + lines[i + 1].text;
4576
+ items.push(...lines[i + 1].items);
4577
+ }
4578
+ const block = { type: "paragraph", text, pageNumber: pageNum, bbox: computeBBox(items, pageNum), style: dominantStyle(items) };
4579
+ PARA_LAST_LINE.set(block.bbox, { right: geo[i].right, width: geo[i].right - geo[i].left, fontSize: geo[i].fontSize });
4580
+ out.push(block);
4581
+ i++;
4582
+ }
4583
+ }
4584
+ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTables = true, lex) {
3858
4585
  if (items.length === 0) return [];
3859
4586
  const blocks = [];
3860
4587
  const clusterItems = items.map((i) => ({
@@ -3880,15 +4607,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
3880
4607
  blocks.push({ type: "table", table: cr.table, pageNumber: pageNum, bbox: cr.bbox });
3881
4608
  }
3882
4609
  const remaining = items.filter((_, idx) => !usedIndices.has(idx));
3883
- if (remaining.length > 0) {
3884
- const yLines = mergeSuperscriptLines(groupByY(remaining));
3885
- for (const line of yLines) {
3886
- const text = mergeLineSimple(line);
3887
- if (!text.trim()) continue;
3888
- const bbox = computeBBox(line, pageNum);
3889
- blocks.push({ type: "paragraph", text, pageNumber: pageNum, bbox, style: dominantStyle(line) });
3890
- }
3891
- }
4610
+ if (remaining.length > 0) pushLineParagraphs(blocks, mergeSuperscriptLines(groupByY(remaining)), pageNum, lex);
3892
4611
  blocks.sort((a, b) => {
3893
4612
  const ay = a.bbox ? a.bbox.y + a.bbox.height : 0;
3894
4613
  const by = b.bbox ? b.bbox.y + b.bbox.height : 0;
@@ -3919,12 +4638,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
3919
4638
  const bbox = computeBBox(group, pageNum);
3920
4639
  blocks.push({ type: "paragraph", text: tableText, pageNumber: pageNum, bbox, style: dominantStyle(group) });
3921
4640
  } else {
3922
- for (const line of yLines) {
3923
- const text = mergeLineSimple(line);
3924
- if (!text.trim()) continue;
3925
- const bbox = computeBBox(line, pageNum);
3926
- blocks.push({ type: "paragraph", text, pageNumber: pageNum, bbox, style: dominantStyle(line) });
3927
- }
4641
+ pushLineParagraphs(blocks, yLines, pageNum, lex);
3928
4642
  }
3929
4643
  }
3930
4644
  }
@@ -3937,13 +4651,22 @@ export {
3937
4651
  filterHiddenText,
3938
4652
  collapseEvenSpacing,
3939
4653
  normalizeItems,
4654
+ WrapLexicon,
4655
+ joinPageBreakWraps,
3940
4656
  computeMedianFontSizeFromFreq,
3941
4657
  detectHeadings,
3942
4658
  detectMarkerHeadings,
3943
4659
  detectTableCaptions,
3944
4660
  detectKoreanListBlocks,
3945
4661
  removeHeaderFooterBlocks,
3946
- extractPageBlocksWithLines,
3947
- mergeCrossPageTables
4662
+ CLIP_TABLES,
4663
+ TABLE_COLXS,
4664
+ EMPTY_PARTS,
4665
+ IMAGE_CELLS,
4666
+ FILLER_CELLS,
4667
+ CONT_PARTS,
4668
+ CELL_LINES,
4669
+ trimTrailingEmptyTableCols,
4670
+ extractPageBlocksWithLines
3948
4671
  };
3949
- //# sourceMappingURL=chunk-3DUADAY2.js.map
4672
+ //# sourceMappingURL=chunk-YAVHVFRY.js.map