kordoc 4.18.12 → 4.18.13
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 +2 -2
- package/dist/{-WZOQMDES.js → -DC2OO5KO.js} +11 -11
- package/dist/{chunk-GZAYV2SV.js → chunk-32ZUIHSY.js} +3 -3
- package/dist/{chunk-OB3GPKKZ.cjs → chunk-3FGHQWJP.cjs} +38 -24
- package/dist/chunk-3FGHQWJP.cjs.map +1 -0
- package/dist/{chunk-PTWSL4FG.js → chunk-6AYTYTFY.js} +3 -3
- package/dist/{chunk-A7VXXIRT.cjs → chunk-6CQZMVXW.cjs} +3 -3
- package/dist/{chunk-A7VXXIRT.cjs.map → chunk-6CQZMVXW.cjs.map} +1 -1
- package/dist/{chunk-5WLN2JNN.cjs → chunk-6FW3AZWN.cjs} +6 -6
- package/dist/{chunk-5WLN2JNN.cjs.map → chunk-6FW3AZWN.cjs.map} +1 -1
- package/dist/{chunk-A72XCFET.js → chunk-7LOD4IGO.js} +4 -4
- package/dist/{chunk-PTLYGMNQ.js → chunk-DHEUQXXR.js} +2 -2
- package/dist/{chunk-APPUKIPQ.js → chunk-EBXJ45VZ.js} +2 -2
- package/dist/{chunk-XRVJ7UHC.js → chunk-EZSWBHHI.js} +2 -2
- package/dist/{chunk-GGX5AA2L.cjs → chunk-GGMIPTM5.cjs} +12 -12
- package/dist/{chunk-GGX5AA2L.cjs.map → chunk-GGMIPTM5.cjs.map} +1 -1
- package/dist/{chunk-XM2ZOOEW.js → chunk-GSW7W7AL.js} +2 -2
- package/dist/{chunk-T3AJHRWN.js → chunk-GUH6HYSZ.js} +10 -10
- package/dist/{chunk-G2PGKJUH.js → chunk-INESPVEV.js} +19 -5
- package/dist/chunk-INESPVEV.js.map +1 -0
- package/dist/{chunk-M7CCB6J3.js → chunk-IRN5SDY2.js} +2 -2
- package/dist/{chunk-CXNMHMPS.js → chunk-ISC2TH24.js} +2 -2
- package/dist/{chunk-MFA3TJWM.js → chunk-JKHU7HID.js} +2 -2
- package/dist/{chunk-FWLSXLDC.js → chunk-JPSLMPFI.js} +2 -2
- package/dist/{chunk-QYT3XW2J.js → chunk-JWTZ3OVZ.js} +2 -2
- package/dist/{chunk-2CMRR7SH.cjs → chunk-K6WSMTVU.cjs} +2 -2
- package/dist/{chunk-2CMRR7SH.cjs.map → chunk-K6WSMTVU.cjs.map} +1 -1
- package/dist/{chunk-MXCQIXGJ.js → chunk-LKA7Z2NH.js} +2 -2
- package/dist/{chunk-A2B5AYXD.js → chunk-PHMWSWHJ.js} +2 -2
- package/dist/{chunk-5V4XXKGX.js → chunk-PN5WWW56.js} +2 -2
- package/dist/{chunk-XRJRZCJA.cjs → chunk-RQEDNI4P.cjs} +3 -3
- package/dist/{chunk-XRJRZCJA.cjs.map → chunk-RQEDNI4P.cjs.map} +1 -1
- package/dist/{chunk-4SKWUNN7.js → chunk-SVIOP7PE.js} +6 -6
- package/dist/{chunk-4RVCC5YD.js → chunk-V7LKQNC4.js} +2 -2
- package/dist/{chunk-C7P6EZHR.js → chunk-VDEENOPW.js} +2 -2
- package/dist/{chunk-R2NGHNS3.js → chunk-XVB3PUPJ.js} +2 -2
- package/dist/{chunk-IRTDNELH.js → chunk-ZNY5BRV6.js} +20 -6
- package/dist/chunk-ZNY5BRV6.js.map +1 -0
- package/dist/chunks-JZW2TXIZ.js +10 -0
- package/dist/cli.js +26 -26
- package/dist/{formula-7FODYMWG.js → formula-DAMFHLQO.js} +3 -3
- package/dist/{formula-CAUZGR3C.cjs → formula-HJNQGPRC.cjs} +9 -9
- package/dist/{formula-CAUZGR3C.cjs.map → formula-HJNQGPRC.cjs.map} +1 -1
- package/dist/{formula-VWCF7AV5.js → formula-YCAO5IVW.js} +4 -4
- package/dist/{image-ocr-YLTHT7IE.cjs → image-ocr-AZ25IOC7.cjs} +15 -15
- package/dist/{image-ocr-YLTHT7IE.cjs.map → image-ocr-AZ25IOC7.cjs.map} +1 -1
- package/dist/{image-ocr-UOAHUR4Y.js → image-ocr-PAF76DAW.js} +9 -9
- package/dist/{image-ocr-LFMD7GFW.js → image-ocr-VH54SDZ7.js} +6 -6
- package/dist/index.cjs +277 -277
- package/dist/index.js +5 -5
- package/dist/mcp.js +22 -22
- package/dist/{model-bundle-IRGZNITZ.js → model-bundle-53CHDYCG.js} +5 -5
- package/dist/{models-QLW4GG62.js → models-UVLAKK3M.js} +5 -5
- package/dist/{parser-MOZDJZHH.cjs → parser-HWAOPQYK.cjs} +126 -126
- package/dist/{parser-MOZDJZHH.cjs.map → parser-HWAOPQYK.cjs.map} +1 -1
- package/dist/{parser-KPWQK4CU.js → parser-I4D3TRGU.js} +11 -11
- package/dist/{parser-VDZTRLXN.js → parser-Z6PHB54O.js} +8 -8
- package/dist/pdf-ocr-F4ICXO7W.js +20 -0
- package/dist/pdf-ocr-HR6CXGTX.cjs +17 -0
- package/dist/{pdf-ocr-IDZQ2KUS.cjs.map → pdf-ocr-HR6CXGTX.cjs.map} +1 -1
- package/dist/pdf-ocr-JI3K7JIN.js +16 -0
- package/dist/{profile-io-UJWTARCU.js → profile-io-WHCWCOPW.js} +3 -3
- package/dist/{rasterize-2FB766KD.js → rasterize-5DZ3DHGH.js} +3 -3
- package/dist/{redact-doc-J6U6WWKD.js → redact-doc-RNJVMFGQ.js} +11 -11
- package/dist/{render-CCIUMRLV.js → render-NZ6RA6SW.js} +7 -7
- package/dist/seal-WHJ6O5HT.js +10 -0
- package/dist/{setup-EEMGK6BR.js → setup-U3EQNK4Z.js} +3 -3
- package/dist/visual-JDAS4R43.js +20 -0
- package/dist/{watch-6I73YNFW.js → watch-GB2M7XU6.js} +12 -12
- package/package.json +1 -1
- package/dist/chunk-G2PGKJUH.js.map +0 -1
- package/dist/chunk-IRTDNELH.js.map +0 -1
- package/dist/chunk-OB3GPKKZ.cjs.map +0 -1
- package/dist/chunks-UQYMW7PG.js +0 -10
- package/dist/pdf-ocr-7MEF7NN4.js +0 -16
- package/dist/pdf-ocr-GO7BGPHA.js +0 -20
- package/dist/pdf-ocr-IDZQ2KUS.cjs +0 -17
- package/dist/seal-NKE6UTJL.js +0 -10
- package/dist/visual-6K3FOUEP.js +0 -20
- /package/dist/{-WZOQMDES.js.map → -DC2OO5KO.js.map} +0 -0
- /package/dist/{chunk-GZAYV2SV.js.map → chunk-32ZUIHSY.js.map} +0 -0
- /package/dist/{chunk-PTWSL4FG.js.map → chunk-6AYTYTFY.js.map} +0 -0
- /package/dist/{chunk-A72XCFET.js.map → chunk-7LOD4IGO.js.map} +0 -0
- /package/dist/{chunk-PTLYGMNQ.js.map → chunk-DHEUQXXR.js.map} +0 -0
- /package/dist/{chunk-APPUKIPQ.js.map → chunk-EBXJ45VZ.js.map} +0 -0
- /package/dist/{chunk-XRVJ7UHC.js.map → chunk-EZSWBHHI.js.map} +0 -0
- /package/dist/{chunk-XM2ZOOEW.js.map → chunk-GSW7W7AL.js.map} +0 -0
- /package/dist/{chunk-T3AJHRWN.js.map → chunk-GUH6HYSZ.js.map} +0 -0
- /package/dist/{chunk-M7CCB6J3.js.map → chunk-IRN5SDY2.js.map} +0 -0
- /package/dist/{chunk-CXNMHMPS.js.map → chunk-ISC2TH24.js.map} +0 -0
- /package/dist/{chunk-MFA3TJWM.js.map → chunk-JKHU7HID.js.map} +0 -0
- /package/dist/{chunk-FWLSXLDC.js.map → chunk-JPSLMPFI.js.map} +0 -0
- /package/dist/{chunk-QYT3XW2J.js.map → chunk-JWTZ3OVZ.js.map} +0 -0
- /package/dist/{chunk-MXCQIXGJ.js.map → chunk-LKA7Z2NH.js.map} +0 -0
- /package/dist/{chunk-A2B5AYXD.js.map → chunk-PHMWSWHJ.js.map} +0 -0
- /package/dist/{chunk-5V4XXKGX.js.map → chunk-PN5WWW56.js.map} +0 -0
- /package/dist/{chunk-4SKWUNN7.js.map → chunk-SVIOP7PE.js.map} +0 -0
- /package/dist/{chunk-4RVCC5YD.js.map → chunk-V7LKQNC4.js.map} +0 -0
- /package/dist/{chunk-C7P6EZHR.js.map → chunk-VDEENOPW.js.map} +0 -0
- /package/dist/{chunk-R2NGHNS3.js.map → chunk-XVB3PUPJ.js.map} +0 -0
- /package/dist/{chunks-UQYMW7PG.js.map → chunks-JZW2TXIZ.js.map} +0 -0
- /package/dist/{formula-7FODYMWG.js.map → formula-DAMFHLQO.js.map} +0 -0
- /package/dist/{formula-VWCF7AV5.js.map → formula-YCAO5IVW.js.map} +0 -0
- /package/dist/{image-ocr-UOAHUR4Y.js.map → image-ocr-PAF76DAW.js.map} +0 -0
- /package/dist/{image-ocr-LFMD7GFW.js.map → image-ocr-VH54SDZ7.js.map} +0 -0
- /package/dist/{model-bundle-IRGZNITZ.js.map → model-bundle-53CHDYCG.js.map} +0 -0
- /package/dist/{models-QLW4GG62.js.map → models-UVLAKK3M.js.map} +0 -0
- /package/dist/{parser-KPWQK4CU.js.map → parser-I4D3TRGU.js.map} +0 -0
- /package/dist/{parser-VDZTRLXN.js.map → parser-Z6PHB54O.js.map} +0 -0
- /package/dist/{pdf-ocr-7MEF7NN4.js.map → pdf-ocr-F4ICXO7W.js.map} +0 -0
- /package/dist/{pdf-ocr-GO7BGPHA.js.map → pdf-ocr-JI3K7JIN.js.map} +0 -0
- /package/dist/{profile-io-UJWTARCU.js.map → profile-io-WHCWCOPW.js.map} +0 -0
- /package/dist/{rasterize-2FB766KD.js.map → rasterize-5DZ3DHGH.js.map} +0 -0
- /package/dist/{redact-doc-J6U6WWKD.js.map → redact-doc-RNJVMFGQ.js.map} +0 -0
- /package/dist/{render-CCIUMRLV.js.map → render-NZ6RA6SW.js.map} +0 -0
- /package/dist/{seal-NKE6UTJL.js.map → seal-WHJ6O5HT.js.map} +0 -0
- /package/dist/{setup-EEMGK6BR.js.map → setup-U3EQNK4Z.js.map} +0 -0
- /package/dist/{visual-6K3FOUEP.js.map → visual-JDAS4R43.js.map} +0 -0
- /package/dist/{watch-6I73YNFW.js.map → watch-GB2M7XU6.js.map} +0 -0
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _chunkGGMIPTM5cjs = require('./chunk-GGMIPTM5.cjs');
|
|
9
9
|
require('./chunk-TK3FDAEE.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
var
|
|
13
|
-
require('./chunk-
|
|
14
|
-
require('./chunk-
|
|
12
|
+
var _chunk3FGHQWJPcjs = require('./chunk-3FGHQWJP.cjs');
|
|
13
|
+
require('./chunk-6FW3AZWN.cjs');
|
|
14
|
+
require('./chunk-6CQZMVXW.cjs');
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
var
|
|
18
|
+
var _chunkK6WSMTVUcjs = require('./chunk-K6WSMTVU.cjs');
|
|
19
19
|
require('./chunk-GS7T56RP.cjs');
|
|
20
20
|
|
|
21
21
|
// src/ocr/image-ocr.ts
|
|
@@ -43,14 +43,14 @@ async function parseImageDocument(buffer, options) {
|
|
|
43
43
|
const { data: decoded, width, height, density, shrink } = await decodeToRgba(buffer);
|
|
44
44
|
if (shrink < 1) warnings.push({ page: 1, code: "PARTIAL_PARSE", message: "OCR \uB798\uC2A4\uD130 \uD53D\uC140 \uC0C1\uD55C\uC73C\uB85C \uC774\uBBF8\uC9C0 \uD574\uC0C1\uB3C4\uB97C \uCD95\uC18C\uD588\uC2B5\uB2C8\uB2E4 (\uC791\uC740 \uAE00\uC790 \uC778\uC2DD \uACB0\uC190 \uAC00\uB2A5)" });
|
|
45
45
|
const scale = imageScale(width / shrink, height / shrink, density) * shrink;
|
|
46
|
-
const { rgba: data } =
|
|
47
|
-
await
|
|
46
|
+
const { rgba: data } = _chunkGGMIPTM5cjs.deskewPage.call(void 0, decoded, width, height);
|
|
47
|
+
await _chunk3FGHQWJPcjs.ensureOcrModels.call(void 0, (p) => {
|
|
48
48
|
if (p.phase === "download" && p.downloaded === 0) {
|
|
49
49
|
process.stderr.write(`[kordoc-ocr] ${p.spec.name} \uB2E4\uC6B4\uB85C\uB4DC \uC911 (~${p.spec.sizeMb}MB)...
|
|
50
50
|
`);
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
|
-
const engine = await
|
|
53
|
+
const engine = await _chunkGGMIPTM5cjs.getOcrEngine.call(void 0, );
|
|
54
54
|
const stats = { droppedLowConf: 0 };
|
|
55
55
|
const items = await engine.recognizePage(data, width, height, stats);
|
|
56
56
|
if (stats.droppedLowConf > 0) {
|
|
@@ -69,9 +69,9 @@ async function parseImageDocument(buffer, options) {
|
|
|
69
69
|
}
|
|
70
70
|
const pdfW = width / scale;
|
|
71
71
|
const pdfH = height / scale;
|
|
72
|
-
const ruling =
|
|
73
|
-
const extraLines =
|
|
74
|
-
return { blocks:
|
|
72
|
+
const ruling = _chunkGGMIPTM5cjs.detectRulingLines.call(void 0, data, width, height, scale);
|
|
73
|
+
const extraLines = _chunkGGMIPTM5cjs.rulingToPdfLines.call(void 0, ruling, scale, pdfH);
|
|
74
|
+
return { blocks: _chunkGGMIPTM5cjs.ocrItemsToBlocks.call(void 0, items, 1, pdfW, pdfH, scale, extraLines, _optionalChain([options, 'optionalAccess', _2 => _2.tables]) !== false), warnings };
|
|
75
75
|
}
|
|
76
76
|
async function decodeToRgba(buffer) {
|
|
77
77
|
let sharp;
|
|
@@ -79,14 +79,14 @@ async function decodeToRgba(buffer) {
|
|
|
79
79
|
const mod = await Promise.resolve().then(() => _interopRequireWildcard(require("sharp")));
|
|
80
80
|
sharp = typeof mod === "function" ? mod : _nullishCoalesce(mod.default, () => ( mod));
|
|
81
81
|
} catch (e) {
|
|
82
|
-
throw new (0,
|
|
83
|
-
`\uC774\uBBF8\uC9C0 \uD30C\uC2F1\uC5D0\uB294 optional dependency 'sharp' \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \`npm install sharp\` \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.${
|
|
82
|
+
throw new (0, _chunkK6WSMTVUcjs.KordocError)(
|
|
83
|
+
`\uC774\uBBF8\uC9C0 \uD30C\uC2F1\uC5D0\uB294 optional dependency 'sharp' \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \`npm install sharp\` \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.${_chunkK6WSMTVUcjs.OPTIONAL_DEP_INSTALL_HINT} \uC6D0\uC778: ${e.message}`
|
|
84
84
|
);
|
|
85
85
|
}
|
|
86
86
|
const input = Buffer.from(buffer);
|
|
87
87
|
const meta = await sharp(input).metadata().catch(() => ({}));
|
|
88
88
|
const [w0, h0] = (_nullishCoalesce(meta.orientation, () => ( 1))) >= 5 ? [_nullishCoalesce(meta.height, () => ( 0)), _nullishCoalesce(meta.width, () => ( 0))] : [_nullishCoalesce(meta.width, () => ( 0)), _nullishCoalesce(meta.height, () => ( 0))];
|
|
89
|
-
const shrink = w0 * h0 >
|
|
89
|
+
const shrink = w0 * h0 > _chunkGGMIPTM5cjs.MAX_OCR_PIXELS ? Math.sqrt(_chunkGGMIPTM5cjs.MAX_OCR_PIXELS / (w0 * h0)) : 1;
|
|
90
90
|
let chain = sharp(input).rotate();
|
|
91
91
|
if (shrink < 1) chain = chain.resize(Math.max(1, Math.floor(w0 * shrink)), Math.max(1, Math.floor(h0 * shrink)), { fit: "fill" });
|
|
92
92
|
const { data, info } = await chain.ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
@@ -109,4 +109,4 @@ function detectImageMime(buffer) {
|
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
exports.decodeToRgba = decodeToRgba; exports.imageScale = imageScale; exports.parseImageDocument = parseImageDocument;
|
|
112
|
-
//# sourceMappingURL=image-ocr-
|
|
112
|
+
//# sourceMappingURL=image-ocr-AZ25IOC7.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/mong-e/workspace/kordoc/dist/image-ocr-YLTHT7IE.cjs","../src/ocr/image-ocr.ts"],"names":[],"mappings":"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","file":"/Users/mong-e/workspace/kordoc/dist/image-ocr-YLTHT7IE.cjs","sourcesContent":[null,"/**\n * 이미지 직접 입력 OCR — PNG/JPEG/WebP 를 PDF 래핑 없이 바로 파싱한다.\n *\n * 이미지는 텍스트층이 없으므로 OCR 이 항상 필요하다 — `--ocr` 플래그 없이도\n * 내장 엔진(PP-OCRv5 korean)을 기본 적용하고, OcrProvider 가 주어지면 위임한다.\n * 좌표는 이미지 해상도(imageScale — 메타데이터 DPI, 없으면 A4·Letter 판형 추정, 그것도\n * 아니면 216dpi)로 PDF pt 공간에 환산해 기존 블록 파이프라인\n * (괘선 감지 → 선 기반 표 → 클러스터 → xy-cut)을 그대로 태운다.\n *\n * 디코딩은 optional dependency `sharp` — 미설치면 MISSING_DEPENDENCY 안내.\n */\n\nimport type { IRBlock, ParseOptions, ParseWarning } from \"../types.js\"\nimport { KordocError, OPTIONAL_DEP_INSTALL_HINT } from \"../utils.js\"\nimport { getOcrEngine, type OcrPageStats } from \"./engine.js\"\nimport { ensureOcrModels } from \"./models.js\"\nimport { detectRulingLines, rulingToPdfLines } from \"./ruling-lines.js\"\nimport { MAX_OCR_PIXELS, ocrItemsToBlocks } from \"./pdf-ocr.js\"\nimport { deskewPage } from \"./deskew.js\"\n\n/** 좌표 환산 기본 스케일 (px/pt) — PDF OCR 렌더(216dpi)와 동일 기준 */\nconst IMAGE_SCALE = 3\n\n/**\n * 이미지 px/pt 스케일 추정. 블록 파이프라인의 문턱(줄 묶음 3pt·괘선 최소 길이 20pt·표 간격)은\n * pt 기준이라, 150dpi 스캔을 216dpi 로 가정하면 페이지가 0.69배로 쪼그라든 좌표가 된다.\n * 1) 메타데이터 DPI(JFIF·pHYs·EXIF) 100~1200 — 72/96 은 기본값·화면값이라 불신\n * 2) 판형: 긴 변/짧은 변이 √2(A판)·11/8.5(Letter) ±2% 면 전면 스캔 — A4 842pt·Letter 792pt 긴 변\n * 3) 그 밖: 216dpi 가정 (종전 동작)\n */\nexport function imageScale(width: number, height: number, densityDpi?: number): number {\n if (densityDpi && densityDpi >= 100 && densityDpi <= 1200) return densityDpi / 72\n const long = Math.max(width, height), short = Math.min(width, height)\n if (short > 0) {\n const r = long / short\n if (Math.abs(r / Math.SQRT2 - 1) <= 0.02) return long / 842\n if (Math.abs(r / (11 / 8.5) - 1) <= 0.02) return long / 792\n }\n return IMAGE_SCALE\n}\n\nexport interface ImageOcrResult {\n blocks: IRBlock[]\n warnings: ParseWarning[]\n}\n\n/** 이미지 버퍼 → OCR → IRBlock[] (표 괘선 감지 포함) */\nexport async function parseImageDocument(\n buffer: ArrayBuffer,\n options?: ParseOptions,\n): Promise<ImageOcrResult> {\n const warnings: ParseWarning[] = []\n\n // 사용자 OcrProvider — 원본 바이트 그대로 위임 (종전 PDF 경로와 같은 계약)\n if (typeof options?.ocr === \"function\") {\n const text = await options.ocr(new Uint8Array(buffer), 1, detectImageMime(buffer))\n if (!text.trim()) {\n warnings.push({ page: 1, message: \"OCR 결과 없음\", code: \"OCR_FAILED\" })\n return { blocks: [], warnings }\n }\n return { blocks: [{ type: \"paragraph\", text: text.trim(), pageNumber: 1 }], warnings }\n }\n\n const { data: decoded, width, height, density, shrink } = await decodeToRgba(buffer)\n if (shrink < 1) warnings.push({ page: 1, code: \"PARTIAL_PARSE\", message: \"OCR 래스터 픽셀 상한으로 이미지 해상도를 축소했습니다 (작은 글자 인식 결손 가능)\" })\n // 픽셀 상한으로 줄인 이미지는 같은 지면을 낮은 해상도로 본 것 — px/pt 도 같은 비율로\n const scale = imageScale(width / shrink, height / shrink, density) * shrink\n // 스캔·사진 기울기 보정 — 인식과 괘선 감지가 같은(바로 선) 래스터를 본다\n const { rgba: data } = deskewPage(decoded, width, height)\n\n await ensureOcrModels(p => {\n if (p.phase === \"download\" && p.downloaded === 0) {\n process.stderr.write(`[kordoc-ocr] ${p.spec.name} 다운로드 중 (~${p.spec.sizeMb}MB)...\\n`)\n }\n })\n const engine = await getOcrEngine()\n const stats: OcrPageStats = { droppedLowConf: 0 }\n const items = await engine.recognizePage(data, width, height, stats)\n if (stats.droppedLowConf > 0) {\n warnings.push({\n page: 1,\n message: `저신뢰 OCR 라인 ${stats.droppedLowConf}개 폐기 (인식 결손 가능)`,\n code: \"OCR_LOW_CONF\",\n })\n }\n if (stats.truncatedBoxes) {\n warnings.push({ page: 1, message: `OCR 검출 상자가 너무 많아 ${stats.truncatedBoxes}개는 인식하지 않음 (일부 글 결손)`, code: \"PARTIAL_PARSE\" })\n }\n if (items.length === 0) {\n warnings.push({ page: 1, message: \"이미지에서 텍스트를 인식하지 못했습니다\", code: \"OCR_FAILED\" })\n return { blocks: [], warnings }\n }\n\n const pdfW = width / scale\n const pdfH = height / scale\n const ruling = detectRulingLines(data, width, height, scale)\n const extraLines = rulingToPdfLines(ruling, scale, pdfH)\n return { blocks: ocrItemsToBlocks(items, 1, pdfW, pdfH, scale, extraLines, options?.tables !== false), warnings }\n}\n\n/**\n * sharp 로 RGBA 디코딩 — 미설치는 MISSING_DEPENDENCY 로 분류되도록 안내 메시지 throw.\n * EXIF 방향대로 세운다(`rotate()`): 폰으로 세로 촬영한 JPEG 은 픽셀을 눕혀 저장하고 Orientation 6 으로 표시 방향만 적는다 —\n * 무시하면 누운 글을 읽어 한글 131자 쪽이 쓰레기 57자가 됐다. 픽셀이 MAX_OCR_PIXELS 를 넘으면 그 안으로 줄인다(shrink = 줄인 배율).\n * (테스트용 export)\n */\nexport async function decodeToRgba(\n buffer: ArrayBuffer,\n): Promise<{ data: Uint8Array; width: number; height: number; density?: number; shrink: number }> {\n type Chain = {\n rotate(): Chain\n resize(w: number, h: number, opts: { fit: \"fill\" }): Chain\n ensureAlpha(): { raw(): { toBuffer(opts: { resolveWithObject: true }): Promise<{ data: Buffer; info: { width: number; height: number } }> } }\n metadata(): Promise<{ density?: number; width?: number; height?: number; orientation?: number }>\n }\n type SharpFactory = (input: Buffer) => Chain\n let sharp: SharpFactory\n try {\n const mod = (await import(\"sharp\")) as unknown as SharpFactory | { default?: SharpFactory }\n sharp = typeof mod === \"function\" ? mod : (mod.default ?? (mod as unknown as SharpFactory))\n } catch (e) {\n // KordocError + \"optional dependency\" 문구 → sanitizeError 메시지 보존 + MISSING_DEPENDENCY 분류\n throw new KordocError(\n \"이미지 파싱에는 optional dependency 'sharp' 가 필요합니다. \" +\n `\\`npm install sharp\\` 후 다시 실행하세요.${OPTIONAL_DEP_INSTALL_HINT} 원인: ${(e as Error).message}`,\n )\n }\n const input = Buffer.from(buffer)\n const meta: { density?: number; width?: number; height?: number; orientation?: number } = await sharp(input).metadata().catch(() => ({}))\n // 5~8 은 90° 돌린 방향 — 세운 뒤 가로·세로가 바뀐다\n const [w0, h0] = (meta.orientation ?? 1) >= 5 ? [meta.height ?? 0, meta.width ?? 0] : [meta.width ?? 0, meta.height ?? 0]\n const shrink = w0 * h0 > MAX_OCR_PIXELS ? Math.sqrt(MAX_OCR_PIXELS / (w0 * h0)) : 1\n let chain = sharp(input).rotate()\n if (shrink < 1) chain = chain.resize(Math.max(1, Math.floor(w0 * shrink)), Math.max(1, Math.floor(h0 * shrink)), { fit: \"fill\" })\n const { data, info } = await chain.ensureAlpha().raw().toBuffer({ resolveWithObject: true })\n return {\n data: new Uint8Array(data.buffer, data.byteOffset, data.byteLength), width: info.width, height: info.height, density: meta.density,\n shrink: w0 ? info.width / w0 : 1,\n }\n}\n\n/** OcrProvider 계약용 mime 판별 */\nfunction detectImageMime(buffer: ArrayBuffer): \"image/png\" | \"image/jpeg\" | \"image/webp\" {\n const b = new Uint8Array(buffer, 0, Math.min(12, buffer.byteLength))\n if (b[0] === 0xff && b[1] === 0xd8) return \"image/jpeg\"\n if (b[0] === 0x52 && b[1] === 0x49) return \"image/webp\"\n return \"image/png\"\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/mong-e/workspace/kordoc/dist/image-ocr-AZ25IOC7.cjs","../src/ocr/image-ocr.ts"],"names":[],"mappings":"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","file":"/Users/mong-e/workspace/kordoc/dist/image-ocr-AZ25IOC7.cjs","sourcesContent":[null,"/**\n * 이미지 직접 입력 OCR — PNG/JPEG/WebP 를 PDF 래핑 없이 바로 파싱한다.\n *\n * 이미지는 텍스트층이 없으므로 OCR 이 항상 필요하다 — `--ocr` 플래그 없이도\n * 내장 엔진(PP-OCRv5 korean)을 기본 적용하고, OcrProvider 가 주어지면 위임한다.\n * 좌표는 이미지 해상도(imageScale — 메타데이터 DPI, 없으면 A4·Letter 판형 추정, 그것도\n * 아니면 216dpi)로 PDF pt 공간에 환산해 기존 블록 파이프라인\n * (괘선 감지 → 선 기반 표 → 클러스터 → xy-cut)을 그대로 태운다.\n *\n * 디코딩은 optional dependency `sharp` — 미설치면 MISSING_DEPENDENCY 안내.\n */\n\nimport type { IRBlock, ParseOptions, ParseWarning } from \"../types.js\"\nimport { KordocError, OPTIONAL_DEP_INSTALL_HINT } from \"../utils.js\"\nimport { getOcrEngine, type OcrPageStats } from \"./engine.js\"\nimport { ensureOcrModels } from \"./models.js\"\nimport { detectRulingLines, rulingToPdfLines } from \"./ruling-lines.js\"\nimport { MAX_OCR_PIXELS, ocrItemsToBlocks } from \"./pdf-ocr.js\"\nimport { deskewPage } from \"./deskew.js\"\n\n/** 좌표 환산 기본 스케일 (px/pt) — PDF OCR 렌더(216dpi)와 동일 기준 */\nconst IMAGE_SCALE = 3\n\n/**\n * 이미지 px/pt 스케일 추정. 블록 파이프라인의 문턱(줄 묶음 3pt·괘선 최소 길이 20pt·표 간격)은\n * pt 기준이라, 150dpi 스캔을 216dpi 로 가정하면 페이지가 0.69배로 쪼그라든 좌표가 된다.\n * 1) 메타데이터 DPI(JFIF·pHYs·EXIF) 100~1200 — 72/96 은 기본값·화면값이라 불신\n * 2) 판형: 긴 변/짧은 변이 √2(A판)·11/8.5(Letter) ±2% 면 전면 스캔 — A4 842pt·Letter 792pt 긴 변\n * 3) 그 밖: 216dpi 가정 (종전 동작)\n */\nexport function imageScale(width: number, height: number, densityDpi?: number): number {\n if (densityDpi && densityDpi >= 100 && densityDpi <= 1200) return densityDpi / 72\n const long = Math.max(width, height), short = Math.min(width, height)\n if (short > 0) {\n const r = long / short\n if (Math.abs(r / Math.SQRT2 - 1) <= 0.02) return long / 842\n if (Math.abs(r / (11 / 8.5) - 1) <= 0.02) return long / 792\n }\n return IMAGE_SCALE\n}\n\nexport interface ImageOcrResult {\n blocks: IRBlock[]\n warnings: ParseWarning[]\n}\n\n/** 이미지 버퍼 → OCR → IRBlock[] (표 괘선 감지 포함) */\nexport async function parseImageDocument(\n buffer: ArrayBuffer,\n options?: ParseOptions,\n): Promise<ImageOcrResult> {\n const warnings: ParseWarning[] = []\n\n // 사용자 OcrProvider — 원본 바이트 그대로 위임 (종전 PDF 경로와 같은 계약)\n if (typeof options?.ocr === \"function\") {\n const text = await options.ocr(new Uint8Array(buffer), 1, detectImageMime(buffer))\n if (!text.trim()) {\n warnings.push({ page: 1, message: \"OCR 결과 없음\", code: \"OCR_FAILED\" })\n return { blocks: [], warnings }\n }\n return { blocks: [{ type: \"paragraph\", text: text.trim(), pageNumber: 1 }], warnings }\n }\n\n const { data: decoded, width, height, density, shrink } = await decodeToRgba(buffer)\n if (shrink < 1) warnings.push({ page: 1, code: \"PARTIAL_PARSE\", message: \"OCR 래스터 픽셀 상한으로 이미지 해상도를 축소했습니다 (작은 글자 인식 결손 가능)\" })\n // 픽셀 상한으로 줄인 이미지는 같은 지면을 낮은 해상도로 본 것 — px/pt 도 같은 비율로\n const scale = imageScale(width / shrink, height / shrink, density) * shrink\n // 스캔·사진 기울기 보정 — 인식과 괘선 감지가 같은(바로 선) 래스터를 본다\n const { rgba: data } = deskewPage(decoded, width, height)\n\n await ensureOcrModels(p => {\n if (p.phase === \"download\" && p.downloaded === 0) {\n process.stderr.write(`[kordoc-ocr] ${p.spec.name} 다운로드 중 (~${p.spec.sizeMb}MB)...\\n`)\n }\n })\n const engine = await getOcrEngine()\n const stats: OcrPageStats = { droppedLowConf: 0 }\n const items = await engine.recognizePage(data, width, height, stats)\n if (stats.droppedLowConf > 0) {\n warnings.push({\n page: 1,\n message: `저신뢰 OCR 라인 ${stats.droppedLowConf}개 폐기 (인식 결손 가능)`,\n code: \"OCR_LOW_CONF\",\n })\n }\n if (stats.truncatedBoxes) {\n warnings.push({ page: 1, message: `OCR 검출 상자가 너무 많아 ${stats.truncatedBoxes}개는 인식하지 않음 (일부 글 결손)`, code: \"PARTIAL_PARSE\" })\n }\n if (items.length === 0) {\n warnings.push({ page: 1, message: \"이미지에서 텍스트를 인식하지 못했습니다\", code: \"OCR_FAILED\" })\n return { blocks: [], warnings }\n }\n\n const pdfW = width / scale\n const pdfH = height / scale\n const ruling = detectRulingLines(data, width, height, scale)\n const extraLines = rulingToPdfLines(ruling, scale, pdfH)\n return { blocks: ocrItemsToBlocks(items, 1, pdfW, pdfH, scale, extraLines, options?.tables !== false), warnings }\n}\n\n/**\n * sharp 로 RGBA 디코딩 — 미설치는 MISSING_DEPENDENCY 로 분류되도록 안내 메시지 throw.\n * EXIF 방향대로 세운다(`rotate()`): 폰으로 세로 촬영한 JPEG 은 픽셀을 눕혀 저장하고 Orientation 6 으로 표시 방향만 적는다 —\n * 무시하면 누운 글을 읽어 한글 131자 쪽이 쓰레기 57자가 됐다. 픽셀이 MAX_OCR_PIXELS 를 넘으면 그 안으로 줄인다(shrink = 줄인 배율).\n * (테스트용 export)\n */\nexport async function decodeToRgba(\n buffer: ArrayBuffer,\n): Promise<{ data: Uint8Array; width: number; height: number; density?: number; shrink: number }> {\n type Chain = {\n rotate(): Chain\n resize(w: number, h: number, opts: { fit: \"fill\" }): Chain\n ensureAlpha(): { raw(): { toBuffer(opts: { resolveWithObject: true }): Promise<{ data: Buffer; info: { width: number; height: number } }> } }\n metadata(): Promise<{ density?: number; width?: number; height?: number; orientation?: number }>\n }\n type SharpFactory = (input: Buffer) => Chain\n let sharp: SharpFactory\n try {\n const mod = (await import(\"sharp\")) as unknown as SharpFactory | { default?: SharpFactory }\n sharp = typeof mod === \"function\" ? mod : (mod.default ?? (mod as unknown as SharpFactory))\n } catch (e) {\n // KordocError + \"optional dependency\" 문구 → sanitizeError 메시지 보존 + MISSING_DEPENDENCY 분류\n throw new KordocError(\n \"이미지 파싱에는 optional dependency 'sharp' 가 필요합니다. \" +\n `\\`npm install sharp\\` 후 다시 실행하세요.${OPTIONAL_DEP_INSTALL_HINT} 원인: ${(e as Error).message}`,\n )\n }\n const input = Buffer.from(buffer)\n const meta: { density?: number; width?: number; height?: number; orientation?: number } = await sharp(input).metadata().catch(() => ({}))\n // 5~8 은 90° 돌린 방향 — 세운 뒤 가로·세로가 바뀐다\n const [w0, h0] = (meta.orientation ?? 1) >= 5 ? [meta.height ?? 0, meta.width ?? 0] : [meta.width ?? 0, meta.height ?? 0]\n const shrink = w0 * h0 > MAX_OCR_PIXELS ? Math.sqrt(MAX_OCR_PIXELS / (w0 * h0)) : 1\n let chain = sharp(input).rotate()\n if (shrink < 1) chain = chain.resize(Math.max(1, Math.floor(w0 * shrink)), Math.max(1, Math.floor(h0 * shrink)), { fit: \"fill\" })\n const { data, info } = await chain.ensureAlpha().raw().toBuffer({ resolveWithObject: true })\n return {\n data: new Uint8Array(data.buffer, data.byteOffset, data.byteLength), width: info.width, height: info.height, density: meta.density,\n shrink: w0 ? info.width / w0 : 1,\n }\n}\n\n/** OcrProvider 계약용 mime 판별 */\nfunction detectImageMime(buffer: ArrayBuffer): \"image/png\" | \"image/jpeg\" | \"image/webp\" {\n const b = new Uint8Array(buffer, 0, Math.min(12, buffer.byteLength))\n if (b[0] === 0xff && b[1] === 0xd8) return \"image/jpeg\"\n if (b[0] === 0x52 && b[1] === 0x49) return \"image/webp\"\n return \"image/png\"\n}\n"]}
|
|
@@ -6,20 +6,20 @@ import {
|
|
|
6
6
|
getOcrEngine,
|
|
7
7
|
ocrItemsToBlocks,
|
|
8
8
|
rulingToPdfLines
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-7LOD4IGO.js";
|
|
10
10
|
import "./chunk-6H25SXDI.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-INESPVEV.js";
|
|
12
12
|
import {
|
|
13
13
|
ensureOcrModels
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-LKA7Z2NH.js";
|
|
15
|
+
import "./chunk-JWTZ3OVZ.js";
|
|
16
|
+
import "./chunk-ISC2TH24.js";
|
|
17
|
+
import "./chunk-XVB3PUPJ.js";
|
|
18
|
+
import "./chunk-JKHU7HID.js";
|
|
19
19
|
import {
|
|
20
20
|
KordocError,
|
|
21
21
|
OPTIONAL_DEP_INSTALL_HINT
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-PN5WWW56.js";
|
|
23
23
|
|
|
24
24
|
// src/ocr/image-ocr.ts
|
|
25
25
|
var IMAGE_SCALE = 3;
|
|
@@ -112,4 +112,4 @@ export {
|
|
|
112
112
|
imageScale,
|
|
113
113
|
parseImageDocument
|
|
114
114
|
};
|
|
115
|
-
//# sourceMappingURL=image-ocr-
|
|
115
|
+
//# sourceMappingURL=image-ocr-PAF76DAW.js.map
|
|
@@ -5,17 +5,17 @@ import {
|
|
|
5
5
|
getOcrEngine,
|
|
6
6
|
ocrItemsToBlocks,
|
|
7
7
|
rulingToPdfLines
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-32ZUIHSY.js";
|
|
9
9
|
import "./chunk-DC6CTQTK.js";
|
|
10
10
|
import {
|
|
11
11
|
ensureOcrModels
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-ZNY5BRV6.js";
|
|
13
|
+
import "./chunk-JPSLMPFI.js";
|
|
14
|
+
import "./chunk-PHMWSWHJ.js";
|
|
15
15
|
import {
|
|
16
16
|
KordocError,
|
|
17
17
|
OPTIONAL_DEP_INSTALL_HINT
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-EBXJ45VZ.js";
|
|
19
19
|
|
|
20
20
|
// src/ocr/image-ocr.ts
|
|
21
21
|
var IMAGE_SCALE = 3;
|
|
@@ -108,4 +108,4 @@ export {
|
|
|
108
108
|
imageScale,
|
|
109
109
|
parseImageDocument
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=image-ocr-
|
|
111
|
+
//# sourceMappingURL=image-ocr-VH54SDZ7.js.map
|