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
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
HEADING_RATIO_H1,
|
|
4
4
|
HEADING_RATIO_H2,
|
|
5
5
|
HEADING_RATIO_H3
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-6BWU6SQX.js";
|
|
7
7
|
import {
|
|
8
8
|
safeMax,
|
|
9
9
|
safeMin
|
|
10
|
-
} from "./chunk-
|
|
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 <
|
|
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)
|
|
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
|
-
|
|
977
|
-
if ((Math.abs(a.
|
|
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] >
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1217
|
+
if (tableClip[first]) continue;
|
|
1218
|
+
const parentRect = parentAttachable(first) ? cells[effParent(first)] : void 0;
|
|
1070
1219
|
if (idxs.length < CLIP_MIN_GROUP) {
|
|
1071
|
-
|
|
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
|
|
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[
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
-
|
|
2246
|
-
let hasR = false;
|
|
2919
|
+
const L = [], R = [];
|
|
2247
2920
|
for (const i of line.items) {
|
|
2248
|
-
if (i.x + i.w <= cutX)
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3947
|
-
|
|
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-
|
|
4672
|
+
//# sourceMappingURL=chunk-YAVHVFRY.js.map
|