kordoc 2.7.2 → 2.9.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 +20 -1
- package/dist/{chunk-4NWDJGAU.js → chunk-M24KMDAR.js} +53 -26
- package/dist/chunk-M24KMDAR.js.map +1 -0
- package/dist/{chunk-Y476BOHI.cjs → chunk-QB7CS534.cjs} +2 -2
- package/dist/{chunk-Y476BOHI.cjs.map → chunk-QB7CS534.cjs.map} +1 -1
- package/dist/{chunk-LB7E2KDF.js → chunk-RXZLTACX.js} +2 -2
- package/dist/{chunk-4SK2PDMQ.js.map → chunk-RXZLTACX.js.map} +1 -1
- package/dist/{chunk-4SK2PDMQ.js → chunk-SJ5TPMBT.js} +2 -2
- package/dist/{chunk-LB7E2KDF.js.map → chunk-SJ5TPMBT.js.map} +1 -1
- package/dist/cli.js +3 -3
- package/dist/index.cjs +162 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -2
- package/dist/index.d.ts +56 -2
- package/dist/index.js +52 -25
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/{parser-7OFQ67QL.cjs → parser-EL5YETUA.cjs} +158 -18
- package/dist/parser-EL5YETUA.cjs.map +1 -0
- package/dist/{parser-DJCMY3OO.js → parser-OMPBVEFU.js} +145 -5
- package/dist/parser-OMPBVEFU.js.map +1 -0
- package/dist/{parser-QMMQ7Y7R.js → parser-XBYGROQB.js} +145 -5
- package/dist/parser-XBYGROQB.js.map +1 -0
- package/dist/{watch-FVMVIZ5Q.js → watch-ULLLK7ID.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-4NWDJGAU.js.map +0 -1
- package/dist/parser-7OFQ67QL.cjs.map +0 -1
- package/dist/parser-DJCMY3OO.js.map +0 -1
- package/dist/parser-QMMQ7Y7R.js.map +0 -1
- /package/dist/{watch-FVMVIZ5Q.js.map → watch-ULLLK7ID.js.map} +0 -0
package/dist/mcp.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
fillHwpx,
|
|
9
9
|
markdownToHwpx,
|
|
10
10
|
parse
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-M24KMDAR.js";
|
|
12
12
|
import {
|
|
13
13
|
detectFormat,
|
|
14
14
|
detectZipFormat
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
blocksToMarkdown,
|
|
20
20
|
sanitizeError,
|
|
21
21
|
toArrayBuffer
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-SJ5TPMBT.js";
|
|
23
23
|
import "./chunk-MOL7MDBG.js";
|
|
24
24
|
|
|
25
25
|
// src/mcp.ts
|
|
@@ -191,7 +191,7 @@ server.tool(
|
|
|
191
191
|
break;
|
|
192
192
|
case "pdf":
|
|
193
193
|
try {
|
|
194
|
-
const { extractPdfMetadataOnly } = await import("./parser-
|
|
194
|
+
const { extractPdfMetadataOnly } = await import("./parser-XBYGROQB.js");
|
|
195
195
|
metadata = await extractPdfMetadataOnly(buffer);
|
|
196
196
|
} catch {
|
|
197
197
|
metadata = void 0;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkQB7CS534cjs = require('./chunk-QB7CS534.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
var _chunkMUOQXDZ4cjs = require('./chunk-MUOQXDZ4.cjs');
|
|
@@ -1142,6 +1142,120 @@ function buildClusterTable(rows, columns, pageNum) {
|
|
|
1142
1142
|
};
|
|
1143
1143
|
}
|
|
1144
1144
|
|
|
1145
|
+
// src/pdf/quality.ts
|
|
1146
|
+
function computePageQuality(page, text) {
|
|
1147
|
+
let total = 0;
|
|
1148
|
+
let hangul = 0;
|
|
1149
|
+
let control = 0;
|
|
1150
|
+
let replacement = 0;
|
|
1151
|
+
let pua = 0;
|
|
1152
|
+
for (let i = 0; i < text.length; i++) {
|
|
1153
|
+
const code = text.charCodeAt(i);
|
|
1154
|
+
if (code === 32 || code === 9 || code === 10 || code === 13) continue;
|
|
1155
|
+
total++;
|
|
1156
|
+
if (code < 32 || code === 127 || code >= 128 && code <= 159) {
|
|
1157
|
+
control++;
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
if (code === 65533) {
|
|
1161
|
+
replacement++;
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (code >= 44032 && code <= 55203) {
|
|
1165
|
+
hangul++;
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
if (code >= 57344 && code <= 63743 || code >= 56192 && code <= 56319) {
|
|
1169
|
+
pua++;
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
const denom = total || 1;
|
|
1174
|
+
const puaRatio = pua / denom;
|
|
1175
|
+
const controlCharRatio = control / denom;
|
|
1176
|
+
const replacementCharRatio = replacement / denom;
|
|
1177
|
+
let needsOcr = false;
|
|
1178
|
+
let ocrReason;
|
|
1179
|
+
if (total < LOW_TEXT_THRESHOLD) {
|
|
1180
|
+
needsOcr = true;
|
|
1181
|
+
ocrReason = "low_text";
|
|
1182
|
+
} else if (puaRatio >= HIGH_PUA_THRESHOLD) {
|
|
1183
|
+
needsOcr = true;
|
|
1184
|
+
ocrReason = "high_pua";
|
|
1185
|
+
} else if (controlCharRatio >= HIGH_CONTROL_THRESHOLD) {
|
|
1186
|
+
needsOcr = true;
|
|
1187
|
+
ocrReason = "high_control";
|
|
1188
|
+
} else if (replacementCharRatio >= HIGH_REPLACEMENT_THRESHOLD) {
|
|
1189
|
+
needsOcr = true;
|
|
1190
|
+
ocrReason = "high_replacement";
|
|
1191
|
+
}
|
|
1192
|
+
return {
|
|
1193
|
+
page,
|
|
1194
|
+
textChars: total,
|
|
1195
|
+
hangulRatio: hangul / denom,
|
|
1196
|
+
controlCharRatio,
|
|
1197
|
+
replacementCharRatio,
|
|
1198
|
+
puaRatio,
|
|
1199
|
+
needsOcr,
|
|
1200
|
+
ocrReason
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
var LOW_TEXT_THRESHOLD = 20;
|
|
1204
|
+
var HIGH_PUA_THRESHOLD = 0.2;
|
|
1205
|
+
var HIGH_CONTROL_THRESHOLD = 0.05;
|
|
1206
|
+
var HIGH_REPLACEMENT_THRESHOLD = 0.05;
|
|
1207
|
+
var DOC_NEEDS_OCR_PAGE_RATIO = 0.3;
|
|
1208
|
+
function stripControlChars(text) {
|
|
1209
|
+
return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F\x80-\x9F]/g, "");
|
|
1210
|
+
}
|
|
1211
|
+
function summarizeDocumentQuality(pages) {
|
|
1212
|
+
if (pages.length === 0) {
|
|
1213
|
+
return {
|
|
1214
|
+
totalPages: 0,
|
|
1215
|
+
totalTextChars: 0,
|
|
1216
|
+
avgHangulRatio: 0,
|
|
1217
|
+
avgControlCharRatio: 0,
|
|
1218
|
+
avgReplacementCharRatio: 0,
|
|
1219
|
+
avgPuaRatio: 0,
|
|
1220
|
+
lowTextPageCount: 0,
|
|
1221
|
+
highPuaPageCount: 0,
|
|
1222
|
+
needsOcr: false,
|
|
1223
|
+
ocrCandidatePages: []
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
let textChars = 0;
|
|
1227
|
+
let hangul = 0;
|
|
1228
|
+
let control = 0;
|
|
1229
|
+
let replacement = 0;
|
|
1230
|
+
let pua = 0;
|
|
1231
|
+
let lowText = 0;
|
|
1232
|
+
let highPua = 0;
|
|
1233
|
+
const ocrCandidatePages = [];
|
|
1234
|
+
for (const p of pages) {
|
|
1235
|
+
textChars += p.textChars;
|
|
1236
|
+
hangul += p.hangulRatio;
|
|
1237
|
+
control += p.controlCharRatio;
|
|
1238
|
+
replacement += p.replacementCharRatio;
|
|
1239
|
+
pua += p.puaRatio;
|
|
1240
|
+
if (p.textChars < LOW_TEXT_THRESHOLD) lowText++;
|
|
1241
|
+
if (p.puaRatio >= HIGH_PUA_THRESHOLD) highPua++;
|
|
1242
|
+
if (p.needsOcr) ocrCandidatePages.push(p.page);
|
|
1243
|
+
}
|
|
1244
|
+
const n = pages.length;
|
|
1245
|
+
return {
|
|
1246
|
+
totalPages: n,
|
|
1247
|
+
totalTextChars: textChars,
|
|
1248
|
+
avgHangulRatio: hangul / n,
|
|
1249
|
+
avgControlCharRatio: control / n,
|
|
1250
|
+
avgReplacementCharRatio: replacement / n,
|
|
1251
|
+
avgPuaRatio: pua / n,
|
|
1252
|
+
lowTextPageCount: lowText,
|
|
1253
|
+
highPuaPageCount: highPua,
|
|
1254
|
+
needsOcr: ocrCandidatePages.length / n >= DOC_NEEDS_OCR_PAGE_RATIO,
|
|
1255
|
+
ocrCandidatePages
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1145
1259
|
// src/pdf/polyfill.ts
|
|
1146
1260
|
var _pdfworkermjs = require('pdfjs-dist/legacy/build/pdf.worker.mjs'); var pdfjsWorker = _interopRequireWildcard(_pdfworkermjs);
|
|
1147
1261
|
var g = globalThis;
|
|
@@ -1179,7 +1293,7 @@ async function loadPdfWithTimeout(buffer) {
|
|
|
1179
1293
|
new Promise((_, reject) => {
|
|
1180
1294
|
timer = setTimeout(() => {
|
|
1181
1295
|
loadingTask.destroy();
|
|
1182
|
-
reject(new (0,
|
|
1296
|
+
reject(new (0, _chunkQB7CS534cjs.KordocError)("PDF \uB85C\uB529 \uD0C0\uC784\uC544\uC6C3 (30\uCD08 \uCD08\uACFC)"));
|
|
1183
1297
|
}, PDF_LOAD_TIMEOUT_MS);
|
|
1184
1298
|
})
|
|
1185
1299
|
]);
|
|
@@ -1192,11 +1306,12 @@ async function parsePdfDocument(buffer, options) {
|
|
|
1192
1306
|
const doc = await loadPdfWithTimeout(buffer);
|
|
1193
1307
|
try {
|
|
1194
1308
|
const pageCount = doc.numPages;
|
|
1195
|
-
if (pageCount === 0) throw new (0,
|
|
1309
|
+
if (pageCount === 0) throw new (0, _chunkQB7CS534cjs.KordocError)("PDF\uC5D0 \uD398\uC774\uC9C0\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1196
1310
|
const metadata = { pageCount };
|
|
1197
1311
|
await extractPdfMetadata(doc, metadata);
|
|
1198
1312
|
const blocks = [];
|
|
1199
1313
|
const warnings = [];
|
|
1314
|
+
const pageQuality = [];
|
|
1200
1315
|
let totalChars = 0;
|
|
1201
1316
|
let totalTextBytes = 0;
|
|
1202
1317
|
const effectivePageCount = Math.min(pageCount, MAX_PAGES);
|
|
@@ -1224,16 +1339,19 @@ async function parsePdfDocument(buffer, options) {
|
|
|
1224
1339
|
const opList = await page.getOperatorList();
|
|
1225
1340
|
const pageBlocks = extractPageBlocksWithLines(visible, i, opList, viewport.width, viewport.height);
|
|
1226
1341
|
for (const b of pageBlocks) blocks.push(b);
|
|
1342
|
+
let pageText = "";
|
|
1227
1343
|
for (const b of pageBlocks) {
|
|
1228
1344
|
const t = b.text || "";
|
|
1229
1345
|
totalChars += t.replace(/\s/g, "").length;
|
|
1230
1346
|
totalTextBytes += t.length * 2;
|
|
1347
|
+
pageText += pageText ? "\n" + t : t;
|
|
1231
1348
|
}
|
|
1232
|
-
|
|
1349
|
+
pageQuality.push(computePageQuality(i, pageText));
|
|
1350
|
+
if (totalTextBytes > MAX_TOTAL_TEXT) throw new (0, _chunkQB7CS534cjs.KordocError)("\uD14D\uC2A4\uD2B8 \uCD94\uCD9C \uD06C\uAE30 \uCD08\uACFC");
|
|
1233
1351
|
parsedPages++;
|
|
1234
1352
|
_optionalChain([options, 'optionalAccess', _12 => _12.onProgress, 'optionalCall', _13 => _13(parsedPages, totalTarget)]);
|
|
1235
1353
|
} catch (pageErr) {
|
|
1236
|
-
if (pageErr instanceof
|
|
1354
|
+
if (pageErr instanceof _chunkQB7CS534cjs.KordocError) throw pageErr;
|
|
1237
1355
|
warnings.push({ page: i, message: `\uD398\uC774\uC9C0 ${i} \uD30C\uC2F1 \uC2E4\uD328: ${pageErr instanceof Error ? pageErr.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`, code: "PARTIAL_PARSE" });
|
|
1238
1356
|
}
|
|
1239
1357
|
}
|
|
@@ -1245,12 +1363,12 @@ async function parsePdfDocument(buffer, options) {
|
|
|
1245
1363
|
const ocrBlocks = await ocrPages(doc, options.ocr, pageFilter, effectivePageCount);
|
|
1246
1364
|
if (ocrBlocks.length > 0) {
|
|
1247
1365
|
const ocrMarkdown = ocrBlocks.map((b) => b.text || "").filter(Boolean).join("\n\n");
|
|
1248
|
-
return { markdown: ocrMarkdown, blocks: ocrBlocks, metadata, warnings, isImageBased: true };
|
|
1366
|
+
return { markdown: ocrMarkdown, blocks: ocrBlocks, metadata, warnings, isImageBased: true, pageQuality, qualitySummary: summarizeDocumentQuality(pageQuality) };
|
|
1249
1367
|
}
|
|
1250
1368
|
} catch (e2) {
|
|
1251
1369
|
}
|
|
1252
1370
|
}
|
|
1253
|
-
throw Object.assign(new (0,
|
|
1371
|
+
throw Object.assign(new (0, _chunkQB7CS534cjs.KordocError)(`\uC774\uBBF8\uC9C0 \uAE30\uBC18 PDF (${pageCount}\uD398\uC774\uC9C0, ${totalChars}\uC790)`), { isImageBased: true });
|
|
1254
1372
|
}
|
|
1255
1373
|
if (_optionalChain([options, 'optionalAccess', _15 => _15.removeHeaderFooter]) !== false && parsedPageCount >= 3) {
|
|
1256
1374
|
const removed = removeHeaderFooterBlocks(blocks, pageHeights, warnings);
|
|
@@ -1274,8 +1392,17 @@ async function parsePdfDocument(buffer, options) {
|
|
|
1274
1392
|
}
|
|
1275
1393
|
detectMarkerHeadings(blocks);
|
|
1276
1394
|
const outline = blocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
|
|
1277
|
-
|
|
1278
|
-
|
|
1395
|
+
sanitizeBlockControlChars(blocks);
|
|
1396
|
+
let markdown = cleanPdfText(_chunkQB7CS534cjs.blocksToMarkdown.call(void 0, blocks));
|
|
1397
|
+
return {
|
|
1398
|
+
markdown,
|
|
1399
|
+
blocks,
|
|
1400
|
+
metadata,
|
|
1401
|
+
outline: outline.length > 0 ? outline : void 0,
|
|
1402
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
1403
|
+
pageQuality,
|
|
1404
|
+
qualitySummary: summarizeDocumentQuality(pageQuality)
|
|
1405
|
+
};
|
|
1279
1406
|
} finally {
|
|
1280
1407
|
await doc.destroy().catch(() => {
|
|
1281
1408
|
});
|
|
@@ -1353,9 +1480,9 @@ function detectHeadings(blocks, medianFontSize) {
|
|
|
1353
1480
|
if (/^\d+$/.test(text)) continue;
|
|
1354
1481
|
const ratio = block.style.fontSize / medianFontSize;
|
|
1355
1482
|
let level = 0;
|
|
1356
|
-
if (ratio >=
|
|
1357
|
-
else if (ratio >=
|
|
1358
|
-
else if (ratio >=
|
|
1483
|
+
if (ratio >= _chunkQB7CS534cjs.HEADING_RATIO_H1) level = 1;
|
|
1484
|
+
else if (ratio >= _chunkQB7CS534cjs.HEADING_RATIO_H2) level = 2;
|
|
1485
|
+
else if (ratio >= _chunkQB7CS534cjs.HEADING_RATIO_H3) level = 3;
|
|
1359
1486
|
if (level > 0) {
|
|
1360
1487
|
block.type = "heading";
|
|
1361
1488
|
block.level = level;
|
|
@@ -1603,7 +1730,7 @@ function extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals) {
|
|
|
1603
1730
|
}
|
|
1604
1731
|
if (remaining.length > 0) {
|
|
1605
1732
|
const allY = remaining.map((i) => i.y);
|
|
1606
|
-
const pageH =
|
|
1733
|
+
const pageH = _chunkQB7CS534cjs.safeMax.call(void 0, allY) - _chunkQB7CS534cjs.safeMin.call(void 0, allY);
|
|
1607
1734
|
const groups = xyCutOrder(remaining, Math.max(15, pageH * 0.03));
|
|
1608
1735
|
const textBlocks = [];
|
|
1609
1736
|
for (const group of groups) {
|
|
@@ -1691,7 +1818,7 @@ function extractPageBlocksFallback(items, pageNum) {
|
|
|
1691
1818
|
blocks.push({ type: "paragraph", text: tableText, pageNumber: pageNum, bbox, style: dominantStyle(items) });
|
|
1692
1819
|
} else {
|
|
1693
1820
|
const allY = items.map((i) => i.y);
|
|
1694
|
-
const pageHeight =
|
|
1821
|
+
const pageHeight = _chunkQB7CS534cjs.safeMax.call(void 0, allY) - _chunkQB7CS534cjs.safeMin.call(void 0, allY);
|
|
1695
1822
|
const gapThreshold = Math.max(15, pageHeight * 0.03);
|
|
1696
1823
|
const orderedGroups = xyCutOrder(items, gapThreshold);
|
|
1697
1824
|
for (const group of orderedGroups) {
|
|
@@ -1838,14 +1965,14 @@ function isProseSpread(items) {
|
|
|
1838
1965
|
for (let i = 1; i < sorted.length; i++) {
|
|
1839
1966
|
gaps.push(sorted[i].x - (sorted[i - 1].x + sorted[i - 1].w));
|
|
1840
1967
|
}
|
|
1841
|
-
const maxGap =
|
|
1968
|
+
const maxGap = _chunkQB7CS534cjs.safeMax.call(void 0, gaps);
|
|
1842
1969
|
const avgLen = items.reduce((s, i) => s + i.text.length, 0) / items.length;
|
|
1843
1970
|
return maxGap < 40 && avgLen < 5;
|
|
1844
1971
|
}
|
|
1845
1972
|
function detectColumns(yLines) {
|
|
1846
1973
|
const allItems = yLines.flat();
|
|
1847
1974
|
if (allItems.length === 0) return null;
|
|
1848
|
-
const pageWidth =
|
|
1975
|
+
const pageWidth = _chunkQB7CS534cjs.safeMax.call(void 0, allItems.map((i) => i.x + i.w)) - _chunkQB7CS534cjs.safeMin.call(void 0, allItems.map((i) => i.x));
|
|
1849
1976
|
if (pageWidth < 100) return null;
|
|
1850
1977
|
let bigoLineIdx = -1;
|
|
1851
1978
|
for (let i = 0; i < yLines.length; i++) {
|
|
@@ -2079,9 +2206,22 @@ function mergeLineSimple(items) {
|
|
|
2079
2206
|
}
|
|
2080
2207
|
return result;
|
|
2081
2208
|
}
|
|
2209
|
+
function sanitizeBlockControlChars(blocks) {
|
|
2210
|
+
for (const b of blocks) {
|
|
2211
|
+
if (b.text) b.text = stripControlChars(b.text);
|
|
2212
|
+
if (b.table) {
|
|
2213
|
+
for (const row of b.table.cells) {
|
|
2214
|
+
for (const cell of row) {
|
|
2215
|
+
if (cell.text) cell.text = stripControlChars(cell.text);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
if (b.children) sanitizeBlockControlChars(b.children);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2082
2222
|
function cleanPdfText(text) {
|
|
2083
2223
|
return mergeKoreanLines(
|
|
2084
|
-
text.replace(/^\d{1,4}\n/, "").replace(/^[\s]*[-–—]\s*[-–—]?\d+[-–—]?[\s]*[-–—]?[\s]*$/gm, "").replace(/^\s*\d+\s*\/\s*\d+\s*$/gm, "").replace(/\n\d{1,4}\n/g, "\n").replace(/\n\d{1,4}$/, "").replace(/^#{1,6}\s*\d{1,4}\s*$/gm, "")
|
|
2224
|
+
stripControlChars(text).replace(/^\d{1,4}\n/, "").replace(/^[\s]*[-–—]\s*[-–—]?\d+[-–—]?[\s]*[-–—]?[\s]*$/gm, "").replace(/^\s*\d+\s*\/\s*\d+\s*$/gm, "").replace(/\n\d{1,4}\n/g, "\n").replace(/\n\d{1,4}$/, "").replace(/^#{1,6}\s*\d{1,4}\s*$/gm, "")
|
|
2085
2225
|
).replace(/^(?!\| ---).*$/gm, (line) => {
|
|
2086
2226
|
if (/^\s*\${1,2}.+\${1,2}\s*$/.test(line)) return line;
|
|
2087
2227
|
return collapseEvenSpacing(line);
|
|
@@ -2411,4 +2551,4 @@ function formatMb(bytes) {
|
|
|
2411
2551
|
|
|
2412
2552
|
|
|
2413
2553
|
exports.cleanPdfText = cleanPdfText; exports.extractPdfMetadataOnly = extractPdfMetadataOnly; exports.parsePdfDocument = parsePdfDocument;
|
|
2414
|
-
//# sourceMappingURL=parser-
|
|
2554
|
+
//# sourceMappingURL=parser-EL5YETUA.cjs.map
|