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