kordoc 4.17.4 → 4.17.5
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/dist/{-SQRLFE4N.js → -R64MUZYI.js} +11 -11
- package/dist/{chunk-P2ZPPRM7.js → chunk-2DA5KVHL.js} +2 -2
- package/dist/{chunk-PTMYCK7R.js → chunk-3CBCXXMW.js} +2 -2
- package/dist/{chunk-SSY6FQLS.js → chunk-3OAC2DOI.js} +10 -10
- package/dist/{chunk-4D52VUGA.js → chunk-43QRXSO2.js} +3 -3
- package/dist/{chunk-HY33WBAH.js → chunk-55MXXUO5.js} +2 -2
- package/dist/{chunk-AAQR4RIX.js → chunk-6UNH3R5O.js} +2 -2
- package/dist/{chunk-IERSVUW6.js → chunk-CEN6GPOI.js} +2 -2
- package/dist/{chunk-VJP7O6BL.js → chunk-DV6QO76R.js} +2 -2
- package/dist/{chunk-PVJFCDND.js → chunk-FL32PTUU.js} +313 -239
- package/dist/chunk-FL32PTUU.js.map +1 -0
- package/dist/{chunk-FSCVZR4A.js → chunk-FSEYF24M.js} +4 -4
- package/dist/{chunk-GNORDND5.cjs → chunk-GGB6Q2DZ.cjs} +12 -12
- package/dist/{chunk-GNORDND5.cjs.map → chunk-GGB6Q2DZ.cjs.map} +1 -1
- package/dist/{chunk-RCS7WXOM.js → chunk-JBFGXC7Q.js} +2 -2
- package/dist/{chunk-WNF3TSV3.cjs → chunk-KOYJ4ZIV.cjs} +398 -324
- package/dist/chunk-KOYJ4ZIV.cjs.map +1 -0
- package/dist/{chunk-ODVIUG6Q.js → chunk-L4QCMV7B.js} +2 -2
- package/dist/{chunk-O5T5OIJM.js → chunk-M3F6AGZ2.js} +2 -2
- package/dist/{chunk-EXBZHI25.cjs → chunk-Q7VVEQI3.cjs} +2 -2
- package/dist/{chunk-EXBZHI25.cjs.map → chunk-Q7VVEQI3.cjs.map} +1 -1
- package/dist/{chunk-KIYHQQVO.cjs → chunk-S6KI2DO4.cjs} +3 -3
- package/dist/{chunk-KIYHQQVO.cjs.map → chunk-S6KI2DO4.cjs.map} +1 -1
- package/dist/{chunk-AFXX3YSP.cjs → chunk-TGSNDG2T.cjs} +6 -6
- package/dist/{chunk-AFXX3YSP.cjs.map → chunk-TGSNDG2T.cjs.map} +1 -1
- package/dist/{chunk-PNXJYEOR.js → chunk-TKAMX4O3.js} +2 -2
- package/dist/{chunk-7TE6TK52.js → chunk-TTEDRIWZ.js} +2 -2
- package/dist/{chunk-TMHMJUYU.js → chunk-USQM542U.js} +6 -6
- package/dist/{chunk-6DXSRQ65.js → chunk-UYNZAEE3.js} +2 -2
- package/dist/{chunk-NNNRN6BY.js → chunk-XHOMGWUB.js} +2 -2
- package/dist/{chunk-HQ6TM2JE.js → chunk-XYVRNYED.js} +3 -3
- package/dist/{chunk-YBIWPCUX.js → chunk-Y4JTKUYC.js} +314 -240
- package/dist/chunk-Y4JTKUYC.js.map +1 -0
- package/dist/{chunk-W3OY3SLY.js → chunk-YAIMKWBV.js} +2 -2
- package/dist/{chunk-AOONZC4S.cjs → chunk-Z3LXCCIB.cjs} +3 -3
- package/dist/{chunk-AOONZC4S.cjs.map → chunk-Z3LXCCIB.cjs.map} +1 -1
- package/dist/{chunk-DD6AY6EI.js → chunk-ZG7V4LAC.js} +2 -2
- package/dist/chunks-JFONNZNK.js +10 -0
- package/dist/cli.js +26 -26
- package/dist/{formula-U37MTMBD.cjs → formula-2PPRV75T.cjs} +9 -9
- package/dist/{formula-U37MTMBD.cjs.map → formula-2PPRV75T.cjs.map} +1 -1
- package/dist/{formula-U3MJXXIL.js → formula-655OJTYT.js} +4 -4
- package/dist/{formula-WZH5CMA3.js → formula-AODR6NCO.js} +3 -3
- package/dist/{image-ocr-V6MICC6R.js → image-ocr-BGVJ2CHC.js} +9 -9
- package/dist/{image-ocr-HYWDBN72.js → image-ocr-YXXZQHON.js} +6 -6
- package/dist/{image-ocr-OXU55EW3.cjs → image-ocr-ZA2TM3K4.cjs} +15 -15
- package/dist/{image-ocr-OXU55EW3.cjs.map → image-ocr-ZA2TM3K4.cjs.map} +1 -1
- package/dist/index.cjs +259 -259
- package/dist/index.js +5 -5
- package/dist/mcp.js +22 -22
- package/dist/{model-bundle-AHBUQ6SD.js → model-bundle-R2VUWXEN.js} +5 -5
- package/dist/{models-UF7B5LLG.js → models-BIML5V3Q.js} +5 -5
- package/dist/{parser-IXRGUYZW.cjs → parser-CATQZG3R.cjs} +175 -157
- package/dist/parser-CATQZG3R.cjs.map +1 -0
- package/dist/{parser-T4FBG2FR.js → parser-VODVQE4B.js} +28 -10
- package/dist/{parser-F6N6ID25.js.map → parser-VODVQE4B.js.map} +1 -1
- package/dist/{parser-F6N6ID25.js → parser-WMPQ6ZRT.js} +31 -13
- package/dist/{parser-T4FBG2FR.js.map → parser-WMPQ6ZRT.js.map} +1 -1
- package/dist/pdf-ocr-22UEFX5Q.js +19 -0
- package/dist/pdf-ocr-635HWHUS.js +15 -0
- package/dist/pdf-ocr-E7QWBW25.cjs +16 -0
- package/dist/{pdf-ocr-2CITOAIG.cjs.map → pdf-ocr-E7QWBW25.cjs.map} +1 -1
- package/dist/{profile-io-GM3NVLV3.js → profile-io-QFLRG2EA.js} +3 -3
- package/dist/{rasterize-4A3LJXTG.js → rasterize-ZRPBD4YU.js} +3 -3
- package/dist/{redact-doc-SQ7C3LIG.js → redact-doc-DXY2ZDDN.js} +11 -11
- package/dist/{render-6LTQZG4W.js → render-WI6N54BP.js} +7 -7
- package/dist/seal-KEJ34B4R.js +10 -0
- package/dist/{setup-DGBJOROA.js → setup-2D4EJV67.js} +3 -3
- package/dist/visual-TON626IM.js +20 -0
- package/dist/{watch-DJVJRSLE.js → watch-5J72AED4.js} +12 -12
- package/package.json +1 -1
- package/dist/chunk-PVJFCDND.js.map +0 -1
- package/dist/chunk-WNF3TSV3.cjs.map +0 -1
- package/dist/chunk-YBIWPCUX.js.map +0 -1
- package/dist/chunks-VNSLMJQD.js +0 -10
- package/dist/parser-IXRGUYZW.cjs.map +0 -1
- package/dist/pdf-ocr-2CITOAIG.cjs +0 -16
- package/dist/pdf-ocr-RG2MFWBX.js +0 -15
- package/dist/pdf-ocr-U7Z4IUMA.js +0 -19
- package/dist/seal-ROSVLTRQ.js +0 -10
- package/dist/visual-6W34SFIW.js +0 -20
- /package/dist/{-SQRLFE4N.js.map → -R64MUZYI.js.map} +0 -0
- /package/dist/{chunk-P2ZPPRM7.js.map → chunk-2DA5KVHL.js.map} +0 -0
- /package/dist/{chunk-PTMYCK7R.js.map → chunk-3CBCXXMW.js.map} +0 -0
- /package/dist/{chunk-SSY6FQLS.js.map → chunk-3OAC2DOI.js.map} +0 -0
- /package/dist/{chunk-4D52VUGA.js.map → chunk-43QRXSO2.js.map} +0 -0
- /package/dist/{chunk-HY33WBAH.js.map → chunk-55MXXUO5.js.map} +0 -0
- /package/dist/{chunk-AAQR4RIX.js.map → chunk-6UNH3R5O.js.map} +0 -0
- /package/dist/{chunk-IERSVUW6.js.map → chunk-CEN6GPOI.js.map} +0 -0
- /package/dist/{chunk-VJP7O6BL.js.map → chunk-DV6QO76R.js.map} +0 -0
- /package/dist/{chunk-FSCVZR4A.js.map → chunk-FSEYF24M.js.map} +0 -0
- /package/dist/{chunk-RCS7WXOM.js.map → chunk-JBFGXC7Q.js.map} +0 -0
- /package/dist/{chunk-ODVIUG6Q.js.map → chunk-L4QCMV7B.js.map} +0 -0
- /package/dist/{chunk-O5T5OIJM.js.map → chunk-M3F6AGZ2.js.map} +0 -0
- /package/dist/{chunk-PNXJYEOR.js.map → chunk-TKAMX4O3.js.map} +0 -0
- /package/dist/{chunk-7TE6TK52.js.map → chunk-TTEDRIWZ.js.map} +0 -0
- /package/dist/{chunk-TMHMJUYU.js.map → chunk-USQM542U.js.map} +0 -0
- /package/dist/{chunk-6DXSRQ65.js.map → chunk-UYNZAEE3.js.map} +0 -0
- /package/dist/{chunk-NNNRN6BY.js.map → chunk-XHOMGWUB.js.map} +0 -0
- /package/dist/{chunk-HQ6TM2JE.js.map → chunk-XYVRNYED.js.map} +0 -0
- /package/dist/{chunk-W3OY3SLY.js.map → chunk-YAIMKWBV.js.map} +0 -0
- /package/dist/{chunk-DD6AY6EI.js.map → chunk-ZG7V4LAC.js.map} +0 -0
- /package/dist/{chunks-VNSLMJQD.js.map → chunks-JFONNZNK.js.map} +0 -0
- /package/dist/{formula-U3MJXXIL.js.map → formula-655OJTYT.js.map} +0 -0
- /package/dist/{formula-WZH5CMA3.js.map → formula-AODR6NCO.js.map} +0 -0
- /package/dist/{image-ocr-V6MICC6R.js.map → image-ocr-BGVJ2CHC.js.map} +0 -0
- /package/dist/{image-ocr-HYWDBN72.js.map → image-ocr-YXXZQHON.js.map} +0 -0
- /package/dist/{model-bundle-AHBUQ6SD.js.map → model-bundle-R2VUWXEN.js.map} +0 -0
- /package/dist/{models-UF7B5LLG.js.map → models-BIML5V3Q.js.map} +0 -0
- /package/dist/{pdf-ocr-RG2MFWBX.js.map → pdf-ocr-22UEFX5Q.js.map} +0 -0
- /package/dist/{pdf-ocr-U7Z4IUMA.js.map → pdf-ocr-635HWHUS.js.map} +0 -0
- /package/dist/{profile-io-GM3NVLV3.js.map → profile-io-QFLRG2EA.js.map} +0 -0
- /package/dist/{rasterize-4A3LJXTG.js.map → rasterize-ZRPBD4YU.js.map} +0 -0
- /package/dist/{redact-doc-SQ7C3LIG.js.map → redact-doc-DXY2ZDDN.js.map} +0 -0
- /package/dist/{render-6LTQZG4W.js.map → render-WI6N54BP.js.map} +0 -0
- /package/dist/{seal-ROSVLTRQ.js.map → seal-KEJ34B4R.js.map} +0 -0
- /package/dist/{setup-DGBJOROA.js.map → setup-2D4EJV67.js.map} +0 -0
- /package/dist/{visual-6W34SFIW.js.map → visual-TON626IM.js.map} +0 -0
- /package/dist/{watch-DJVJRSLE.js.map → watch-5J72AED4.js.map} +0 -0
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
HEADING_RATIO_H2,
|
|
6
6
|
HEADING_RATIO_H3,
|
|
7
7
|
tidyScriptTags
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-L4QCMV7B.js";
|
|
9
9
|
import {
|
|
10
10
|
safeMax,
|
|
11
11
|
safeMin
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZG7V4LAC.js";
|
|
13
13
|
|
|
14
14
|
// src/pdf/line-extract.ts
|
|
15
15
|
import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
@@ -3163,6 +3163,297 @@ function panelBlocks(items, panel, pageNum) {
|
|
|
3163
3163
|
});
|
|
3164
3164
|
}
|
|
3165
3165
|
|
|
3166
|
+
// src/pdf/xy-cut.ts
|
|
3167
|
+
var MAX_XYCUT_DEPTH = 50;
|
|
3168
|
+
var XYCUT_MIN_GAP = 5;
|
|
3169
|
+
var CROSS_LAYOUT_BETA = 2;
|
|
3170
|
+
var CROSS_OVERLAP_RATIO = 0.1;
|
|
3171
|
+
var CROSS_MIN_OVERLAPS = 2;
|
|
3172
|
+
var CROSS_MAX_MASK_RATIO = 0.2;
|
|
3173
|
+
var NARROW_ELEMENT_WIDTH_RATIO = 0.1;
|
|
3174
|
+
var PROSE_GUTTER_MIN_GAP = 6;
|
|
3175
|
+
var XY_WRAP_BANDS = /* @__PURE__ */ new WeakMap();
|
|
3176
|
+
var XY_WRAP_ITEMS = /* @__PURE__ */ new WeakMap();
|
|
3177
|
+
var leaf = (items, wrapped) => {
|
|
3178
|
+
wrapped = validWrapBands(items, wrapped);
|
|
3179
|
+
const source = wrapped.length ? [...items] : [];
|
|
3180
|
+
for (const item of items) {
|
|
3181
|
+
if (wrapped.length) {
|
|
3182
|
+
XY_WRAP_BANDS.set(item, wrapped);
|
|
3183
|
+
XY_WRAP_ITEMS.set(item, source);
|
|
3184
|
+
} else {
|
|
3185
|
+
XY_WRAP_BANDS.delete(item);
|
|
3186
|
+
XY_WRAP_ITEMS.delete(item);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
return [items];
|
|
3190
|
+
};
|
|
3191
|
+
function xyCutOrder(items, gapThreshold, depth = 0, wrapped) {
|
|
3192
|
+
if (items.length === 0) return [];
|
|
3193
|
+
const newRegion = wrapped === void 0;
|
|
3194
|
+
wrapped ??= inheritedWrapBands(items);
|
|
3195
|
+
if (items.length <= 2 || depth >= MAX_XYCUT_DEPTH) return leaf(items, wrapped);
|
|
3196
|
+
if (depth === 0 && items.length >= 3) {
|
|
3197
|
+
const cross = identifyCrossLayoutItems(items);
|
|
3198
|
+
if (cross.size > 0 && cross.size <= items.length * CROSS_MAX_MASK_RATIO) {
|
|
3199
|
+
const rest = items.filter((i) => !cross.has(i));
|
|
3200
|
+
if (rest.length > 0) {
|
|
3201
|
+
const groups = xyCutOrder(rest, gapThreshold, 1);
|
|
3202
|
+
return mergeCrossLayoutGroups(groups, [...cross]);
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
const minGap = Math.max(XYCUT_MIN_GAP, gapThreshold);
|
|
3207
|
+
const sortedY = [...items].sort((a, b) => b.y - a.y);
|
|
3208
|
+
let hCut = findHorizontalCut(sortedY, wrapped);
|
|
3209
|
+
if (newRegion && hCut.gap >= minGap) {
|
|
3210
|
+
wrapped = wrappedLineBands(items, wrapped);
|
|
3211
|
+
hCut = findHorizontalCut(sortedY, wrapped);
|
|
3212
|
+
}
|
|
3213
|
+
const vCut = findVerticalCutWithOutlierFilter(items, minGap);
|
|
3214
|
+
const hValid = hCut.gap >= minGap;
|
|
3215
|
+
const vValid = (vCut.gap >= minGap || vCut.gap >= PROSE_GUTTER_MIN_GAP && isProseGutter(items, vCut.position)) && !splitsSpacedLabel(items, vCut.position);
|
|
3216
|
+
let useHorizontal;
|
|
3217
|
+
if (hValid && vValid) useHorizontal = vCut.gap <= hCut.gap * 1.5 || staggeredSides(items, vCut.position);
|
|
3218
|
+
else if (hValid) useHorizontal = true;
|
|
3219
|
+
else if (vValid) useHorizontal = false;
|
|
3220
|
+
else return splitEdgeSpannedColumns(items, gapThreshold, depth) ?? leaf(items, wrapped);
|
|
3221
|
+
if (useHorizontal) {
|
|
3222
|
+
const upper = items.filter((i) => i.y > hCut.position);
|
|
3223
|
+
const lower = items.filter((i) => i.y <= hCut.position);
|
|
3224
|
+
if (upper.length > 0 && lower.length > 0 && upper.length < items.length) {
|
|
3225
|
+
return [...xyCutOrder(upper, gapThreshold, depth + 1, wrapped), ...xyCutOrder(lower, gapThreshold, depth + 1, wrapped)];
|
|
3226
|
+
}
|
|
3227
|
+
} else {
|
|
3228
|
+
const left = items.filter((i) => i.x + i.w / 2 < vCut.position);
|
|
3229
|
+
const right = items.filter((i) => i.x + i.w / 2 >= vCut.position);
|
|
3230
|
+
if (left.length > 0 && right.length > 0 && left.length < items.length) {
|
|
3231
|
+
return [...xyCutOrder(left, gapThreshold, depth + 1), ...xyCutOrder(right, gapThreshold, depth + 1)];
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
return leaf(items, wrapped);
|
|
3235
|
+
}
|
|
3236
|
+
function splitsSpacedLabel(items, cutX) {
|
|
3237
|
+
const lone = (i) => /^[가-힣]$/.test(i.text.trim());
|
|
3238
|
+
const sameRow = (a, b) => Math.min(a.y, b.y) > Math.max(a.y - a.h, b.y - b.h);
|
|
3239
|
+
const left = items.filter((i) => i.x + i.w / 2 < cutX);
|
|
3240
|
+
const right = items.filter((i) => i.x + i.w / 2 >= cutX);
|
|
3241
|
+
let spaced = 0;
|
|
3242
|
+
for (const r of right) {
|
|
3243
|
+
if (right.some((o) => o !== r && o.x < r.x && sameRow(o, r))) continue;
|
|
3244
|
+
const row = left.filter((l2) => sameRow(l2, r));
|
|
3245
|
+
if (row.length === 0) continue;
|
|
3246
|
+
const l = row.reduce((a, b) => b.x + b.w > a.x + a.w ? b : a);
|
|
3247
|
+
if (!lone(l) || !lone(r)) return false;
|
|
3248
|
+
spaced++;
|
|
3249
|
+
}
|
|
3250
|
+
return spaced > 0;
|
|
3251
|
+
}
|
|
3252
|
+
function staggeredSides(items, cutX) {
|
|
3253
|
+
const left = items.filter((i) => i.x + i.w / 2 < cutX);
|
|
3254
|
+
const right = items.filter((i) => i.x + i.w / 2 >= cutX);
|
|
3255
|
+
if (left.length * right.length > 2e5) return false;
|
|
3256
|
+
const shareRow = (a, b) => Math.min(a.y, b.y) > Math.max(a.y - a.h, b.y - b.h);
|
|
3257
|
+
if (left.some((a) => right.some((b) => shareRow(a, b)))) return false;
|
|
3258
|
+
const sides = [...items].sort((a, b) => b.y - a.y).map((i) => i.x + i.w / 2 < cutX);
|
|
3259
|
+
let switches = 0;
|
|
3260
|
+
for (let k = 1; k < sides.length; k++) if (sides[k] !== sides[k - 1]) switches++;
|
|
3261
|
+
return switches >= 3;
|
|
3262
|
+
}
|
|
3263
|
+
function identifyCrossLayoutItems(items) {
|
|
3264
|
+
const cross = /* @__PURE__ */ new Set();
|
|
3265
|
+
if (items.length < 3) return cross;
|
|
3266
|
+
let maxWidth = 0;
|
|
3267
|
+
for (const i of items) {
|
|
3268
|
+
if (i.w > maxWidth) maxWidth = i.w;
|
|
3269
|
+
}
|
|
3270
|
+
const threshold = CROSS_LAYOUT_BETA * maxWidth;
|
|
3271
|
+
for (const item of items) {
|
|
3272
|
+
if (item.w < threshold) continue;
|
|
3273
|
+
let overlaps = 0;
|
|
3274
|
+
for (const other of items) {
|
|
3275
|
+
if (other === item) continue;
|
|
3276
|
+
const left = Math.max(item.x, other.x);
|
|
3277
|
+
const right = Math.min(item.x + item.w, other.x + other.w);
|
|
3278
|
+
const overlapW = right - left;
|
|
3279
|
+
if (overlapW <= 0) continue;
|
|
3280
|
+
const smaller = Math.min(item.w, other.w);
|
|
3281
|
+
if (smaller > 0 && overlapW / smaller >= CROSS_OVERLAP_RATIO) {
|
|
3282
|
+
overlaps++;
|
|
3283
|
+
if (overlaps >= CROSS_MIN_OVERLAPS) break;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
if (overlaps >= CROSS_MIN_OVERLAPS) cross.add(item);
|
|
3287
|
+
}
|
|
3288
|
+
return cross;
|
|
3289
|
+
}
|
|
3290
|
+
function mergeCrossLayoutGroups(groups, cross) {
|
|
3291
|
+
if (cross.length === 0) return groups;
|
|
3292
|
+
const sortedCross = [...cross].sort((a, b) => b.y + b.h - (a.y + a.h) || a.x - b.x);
|
|
3293
|
+
const groupTop = (g) => {
|
|
3294
|
+
let top = -Infinity;
|
|
3295
|
+
for (const i of g) {
|
|
3296
|
+
const t = i.y + i.h;
|
|
3297
|
+
if (t > top) top = t;
|
|
3298
|
+
}
|
|
3299
|
+
return top;
|
|
3300
|
+
};
|
|
3301
|
+
const result = [];
|
|
3302
|
+
let gi = 0, ci = 0;
|
|
3303
|
+
while (gi < groups.length || ci < sortedCross.length) {
|
|
3304
|
+
if (ci >= sortedCross.length) {
|
|
3305
|
+
result.push(groups[gi++]);
|
|
3306
|
+
continue;
|
|
3307
|
+
}
|
|
3308
|
+
if (gi >= groups.length) {
|
|
3309
|
+
result.push([sortedCross[ci++]]);
|
|
3310
|
+
continue;
|
|
3311
|
+
}
|
|
3312
|
+
const crossTop = sortedCross[ci].y + sortedCross[ci].h;
|
|
3313
|
+
if (crossTop >= groupTop(groups[gi])) result.push([sortedCross[ci++]]);
|
|
3314
|
+
else result.push(groups[gi++]);
|
|
3315
|
+
}
|
|
3316
|
+
return result;
|
|
3317
|
+
}
|
|
3318
|
+
function validWrapBands(items, bands) {
|
|
3319
|
+
if (!bands.length) return [];
|
|
3320
|
+
const present = new Set(items);
|
|
3321
|
+
return bands.filter((b) => b.sources.every(({ item: a, state: s }) => present.has(a) && a.text === s.text && a.x === s.x && a.y === s.y && a.w === s.w && a.h === s.h && a.fontSize === s.fontSize && a.fontName === s.fontName && a.isHidden === s.isHidden && a.hasSpaceBefore === s.hasSpaceBefore && a.syntheticSpace === s.syntheticSpace && a.strike === s.strike && a.underline === s.underline && a.seq === s.seq && a.rotated === s.rotated));
|
|
3322
|
+
}
|
|
3323
|
+
function inheritedWrapBands(items) {
|
|
3324
|
+
const source = XY_WRAP_ITEMS.get(items[0]);
|
|
3325
|
+
const present = source?.length === items.length ? new Set(items) : null;
|
|
3326
|
+
const sameLeaf = present && source.every((item) => present.has(item));
|
|
3327
|
+
return sameLeaf ? validWrapBands(items, XY_WRAP_BANDS.get(items[0]) ?? []) : [];
|
|
3328
|
+
}
|
|
3329
|
+
function wrappedLineBands(items, inherited) {
|
|
3330
|
+
const lines = groupByY(items).map((row) => {
|
|
3331
|
+
const box = computeBBox(row, 0);
|
|
3332
|
+
return {
|
|
3333
|
+
text: mergeLineSimple(row).replace(/<\/?u>|~~/g, ""),
|
|
3334
|
+
left: box.x,
|
|
3335
|
+
right: box.x + box.width,
|
|
3336
|
+
y: row.reduce((n, i) => n + i.y, 0) / row.length,
|
|
3337
|
+
fontSize: dominantStyle(row)?.fontSize ?? 0,
|
|
3338
|
+
sources: row.map((item) => ({ item, state: { ...item } }))
|
|
3339
|
+
};
|
|
3340
|
+
});
|
|
3341
|
+
const joins = bodyLineJoins(lines);
|
|
3342
|
+
const fresh = lines.slice(0, -1).flatMap((line, i) => joins[i] === "\n" || line.text.trim() === lines[i + 1].text.trim() ? [] : [{ top: line.y, bottom: lines[i + 1].y, sources: [...line.sources, ...lines[i + 1].sources] }]);
|
|
3343
|
+
const freshKeys = new Set(fresh.map((b) => `${b.top}:${b.bottom}`));
|
|
3344
|
+
return [...inherited.filter((b) => !freshKeys.has(`${b.top}:${b.bottom}`)), ...fresh].sort((a, b) => b.top - a.top);
|
|
3345
|
+
}
|
|
3346
|
+
function findHorizontalCut(sorted, wrapped) {
|
|
3347
|
+
if (sorted.length < 2) return { position: 0, gap: 0 };
|
|
3348
|
+
let largestGap = 0;
|
|
3349
|
+
let position = 0;
|
|
3350
|
+
let wrapIndex = 0;
|
|
3351
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
3352
|
+
const prevBottom = sorted[i - 1].y - sorted[i - 1].h;
|
|
3353
|
+
const currTop = sorted[i].y;
|
|
3354
|
+
const gap = prevBottom - currTop;
|
|
3355
|
+
if (gap <= 0) continue;
|
|
3356
|
+
const at = (prevBottom + currTop) / 2;
|
|
3357
|
+
while (wrapIndex < wrapped.length && at <= wrapped[wrapIndex].bottom) wrapIndex++;
|
|
3358
|
+
const band = wrapped[wrapIndex];
|
|
3359
|
+
if (gap > largestGap && !(band && at < band.top && at > band.bottom)) {
|
|
3360
|
+
largestGap = gap;
|
|
3361
|
+
position = at;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
return { position, gap: largestGap };
|
|
3365
|
+
}
|
|
3366
|
+
function findVerticalCutWithOutlierFilter(items, minGap) {
|
|
3367
|
+
const edgeCut = findVerticalCut(items);
|
|
3368
|
+
if (edgeCut.gap >= minGap) return edgeCut;
|
|
3369
|
+
if (items.length >= 3) {
|
|
3370
|
+
let minX = Infinity, maxX = -Infinity;
|
|
3371
|
+
for (const i of items) {
|
|
3372
|
+
if (i.x < minX) minX = i.x;
|
|
3373
|
+
const r = i.x + i.w;
|
|
3374
|
+
if (r > maxX) maxX = r;
|
|
3375
|
+
}
|
|
3376
|
+
const narrowThreshold = (maxX - minX) * NARROW_ELEMENT_WIDTH_RATIO;
|
|
3377
|
+
const filtered = items.filter((i) => i.w >= narrowThreshold);
|
|
3378
|
+
if (filtered.length >= 2 && filtered.length < items.length && filtered.length >= items.length * 0.7) {
|
|
3379
|
+
const filteredCut = findVerticalCut(filtered);
|
|
3380
|
+
if (filteredCut.gap > edgeCut.gap && filteredCut.gap >= minGap) {
|
|
3381
|
+
return filteredCut;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
return edgeCut;
|
|
3386
|
+
}
|
|
3387
|
+
function findVerticalCut(items) {
|
|
3388
|
+
if (items.length < 2) return { position: 0, gap: 0 };
|
|
3389
|
+
const sorted = [...items].sort((a, b) => a.x - b.x || a.x + a.w - (b.x + b.w));
|
|
3390
|
+
let largestGap = 0;
|
|
3391
|
+
let position = 0;
|
|
3392
|
+
let prevRight = null;
|
|
3393
|
+
for (const it of sorted) {
|
|
3394
|
+
const left = it.x;
|
|
3395
|
+
const right = it.x + it.w;
|
|
3396
|
+
if (prevRight !== null && left > prevRight) {
|
|
3397
|
+
const gap = left - prevRight;
|
|
3398
|
+
if (gap > largestGap) {
|
|
3399
|
+
largestGap = gap;
|
|
3400
|
+
position = (prevRight + left) / 2;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
prevRight = prevRight === null ? right : Math.max(prevRight, right);
|
|
3404
|
+
}
|
|
3405
|
+
return { position, gap: largestGap };
|
|
3406
|
+
}
|
|
3407
|
+
function isProseGutter(items, x) {
|
|
3408
|
+
const sideLines = (side) => {
|
|
3409
|
+
const lines = [];
|
|
3410
|
+
for (const item of [...side].sort((a, b) => b.y - a.y)) {
|
|
3411
|
+
const line = lines.find((l) => Math.abs(l.y - item.y) <= 2);
|
|
3412
|
+
if (line) {
|
|
3413
|
+
line.chars += item.text.length;
|
|
3414
|
+
line.left = Math.min(line.left, item.x);
|
|
3415
|
+
line.right = Math.max(line.right, item.x + item.w);
|
|
3416
|
+
} else lines.push({ y: item.y, chars: item.text.length, left: item.x, right: item.x + item.w });
|
|
3417
|
+
}
|
|
3418
|
+
return lines;
|
|
3419
|
+
};
|
|
3420
|
+
const left = sideLines(items.filter((i) => i.x + i.w / 2 < x));
|
|
3421
|
+
const right = sideLines(items.filter((i) => i.x + i.w / 2 >= x));
|
|
3422
|
+
if (left.length < 4 || right.length < 4) return false;
|
|
3423
|
+
const prose = (lines) => {
|
|
3424
|
+
const width = Math.max(...lines.map((l) => l.right)) - Math.min(...lines.map((l) => l.left));
|
|
3425
|
+
const full = lines.filter((l) => l.chars >= 25 && l.right - l.left >= width * 0.6).length;
|
|
3426
|
+
return full >= lines.length * 0.6;
|
|
3427
|
+
};
|
|
3428
|
+
if (!prose(left) || !prose(right)) return false;
|
|
3429
|
+
const span = (lines) => [Math.min(...lines.map((l) => l.y)), Math.max(...lines.map((l) => l.y))];
|
|
3430
|
+
const [l0, l1] = span(left), [r0, r1] = span(right);
|
|
3431
|
+
return Math.min(l1, r1) - Math.max(l0, r0) >= Math.min(l1 - l0, r1 - r0) * 0.5;
|
|
3432
|
+
}
|
|
3433
|
+
function splitEdgeSpannedColumns(items, gapThreshold, depth) {
|
|
3434
|
+
let minX = Infinity, maxX = -Infinity;
|
|
3435
|
+
for (const i of items) {
|
|
3436
|
+
minX = Math.min(minX, i.x);
|
|
3437
|
+
maxX = Math.max(maxX, i.x + i.w);
|
|
3438
|
+
}
|
|
3439
|
+
const narrow = items.filter((i) => i.w < (maxX - minX) * 0.5);
|
|
3440
|
+
if (narrow.length === items.length || narrow.length < 8) return null;
|
|
3441
|
+
const cut = findVerticalCut(narrow);
|
|
3442
|
+
if (cut.gap < PROSE_GUTTER_MIN_GAP || !isProseGutter(narrow, cut.position)) return null;
|
|
3443
|
+
const crossing = items.filter((i) => i.x < cut.position && i.x + i.w > cut.position);
|
|
3444
|
+
const rest = items.filter((i) => !crossing.includes(i));
|
|
3445
|
+
const restTop = Math.max(...rest.map((i) => i.y)), restBottom = Math.min(...rest.map((i) => i.y));
|
|
3446
|
+
const above = crossing.filter((i) => i.y > restTop), below = crossing.filter((i) => i.y < restBottom);
|
|
3447
|
+
if (above.length + below.length !== crossing.length) return null;
|
|
3448
|
+
const left = rest.filter((i) => i.x + i.w / 2 < cut.position), right = rest.filter((i) => i.x + i.w / 2 >= cut.position);
|
|
3449
|
+
return [
|
|
3450
|
+
...above.length ? [above] : [],
|
|
3451
|
+
...xyCutOrder(left, gapThreshold, depth + 1),
|
|
3452
|
+
...xyCutOrder(right, gapThreshold, depth + 1),
|
|
3453
|
+
...below.length ? [below] : []
|
|
3454
|
+
];
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3166
3457
|
// src/pdf/paragraph-lines.ts
|
|
3167
3458
|
var FACE_CHARS = /* @__PURE__ */ new WeakMap();
|
|
3168
3459
|
function hasNumberedStyledTitle(lines) {
|
|
@@ -3180,6 +3471,25 @@ function pushLineParagraphs(out, yLines, pageNum, lex) {
|
|
|
3180
3471
|
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 };
|
|
3181
3472
|
});
|
|
3182
3473
|
const joins = bodyLineJoins(geo, lex);
|
|
3474
|
+
const first = lines[0]?.items[0];
|
|
3475
|
+
const bands = first ? XY_WRAP_BANDS.get(first) ?? [] : [];
|
|
3476
|
+
const hasBand = (top, bottom) => {
|
|
3477
|
+
let lo = 0, hi = bands.length;
|
|
3478
|
+
while (lo < hi) {
|
|
3479
|
+
const mid = lo + hi >>> 1;
|
|
3480
|
+
if (bands[mid].top > top + 3) lo = mid + 1;
|
|
3481
|
+
else hi = mid;
|
|
3482
|
+
}
|
|
3483
|
+
for (; lo < bands.length && bands[lo].top >= top - 3; lo++) {
|
|
3484
|
+
if (Math.abs(bands[lo].bottom - bottom) <= 3) return true;
|
|
3485
|
+
}
|
|
3486
|
+
return false;
|
|
3487
|
+
};
|
|
3488
|
+
for (let i = 0; i + 1 < geo.length; i++) {
|
|
3489
|
+
if (joins[i] === "\n" && Math.abs(geo[i + 1].fontSize - geo[i].fontSize) <= 0.15 * geo[i].fontSize && !startsNewItem(geo[i].text, geo[i + 1].text) && hasBand(geo[i].y, geo[i + 1].y)) {
|
|
3490
|
+
joins[i] = wrapJoiner(geo[i].text, geo[i + 1].text, lex);
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3183
3493
|
for (let i = 0; i + 1 < geo.length; i++) {
|
|
3184
3494
|
if (/^\d{1,2}$/.test(geo[i].text.trim()) && geo[i].fontSize >= geo[i + 1].fontSize * 1.2) joins[i] = "\n";
|
|
3185
3495
|
}
|
|
@@ -4863,242 +5173,6 @@ function findRuledColumnDivider(regions, horizontals, verticals, pageWidth, page
|
|
|
4863
5173
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4864
5174
|
}
|
|
4865
5175
|
|
|
4866
|
-
// src/pdf/xy-cut.ts
|
|
4867
|
-
var MAX_XYCUT_DEPTH = 50;
|
|
4868
|
-
var XYCUT_MIN_GAP = 5;
|
|
4869
|
-
var CROSS_LAYOUT_BETA = 2;
|
|
4870
|
-
var CROSS_OVERLAP_RATIO = 0.1;
|
|
4871
|
-
var CROSS_MIN_OVERLAPS = 2;
|
|
4872
|
-
var CROSS_MAX_MASK_RATIO = 0.2;
|
|
4873
|
-
var NARROW_ELEMENT_WIDTH_RATIO = 0.1;
|
|
4874
|
-
var PROSE_GUTTER_MIN_GAP = 6;
|
|
4875
|
-
function xyCutOrder(items, gapThreshold, depth = 0) {
|
|
4876
|
-
if (items.length === 0) return [];
|
|
4877
|
-
if (items.length <= 2 || depth >= MAX_XYCUT_DEPTH) return [items];
|
|
4878
|
-
if (depth === 0 && items.length >= 3) {
|
|
4879
|
-
const cross = identifyCrossLayoutItems(items);
|
|
4880
|
-
if (cross.size > 0 && cross.size <= items.length * CROSS_MAX_MASK_RATIO) {
|
|
4881
|
-
const rest = items.filter((i) => !cross.has(i));
|
|
4882
|
-
if (rest.length > 0) {
|
|
4883
|
-
const groups = xyCutOrder(rest, gapThreshold, 1);
|
|
4884
|
-
return mergeCrossLayoutGroups(groups, [...cross]);
|
|
4885
|
-
}
|
|
4886
|
-
}
|
|
4887
|
-
}
|
|
4888
|
-
const minGap = Math.max(XYCUT_MIN_GAP, gapThreshold);
|
|
4889
|
-
const hCut = findHorizontalCut(items);
|
|
4890
|
-
const vCut = findVerticalCutWithOutlierFilter(items, minGap);
|
|
4891
|
-
const hValid = hCut.gap >= minGap;
|
|
4892
|
-
const vValid = (vCut.gap >= minGap || vCut.gap >= PROSE_GUTTER_MIN_GAP && isProseGutter(items, vCut.position)) && !splitsSpacedLabel(items, vCut.position);
|
|
4893
|
-
let useHorizontal;
|
|
4894
|
-
if (hValid && vValid) useHorizontal = vCut.gap <= hCut.gap * 1.5 || staggeredSides(items, vCut.position);
|
|
4895
|
-
else if (hValid) useHorizontal = true;
|
|
4896
|
-
else if (vValid) useHorizontal = false;
|
|
4897
|
-
else return splitEdgeSpannedColumns(items, gapThreshold, depth) ?? [items];
|
|
4898
|
-
if (useHorizontal) {
|
|
4899
|
-
const upper = items.filter((i) => i.y > hCut.position);
|
|
4900
|
-
const lower = items.filter((i) => i.y <= hCut.position);
|
|
4901
|
-
if (upper.length > 0 && lower.length > 0 && upper.length < items.length) {
|
|
4902
|
-
return [...xyCutOrder(upper, gapThreshold, depth + 1), ...xyCutOrder(lower, gapThreshold, depth + 1)];
|
|
4903
|
-
}
|
|
4904
|
-
} else {
|
|
4905
|
-
const left = items.filter((i) => i.x + i.w / 2 < vCut.position);
|
|
4906
|
-
const right = items.filter((i) => i.x + i.w / 2 >= vCut.position);
|
|
4907
|
-
if (left.length > 0 && right.length > 0 && left.length < items.length) {
|
|
4908
|
-
return [...xyCutOrder(left, gapThreshold, depth + 1), ...xyCutOrder(right, gapThreshold, depth + 1)];
|
|
4909
|
-
}
|
|
4910
|
-
}
|
|
4911
|
-
return [items];
|
|
4912
|
-
}
|
|
4913
|
-
function splitsSpacedLabel(items, cutX) {
|
|
4914
|
-
const lone = (i) => /^[가-힣]$/.test(i.text.trim());
|
|
4915
|
-
const sameRow = (a, b) => Math.min(a.y, b.y) > Math.max(a.y - a.h, b.y - b.h);
|
|
4916
|
-
const left = items.filter((i) => i.x + i.w / 2 < cutX);
|
|
4917
|
-
const right = items.filter((i) => i.x + i.w / 2 >= cutX);
|
|
4918
|
-
let spaced = 0;
|
|
4919
|
-
for (const r of right) {
|
|
4920
|
-
if (right.some((o) => o !== r && o.x < r.x && sameRow(o, r))) continue;
|
|
4921
|
-
const row = left.filter((l2) => sameRow(l2, r));
|
|
4922
|
-
if (row.length === 0) continue;
|
|
4923
|
-
const l = row.reduce((a, b) => b.x + b.w > a.x + a.w ? b : a);
|
|
4924
|
-
if (!lone(l) || !lone(r)) return false;
|
|
4925
|
-
spaced++;
|
|
4926
|
-
}
|
|
4927
|
-
return spaced > 0;
|
|
4928
|
-
}
|
|
4929
|
-
function staggeredSides(items, cutX) {
|
|
4930
|
-
const left = items.filter((i) => i.x + i.w / 2 < cutX);
|
|
4931
|
-
const right = items.filter((i) => i.x + i.w / 2 >= cutX);
|
|
4932
|
-
if (left.length * right.length > 2e5) return false;
|
|
4933
|
-
const shareRow = (a, b) => Math.min(a.y, b.y) > Math.max(a.y - a.h, b.y - b.h);
|
|
4934
|
-
if (left.some((a) => right.some((b) => shareRow(a, b)))) return false;
|
|
4935
|
-
const sides = [...items].sort((a, b) => b.y - a.y).map((i) => i.x + i.w / 2 < cutX);
|
|
4936
|
-
let switches = 0;
|
|
4937
|
-
for (let k = 1; k < sides.length; k++) if (sides[k] !== sides[k - 1]) switches++;
|
|
4938
|
-
return switches >= 3;
|
|
4939
|
-
}
|
|
4940
|
-
function identifyCrossLayoutItems(items) {
|
|
4941
|
-
const cross = /* @__PURE__ */ new Set();
|
|
4942
|
-
if (items.length < 3) return cross;
|
|
4943
|
-
let maxWidth = 0;
|
|
4944
|
-
for (const i of items) {
|
|
4945
|
-
if (i.w > maxWidth) maxWidth = i.w;
|
|
4946
|
-
}
|
|
4947
|
-
const threshold = CROSS_LAYOUT_BETA * maxWidth;
|
|
4948
|
-
for (const item of items) {
|
|
4949
|
-
if (item.w < threshold) continue;
|
|
4950
|
-
let overlaps = 0;
|
|
4951
|
-
for (const other of items) {
|
|
4952
|
-
if (other === item) continue;
|
|
4953
|
-
const left = Math.max(item.x, other.x);
|
|
4954
|
-
const right = Math.min(item.x + item.w, other.x + other.w);
|
|
4955
|
-
const overlapW = right - left;
|
|
4956
|
-
if (overlapW <= 0) continue;
|
|
4957
|
-
const smaller = Math.min(item.w, other.w);
|
|
4958
|
-
if (smaller > 0 && overlapW / smaller >= CROSS_OVERLAP_RATIO) {
|
|
4959
|
-
overlaps++;
|
|
4960
|
-
if (overlaps >= CROSS_MIN_OVERLAPS) break;
|
|
4961
|
-
}
|
|
4962
|
-
}
|
|
4963
|
-
if (overlaps >= CROSS_MIN_OVERLAPS) cross.add(item);
|
|
4964
|
-
}
|
|
4965
|
-
return cross;
|
|
4966
|
-
}
|
|
4967
|
-
function mergeCrossLayoutGroups(groups, cross) {
|
|
4968
|
-
if (cross.length === 0) return groups;
|
|
4969
|
-
const sortedCross = [...cross].sort((a, b) => b.y + b.h - (a.y + a.h) || a.x - b.x);
|
|
4970
|
-
const groupTop = (g) => {
|
|
4971
|
-
let top = -Infinity;
|
|
4972
|
-
for (const i of g) {
|
|
4973
|
-
const t = i.y + i.h;
|
|
4974
|
-
if (t > top) top = t;
|
|
4975
|
-
}
|
|
4976
|
-
return top;
|
|
4977
|
-
};
|
|
4978
|
-
const result = [];
|
|
4979
|
-
let gi = 0, ci = 0;
|
|
4980
|
-
while (gi < groups.length || ci < sortedCross.length) {
|
|
4981
|
-
if (ci >= sortedCross.length) {
|
|
4982
|
-
result.push(groups[gi++]);
|
|
4983
|
-
continue;
|
|
4984
|
-
}
|
|
4985
|
-
if (gi >= groups.length) {
|
|
4986
|
-
result.push([sortedCross[ci++]]);
|
|
4987
|
-
continue;
|
|
4988
|
-
}
|
|
4989
|
-
const crossTop = sortedCross[ci].y + sortedCross[ci].h;
|
|
4990
|
-
if (crossTop >= groupTop(groups[gi])) result.push([sortedCross[ci++]]);
|
|
4991
|
-
else result.push(groups[gi++]);
|
|
4992
|
-
}
|
|
4993
|
-
return result;
|
|
4994
|
-
}
|
|
4995
|
-
function findHorizontalCut(items) {
|
|
4996
|
-
if (items.length < 2) return { position: 0, gap: 0 };
|
|
4997
|
-
const sorted = [...items].sort((a, b) => b.y - a.y);
|
|
4998
|
-
let largestGap = 0;
|
|
4999
|
-
let position = 0;
|
|
5000
|
-
for (let i = 1; i < sorted.length; i++) {
|
|
5001
|
-
const prevBottom = sorted[i - 1].y - sorted[i - 1].h;
|
|
5002
|
-
const currTop = sorted[i].y;
|
|
5003
|
-
const gap = prevBottom - currTop;
|
|
5004
|
-
if (gap > largestGap) {
|
|
5005
|
-
largestGap = gap;
|
|
5006
|
-
position = (prevBottom + currTop) / 2;
|
|
5007
|
-
}
|
|
5008
|
-
}
|
|
5009
|
-
return { position, gap: largestGap };
|
|
5010
|
-
}
|
|
5011
|
-
function findVerticalCutWithOutlierFilter(items, minGap) {
|
|
5012
|
-
const edgeCut = findVerticalCut(items);
|
|
5013
|
-
if (edgeCut.gap >= minGap) return edgeCut;
|
|
5014
|
-
if (items.length >= 3) {
|
|
5015
|
-
let minX = Infinity, maxX = -Infinity;
|
|
5016
|
-
for (const i of items) {
|
|
5017
|
-
if (i.x < minX) minX = i.x;
|
|
5018
|
-
const r = i.x + i.w;
|
|
5019
|
-
if (r > maxX) maxX = r;
|
|
5020
|
-
}
|
|
5021
|
-
const narrowThreshold = (maxX - minX) * NARROW_ELEMENT_WIDTH_RATIO;
|
|
5022
|
-
const filtered = items.filter((i) => i.w >= narrowThreshold);
|
|
5023
|
-
if (filtered.length >= 2 && filtered.length < items.length && filtered.length >= items.length * 0.7) {
|
|
5024
|
-
const filteredCut = findVerticalCut(filtered);
|
|
5025
|
-
if (filteredCut.gap > edgeCut.gap && filteredCut.gap >= minGap) {
|
|
5026
|
-
return filteredCut;
|
|
5027
|
-
}
|
|
5028
|
-
}
|
|
5029
|
-
}
|
|
5030
|
-
return edgeCut;
|
|
5031
|
-
}
|
|
5032
|
-
function findVerticalCut(items) {
|
|
5033
|
-
if (items.length < 2) return { position: 0, gap: 0 };
|
|
5034
|
-
const sorted = [...items].sort((a, b) => a.x - b.x || a.x + a.w - (b.x + b.w));
|
|
5035
|
-
let largestGap = 0;
|
|
5036
|
-
let position = 0;
|
|
5037
|
-
let prevRight = null;
|
|
5038
|
-
for (const it of sorted) {
|
|
5039
|
-
const left = it.x;
|
|
5040
|
-
const right = it.x + it.w;
|
|
5041
|
-
if (prevRight !== null && left > prevRight) {
|
|
5042
|
-
const gap = left - prevRight;
|
|
5043
|
-
if (gap > largestGap) {
|
|
5044
|
-
largestGap = gap;
|
|
5045
|
-
position = (prevRight + left) / 2;
|
|
5046
|
-
}
|
|
5047
|
-
}
|
|
5048
|
-
prevRight = prevRight === null ? right : Math.max(prevRight, right);
|
|
5049
|
-
}
|
|
5050
|
-
return { position, gap: largestGap };
|
|
5051
|
-
}
|
|
5052
|
-
function isProseGutter(items, x) {
|
|
5053
|
-
const sideLines = (side) => {
|
|
5054
|
-
const lines = [];
|
|
5055
|
-
for (const item of [...side].sort((a, b) => b.y - a.y)) {
|
|
5056
|
-
const line = lines.find((l) => Math.abs(l.y - item.y) <= 2);
|
|
5057
|
-
if (line) {
|
|
5058
|
-
line.chars += item.text.length;
|
|
5059
|
-
line.left = Math.min(line.left, item.x);
|
|
5060
|
-
line.right = Math.max(line.right, item.x + item.w);
|
|
5061
|
-
} else lines.push({ y: item.y, chars: item.text.length, left: item.x, right: item.x + item.w });
|
|
5062
|
-
}
|
|
5063
|
-
return lines;
|
|
5064
|
-
};
|
|
5065
|
-
const left = sideLines(items.filter((i) => i.x + i.w / 2 < x));
|
|
5066
|
-
const right = sideLines(items.filter((i) => i.x + i.w / 2 >= x));
|
|
5067
|
-
if (left.length < 4 || right.length < 4) return false;
|
|
5068
|
-
const prose = (lines) => {
|
|
5069
|
-
const width = Math.max(...lines.map((l) => l.right)) - Math.min(...lines.map((l) => l.left));
|
|
5070
|
-
const full = lines.filter((l) => l.chars >= 25 && l.right - l.left >= width * 0.6).length;
|
|
5071
|
-
return full >= lines.length * 0.6;
|
|
5072
|
-
};
|
|
5073
|
-
if (!prose(left) || !prose(right)) return false;
|
|
5074
|
-
const span = (lines) => [Math.min(...lines.map((l) => l.y)), Math.max(...lines.map((l) => l.y))];
|
|
5075
|
-
const [l0, l1] = span(left), [r0, r1] = span(right);
|
|
5076
|
-
return Math.min(l1, r1) - Math.max(l0, r0) >= Math.min(l1 - l0, r1 - r0) * 0.5;
|
|
5077
|
-
}
|
|
5078
|
-
function splitEdgeSpannedColumns(items, gapThreshold, depth) {
|
|
5079
|
-
let minX = Infinity, maxX = -Infinity;
|
|
5080
|
-
for (const i of items) {
|
|
5081
|
-
minX = Math.min(minX, i.x);
|
|
5082
|
-
maxX = Math.max(maxX, i.x + i.w);
|
|
5083
|
-
}
|
|
5084
|
-
const narrow = items.filter((i) => i.w < (maxX - minX) * 0.5);
|
|
5085
|
-
if (narrow.length === items.length || narrow.length < 8) return null;
|
|
5086
|
-
const cut = findVerticalCut(narrow);
|
|
5087
|
-
if (cut.gap < PROSE_GUTTER_MIN_GAP || !isProseGutter(narrow, cut.position)) return null;
|
|
5088
|
-
const crossing = items.filter((i) => i.x < cut.position && i.x + i.w > cut.position);
|
|
5089
|
-
const rest = items.filter((i) => !crossing.includes(i));
|
|
5090
|
-
const restTop = Math.max(...rest.map((i) => i.y)), restBottom = Math.min(...rest.map((i) => i.y));
|
|
5091
|
-
const above = crossing.filter((i) => i.y > restTop), below = crossing.filter((i) => i.y < restBottom);
|
|
5092
|
-
if (above.length + below.length !== crossing.length) return null;
|
|
5093
|
-
const left = rest.filter((i) => i.x + i.w / 2 < cut.position), right = rest.filter((i) => i.x + i.w / 2 >= cut.position);
|
|
5094
|
-
return [
|
|
5095
|
-
...above.length ? [above] : [],
|
|
5096
|
-
...xyCutOrder(left, gapThreshold, depth + 1),
|
|
5097
|
-
...xyCutOrder(right, gapThreshold, depth + 1),
|
|
5098
|
-
...below.length ? [below] : []
|
|
5099
|
-
];
|
|
5100
|
-
}
|
|
5101
|
-
|
|
5102
5176
|
// src/pdf/two-column.ts
|
|
5103
5177
|
var GUTTER_SCAN_LO = 0.3;
|
|
5104
5178
|
var GUTTER_SCAN_HI = 0.7;
|
|
@@ -7429,4 +7503,4 @@ export {
|
|
|
7429
7503
|
joinCellEdges,
|
|
7430
7504
|
extractPageBlocksWithLines
|
|
7431
7505
|
};
|
|
7432
|
-
//# sourceMappingURL=chunk-
|
|
7506
|
+
//# sourceMappingURL=chunk-FL32PTUU.js.map
|