kordoc 4.14.3 → 4.15.0
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 +30 -4
- package/dist/{-7XBPXCXN.js → -WXTUDCBW.js} +11 -11
- package/dist/{chunk-UVVEBI22.js → chunk-23AJ65GX.js} +707 -62
- package/dist/chunk-23AJ65GX.js.map +1 -0
- package/dist/chunk-2FT2DHE6.js +2400 -0
- package/dist/chunk-2FT2DHE6.js.map +1 -0
- package/dist/{chunk-EBZUZC6F.cjs → chunk-3CWXYYCL.cjs} +38 -30
- package/dist/chunk-3CWXYYCL.cjs.map +1 -0
- package/dist/{chunk-A275ZQLX.js → chunk-4OLS5RDH.js} +707 -62
- package/dist/chunk-4OLS5RDH.js.map +1 -0
- package/dist/{chunk-NQCUAGLR.js → chunk-6BWU6SQX.js} +1 -1
- package/dist/{chunk-2LQGSDZ6.js.map → chunk-6BWU6SQX.js.map} +1 -1
- package/dist/{chunk-2LQGSDZ6.js → chunk-6CUIXJOC.js} +1 -1
- package/dist/{chunk-NQCUAGLR.js.map → chunk-6CUIXJOC.js.map} +1 -1
- package/dist/{chunk-L5EH2NVO.js → chunk-6Y6KUOA6.js} +14 -3
- package/dist/chunk-6Y6KUOA6.js.map +1 -0
- package/dist/{chunk-QGW7NMTX.js → chunk-AKWMQ5XT.js} +17 -9
- package/dist/chunk-AKWMQ5XT.js.map +1 -0
- package/dist/{chunk-O2PGQNYE.js → chunk-B3NAP4FA.js} +2 -2
- package/dist/{chunk-CVEERPMP.js → chunk-DTM6FV74.js} +1727 -1680
- package/dist/chunk-DTM6FV74.js.map +1 -0
- package/dist/{chunk-IW2CB755.cjs → chunk-EP4DTCVJ.cjs} +392 -42
- package/dist/chunk-EP4DTCVJ.cjs.map +1 -0
- package/dist/{chunk-5VVS2QIJ.cjs → chunk-FSQJJTHS.cjs} +3 -3
- package/dist/{chunk-5VVS2QIJ.cjs.map → chunk-FSQJJTHS.cjs.map} +1 -1
- package/dist/{chunk-D6KQKMU3.js → chunk-H5XLUEZG.js} +2 -2
- package/dist/{chunk-GTINVM5K.js → chunk-IMR5HSB5.js} +2 -2
- package/dist/{chunk-FAZR34CA.js → chunk-KNZUG3SV.js} +2 -2
- package/dist/{chunk-I4NNYWIR.js → chunk-KO5S25RD.js} +23 -3
- package/dist/chunk-KO5S25RD.js.map +1 -0
- package/dist/{chunk-EMGF6EEV.cjs → chunk-LBYKEJUJ.cjs} +17 -6
- package/dist/chunk-LBYKEJUJ.cjs.map +1 -0
- package/dist/{chunk-VYOGXNQ3.js → chunk-M26GSDV3.js} +16 -8
- package/dist/chunk-M26GSDV3.js.map +1 -0
- package/dist/{chunk-IHZEFW4L.js → chunk-MX36AA26.js} +2 -2
- package/dist/{chunk-4R67INKT.js → chunk-NJP76IZ5.js} +389 -39
- package/dist/chunk-NJP76IZ5.js.map +1 -0
- package/dist/{chunk-YGBZABI4.js → chunk-PJ2K6B3V.js} +78 -67
- package/dist/chunk-PJ2K6B3V.js.map +1 -0
- package/dist/{chunk-VNFJPTU4.js → chunk-QMUM25RB.js} +2 -2
- package/dist/{chunk-VJHAUMCF.js → chunk-S5PJ7ZE2.js} +389 -39
- package/dist/chunk-S5PJ7ZE2.js.map +1 -0
- package/dist/{chunk-JCVIOLBF.js → chunk-TDYGL267.js} +11 -13
- package/dist/{chunk-JCVIOLBF.js.map → chunk-TDYGL267.js.map} +1 -1
- package/dist/{chunk-WB5LW52Z.js → chunk-UCDOIHGW.js} +2 -2
- package/dist/{chunk-HX4UUF7Y.cjs → chunk-VWUYVEFN.cjs} +1 -1
- package/dist/{chunk-HX4UUF7Y.cjs.map → chunk-VWUYVEFN.cjs.map} +1 -1
- package/dist/{chunk-NOAQRCV6.cjs → chunk-W33QEDLY.cjs} +730 -85
- package/dist/chunk-W33QEDLY.cjs.map +1 -0
- package/dist/{chunk-4RJ5D7MT.js → chunk-YINJO3PR.js} +2 -2
- package/dist/chunks-S2XR4JDI.js +10 -0
- package/dist/cli.js +44 -45
- package/dist/cli.js.map +1 -1
- package/dist/{formula-CPO2TZGB.cjs → formula-RGK6WGD2.cjs} +9 -9
- package/dist/{formula-CPO2TZGB.cjs.map → formula-RGK6WGD2.cjs.map} +1 -1
- package/dist/{formula-X44GV7TN.js → formula-USQK6NHN.js} +3 -3
- package/dist/{formula-NOIDZEBA.js → formula-WGBYDRVU.js} +4 -4
- package/dist/{image-ocr-SUGT64ZM.cjs → image-ocr-JPP6JRFU.cjs} +13 -13
- package/dist/{image-ocr-SUGT64ZM.cjs.map → image-ocr-JPP6JRFU.cjs.map} +1 -1
- package/dist/{image-ocr-R6W6A75P.js → image-ocr-SLYPOW7G.js} +6 -6
- package/dist/{image-ocr-72RAR4WR.js → image-ocr-WCKYPPXY.js} +8 -8
- package/dist/index.cjs +4333 -2500
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -19
- package/dist/index.d.ts +12 -19
- package/dist/index.js +3816 -1983
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +46 -35
- package/dist/mcp.js.map +1 -1
- package/dist/{model-bundle-57CLEBCO.js → model-bundle-BIS5HNXA.js} +5 -5
- package/dist/{models-7ELIFTD7.js → models-APDLDMGJ.js} +5 -5
- package/dist/{parser-4NKAYWAX.js → parser-4D2YTTIH.js} +494 -50
- package/dist/parser-4D2YTTIH.js.map +1 -0
- package/dist/{parser-HWFQRSUQ.js → parser-6ZWJPQC2.js} +494 -50
- package/dist/parser-6ZWJPQC2.js.map +1 -0
- package/dist/{parser-RANGXGBD.cjs → parser-Y5NX55GC.cjs} +543 -99
- package/dist/parser-Y5NX55GC.cjs.map +1 -0
- package/dist/pdf-ocr-E3IZ3EI5.cjs +14 -0
- package/dist/{pdf-ocr-VHSFACH7.cjs.map → pdf-ocr-E3IZ3EI5.cjs.map} +1 -1
- package/dist/pdf-ocr-FNXYMNM6.js +16 -0
- package/dist/pdf-ocr-WSYPOBBU.js +13 -0
- package/dist/{profile-io-V52PL5ZO.js → profile-io-VJUZYOJA.js} +3 -3
- package/dist/{rasterize-ZIGLJDOG.js → rasterize-6U5NWILD.js} +3 -3
- package/dist/{redact-TIQEQI3M.js → redact-3CY3KGVD.js} +2 -2
- package/dist/{redact-doc-KXZ3TZAG.js → redact-doc-H56QYJGQ.js} +98 -30
- package/dist/redact-doc-H56QYJGQ.js.map +1 -0
- package/dist/{render-U5PE6V2E.js → render-HNMDLMI2.js} +7 -7
- package/dist/seal-VXNEOHZV.js +10 -0
- package/dist/{setup-YRDM4ODP.js → setup-RZVYAXAV.js} +3 -3
- package/dist/visual-NSZP2FWZ.js +19 -0
- package/dist/{watch-5IOLBATT.js → watch-VA6GK56C.js} +46 -23
- package/dist/watch-VA6GK56C.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-4R67INKT.js.map +0 -1
- package/dist/chunk-A275ZQLX.js.map +0 -1
- package/dist/chunk-CVEERPMP.js.map +0 -1
- package/dist/chunk-EBZUZC6F.cjs.map +0 -1
- package/dist/chunk-EMGF6EEV.cjs.map +0 -1
- package/dist/chunk-I4NNYWIR.js.map +0 -1
- package/dist/chunk-IH56ZISC.js +0 -620
- package/dist/chunk-IH56ZISC.js.map +0 -1
- package/dist/chunk-IW2CB755.cjs.map +0 -1
- package/dist/chunk-L5EH2NVO.js.map +0 -1
- package/dist/chunk-NOAQRCV6.cjs.map +0 -1
- package/dist/chunk-QGW7NMTX.js.map +0 -1
- package/dist/chunk-UVVEBI22.js.map +0 -1
- package/dist/chunk-VJHAUMCF.js.map +0 -1
- package/dist/chunk-VYOGXNQ3.js.map +0 -1
- package/dist/chunk-YGBZABI4.js.map +0 -1
- package/dist/chunks-I7TEMJXH.js +0 -10
- package/dist/parser-4NKAYWAX.js.map +0 -1
- package/dist/parser-HWFQRSUQ.js.map +0 -1
- package/dist/parser-RANGXGBD.cjs.map +0 -1
- package/dist/pdf-ocr-BNC34MNU.js +0 -13
- package/dist/pdf-ocr-QX2GB33Q.js +0 -16
- package/dist/pdf-ocr-VHSFACH7.cjs +0 -14
- package/dist/redact-doc-KXZ3TZAG.js.map +0 -1
- package/dist/seal-DNXJBILR.js +0 -10
- package/dist/visual-5XWMA4EV.js +0 -19
- package/dist/watch-5IOLBATT.js.map +0 -1
- /package/dist/{-7XBPXCXN.js.map → -WXTUDCBW.js.map} +0 -0
- /package/dist/{chunk-O2PGQNYE.js.map → chunk-B3NAP4FA.js.map} +0 -0
- /package/dist/{chunk-D6KQKMU3.js.map → chunk-H5XLUEZG.js.map} +0 -0
- /package/dist/{chunk-GTINVM5K.js.map → chunk-IMR5HSB5.js.map} +0 -0
- /package/dist/{chunk-FAZR34CA.js.map → chunk-KNZUG3SV.js.map} +0 -0
- /package/dist/{chunk-IHZEFW4L.js.map → chunk-MX36AA26.js.map} +0 -0
- /package/dist/{chunk-VNFJPTU4.js.map → chunk-QMUM25RB.js.map} +0 -0
- /package/dist/{chunk-WB5LW52Z.js.map → chunk-UCDOIHGW.js.map} +0 -0
- /package/dist/{chunk-4RJ5D7MT.js.map → chunk-YINJO3PR.js.map} +0 -0
- /package/dist/{chunks-I7TEMJXH.js.map → chunks-S2XR4JDI.js.map} +0 -0
- /package/dist/{formula-X44GV7TN.js.map → formula-USQK6NHN.js.map} +0 -0
- /package/dist/{formula-NOIDZEBA.js.map → formula-WGBYDRVU.js.map} +0 -0
- /package/dist/{image-ocr-R6W6A75P.js.map → image-ocr-SLYPOW7G.js.map} +0 -0
- /package/dist/{image-ocr-72RAR4WR.js.map → image-ocr-WCKYPPXY.js.map} +0 -0
- /package/dist/{model-bundle-57CLEBCO.js.map → model-bundle-BIS5HNXA.js.map} +0 -0
- /package/dist/{models-7ELIFTD7.js.map → models-APDLDMGJ.js.map} +0 -0
- /package/dist/{pdf-ocr-BNC34MNU.js.map → pdf-ocr-FNXYMNM6.js.map} +0 -0
- /package/dist/{pdf-ocr-QX2GB33Q.js.map → pdf-ocr-WSYPOBBU.js.map} +0 -0
- /package/dist/{profile-io-V52PL5ZO.js.map → profile-io-VJUZYOJA.js.map} +0 -0
- /package/dist/{rasterize-ZIGLJDOG.js.map → rasterize-6U5NWILD.js.map} +0 -0
- /package/dist/{redact-TIQEQI3M.js.map → redact-3CY3KGVD.js.map} +0 -0
- /package/dist/{render-U5PE6V2E.js.map → render-HNMDLMI2.js.map} +0 -0
- /package/dist/{seal-DNXJBILR.js.map → seal-VXNEOHZV.js.map} +0 -0
- /package/dist/{setup-YRDM4ODP.js.map → setup-RZVYAXAV.js.map} +0 -0
- /package/dist/{visual-5XWMA4EV.js.map → visual-NSZP2FWZ.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-6Y6KUOA6.js";
|
|
10
10
|
|
|
11
11
|
// src/pdf/image-regions.ts
|
|
12
12
|
import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
@@ -68,6 +68,8 @@ 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 = [];
|
|
72
74
|
const fillRects = [];
|
|
73
75
|
let pathRects = [];
|
|
@@ -116,7 +118,7 @@ function extractLines(fnArray, argsArray) {
|
|
|
116
118
|
pendingClip = false;
|
|
117
119
|
const effWidth = lineWidth * ctmScale();
|
|
118
120
|
for (const seg of currentPath) {
|
|
119
|
-
classifyAndAdd(seg, effWidth, horizontals, verticals, fromFill);
|
|
121
|
+
classifyAndAdd(seg, effWidth, horizontals, verticals, fromFill, fromFill ? void 0 : { h: shortH, v: shortV });
|
|
120
122
|
}
|
|
121
123
|
currentPath = [];
|
|
122
124
|
}
|
|
@@ -247,7 +249,56 @@ function extractLines(fnArray, argsArray) {
|
|
|
247
249
|
break;
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
|
-
return { horizontals, verticals, clipRects, fillRects };
|
|
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];
|
|
251
302
|
}
|
|
252
303
|
var CLIP_MIN_W = 4;
|
|
253
304
|
var CLIP_MIN_H = 2;
|
|
@@ -265,11 +316,21 @@ function captureClipRect(path, out, minW = CLIP_MIN_W, minH = CLIP_MIN_H) {
|
|
|
265
316
|
if (x2 - x1 < minW || y2 - y1 < minH) return;
|
|
266
317
|
out.push({ x1, y1, x2, y2 });
|
|
267
318
|
}
|
|
268
|
-
function classifyAndAdd(seg, lineWidth, horizontals, verticals, fromFill = false) {
|
|
319
|
+
function classifyAndAdd(seg, lineWidth, horizontals, verticals, fromFill = false, short) {
|
|
269
320
|
const dx = Math.abs(seg.x2 - seg.x1);
|
|
270
321
|
const dy = Math.abs(seg.y2 - seg.y1);
|
|
271
322
|
const length = Math.sqrt(dx * dx + dy * dy);
|
|
272
|
-
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
|
+
}
|
|
273
334
|
if (dy <= ORIENTATION_TOL) {
|
|
274
335
|
const y = (seg.y1 + seg.y2) / 2;
|
|
275
336
|
const x1 = Math.min(seg.x1, seg.x2);
|
|
@@ -750,6 +811,23 @@ function buildTableGrids(horizontals, verticals) {
|
|
|
750
811
|
}
|
|
751
812
|
return mergeAdjacentGrids(grids);
|
|
752
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
|
+
}
|
|
753
831
|
function enforceMinWidth(colXs, minWidth) {
|
|
754
832
|
if (colXs.length <= 2) return colXs;
|
|
755
833
|
const result = [colXs[0]];
|
|
@@ -972,6 +1050,7 @@ var CLIP_EDGE_TOL = 1.5;
|
|
|
972
1050
|
var CLIP_ADJ_GAP = 0.15;
|
|
973
1051
|
var CLIP_MIN_GROUP = 2;
|
|
974
1052
|
var CLIP_MAX_PAGE_FRAC = 0.75;
|
|
1053
|
+
var STRADDLE_MIN = 0.1;
|
|
975
1054
|
var TITLED_FRAME_MIN_TOP = 0.2;
|
|
976
1055
|
var TITLED_FRAME_MIN_WIDTH = 0.6;
|
|
977
1056
|
var HEADER_BAND = 0.08;
|
|
@@ -1023,7 +1102,8 @@ function edgeStroked(lines, dir, pos, a1, a2) {
|
|
|
1023
1102
|
}
|
|
1024
1103
|
return false;
|
|
1025
1104
|
}
|
|
1026
|
-
|
|
1105
|
+
var CONT_X_TOL = 0.1;
|
|
1106
|
+
function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, textPoints = [], fillRects = [], prev = { lastCells: [], clips: [] }) {
|
|
1027
1107
|
const pageArea = pageWidth * pageHeight;
|
|
1028
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;
|
|
1029
1109
|
const cells = [];
|
|
@@ -1032,16 +1112,24 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1032
1112
|
if (pageArea > 0 && (r.x2 - r.x1) * (r.y2 - r.y1) >= pageArea * CLIP_MAX_PAGE_FRAC) continue;
|
|
1033
1113
|
if (!cells.some((c) => sameRect(c, r))) cells.push(r);
|
|
1034
1114
|
}
|
|
1035
|
-
if (cells.length < 1) return { grids: [], containers: [] };
|
|
1115
|
+
if (cells.length < 1) return { grids: [], containers: [], page: { lastCells: [], clips: [] } };
|
|
1036
1116
|
const parent = new Array(cells.length).fill(-1);
|
|
1037
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)));
|
|
1038
1125
|
for (let i = 0; i < cells.length; i++) {
|
|
1039
1126
|
for (let j = 0; j < cells.length; j++) {
|
|
1040
|
-
if (i === j || area(cells[j]) <= area(cells[i]) || !contains(cells[j], cells[i])) continue;
|
|
1127
|
+
if (i === j || !canParent[j] || area(cells[j]) <= area(cells[i]) || !contains(cells[j], cells[i])) continue;
|
|
1041
1128
|
const cur = parent[i];
|
|
1042
1129
|
if (cur < 0 || contains(cells[cur], cells[j])) parent[i] = j;
|
|
1043
1130
|
}
|
|
1044
1131
|
}
|
|
1132
|
+
const ruledGaps = findRuledGaps(cells, parent, strokedH, strokedV);
|
|
1045
1133
|
const isContainer = new Array(cells.length).fill(false);
|
|
1046
1134
|
for (const p of parent) if (p >= 0) isContainer[p] = true;
|
|
1047
1135
|
const tileParent = new Array(cells.length).fill(false);
|
|
@@ -1068,7 +1156,7 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1068
1156
|
const effParent = (i) => {
|
|
1069
1157
|
let p = parent[i];
|
|
1070
1158
|
for (let steps = 0; p >= 0 && steps <= cells.length; steps++) {
|
|
1071
|
-
if (!tableClip[p] && (isGridMember(p) || loneFrame(p))) return p;
|
|
1159
|
+
if (!tableClip[p] && (isGridMember(p) || loneFrame(p) || continues(p))) return p;
|
|
1072
1160
|
p = parent[p];
|
|
1073
1161
|
}
|
|
1074
1162
|
return -1;
|
|
@@ -1091,6 +1179,11 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1091
1179
|
}
|
|
1092
1180
|
}
|
|
1093
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
|
+
}
|
|
1094
1187
|
const groups = /* @__PURE__ */ new Map();
|
|
1095
1188
|
for (let i = 0; i < cells.length; i++) {
|
|
1096
1189
|
const r = find(i);
|
|
@@ -1104,6 +1197,17 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1104
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);
|
|
1105
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);
|
|
1106
1199
|
const loneFrame = (i) => isContainer[i] && !isGridMember(i) && (framed(cells[i]) || titledFrame(cells[i]));
|
|
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
|
+
};
|
|
1107
1211
|
const parentAttachable = (i) => effParent(i) >= 0;
|
|
1108
1212
|
const onlyChild = (i) => parent[i] >= 0 && parent.filter((p) => p === parent[i]).length === 1;
|
|
1109
1213
|
const grids = [];
|
|
@@ -1112,7 +1216,8 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1112
1216
|
if (tableClip[first]) continue;
|
|
1113
1217
|
const parentRect = parentAttachable(first) ? cells[effParent(first)] : void 0;
|
|
1114
1218
|
if (idxs.length < CLIP_MIN_GROUP) {
|
|
1115
|
-
|
|
1219
|
+
const from = continues(first);
|
|
1220
|
+
if (!from && !loneFrame(first) && !((parentRect || onlyChild(first)) && framed(cells[first]))) continue;
|
|
1116
1221
|
const r = cells[first];
|
|
1117
1222
|
grids.push({
|
|
1118
1223
|
rowYs: [r.y2, r.y1],
|
|
@@ -1120,11 +1225,13 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1120
1225
|
bbox: { x1: r.x1, y1: r.y1, x2: r.x2, y2: r.y2 },
|
|
1121
1226
|
vertexRadius: 1,
|
|
1122
1227
|
cells: [{ row: 0, col: 0, rowSpan: 1, colSpan: 1, bbox: { x1: r.x1, y1: r.y1, x2: r.x2, y2: r.y2 } }],
|
|
1123
|
-
...parentRect ? { clipParent: parentRect } : {}
|
|
1228
|
+
...parentRect ? { clipParent: parentRect } : {},
|
|
1229
|
+
...from ? { continues: from } : {}
|
|
1124
1230
|
});
|
|
1125
1231
|
continue;
|
|
1126
1232
|
}
|
|
1127
|
-
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]);
|
|
1128
1235
|
let edges = 0, invisible = 0;
|
|
1129
1236
|
for (const r of members) {
|
|
1130
1237
|
edges += 4;
|
|
@@ -1169,7 +1276,130 @@ function buildClipCellGrids(rects, strokedH, strokedV, pageWidth, pageHeight, te
|
|
|
1169
1276
|
...parentRect ? { clipParent: parentRect } : {}
|
|
1170
1277
|
});
|
|
1171
1278
|
}
|
|
1172
|
-
|
|
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;
|
|
1173
1403
|
}
|
|
1174
1404
|
function addNarrowEdgeCols(colXs, rowYs, out, fills) {
|
|
1175
1405
|
if (!fills.length) return;
|
|
@@ -1322,6 +1552,203 @@ function hasHorizontalLine(horizontals, y, leftX, rightX, vertexRadius) {
|
|
|
1322
1552
|
return false;
|
|
1323
1553
|
}
|
|
1324
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
|
+
/** 줄 안에서 이웃한 두 어절("개인정보 처리") — 이어 붙인 꼴 증거(joinedWord)의 거부권 */
|
|
1574
|
+
pairs = /* @__PURE__ */ new Set();
|
|
1575
|
+
/** 줄 글 한 줄을 증거로 더한다 — 탭(큰 갭)으로 나뉜 조각은 다른 칸·단이라 조각 사이는 어절 경계 증거로 쓰지 않는다 */
|
|
1576
|
+
addLine(text) {
|
|
1577
|
+
for (const seg of text.replace(MARKUP, "").split(" ")) {
|
|
1578
|
+
const toks = seg.split(" ").filter((t) => t && !t.includes("]("));
|
|
1579
|
+
for (let i = 0; i < toks.length; i++) {
|
|
1580
|
+
const t = toks[i], n = t.length;
|
|
1581
|
+
if (i > 0 && i < toks.length - 1) bump(this.words, t);
|
|
1582
|
+
if (i + 1 < toks.length) this.pairs.add(t + " " + toks[i + 1]);
|
|
1583
|
+
for (let a = 0; a + 1 < n; a++) {
|
|
1584
|
+
const c1 = t.charCodeAt(a), c2 = t.charCodeAt(a + 1);
|
|
1585
|
+
bump(this.joined1, pairKey(c1, c2));
|
|
1586
|
+
if (a >= 1 && a + 2 < n) bump2(this.joined2, pairKey(t.charCodeAt(a - 1), c1), pairKey(c2, t.charCodeAt(a + 2)));
|
|
1587
|
+
}
|
|
1588
|
+
if (i + 1 < toks.length) {
|
|
1589
|
+
const u = toks[i + 1];
|
|
1590
|
+
bump(this.spaced1, pairKey(t.charCodeAt(n - 1), u.charCodeAt(0)));
|
|
1591
|
+
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)));
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
/** 문서 줄 안에서 홀로 선 어절로 나온 적 있나 */
|
|
1597
|
+
isWord(word) {
|
|
1598
|
+
return (this.words.get(word) ?? 0) > 0;
|
|
1599
|
+
}
|
|
1600
|
+
/** 꺾인 두 조각을 이어 붙인 꼴이 줄 안 한 어절로 나왔고, 두 조각이 줄 안에서 띄어 쓴 이웃 어절로는 나온 적 없나 */
|
|
1601
|
+
joinedWord(left, right) {
|
|
1602
|
+
return this.isWord(left + right) && !this.pairs.has(left + " " + right);
|
|
1603
|
+
}
|
|
1604
|
+
/** 꺾인 자리 증거: "" 붙음 · " " 띄움 · null 모름 (두 글자 증거가 갈리거나 없으면 한 글자) */
|
|
1605
|
+
evidence(left, right) {
|
|
1606
|
+
const n = left.length;
|
|
1607
|
+
if (n >= 2 && right.length >= 2) {
|
|
1608
|
+
const k1 = pairKey(left.charCodeAt(n - 2), left.charCodeAt(n - 1)), k2 = pairKey(right.charCodeAt(0), right.charCodeAt(1));
|
|
1609
|
+
const v = decideCounts(this.joined2.get(k1)?.get(k2) ?? 0, this.spaced2.get(k1)?.get(k2) ?? 0);
|
|
1610
|
+
if (v !== null) return v;
|
|
1611
|
+
}
|
|
1612
|
+
if (n && right.length) {
|
|
1613
|
+
const k = pairKey(left.charCodeAt(n - 1), right.charCodeAt(0));
|
|
1614
|
+
return decideCounts(this.joined1.get(k) ?? 0, this.spaced1.get(k) ?? 0);
|
|
1615
|
+
}
|
|
1616
|
+
return null;
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
var decideCounts = (joined, spaced) => joined && !spaced ? "" : spaced && !joined ? " " : null;
|
|
1620
|
+
var hasBatchim = (c) => {
|
|
1621
|
+
const k = c.charCodeAt(0) - 44032;
|
|
1622
|
+
return k >= 0 && k < 11172 && k % 28 !== 0;
|
|
1623
|
+
};
|
|
1624
|
+
var CLOSE_TAIL = /[’”」』)\]〉》>]+$/;
|
|
1625
|
+
var TAIL = "[.,)\u300D\u300F\u2019\u201D]*";
|
|
1626
|
+
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}$`);
|
|
1627
|
+
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|\uC778|\uC778\uB370|\uC778\uC9C0|\uC784|\uC774\uC5B4\uC57C|\uC774\uBBC0\uB85C|\uC774\uC9C0\uB9CC|\uC774\uBA74\uC11C)${TAIL}$`);
|
|
1628
|
+
var PARTICLE_AFTER_VOWEL = new RegExp(`^(?:\uAC00|\uC640|\uB77C\uB294)${TAIL}$`);
|
|
1629
|
+
var ENDING = new RegExp(`^(?:\uC5B4\uC57C|\uC544\uC57C|\uC5EC\uC57C|\uBBC0\uB85C|\uC73C\uBBC0\uB85C|\uC9C0\uB9CC|\uBA74\uC11C|\uC73C\uBA74\uC11C|\uC73C\uBA70|\uAC70\uB098|\uB354\uB77C\uB3C4|\uB4E0\uC9C0|\uC5C8\uB2E4|\uC558\uB2E4|\uC600\uB2E4|\uC5C8\uC73C\uBA70|\uC558\uC73C\uBA70|\uC600\uC73C\uBA70|\uC5C8\uACE0|\uC558\uACE0|\uC600\uACE0|\uACA0\uB2E4|\uACA0\uACE0|\uACA0\uC73C\uBA70|\uC2B5\uB2C8\uB2E4|\uB2C8\uB2E4|\uB2C8\uAE4C)${TAIL}$`);
|
|
1630
|
+
var ADNOMINAL_HAN = new RegExp(`^\uD55C${TAIL}$`);
|
|
1631
|
+
var SUFFIX_JEOK = new RegExp(`^\uC801(?:\uC778|\uC73C\uB85C|\uC774\uB2E4|\uC774\uBA70|\uC774\uACE0|\uC784|\uC778\uB370|\uC774\uB77C|\uC774\uC9C0\uB9CC|\uC73C\uB85C\uC11C)${TAIL}$`);
|
|
1632
|
+
var SPLIT_PARTICLE = [["\uC5D0", new RegExp(`^\uC11C(?:\uB294|\uB3C4|\uC758|\uBD80\uD130|\uB9CC)?${TAIL}$`)], ["\uC774", new RegExp(`^\uB098${TAIL}$`)]];
|
|
1633
|
+
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|\uD558\uAE30|\uD558\uAC8C|\uD558\uC9C0|\uD558\uAC70\uB098|\uD574\uC11C|\uD558\uB294\uB370|\uD568\uC73C\uB85C\uC368|\uD568\uC5D0|\uD568\uC744|\uD568\uC774|\uD558\uAE30\uC5D0|\uD558\uAE30\uB3C4|\uB418\uC9C0|\uB418\uAE30|\uB418\uB3C4\uB85D|\uB418\uAC70\uB098|\uB418\uBA74\uC11C)${TAIL}$`);
|
|
1634
|
+
var AUX_BEFORE = /(?:야|록|로|게|도|히|자|를|을|면|서|고|며|라|려|까지)$/;
|
|
1635
|
+
var BECOME = /^[되된됐됨돼]/;
|
|
1636
|
+
var STANDALONE_SYLLABLE = /^(?:및|등|수|것|그|이|저|더|또|각|약|총|중|간|때|뿐|듯|채|전|후|내|외|한|두|세|네|몇|새|첫|온|본|곧|꼭|잘|못|안|좀|늘|다|왜|뭐|시|할|데|뒤|된)$/;
|
|
1637
|
+
var DATE_DAY_END = /(?:^|\s)\d*월\s+\d*일$/;
|
|
1638
|
+
var COUNTER = /^(?:명|일|월|년|개|원|건|회|차|호|조|항|층|톤|대|곳|시|분|초|배|위|점|주|종|억|만|천|%|퍼센트)/;
|
|
1639
|
+
function endsAsWord(word) {
|
|
1640
|
+
const h = word.replace(CLOSE_TAIL, "");
|
|
1641
|
+
if (!/^[가-힣]{2,}$/.test(h)) return false;
|
|
1642
|
+
if (/(?:을|를|는|은|히|른|할|야|게|의)$/.test(h)) return true;
|
|
1643
|
+
const prev = h[h.length - 2];
|
|
1644
|
+
if (/(?:이|과)$/.test(h)) return hasBatchim(prev);
|
|
1645
|
+
if (/(?:가|와)$/.test(h)) return !hasBatchim(prev);
|
|
1646
|
+
return false;
|
|
1647
|
+
}
|
|
1648
|
+
function particleContinues(left, rightWord) {
|
|
1649
|
+
const right = rightWord.replace(/[”’"」』].+$/, "");
|
|
1650
|
+
const h = left.replace(CLOSE_TAIL, "");
|
|
1651
|
+
const last = h[h.length - 1];
|
|
1652
|
+
if (!last || !/[가-힣A-Za-z0-9]/.test(last)) return false;
|
|
1653
|
+
if (PARTICLE.test(right)) return true;
|
|
1654
|
+
if (!/[가-힣]/.test(last)) return false;
|
|
1655
|
+
if (SUFFIX_JEOK.test(right)) return true;
|
|
1656
|
+
if (ENDING.test(right)) return true;
|
|
1657
|
+
if (/^다[.;:!?]/.test(right)) return true;
|
|
1658
|
+
if (SPLIT_PARTICLE.some(([a, b]) => last === a && b.test(right))) return true;
|
|
1659
|
+
if (PARTICLE_AFTER_CONS.test(right)) return hasBatchim(last);
|
|
1660
|
+
if (PARTICLE_AFTER_VOWEL.test(right)) return !hasBatchim(last);
|
|
1661
|
+
if (ADNOMINAL_HAN.test(right)) return h.length >= 2 && !AUX_BEFORE.test(h) && !/(?:는|은|한|다|어|년)$/.test(h);
|
|
1662
|
+
return VERB_FORM.test(right) && !AUX_BEFORE.test(h) && !((last === "\uC774" || last === "\uAC00" && h.length >= 3) && BECOME.test(right));
|
|
1663
|
+
}
|
|
1664
|
+
var ITEM_HEAD = /^(?:[□■◆◇○●◎◦▪▫•※▶▷►❍❏❑✓✔➢➤☞]|[①-⑳]|[-–·∙ㆍ*](?=\s)|\(?\d{1,2}[.)](?!\d)|\(?[가-하][.)]|\([가-하\d]{1,2}\)|[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]|제\d+[조항호장절])/;
|
|
1665
|
+
function startsNewItem(prevText, nextText) {
|
|
1666
|
+
const t = nextText.replace(MARKUP, "").trimStart();
|
|
1667
|
+
return ITEM_HEAD.test(t) && !(/^다\./.test(t) && /[가-힣]$/.test(prevText.replace(MARKUP, "").trimEnd()));
|
|
1668
|
+
}
|
|
1669
|
+
function wrapJoiner(prevText, nextText, lex) {
|
|
1670
|
+
const prev = prevText.replace(MARKUP, "").trimEnd(), next = nextText.replace(MARKUP, "").trimStart();
|
|
1671
|
+
const a = prev[prev.length - 1], b = next[0];
|
|
1672
|
+
if (!a || !b || /[,;:!?]/.test(a) || DATE_DAY_END.test(prev) && b !== "(") return " ";
|
|
1673
|
+
let s = prev.length;
|
|
1674
|
+
while (s > 0 && !/\s/.test(prev[s - 1])) s--;
|
|
1675
|
+
const left = prev.slice(s), right = next.match(/^\S+/)[0];
|
|
1676
|
+
if (particleContinues(left, right)) return "";
|
|
1677
|
+
if (endsAsWord(left)) return " ";
|
|
1678
|
+
if (/[·ㆍ‧-]/.test(a) && !/[·ㆍ‧…]{2}$/.test(prev) && /[가-힣A-Za-z0-9]/.test(b)) return "";
|
|
1679
|
+
if (/[A-Za-z]/.test(a) && /[A-Za-z]/.test(b) || /\d/.test(a) && /\d/.test(b)) return " ";
|
|
1680
|
+
const ev = lex?.evidence(left, right);
|
|
1681
|
+
if (ev != null) return ev;
|
|
1682
|
+
if (lex && /[가-힣A-Za-z0-9]$/.test(left) && /^[가-힣A-Za-z0-9]/.test(right) && lex.joinedWord(left, right.replace(/[.,)」』’”;:]+$/, ""))) return "";
|
|
1683
|
+
if (/\d$/.test(left) && COUNTER.test(right)) return "";
|
|
1684
|
+
if (lex && /^[가-힣]$/.test(left) && /^[가-힣]/.test(right) && !STANDALONE_SYLLABLE.test(left) && ([...right].length === 1 || !lex.isWord(left))) return "";
|
|
1685
|
+
const r1 = right.replace(/[.,)」』’”]+$/, "");
|
|
1686
|
+
if (lex && /[가-힣]$/.test(left) && /^[가-힣]$/.test(r1) && !STANDALONE_SYLLABLE.test(r1) && !lex.isWord(r1) && !lex.isWord(right)) return "";
|
|
1687
|
+
return " ";
|
|
1688
|
+
}
|
|
1689
|
+
var PARA_LAST_LINE = /* @__PURE__ */ new WeakMap();
|
|
1690
|
+
function joinPageBreakWraps(blocks, lex) {
|
|
1691
|
+
const pageRight = /* @__PURE__ */ new Map();
|
|
1692
|
+
for (const b of blocks) {
|
|
1693
|
+
if (b.type !== "paragraph" || !b.bbox || !b.pageNumber) continue;
|
|
1694
|
+
pageRight.set(b.pageNumber, Math.max(pageRight.get(b.pageNumber) ?? -Infinity, b.bbox.x + b.bbox.width));
|
|
1695
|
+
}
|
|
1696
|
+
for (let i = blocks.length - 1; i > 0; i--) {
|
|
1697
|
+
const a = blocks[i - 1], b = blocks[i];
|
|
1698
|
+
if (!a.pageNumber || b.pageNumber !== a.pageNumber + 1 || !a.text || !b.text) continue;
|
|
1699
|
+
if (a.type !== "paragraph" && a.type !== "list" || b.type !== "paragraph") continue;
|
|
1700
|
+
const last = a.bbox && PARA_LAST_LINE.get(a.bbox), fs = last ? last.fontSize : 0;
|
|
1701
|
+
if (!last || fs <= 0 || (pageRight.get(a.pageNumber) ?? Infinity) - last.right >= BODY_FULL_TOL * fs) continue;
|
|
1702
|
+
if (last.width < BODY_MIN_WIDTH_EM * fs || Math.abs((b.style?.fontSize ?? 0) - fs) > 0.15 * fs) continue;
|
|
1703
|
+
if (startsNewItem(a.text, b.text)) continue;
|
|
1704
|
+
a.text += wrapJoiner(a.text, b.text, lex) + b.text;
|
|
1705
|
+
const bl = b.bbox && PARA_LAST_LINE.get(b.bbox);
|
|
1706
|
+
if (bl) PARA_LAST_LINE.set(a.bbox, bl);
|
|
1707
|
+
else PARA_LAST_LINE.delete(a.bbox);
|
|
1708
|
+
blocks.splice(i, 1);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
var BODY_FULL_TOL = 0.25;
|
|
1712
|
+
var BODY_MIN_WIDTH_EM = 12;
|
|
1713
|
+
var BODY_MAX_PITCH_EM = 2;
|
|
1714
|
+
var BODY_PITCH_REL = 1.05;
|
|
1715
|
+
var BODY_MAX_PITCH_ABS_EM = 3.5;
|
|
1716
|
+
function bodyLineJoins(lines, lex) {
|
|
1717
|
+
let right = -Infinity;
|
|
1718
|
+
for (const l of lines) if (l.right > right) right = l.right;
|
|
1719
|
+
const pitch = (k) => lines[k].y - lines[k + 1].y;
|
|
1720
|
+
const sameSize = (k) => Math.abs(lines[k + 1].fontSize - lines[k].fontSize) <= 0.15 * lines[k].fontSize;
|
|
1721
|
+
let p1 = Infinity, p2 = Infinity, i1 = -1;
|
|
1722
|
+
for (let k = 0; k + 1 < lines.length; k++) {
|
|
1723
|
+
const p = pitch(k);
|
|
1724
|
+
if (p <= 0 || !sameSize(k)) continue;
|
|
1725
|
+
if (p < p1) {
|
|
1726
|
+
p2 = p1;
|
|
1727
|
+
p1 = p;
|
|
1728
|
+
i1 = k;
|
|
1729
|
+
} else if (p < p2) p2 = p;
|
|
1730
|
+
}
|
|
1731
|
+
const out = [];
|
|
1732
|
+
for (let i = 0; i + 1 < lines.length; i++) {
|
|
1733
|
+
const a = lines[i], b = lines[i + 1];
|
|
1734
|
+
const fs = a.fontSize;
|
|
1735
|
+
const others = i === i1 ? p2 : p1;
|
|
1736
|
+
const maxPitch = Number.isFinite(others) ? Math.min(BODY_MAX_PITCH_ABS_EM * fs, Math.max(BODY_MAX_PITCH_EM * fs, others * BODY_PITCH_REL)) : BODY_MAX_PITCH_EM * fs;
|
|
1737
|
+
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 < maxPitch && Math.abs(b.fontSize - fs) <= 0.15 * fs && !startsNewItem(a.text, b.text);
|
|
1738
|
+
out.push(wraps ? wrapJoiner(a.text, b.text, lex) : "\n");
|
|
1739
|
+
}
|
|
1740
|
+
return out;
|
|
1741
|
+
}
|
|
1742
|
+
var CELL_PAD_MAX = 6;
|
|
1743
|
+
function cellLineWraps(box, contentLeft, prevRight, fontSize, nextFirstCharW) {
|
|
1744
|
+
const pad = Math.min(CELL_PAD_MAX, Math.max(0, contentLeft - box.x1));
|
|
1745
|
+
return fontSize > 0 && prevRight + nextFirstCharW - (box.x2 - pad) > 0.5 * fontSize;
|
|
1746
|
+
}
|
|
1747
|
+
function cellLineFills(box, contentLeft, cellRight, lineRight, fontSize) {
|
|
1748
|
+
const pad = Math.min(CELL_PAD_MAX, Math.max(0, contentLeft - box.x1));
|
|
1749
|
+
return fontSize > 0 && cellRight - lineRight < BODY_FULL_TOL * fontSize && lineRight - contentLeft >= 0.8 * (box.x2 - box.x1 - 2 * pad);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1325
1752
|
// src/pdf/cell-text.ts
|
|
1326
1753
|
var CELL_PADDING = 2;
|
|
1327
1754
|
var SPACE_GAP_RATIO = 0.17;
|
|
@@ -1355,9 +1782,47 @@ function mapTextToCells(items, cells) {
|
|
|
1355
1782
|
result.get(bestCell).push(item);
|
|
1356
1783
|
}
|
|
1357
1784
|
}
|
|
1785
|
+
keepWordsInOneCell(result);
|
|
1358
1786
|
return result;
|
|
1359
1787
|
}
|
|
1360
|
-
function
|
|
1788
|
+
function keepWordsInOneCell(result) {
|
|
1789
|
+
const owner = /* @__PURE__ */ new Map();
|
|
1790
|
+
for (const [cell, arr] of result) for (const it of arr) owner.set(it, cell);
|
|
1791
|
+
if (result.size < 2 || owner.size < 2) return;
|
|
1792
|
+
const all = [...owner.keys()].sort((a, b) => b.y - a.y || a.x - b.x);
|
|
1793
|
+
for (let i = 0; i < all.length; ) {
|
|
1794
|
+
let j = i + 1;
|
|
1795
|
+
while (j < all.length && Math.abs(all[j].y - all[i].y) <= 1) j++;
|
|
1796
|
+
const line = all.slice(i, j).sort((a, b) => a.x - b.x);
|
|
1797
|
+
for (let k = 0; k < line.length; ) {
|
|
1798
|
+
let e = k + 1;
|
|
1799
|
+
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++;
|
|
1800
|
+
const word = line.slice(k, e);
|
|
1801
|
+
const width = /* @__PURE__ */ new Map();
|
|
1802
|
+
for (const it of word) {
|
|
1803
|
+
const c = owner.get(it);
|
|
1804
|
+
width.set(c, (width.get(c) ?? 0) + it.w);
|
|
1805
|
+
}
|
|
1806
|
+
const crosses = word.some((it) => {
|
|
1807
|
+
const b = owner.get(it).bbox;
|
|
1808
|
+
return it.x < b.x1 - 1 || it.x + it.w > b.x2 + 1;
|
|
1809
|
+
});
|
|
1810
|
+
if (width.size > 1 && crosses) {
|
|
1811
|
+
const target = [...width].sort((a, b) => b[1] - a[1])[0][0];
|
|
1812
|
+
for (const it of word) {
|
|
1813
|
+
const c = owner.get(it);
|
|
1814
|
+
if (c === target) continue;
|
|
1815
|
+
result.set(c, result.get(c).filter((x) => x !== it));
|
|
1816
|
+
result.get(target).push(it);
|
|
1817
|
+
owner.set(it, target);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
k = e;
|
|
1821
|
+
}
|
|
1822
|
+
i = j;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
function cellTextToString(items, wrap) {
|
|
1361
1826
|
if (items.length === 0) return "";
|
|
1362
1827
|
if (items.length === 1) return items[0].text;
|
|
1363
1828
|
const sorted = [...items].sort((a, b) => b.y - a.y || a.x - b.x);
|
|
@@ -1398,7 +1863,20 @@ function cellTextToString(items) {
|
|
|
1398
1863
|
}
|
|
1399
1864
|
return result;
|
|
1400
1865
|
});
|
|
1401
|
-
return mergeCellTextLines(textLines);
|
|
1866
|
+
if (!wrap) return mergeCellTextLines(textLines);
|
|
1867
|
+
let contentLeft = Infinity;
|
|
1868
|
+
for (const it of items) if (it.x < contentLeft) contentLeft = it.x;
|
|
1869
|
+
const lineEnds = merged.map((line) => {
|
|
1870
|
+
const s = sortLineByX(line);
|
|
1871
|
+
let right = -Infinity;
|
|
1872
|
+
for (const it of s) if (it.x + it.w > right) right = it.x + it.w;
|
|
1873
|
+
const first = s[0];
|
|
1874
|
+
return { right, fontSize: first.fontSize, firstCharW: first.w / Math.max(1, [...first.text].length) };
|
|
1875
|
+
});
|
|
1876
|
+
let cellRight = -Infinity;
|
|
1877
|
+
for (const e of lineEnds) if (e.right > cellRight) cellRight = e.right;
|
|
1878
|
+
const wraps = lineEnds.slice(0, -1).map((a, i) => cellLineWraps(wrap.box, contentLeft, a.right, a.fontSize, lineEnds[i + 1].firstCharW) || lineEnds.length >= 3 && !textLines[i + 1].startsWith("(") && cellLineFills(wrap.box, contentLeft, cellRight, a.right, a.fontSize));
|
|
1879
|
+
return mergeCellTextLines(textLines, { wraps, lex: wrap.lex });
|
|
1402
1880
|
}
|
|
1403
1881
|
function mergeSuperscriptRows(lines) {
|
|
1404
1882
|
if (lines.length <= 1) return lines;
|
|
@@ -1469,6 +1947,12 @@ function detectEvenSpacedItems(items) {
|
|
|
1469
1947
|
return result;
|
|
1470
1948
|
}
|
|
1471
1949
|
function markEvenRun(items, result, start, end) {
|
|
1950
|
+
let dateOnly = true;
|
|
1951
|
+
for (let i = start; i < end; i++) if (!/^[년월일시분초]$/.test(items[i].text)) {
|
|
1952
|
+
dateOnly = false;
|
|
1953
|
+
break;
|
|
1954
|
+
}
|
|
1955
|
+
if (dateOnly) return;
|
|
1472
1956
|
const gaps = [];
|
|
1473
1957
|
for (let i = start + 1; i < end; i++) {
|
|
1474
1958
|
gaps.push(items[i].x - (items[i - 1].x + items[i - 1].w));
|
|
@@ -1487,19 +1971,19 @@ function markEvenRun(items, result, start, end) {
|
|
|
1487
1971
|
}
|
|
1488
1972
|
}
|
|
1489
1973
|
}
|
|
1490
|
-
function mergeCellTextLines(textLines) {
|
|
1974
|
+
function mergeCellTextLines(textLines, wrap) {
|
|
1491
1975
|
if (textLines.length <= 1) return textLines[0] || "";
|
|
1492
1976
|
const merged = [textLines[0]];
|
|
1493
1977
|
for (let i = 1; i < textLines.length; i++) {
|
|
1494
1978
|
const prev = merged[merged.length - 1];
|
|
1495
1979
|
const curr = textLines[i];
|
|
1496
|
-
if (/[가-힣]$/.test(prev) && /^[가-힣]+$/.test(curr) && curr.length <= 8 && !curr.includes(" ")) {
|
|
1980
|
+
if (wrap ? wrap.wraps[i - 1] && !startsNewItem(prev, curr) && wrapJoiner(prev, curr, wrap.lex) === "" : /[가-힣]$/.test(prev) && /^[가-힣]+$/.test(curr) && curr.length <= 8 && !curr.includes(" ")) {
|
|
1497
1981
|
merged[merged.length - 1] = prev + curr;
|
|
1498
1982
|
} else if (curr.trim().length <= 3 && /^[)\]%}]/.test(curr.trim())) {
|
|
1499
1983
|
merged[merged.length - 1] = prev + curr.trim();
|
|
1500
|
-
} else if (/[,(]$/.test(prev.trim()) && curr.trim().length <= 15) {
|
|
1984
|
+
} else if (!wrap && /[,(]$/.test(prev.trim()) && curr.trim().length <= 15) {
|
|
1501
1985
|
merged[merged.length - 1] = prev + curr.trim();
|
|
1502
|
-
} else if (/[\d,]$/.test(prev) && /^[\d,]+[)\]]?$/.test(curr.trim()) && curr.trim().length <= 10 && !(/\d{
|
|
1986
|
+
} 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()))) {
|
|
1503
1987
|
merged[merged.length - 1] = prev + curr.trim();
|
|
1504
1988
|
} else {
|
|
1505
1989
|
merged.push(curr);
|
|
@@ -1654,14 +2138,14 @@ function filterHiddenText(items, pageWidth, pageHeight) {
|
|
|
1654
2138
|
}
|
|
1655
2139
|
return { visible, hiddenCount };
|
|
1656
2140
|
}
|
|
1657
|
-
function collapseEvenSpacing(text) {
|
|
2141
|
+
function collapseEvenSpacing(text, whole = true) {
|
|
1658
2142
|
const tokens = text.split(" ");
|
|
1659
2143
|
const singleCharCount = tokens.filter((t) => t.length === 1).length;
|
|
1660
|
-
if (tokens.length >= 3 && singleCharCount / tokens.length >= 0.7 && !isDateUnitBlank(tokens)) {
|
|
2144
|
+
if (whole && tokens.length >= 3 && singleCharCount / tokens.length >= 0.7 && !isDateUnitBlank(tokens)) {
|
|
1661
2145
|
return tokens.join("");
|
|
1662
2146
|
}
|
|
1663
2147
|
return text.replace(
|
|
1664
|
-
/(?<![
|
|
2148
|
+
/(?<![^\s>])[가-힣](?: [가-힣\d]){2,}(?![^\s<])/g,
|
|
1665
2149
|
(match) => isDateUnitBlank(match.split(" ")) ? match : match.replace(/ /g, "")
|
|
1666
2150
|
);
|
|
1667
2151
|
}
|
|
@@ -1743,6 +2227,7 @@ function normalizeItems(rawItems) {
|
|
|
1743
2227
|
}
|
|
1744
2228
|
if (!isDup) deduped.push(sorted[i]);
|
|
1745
2229
|
}
|
|
2230
|
+
splitOverlaidRuns(deduped);
|
|
1746
2231
|
if (spacePositions.length > 0) {
|
|
1747
2232
|
for (const sp of spacePositions) {
|
|
1748
2233
|
let nearest = null;
|
|
@@ -1758,6 +2243,98 @@ function normalizeItems(rawItems) {
|
|
|
1758
2243
|
}
|
|
1759
2244
|
return deduped;
|
|
1760
2245
|
}
|
|
2246
|
+
var WORD_SPACE_EM = 0.3;
|
|
2247
|
+
var OVERLAY_DUP_EM = 0.5;
|
|
2248
|
+
var OVERLAY_FIT_EM = 0.6;
|
|
2249
|
+
function glyphUnits(s) {
|
|
2250
|
+
let u = 0;
|
|
2251
|
+
for (const ch of s) u += (ch.codePointAt(0) ?? 0) >= 8592 ? 1 : 0.5;
|
|
2252
|
+
return u;
|
|
2253
|
+
}
|
|
2254
|
+
function splitOverlaidRuns(items) {
|
|
2255
|
+
let replaced = null;
|
|
2256
|
+
for (let ci = 0; ci < items.length; ci++) {
|
|
2257
|
+
const c = items[ci];
|
|
2258
|
+
if (c.w <= 0 || !/\S\s+\S/.test(c.text)) continue;
|
|
2259
|
+
const overlays = [];
|
|
2260
|
+
for (let j = ci - 1; j >= 0 && items[j].y - c.y <= 1; j--) if (isInside(items[j], c)) overlays.push(items[j]);
|
|
2261
|
+
for (let j = ci + 1; j < items.length && c.y - items[j].y <= 1; j++) if (isInside(items[j], c)) overlays.push(items[j]);
|
|
2262
|
+
if (overlays.length === 0) continue;
|
|
2263
|
+
const pieces = splitAroundOverlays(c, overlays);
|
|
2264
|
+
if (pieces) (replaced ??= /* @__PURE__ */ new Map()).set(c, pieces);
|
|
2265
|
+
}
|
|
2266
|
+
if (!replaced) return;
|
|
2267
|
+
const out = [];
|
|
2268
|
+
for (const it of items) {
|
|
2269
|
+
const p = replaced.get(it);
|
|
2270
|
+
if (p) out.push(...p);
|
|
2271
|
+
else out.push(it);
|
|
2272
|
+
}
|
|
2273
|
+
items.length = 0;
|
|
2274
|
+
items.push(...out.sort((a, b) => b.y - a.y || a.x - b.x));
|
|
2275
|
+
}
|
|
2276
|
+
function isInside(o, c) {
|
|
2277
|
+
return o.x >= c.x + 1 && o.x + o.w <= c.x + c.w + 1 && o.w < c.w;
|
|
2278
|
+
}
|
|
2279
|
+
function splitAroundOverlays(c, overlays) {
|
|
2280
|
+
const words = c.text.split(/\s+/);
|
|
2281
|
+
const fs = c.fontSize > 0 ? c.fontSize : c.h > 0 ? c.h : 10;
|
|
2282
|
+
const ws = WORD_SPACE_EM * fs;
|
|
2283
|
+
const totalU = words.reduce((s, w) => s + glyphUnits(w), 0);
|
|
2284
|
+
if (totalU <= 0) return null;
|
|
2285
|
+
const a0 = (c.w - (words.length - 1) * ws) / totalU;
|
|
2286
|
+
const ov = overlays.filter((o) => {
|
|
2287
|
+
for (let at = c.text.indexOf(o.text); at >= 0; at = c.text.indexOf(o.text, at + 1)) {
|
|
2288
|
+
const prefix = c.text.slice(0, at);
|
|
2289
|
+
const est = c.x + glyphUnits(prefix.replace(/\s+/g, "")) * a0 + (prefix.match(/\s+/g)?.length ?? 0) * ws;
|
|
2290
|
+
if (Math.abs(est - o.x) <= OVERLAY_DUP_EM * fs) return false;
|
|
2291
|
+
}
|
|
2292
|
+
return true;
|
|
2293
|
+
}).sort((p2, q) => p2.x - q.x);
|
|
2294
|
+
if (ov.length === 0) return null;
|
|
2295
|
+
const holes = Math.min(ov.length, words.length - 1);
|
|
2296
|
+
let ovW = 0;
|
|
2297
|
+
for (const o of ov) ovW += o.w;
|
|
2298
|
+
const a = (c.w - ovW - (words.length - 1 - holes) * ws) / totalU;
|
|
2299
|
+
if (!(a > 0)) return null;
|
|
2300
|
+
const starts = [], ends = [], holeAfter = [];
|
|
2301
|
+
let p = c.x;
|
|
2302
|
+
let k = 0;
|
|
2303
|
+
let assigned = 0;
|
|
2304
|
+
for (let i = 0; i < words.length; i++) {
|
|
2305
|
+
starts.push(p);
|
|
2306
|
+
p += glyphUnits(words[i]) * a;
|
|
2307
|
+
ends.push(p);
|
|
2308
|
+
if (i === words.length - 1) break;
|
|
2309
|
+
while (k < ov.length && ov[k].x < p - a * 0.6) k++;
|
|
2310
|
+
const half = glyphUnits(words[i + 1]) * a / 2;
|
|
2311
|
+
let hole = false;
|
|
2312
|
+
while (k < ov.length && ov[k].x < p + half) {
|
|
2313
|
+
hole = true;
|
|
2314
|
+
p = Math.max(p, ov[k].x + ov[k].w);
|
|
2315
|
+
k++;
|
|
2316
|
+
}
|
|
2317
|
+
holeAfter.push(hole);
|
|
2318
|
+
if (hole) assigned++;
|
|
2319
|
+
else p += ws;
|
|
2320
|
+
}
|
|
2321
|
+
if (assigned === 0 || Math.abs(p - (c.x + c.w)) > Math.max(3, OVERLAY_FIT_EM * fs)) return null;
|
|
2322
|
+
const pieces = [];
|
|
2323
|
+
for (let s = 0; s < words.length; ) {
|
|
2324
|
+
let e = s;
|
|
2325
|
+
while (e < words.length - 1 && !holeAfter[e]) e++;
|
|
2326
|
+
pieces.push({
|
|
2327
|
+
...c,
|
|
2328
|
+
text: words.slice(s, e + 1).join(" "),
|
|
2329
|
+
x: Math.round(starts[s]),
|
|
2330
|
+
w: Math.max(1, Math.round(ends[e] - starts[s])),
|
|
2331
|
+
hasSpaceBefore: s === 0 ? c.hasSpaceBefore : false,
|
|
2332
|
+
seq: c.seq === void 0 ? void 0 : c.seq + pieces.length / 1e4
|
|
2333
|
+
});
|
|
2334
|
+
s = e + 1;
|
|
2335
|
+
}
|
|
2336
|
+
return pieces;
|
|
2337
|
+
}
|
|
1761
2338
|
function splitEvenSpacedItem(text, itemX, itemW, fontSize) {
|
|
1762
2339
|
if (!/^[가-힣\d](?: [가-힣\d]){2,}$/.test(text)) return null;
|
|
1763
2340
|
const chars = text.split(" ");
|
|
@@ -1883,7 +2460,7 @@ function detectHeadings(blocks, medianFontSize) {
|
|
|
1883
2460
|
if (level > 0) {
|
|
1884
2461
|
block.type = "heading";
|
|
1885
2462
|
block.level = level;
|
|
1886
|
-
block.text = collapseEvenSpacing(text);
|
|
2463
|
+
block.text = collapseEvenSpacing(text, false);
|
|
1887
2464
|
}
|
|
1888
2465
|
}
|
|
1889
2466
|
}
|
|
@@ -2247,6 +2824,7 @@ var TABLE_COLXS = /* @__PURE__ */ new WeakMap();
|
|
|
2247
2824
|
var EMPTY_PARTS = /* @__PURE__ */ new WeakSet();
|
|
2248
2825
|
var IMAGE_CELLS = /* @__PURE__ */ new WeakSet();
|
|
2249
2826
|
var FILLER_CELLS = /* @__PURE__ */ new WeakSet();
|
|
2827
|
+
var CONT_PARTS = /* @__PURE__ */ new WeakMap();
|
|
2250
2828
|
var CELL_LINES = /* @__PURE__ */ new WeakMap();
|
|
2251
2829
|
function recordCellLines(cell, items) {
|
|
2252
2830
|
const lines = [];
|
|
@@ -2802,7 +3380,7 @@ function buildClusterTable(rows, columns, pageNum) {
|
|
|
2802
3380
|
}
|
|
2803
3381
|
const assignments = assignRowItems(row.items, columns, numCols);
|
|
2804
3382
|
for (const { col, items } of assignments) {
|
|
2805
|
-
const text = items
|
|
3383
|
+
const text = joinCellItems(items);
|
|
2806
3384
|
const existing = cells[r][col].text;
|
|
2807
3385
|
cells[r][col].text = existing ? existing + " " + text : text;
|
|
2808
3386
|
for (const item of items) usedItems.add(item);
|
|
@@ -2825,6 +3403,7 @@ function buildClusterTable(rows, columns, pageNum) {
|
|
|
2825
3403
|
if (/^[○●▶\-·]/.test(contentText)) continue;
|
|
2826
3404
|
for (let pr = r - 1; pr >= 0; pr--) {
|
|
2827
3405
|
if (cells[pr].some((c) => c.text.trim())) {
|
|
3406
|
+
if (cells[pr][0].colSpan > 1) break;
|
|
2828
3407
|
for (let c = 0; c < numCols; c++) {
|
|
2829
3408
|
const prev = cells[pr][c].text.trim();
|
|
2830
3409
|
const curr = cells[r][c].text.trim();
|
|
@@ -2875,6 +3454,29 @@ function buildClusterTable(rows, columns, pageNum) {
|
|
|
2875
3454
|
usedItems
|
|
2876
3455
|
};
|
|
2877
3456
|
}
|
|
3457
|
+
function joinCellItems(items) {
|
|
3458
|
+
const hOf = (i) => i.h > 0 ? i.h : i.fontSize;
|
|
3459
|
+
const lines = [];
|
|
3460
|
+
for (const it of [...items].sort((a, b) => b.y - a.y || a.x - b.x)) {
|
|
3461
|
+
const bottom = it.y, top = it.y + hOf(it);
|
|
3462
|
+
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);
|
|
3463
|
+
if (line) {
|
|
3464
|
+
line.items.push(it);
|
|
3465
|
+
line.bottom = Math.min(line.bottom, bottom);
|
|
3466
|
+
line.top = Math.max(line.top, top);
|
|
3467
|
+
} else lines.push({ bottom, top, items: [it] });
|
|
3468
|
+
}
|
|
3469
|
+
return lines.sort((a, b) => b.top - a.top).map(({ items: line }) => {
|
|
3470
|
+
line.sort((a, b) => a.x - b.x);
|
|
3471
|
+
let s = line[0].text;
|
|
3472
|
+
for (let i = 1; i < line.length; i++) {
|
|
3473
|
+
const gap = line[i].x - (line[i - 1].x + line[i - 1].w);
|
|
3474
|
+
const fs = (line[i].fontSize + line[i - 1].fontSize) / 2;
|
|
3475
|
+
s += (line[i].hasSpaceBefore && gap >= fs * 0.05 || gap > spaceGapThreshold(fs) ? " " : "") + line[i].text;
|
|
3476
|
+
}
|
|
3477
|
+
return s;
|
|
3478
|
+
}).join(" ");
|
|
3479
|
+
}
|
|
2878
3480
|
|
|
2879
3481
|
// src/pdf/xy-cut.ts
|
|
2880
3482
|
var MAX_XYCUT_DEPTH = 50;
|
|
@@ -3207,7 +3809,10 @@ function detectColumns(yLines) {
|
|
|
3207
3809
|
const xClusters = [];
|
|
3208
3810
|
for (const line of tableYLines) {
|
|
3209
3811
|
if (isProseSpread(line)) continue;
|
|
3210
|
-
|
|
3812
|
+
const byX = [...line].sort((a, b) => a.x - b.x);
|
|
3813
|
+
for (let k = 0; k < byX.length; k++) {
|
|
3814
|
+
const item = byX[k];
|
|
3815
|
+
if (k > 0 && item.x - (byX[k - 1].x + byX[k - 1].w) < Math.max(item.fontSize, byX[k - 1].fontSize)) continue;
|
|
3211
3816
|
let found = false;
|
|
3212
3817
|
for (const c of xClusters) {
|
|
3213
3818
|
if (Math.abs(item.x - c.center) <= CLUSTER_TOL) {
|
|
@@ -3309,12 +3914,9 @@ function extractWithColumns(yLines, columns) {
|
|
|
3309
3914
|
function buildGridTable(lines, columns) {
|
|
3310
3915
|
const numCols = columns.length;
|
|
3311
3916
|
const yRows = lines.map((items) => {
|
|
3312
|
-
const
|
|
3313
|
-
for (const item of items)
|
|
3314
|
-
|
|
3315
|
-
row[col] = row[col] ? row[col] + " " + item.text : item.text;
|
|
3316
|
-
}
|
|
3317
|
-
return row;
|
|
3917
|
+
const byCol = Array.from({ length: numCols }, () => []);
|
|
3918
|
+
for (const item of items) byCol[findColumn(item.x, columns)].push(item);
|
|
3919
|
+
return byCol.map((cell) => mergeLineSimple(cell));
|
|
3318
3920
|
});
|
|
3319
3921
|
const dataColStart = Math.max(2, Math.floor(numCols / 2));
|
|
3320
3922
|
const merged = [];
|
|
@@ -3512,12 +4114,26 @@ function wrapUnderlineRuns(items) {
|
|
|
3512
4114
|
}
|
|
3513
4115
|
|
|
3514
4116
|
// src/pdf/page-blocks.ts
|
|
3515
|
-
function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true) {
|
|
3516
|
-
if (items.length === 0)
|
|
4117
|
+
function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true, carry, lexicon) {
|
|
4118
|
+
if (items.length === 0) {
|
|
4119
|
+
if (carry) carry.clip = void 0;
|
|
4120
|
+
return [];
|
|
4121
|
+
}
|
|
4122
|
+
const lex = lexicon ?? new WrapLexicon();
|
|
4123
|
+
for (const line of streamLines(items)) lex.addLine(mergeLineSimple(line));
|
|
3517
4124
|
const extracted = extractLines(opList.fnArray, opList.argsArray);
|
|
3518
4125
|
let { horizontals, verticals } = extracted;
|
|
3519
|
-
const
|
|
4126
|
+
const prevPage = carry?.page === pageNum - 1 ? carry.clip : void 0;
|
|
4127
|
+
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 };
|
|
4128
|
+
if (carry) {
|
|
4129
|
+
carry.page = pageNum;
|
|
4130
|
+
carry.clip = clipResult.page;
|
|
4131
|
+
}
|
|
3520
4132
|
const clipGrids = clipResult.grids;
|
|
4133
|
+
if (clipGrids.length === 0) {
|
|
4134
|
+
horizontals = chainShortSegments(horizontals, extracted.shortH, "h");
|
|
4135
|
+
verticals = chainShortSegments(verticals, extracted.shortV, "v");
|
|
4136
|
+
}
|
|
3521
4137
|
if (extraLines) {
|
|
3522
4138
|
horizontals = horizontals.concat(extraLines.horizontals);
|
|
3523
4139
|
verticals = verticals.concat(extraLines.verticals);
|
|
@@ -3532,12 +4148,13 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
|
|
|
3532
4148
|
markUnderlineItems(items, horizontals, verticals);
|
|
3533
4149
|
wrapUnderlineRuns(items);
|
|
3534
4150
|
const lineGrids = detectTables ? buildTableGrids(horizontals, verticals) : [];
|
|
3535
|
-
const
|
|
4151
|
+
const tableClipGrids = dropShadingClipGrids(clipGrids, lineGrids, extracted.fillRects, verticals);
|
|
4152
|
+
const grids = [...tableClipGrids, ...dropGridsInside(lineGrids, tableClipGrids, clipResult.containers)];
|
|
3536
4153
|
if (grids.length > 0) {
|
|
3537
4154
|
const imageRegions = extractImageRegions(opList.fnArray, opList.argsArray).filter((r) => r.x2 - r.x1 >= 8 && r.y2 - r.y1 >= 8);
|
|
3538
|
-
return extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, imageRegions);
|
|
4155
|
+
return extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, imageRegions, lex);
|
|
3539
4156
|
}
|
|
3540
|
-
return extractPageBlocksFallback(items, pageNum, true, detectTables);
|
|
4157
|
+
return extractPageBlocksFallback(items, pageNum, true, detectTables, lex);
|
|
3541
4158
|
}
|
|
3542
4159
|
var STRIKE_MAX_THICKNESS = 2;
|
|
3543
4160
|
var STRIKE_MAX_THICKNESS_RATIO = 0.25;
|
|
@@ -3646,13 +4263,13 @@ function takePendingNested(pending, cellBox) {
|
|
|
3646
4263
|
}
|
|
3647
4264
|
return out;
|
|
3648
4265
|
}
|
|
3649
|
-
function buildFrameCellBlocks(cellItems, nested, pageNum) {
|
|
4266
|
+
function buildFrameCellBlocks(cellItems, nested, pageNum, wrap) {
|
|
3650
4267
|
const tables = [...nested].sort((a, b) => b.bbox.y + b.bbox.height - (a.bbox.y + a.bbox.height));
|
|
3651
4268
|
const blocks = [];
|
|
3652
4269
|
let rest = [...cellItems];
|
|
3653
4270
|
const pushParagraphs = (items) => {
|
|
3654
4271
|
if (items.length === 0) return;
|
|
3655
|
-
for (const line of cleanCellText(cellTextToString(items)).split("\n")) {
|
|
4272
|
+
for (const line of cleanCellText(cellTextToString(items, wrap)).split("\n")) {
|
|
3656
4273
|
const t = line.trim();
|
|
3657
4274
|
if (t) blocks.push({ type: "paragraph", text: t, pageNumber: pageNum });
|
|
3658
4275
|
}
|
|
@@ -3667,7 +4284,7 @@ function buildFrameCellBlocks(cellItems, nested, pageNum) {
|
|
|
3667
4284
|
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");
|
|
3668
4285
|
return { blocks, text };
|
|
3669
4286
|
}
|
|
3670
|
-
function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, imageRegions = []) {
|
|
4287
|
+
function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, imageRegions = [], lex) {
|
|
3671
4288
|
const blocks = [];
|
|
3672
4289
|
const usedItems = /* @__PURE__ */ new Set();
|
|
3673
4290
|
const pendingNested = [];
|
|
@@ -3717,12 +4334,13 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, i
|
|
|
3717
4334
|
const nested = grid.cells ? takePendingNested(pendingNested, cell.bbox) : [];
|
|
3718
4335
|
if (nested.length > 0) {
|
|
3719
4336
|
nestedAttached = true;
|
|
3720
|
-
const built = buildFrameCellBlocks(cellItems, nested, pageNum);
|
|
4337
|
+
const built = buildFrameCellBlocks(cellItems, nested, pageNum, { box: cell.bbox, lex });
|
|
3721
4338
|
irGrid[cell.row][cell.col] = { text: built.text, colSpan: cell.colSpan, rowSpan: cell.rowSpan, blocks: built.blocks };
|
|
4339
|
+
if (cellItems.length) recordCellLines(irGrid[cell.row][cell.col], cellItems);
|
|
3722
4340
|
continue;
|
|
3723
4341
|
}
|
|
3724
4342
|
irGrid[cell.row][cell.col] = {
|
|
3725
|
-
text: cleanCellText(cellTextToString(cellItems)),
|
|
4343
|
+
text: cleanCellText(cellTextToString(cellItems, { box: cell.bbox, lex })),
|
|
3726
4344
|
colSpan: cell.colSpan,
|
|
3727
4345
|
rowSpan: cell.rowSpan
|
|
3728
4346
|
};
|
|
@@ -3760,6 +4378,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, i
|
|
|
3760
4378
|
hasHeader: finalRows > 1
|
|
3761
4379
|
};
|
|
3762
4380
|
TABLE_COLXS.set(irTable, grid.colXs);
|
|
4381
|
+
if (grid.continues) CONT_PARTS.set(irTable, grid.continues);
|
|
3763
4382
|
const hasContent = finalGrid.some((row) => row.some((cell) => cell.text.trim() !== ""));
|
|
3764
4383
|
const emptyPart = !hasContent && !!grid.cells && !grid.clipParent && !nestedAttached;
|
|
3765
4384
|
if (!hasContent && !emptyPart) continue;
|
|
@@ -3846,7 +4465,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, i
|
|
|
3846
4465
|
if (side.length === 0) continue;
|
|
3847
4466
|
for (const group of xyCutOrder(side, gapThreshold)) {
|
|
3848
4467
|
if (group.length === 0) continue;
|
|
3849
|
-
const groupBlocks = extractPageBlocksFallback(group, pageNum);
|
|
4468
|
+
const groupBlocks = extractPageBlocksFallback(group, pageNum, false, true, lex);
|
|
3850
4469
|
for (const b of groupBlocks) textBlocks.push(b);
|
|
3851
4470
|
groupSizes.push(groupBlocks.length);
|
|
3852
4471
|
}
|
|
@@ -3859,7 +4478,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals, i
|
|
|
3859
4478
|
const textBlocks = [];
|
|
3860
4479
|
for (const group of groups) {
|
|
3861
4480
|
if (group.length === 0) continue;
|
|
3862
|
-
const groupBlocks = extractPageBlocksFallback(group, pageNum);
|
|
4481
|
+
const groupBlocks = extractPageBlocksFallback(group, pageNum, false, true, lex);
|
|
3863
4482
|
for (const b of groupBlocks) textBlocks.push(b);
|
|
3864
4483
|
groupSizes.push(groupBlocks.length);
|
|
3865
4484
|
}
|
|
@@ -3969,7 +4588,42 @@ function splitTwoColumnProse(items, cutX) {
|
|
|
3969
4588
|
}
|
|
3970
4589
|
return groups;
|
|
3971
4590
|
}
|
|
3972
|
-
function
|
|
4591
|
+
function streamLines(items) {
|
|
4592
|
+
if (items.some((i) => i.seq === void 0)) return groupByY([...items].sort((a, b) => b.y - a.y || a.x - b.x));
|
|
4593
|
+
const lines = [];
|
|
4594
|
+
let cur = [];
|
|
4595
|
+
for (const it of [...items].sort((a, b) => a.seq - b.seq)) {
|
|
4596
|
+
const last = cur[cur.length - 1];
|
|
4597
|
+
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))) {
|
|
4598
|
+
lines.push(cur);
|
|
4599
|
+
cur = [];
|
|
4600
|
+
}
|
|
4601
|
+
cur.push(it);
|
|
4602
|
+
}
|
|
4603
|
+
if (cur.length) lines.push(cur);
|
|
4604
|
+
return lines;
|
|
4605
|
+
}
|
|
4606
|
+
function pushLineParagraphs(out, yLines, pageNum, lex) {
|
|
4607
|
+
const lines = yLines.map((items) => ({ items, text: mergeLineSimple(items) })).filter((l) => l.text.trim());
|
|
4608
|
+
const geo = lines.map((l) => {
|
|
4609
|
+
const b = computeBBox(l.items, pageNum);
|
|
4610
|
+
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 };
|
|
4611
|
+
});
|
|
4612
|
+
const joins = bodyLineJoins(geo, lex);
|
|
4613
|
+
for (let i = 0; i < lines.length; ) {
|
|
4614
|
+
let text = lines[i].text;
|
|
4615
|
+
const items = [...lines[i].items];
|
|
4616
|
+
for (; i + 1 < lines.length && joins[i] !== "\n"; i++) {
|
|
4617
|
+
text += joins[i] + lines[i + 1].text;
|
|
4618
|
+
items.push(...lines[i + 1].items);
|
|
4619
|
+
}
|
|
4620
|
+
const block = { type: "paragraph", text, pageNumber: pageNum, bbox: computeBBox(items, pageNum), style: dominantStyle(items) };
|
|
4621
|
+
PARA_LAST_LINE.set(block.bbox, { right: geo[i].right, width: geo[i].right - geo[i].left, fontSize: geo[i].fontSize });
|
|
4622
|
+
out.push(block);
|
|
4623
|
+
i++;
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTables = true, lex) {
|
|
3973
4627
|
if (items.length === 0) return [];
|
|
3974
4628
|
const blocks = [];
|
|
3975
4629
|
const clusterItems = items.map((i) => ({
|
|
@@ -3995,15 +4649,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
|
|
|
3995
4649
|
blocks.push({ type: "table", table: cr.table, pageNumber: pageNum, bbox: cr.bbox });
|
|
3996
4650
|
}
|
|
3997
4651
|
const remaining = items.filter((_, idx) => !usedIndices.has(idx));
|
|
3998
|
-
if (remaining.length > 0)
|
|
3999
|
-
const yLines = mergeSuperscriptLines(groupByY(remaining));
|
|
4000
|
-
for (const line of yLines) {
|
|
4001
|
-
const text = mergeLineSimple(line);
|
|
4002
|
-
if (!text.trim()) continue;
|
|
4003
|
-
const bbox = computeBBox(line, pageNum);
|
|
4004
|
-
blocks.push({ type: "paragraph", text, pageNumber: pageNum, bbox, style: dominantStyle(line) });
|
|
4005
|
-
}
|
|
4006
|
-
}
|
|
4652
|
+
if (remaining.length > 0) pushLineParagraphs(blocks, mergeSuperscriptLines(groupByY(remaining)), pageNum, lex);
|
|
4007
4653
|
blocks.sort((a, b) => {
|
|
4008
4654
|
const ay = a.bbox ? a.bbox.y + a.bbox.height : 0;
|
|
4009
4655
|
const by = b.bbox ? b.bbox.y + b.bbox.height : 0;
|
|
@@ -4034,12 +4680,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTable
|
|
|
4034
4680
|
const bbox = computeBBox(group, pageNum);
|
|
4035
4681
|
blocks.push({ type: "paragraph", text: tableText, pageNumber: pageNum, bbox, style: dominantStyle(group) });
|
|
4036
4682
|
} else {
|
|
4037
|
-
|
|
4038
|
-
const text = mergeLineSimple(line);
|
|
4039
|
-
if (!text.trim()) continue;
|
|
4040
|
-
const bbox = computeBBox(line, pageNum);
|
|
4041
|
-
blocks.push({ type: "paragraph", text, pageNumber: pageNum, bbox, style: dominantStyle(line) });
|
|
4042
|
-
}
|
|
4683
|
+
pushLineParagraphs(blocks, yLines, pageNum, lex);
|
|
4043
4684
|
}
|
|
4044
4685
|
}
|
|
4045
4686
|
}
|
|
@@ -4052,6 +4693,9 @@ export {
|
|
|
4052
4693
|
filterHiddenText,
|
|
4053
4694
|
collapseEvenSpacing,
|
|
4054
4695
|
normalizeItems,
|
|
4696
|
+
WrapLexicon,
|
|
4697
|
+
wrapJoiner,
|
|
4698
|
+
joinPageBreakWraps,
|
|
4055
4699
|
computeMedianFontSizeFromFreq,
|
|
4056
4700
|
detectHeadings,
|
|
4057
4701
|
detectMarkerHeadings,
|
|
@@ -4063,8 +4707,9 @@ export {
|
|
|
4063
4707
|
EMPTY_PARTS,
|
|
4064
4708
|
IMAGE_CELLS,
|
|
4065
4709
|
FILLER_CELLS,
|
|
4710
|
+
CONT_PARTS,
|
|
4066
4711
|
CELL_LINES,
|
|
4067
4712
|
trimTrailingEmptyTableCols,
|
|
4068
4713
|
extractPageBlocksWithLines
|
|
4069
4714
|
};
|
|
4070
|
-
//# sourceMappingURL=chunk-
|
|
4715
|
+
//# sourceMappingURL=chunk-23AJ65GX.js.map
|