kordoc 1.2.0 → 1.3.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/dist/{chunk-4BKNDXGU.js → chunk-KCGDEP7Q.js} +22 -25
- package/dist/chunk-KCGDEP7Q.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.cjs +21 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +21 -24
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-4BKNDXGU.js.map +0 -1
|
@@ -25,7 +25,7 @@ function detectFormat(buffer) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// src/utils.ts
|
|
28
|
-
var VERSION = true ? "1.
|
|
28
|
+
var VERSION = true ? "1.3.0" : "0.0.0-dev";
|
|
29
29
|
function toArrayBuffer(buf) {
|
|
30
30
|
if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
|
|
31
31
|
return buf.buffer;
|
|
@@ -739,33 +739,30 @@ function arrangeCells(rows, cols, cells) {
|
|
|
739
739
|
return grid.map((row) => row.map((c) => c || { text: "", colSpan: 1, rowSpan: 1 }));
|
|
740
740
|
}
|
|
741
741
|
|
|
742
|
+
// src/pdf/polyfill.ts
|
|
743
|
+
import * as pdfjsWorker from "pdfjs-dist/legacy/build/pdf.worker.mjs";
|
|
744
|
+
var g = globalThis;
|
|
745
|
+
if (typeof g.DOMMatrix === "undefined") {
|
|
746
|
+
g.DOMMatrix = class DOMMatrix {
|
|
747
|
+
m = [1, 0, 0, 1, 0, 0];
|
|
748
|
+
constructor(init) {
|
|
749
|
+
if (init) this.m = init;
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
if (typeof g.Path2D === "undefined") {
|
|
754
|
+
g.Path2D = class Path2D {
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
g.pdfjsWorker = pdfjsWorker;
|
|
758
|
+
|
|
742
759
|
// src/pdf/parser.ts
|
|
743
|
-
import {
|
|
744
|
-
|
|
760
|
+
import { getDocument, GlobalWorkerOptions } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
761
|
+
GlobalWorkerOptions.workerSrc = "";
|
|
745
762
|
var MAX_PAGES = 5e3;
|
|
746
763
|
var MAX_TOTAL_TEXT = 100 * 1024 * 1024;
|
|
747
|
-
var pdfjsModule = null;
|
|
748
|
-
async function loadPdfjs() {
|
|
749
|
-
if (pdfjsModule) return pdfjsModule;
|
|
750
|
-
try {
|
|
751
|
-
const mod = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
752
|
-
const req = createRequire2(import.meta.url);
|
|
753
|
-
const workerPath = req.resolve("pdfjs-dist/legacy/build/pdf.worker.mjs");
|
|
754
|
-
mod.GlobalWorkerOptions.workerSrc = pathToFileURL(workerPath).href;
|
|
755
|
-
pdfjsModule = mod;
|
|
756
|
-
return mod;
|
|
757
|
-
} catch (err) {
|
|
758
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
759
|
-
if (msg.includes("Cannot find") || msg.includes("MODULE_NOT_FOUND")) return null;
|
|
760
|
-
throw new KordocError(`pdfjs-dist \uB85C\uB529 \uC2E4\uD328: ${msg}`);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
764
|
async function parsePdfDocument(buffer) {
|
|
764
|
-
const
|
|
765
|
-
if (!pdfjs) {
|
|
766
|
-
return { success: false, fileType: "pdf", pageCount: 0, error: "pdfjs-dist\uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. npm install pdfjs-dist" };
|
|
767
|
-
}
|
|
768
|
-
const doc = await pdfjs.getDocument({
|
|
765
|
+
const doc = await getDocument({
|
|
769
766
|
data: new Uint8Array(buffer),
|
|
770
767
|
useSystemFonts: true,
|
|
771
768
|
disableFontFace: true,
|
|
@@ -1119,4 +1116,4 @@ export {
|
|
|
1119
1116
|
sanitizeError,
|
|
1120
1117
|
parse
|
|
1121
1118
|
};
|
|
1122
|
-
//# sourceMappingURL=chunk-
|
|
1119
|
+
//# sourceMappingURL=chunk-KCGDEP7Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/detect.ts","../src/utils.ts","../src/hwpx/parser.ts","../src/table/builder.ts","../src/hwp5/record.ts","../src/hwp5/parser.ts","../src/pdf/polyfill.ts","../src/pdf/parser.ts","../src/index.ts"],"sourcesContent":["/** 매직 바이트 기반 파일 포맷 감지 */\n\nimport type { FileType } from \"./types.js\"\n\n/** 매직 바이트 뷰 생성 (복사 없이 view) */\nfunction magicBytes(buffer: ArrayBuffer): Uint8Array {\n return new Uint8Array(buffer, 0, Math.min(4, buffer.byteLength))\n}\n\n/** HWPX (ZIP 기반 한컴 문서): PK\\x03\\x04 */\nexport function isHwpxFile(buffer: ArrayBuffer): boolean {\n const b = magicBytes(buffer)\n return b[0] === 0x50 && b[1] === 0x4b && b[2] === 0x03 && b[3] === 0x04\n}\n\n/** HWP 5.x (OLE2 바이너리 한컴 문서): \\xD0\\xCF\\x11\\xE0 */\nexport function isOldHwpFile(buffer: ArrayBuffer): boolean {\n const b = magicBytes(buffer)\n return b[0] === 0xd0 && b[1] === 0xcf && b[2] === 0x11 && b[3] === 0xe0\n}\n\n/** PDF 문서: %PDF */\nexport function isPdfFile(buffer: ArrayBuffer): boolean {\n const b = magicBytes(buffer)\n return b[0] === 0x25 && b[1] === 0x50 && b[2] === 0x44 && b[3] === 0x46\n}\n\n/** 버퍼로부터 파일 포맷 감지 */\nexport function detectFormat(buffer: ArrayBuffer): FileType {\n if (buffer.byteLength < 4) return \"unknown\"\n if (isHwpxFile(buffer)) return \"hwpx\"\n if (isOldHwpFile(buffer)) return \"hwp\"\n if (isPdfFile(buffer)) return \"pdf\"\n return \"unknown\"\n}\n","/** kordoc 공용 유틸리티 */\n\n/** 빌드 타임에 tsup define으로 주입되는 버전 */\ndeclare const __KORDOC_VERSION__: string\nexport const VERSION: string = typeof __KORDOC_VERSION__ !== \"undefined\" ? __KORDOC_VERSION__ : \"0.0.0-dev\"\n\n/**\n * Node.js Buffer → ArrayBuffer 변환\n * pool Buffer의 공유 ArrayBuffer 문제를 안전하게 처리.\n * offset=0이고 전체 ArrayBuffer를 차지하면 복사 없이 직접 반환.\n */\nexport function toArrayBuffer(buf: Buffer): ArrayBuffer {\n if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {\n return buf.buffer as ArrayBuffer\n }\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n}\n\n/**\n * kordoc 내부 에러 클래스 — 사용자에게 노출해도 안전한 메시지만 포함.\n * MCP 에러 정제에서 instanceof로 판별하여 allowlist 패턴 매칭 없이 안전하게 통과.\n */\nexport class KordocError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"KordocError\"\n }\n}\n\n/**\n * 에러 메시지 정제 — KordocError는 그대로, 나머지는 일반 메시지로 대체.\n * 파일시스템 경로, 스택 트레이스 등 내부 정보 노출 방지.\n */\nexport function sanitizeError(err: unknown): string {\n if (err instanceof KordocError) return err.message\n return \"문서 처리 중 오류가 발생했습니다\"\n}\n\n/**\n * ZIP 엔트리 경로의 경로 순회 여부 판별.\n * 백슬래시 정규화, .., 절대경로, Windows 드라이브 문자 모두 차단.\n */\nexport function isPathTraversal(name: string): boolean {\n const normalized = name.replace(/\\\\/g, \"/\")\n return normalized.includes(\"..\") || normalized.startsWith(\"/\") || /^[A-Za-z]:/.test(normalized)\n}\n","/**\n * HWPX 파서 — manifest 멀티섹션, colSpan/rowSpan, 중첩테이블\n *\n * lexdiff 기반 + edu-facility-ai 손상ZIP 복구\n */\n\nimport JSZip from \"jszip\"\nimport { inflateRawSync } from \"zlib\"\nimport { DOMParser } from \"@xmldom/xmldom\"\nimport { buildTable, convertTableToText, blocksToMarkdown, MAX_COLS, MAX_ROWS } from \"../table/builder.js\"\nimport type { CellContext, IRBlock } from \"../types.js\"\nimport { KordocError, isPathTraversal } from \"../utils.js\"\n\n/** 압축 해제 최대 크기 (100MB) — ZIP bomb 방지 */\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\n/** 손상 ZIP 복구 시 최대 엔트리 수 */\nconst MAX_ZIP_ENTRIES = 500\n\n/** colSpan/rowSpan을 안전한 범위로 클램핑 */\nfunction clampSpan(val: number, max: number): number {\n return Math.max(1, Math.min(val, max))\n}\n\ninterface TableState { rows: CellContext[][]; currentRow: CellContext[]; cell: CellContext | null }\n\n/** XXE/Billion Laughs 방지 — DOCTYPE 제거 (내부 DTD 서브셋 포함) */\nfunction stripDtd(xml: string): string {\n return xml.replace(/<!DOCTYPE\\s[^[>]*(\\[[\\s\\S]*?\\])?\\s*>/gi, \"\")\n}\n\nexport async function parseHwpxDocument(buffer: ArrayBuffer): Promise<string> {\n // Best-effort 사전 검증 — CD 선언 크기 기반 (위조 가능, 실제 방어는 per-file 누적 체크)\n const precheck = precheckZipSize(buffer)\n if (precheck.totalUncompressed > MAX_DECOMPRESS_SIZE) {\n throw new KordocError(\"ZIP 비압축 크기 초과 (ZIP bomb 의심)\")\n }\n if (precheck.entryCount > MAX_ZIP_ENTRIES) {\n throw new KordocError(\"ZIP 엔트리 수 초과 (ZIP bomb 의심)\")\n }\n\n let zip: JSZip\n\n try {\n zip = await JSZip.loadAsync(buffer)\n } catch {\n return extractFromBrokenZip(buffer)\n }\n\n // loadAsync 후 실제 엔트리 수 검증 — CD 위조와 무관한 진짜 방어선\n const actualEntryCount = Object.keys(zip.files).length\n if (actualEntryCount > MAX_ZIP_ENTRIES) {\n throw new KordocError(\"ZIP 엔트리 수 초과 (ZIP bomb 의심)\")\n }\n\n const sectionPaths = await resolveSectionPaths(zip)\n if (sectionPaths.length === 0) throw new KordocError(\"HWPX에서 섹션 파일을 찾을 수 없습니다\")\n\n let totalDecompressed = 0\n const blocks: IRBlock[] = []\n for (const path of sectionPaths) {\n const file = zip.file(path)\n if (!file) continue\n const xml = await file.async(\"text\")\n totalDecompressed += xml.length * 2\n if (totalDecompressed > MAX_DECOMPRESS_SIZE) throw new KordocError(\"ZIP 압축 해제 크기 초과 (ZIP bomb 의심)\")\n blocks.push(...parseSectionXml(xml))\n }\n return blocksToMarkdown(blocks)\n}\n\n/**\n * loadAsync 전 raw buffer에서 Central Directory를 파싱하여\n * 선언된 비압축 크기 합산 + 엔트리 수를 사전 검증.\n *\n * ⚠️ 한계: CD에 선언된 비압축 크기는 공격자가 위조 가능.\n * 이 함수는 \"정직한 ZIP\"에 대한 조기 거부(best-effort early rejection)만 수행.\n * 실제 ZIP bomb 방어는 loadAsync 후 per-file 누적 크기 체크에서 담당.\n *\n * Central Directory가 손상된 경우(extractFromBrokenZip으로 폴백될 케이스)에는\n * 안전한 기본값을 반환하여 loadAsync가 시도되도록 함.\n *\n * @internal 테스트 전용 export — public API(index.ts)에서 재노출하지 않음\n */\nexport function precheckZipSize(buffer: ArrayBuffer): { totalUncompressed: number; entryCount: number } {\n try {\n const data = new DataView(buffer)\n const len = buffer.byteLength\n if (len < 22) return { totalUncompressed: 0, entryCount: 0 }\n\n // End of Central Directory (EOCD) 시그니처를 뒤에서부터 탐색\n // EOCD는 최소 22바이트, comment 최대 65535바이트\n const searchStart = Math.max(0, len - 22 - 65535)\n let eocdOffset = -1\n for (let i = len - 22; i >= searchStart; i--) {\n if (data.getUint32(i, true) === 0x06054b50) { eocdOffset = i; break }\n }\n if (eocdOffset < 0) return { totalUncompressed: 0, entryCount: 0 }\n\n const entryCount = data.getUint16(eocdOffset + 10, true)\n const cdSize = data.getUint32(eocdOffset + 12, true)\n const cdOffset = data.getUint32(eocdOffset + 16, true)\n\n if (cdOffset + cdSize > len) return { totalUncompressed: 0, entryCount }\n\n // Central Directory 엔트리 순회\n let totalUncompressed = 0\n let pos = cdOffset\n for (let i = 0; i < entryCount && pos + 46 <= cdOffset + cdSize; i++) {\n if (data.getUint32(pos, true) !== 0x02014b50) break\n totalUncompressed += data.getUint32(pos + 24, true)\n const nameLen = data.getUint16(pos + 28, true)\n const extraLen = data.getUint16(pos + 30, true)\n const commentLen = data.getUint16(pos + 32, true)\n pos += 46 + nameLen + extraLen + commentLen\n }\n\n return { totalUncompressed, entryCount }\n } catch {\n // DataView 범위 초과 등 예외 시 안전한 기본값 반환\n return { totalUncompressed: 0, entryCount: 0 }\n }\n}\n\n// ─── 손상 ZIP 복구 (edu-facility-ai에서 포팅) ──────────\n\nfunction extractFromBrokenZip(buffer: ArrayBuffer): string {\n const data = new Uint8Array(buffer)\n const view = new DataView(buffer)\n let pos = 0\n const texts: string[] = []\n let totalDecompressed = 0\n let entryCount = 0\n\n while (pos < data.length - 30) {\n // PK\\x03\\x04 시그니처 확인\n if (data[pos] !== 0x50 || data[pos + 1] !== 0x4b || data[pos + 2] !== 0x03 || data[pos + 3] !== 0x04) break\n\n if (++entryCount > MAX_ZIP_ENTRIES) break\n\n const method = view.getUint16(pos + 8, true)\n const compSize = view.getUint32(pos + 18, true)\n const nameLen = view.getUint16(pos + 26, true)\n const extraLen = view.getUint16(pos + 28, true)\n\n // nameLen 상한 — 비정상 값에 의한 대규모 버퍼 할당 방지\n if (nameLen > 1024 || extraLen > 65535) { pos += 30 + nameLen + extraLen; continue }\n\n const fileStart = pos + 30 + nameLen + extraLen\n // 범위 초과 검증 — OOB 및 무한 루프 방지\n if (fileStart + compSize > data.length) break\n if (compSize === 0 && method !== 0) { pos = fileStart; continue }\n\n const nameBytes = data.slice(pos + 30, pos + 30 + nameLen)\n const name = new TextDecoder().decode(nameBytes)\n\n // 경로 순회 방지 — 상위 디렉토리 참조 및 절대 경로 차단\n if (isPathTraversal(name)) { pos = fileStart + compSize; continue }\n const fileData = data.slice(fileStart, fileStart + compSize)\n pos = fileStart + compSize\n\n if (!name.toLowerCase().includes(\"section\") || !name.endsWith(\".xml\")) continue\n\n try {\n let content: string\n if (method === 0) {\n content = new TextDecoder().decode(fileData)\n } else if (method === 8) {\n const decompressed = inflateRawSync(Buffer.from(fileData), { maxOutputLength: MAX_DECOMPRESS_SIZE })\n content = new TextDecoder().decode(decompressed)\n } else {\n continue\n }\n totalDecompressed += content.length * 2\n if (totalDecompressed > MAX_DECOMPRESS_SIZE) throw new KordocError(\"압축 해제 크기 초과\")\n const sectionText = blocksToMarkdown(parseSectionXml(content))\n if (sectionText) texts.push(sectionText)\n } catch {\n continue\n }\n }\n\n if (texts.length === 0) throw new KordocError(\"손상된 HWPX에서 섹션 데이터를 복구할 수 없습니다\")\n return texts.join(\"\\n\\n\")\n}\n\n// ─── Manifest 해석 ───────────────────────────────────\n\nasync function resolveSectionPaths(zip: JSZip): Promise<string[]> {\n const manifestPaths = [\"Contents/content.hpf\", \"content.hpf\"]\n for (const mp of manifestPaths) {\n const mpLower = mp.toLowerCase()\n const file = zip.file(mp) || Object.values(zip.files).find(f => f.name.toLowerCase() === mpLower) || null\n if (!file) continue\n const xml = await file.async(\"text\")\n const paths = parseSectionPathsFromManifest(xml)\n if (paths.length > 0) return paths\n }\n\n // fallback: section*.xml 직접 검색\n const sectionFiles = zip.file(/[Ss]ection\\d+\\.xml$/)\n return sectionFiles.map(f => f.name).sort()\n}\n\nfunction parseSectionPathsFromManifest(xml: string): string[] {\n const parser = new DOMParser()\n const doc = parser.parseFromString(stripDtd(xml), \"text/xml\")\n const items = doc.getElementsByTagName(\"opf:item\")\n const spine = doc.getElementsByTagName(\"opf:itemref\")\n\n const isSectionId = (id: string) => /^s/i.test(id) || id.toLowerCase().includes(\"section\")\n const idToHref = new Map<string, string>()\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const id = item.getAttribute(\"id\") || \"\"\n let href = item.getAttribute(\"href\") || \"\"\n const mediaType = item.getAttribute(\"media-type\") || \"\"\n if (!isSectionId(id) && !mediaType.includes(\"xml\")) continue\n if (!href.startsWith(\"/\") && !href.startsWith(\"Contents/\") && isSectionId(id))\n href = \"Contents/\" + href\n idToHref.set(id, href)\n }\n\n if (spine.length > 0) {\n const ordered: string[] = []\n for (let i = 0; i < spine.length; i++) {\n const href = idToHref.get(spine[i].getAttribute(\"idref\") || \"\")\n if (href) ordered.push(href)\n }\n if (ordered.length > 0) return ordered\n }\n return Array.from(idToHref.entries())\n .filter(([id]) => isSectionId(id))\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([, href]) => href)\n}\n\n// ─── 섹션 XML 파싱 ──────────────────────────────────\n\nfunction parseSectionXml(xml: string): IRBlock[] {\n const parser = new DOMParser()\n const doc = parser.parseFromString(stripDtd(xml), \"text/xml\")\n if (!doc.documentElement) return []\n\n const blocks: IRBlock[] = []\n walkSection(doc.documentElement, blocks, null, [])\n return blocks\n}\n\nfunction walkSection(\n node: Node, blocks: IRBlock[],\n tableCtx: TableState | null, tableStack: TableState[]\n): void {\n const children = node.childNodes\n if (!children) return\n\n for (let i = 0; i < children.length; i++) {\n const el = children[i] as Element\n if (el.nodeType !== 1) continue\n\n const tag = el.tagName || el.localName || \"\"\n const localTag = tag.replace(/^[^:]+:/, \"\")\n\n switch (localTag) {\n case \"tbl\": {\n if (tableCtx) tableStack.push(tableCtx)\n const newTable: TableState = { rows: [], currentRow: [], cell: null }\n walkSection(el, blocks, newTable, tableStack)\n\n if (newTable.rows.length > 0) {\n if (tableStack.length > 0) {\n const parentTable = tableStack.pop()!\n const nestedText = convertTableToText(newTable.rows)\n if (parentTable.cell) {\n parentTable.cell.text += (parentTable.cell.text ? \"\\n\" : \"\") + nestedText\n }\n tableCtx = parentTable\n } else {\n blocks.push({ type: \"table\", table: buildTable(newTable.rows) })\n tableCtx = null\n }\n } else {\n tableCtx = tableStack.length > 0 ? tableStack.pop()! : null\n }\n break\n }\n\n case \"tr\":\n if (tableCtx) {\n tableCtx.currentRow = []\n walkSection(el, blocks, tableCtx, tableStack)\n if (tableCtx.currentRow.length > 0) tableCtx.rows.push(tableCtx.currentRow)\n tableCtx.currentRow = []\n }\n break\n\n case \"tc\":\n if (tableCtx) {\n tableCtx.cell = { text: \"\", colSpan: 1, rowSpan: 1 }\n walkSection(el, blocks, tableCtx, tableStack)\n if (tableCtx.cell) {\n tableCtx.currentRow.push(tableCtx.cell)\n tableCtx.cell = null\n }\n }\n break\n\n case \"cellSpan\":\n if (tableCtx?.cell) {\n const cs = parseInt(el.getAttribute(\"colSpan\") || \"1\", 10)\n const rs = parseInt(el.getAttribute(\"rowSpan\") || \"1\", 10)\n tableCtx.cell.colSpan = clampSpan(cs, MAX_COLS)\n tableCtx.cell.rowSpan = clampSpan(rs, MAX_ROWS)\n }\n break\n\n case \"p\": {\n const text = extractParagraphText(el)\n if (text) {\n if (tableCtx?.cell) {\n tableCtx.cell.text += (tableCtx.cell.text ? \"\\n\" : \"\") + text\n } else if (!tableCtx) {\n blocks.push({ type: \"paragraph\", text })\n }\n }\n walkSection(el, blocks, tableCtx, tableStack)\n break\n }\n\n default:\n walkSection(el, blocks, tableCtx, tableStack)\n break\n }\n }\n}\n\nfunction extractParagraphText(para: Node): string {\n let text = \"\"\n const walk = (node: Node) => {\n const children = node.childNodes\n if (!children) return\n for (let i = 0; i < children.length; i++) {\n const child = children[i] as Element\n if (child.nodeType === 3) { text += child.textContent || \"\"; continue }\n if (child.nodeType !== 1) continue\n\n const tag = (child.tagName || child.localName || \"\").replace(/^[^:]+:/, \"\")\n switch (tag) {\n case \"t\": text += child.textContent || \"\"; break\n case \"tab\": text += \"\\t\"; break\n case \"br\":\n if ((child.getAttribute(\"type\") || \"line\") === \"line\") text += \"\\n\"\n break\n case \"fwSpace\": case \"hwSpace\": text += \" \"; break\n case \"tbl\": break // 테이블은 walkSection에서 처리\n default: walk(child); break\n }\n }\n }\n walk(para)\n return text.replace(/[ \\t]+/g, \" \").trim()\n}\n","/** 2-pass colSpan/rowSpan 테이블 빌더 및 Markdown 변환 */\n\nimport type { CellContext, IRBlock, IRCell, IRTable } from \"../types.js\"\n\n/** 테이블 열 수 상한 — 한국 공공문서 기준 충분한 값 */\nexport const MAX_COLS = 200\n/** 테이블 행 수 상한 — 메모리 폭주 방지 */\nexport const MAX_ROWS = 10000\n\nexport function buildTable(rows: CellContext[][]): IRTable {\n if (rows.length > MAX_ROWS) rows = rows.slice(0, MAX_ROWS)\n const numRows = rows.length\n\n // Pass 1: maxCols 계산 (sparse Set — 메모리 효율적)\n const tempOccupied = new Set<number>()\n let maxCols = 0\n\n for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {\n let colIdx = 0\n for (const cell of rows[rowIdx]) {\n while (colIdx < MAX_COLS && tempOccupied.has(rowIdx * MAX_COLS + colIdx)) colIdx++\n if (colIdx >= MAX_COLS) break\n\n for (let r = rowIdx; r < Math.min(rowIdx + cell.rowSpan, numRows); r++) {\n for (let c = colIdx; c < Math.min(colIdx + cell.colSpan, MAX_COLS); c++) {\n tempOccupied.add(r * MAX_COLS + c)\n }\n }\n colIdx += cell.colSpan\n if (colIdx > maxCols) maxCols = colIdx\n }\n }\n tempOccupied.clear()\n\n if (maxCols === 0) return { rows: 0, cols: 0, cells: [], hasHeader: false }\n\n // Pass 2: 실제 배치\n const grid: IRCell[][] = Array.from({ length: numRows }, () =>\n Array.from({ length: maxCols }, () => ({ text: \"\", colSpan: 1, rowSpan: 1 }))\n )\n const occupied: boolean[][] = Array.from({ length: numRows }, () => Array(maxCols).fill(false))\n\n for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {\n let colIdx = 0\n let cellIdx = 0\n\n while (colIdx < maxCols && cellIdx < rows[rowIdx].length) {\n while (colIdx < maxCols && occupied[rowIdx][colIdx]) colIdx++\n if (colIdx >= maxCols) break\n\n const cell = rows[rowIdx][cellIdx]\n grid[rowIdx][colIdx] = {\n text: cell.text.trim(),\n colSpan: cell.colSpan,\n rowSpan: cell.rowSpan,\n }\n\n for (let r = rowIdx; r < Math.min(rowIdx + cell.rowSpan, numRows); r++) {\n for (let c = colIdx; c < Math.min(colIdx + cell.colSpan, maxCols); c++) {\n occupied[r][c] = true\n }\n }\n\n colIdx += cell.colSpan\n cellIdx++\n }\n }\n\n return { rows: numRows, cols: maxCols, cells: grid, hasHeader: numRows > 1 }\n}\n\nexport function convertTableToText(rows: CellContext[][]): string {\n return rows\n .map(row =>\n row\n .map(c => c.text.trim().replace(/\\n/g, \" \"))\n .filter(Boolean)\n .join(\" | \")\n )\n .filter(Boolean)\n .join(\"\\n\")\n}\n\nexport function blocksToMarkdown(blocks: IRBlock[]): string {\n const lines: string[] = []\n\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i]\n\n if (block.type === \"paragraph\" && block.text) {\n const text = block.text\n\n if (/^\\[별표\\s*\\d+/.test(text)) {\n const nextBlock = blocks[i + 1]\n if (nextBlock?.type === \"paragraph\" && nextBlock.text && /관련\\)?$/.test(nextBlock.text)) {\n lines.push(\"\", `## ${text} ${nextBlock.text}`, \"\")\n i++\n } else {\n lines.push(\"\", `## ${text}`, \"\")\n }\n continue\n }\n\n if (/^\\([^)]*조[^)]*관련\\)$/.test(text)) {\n lines.push(`*${text}*`, \"\")\n continue\n }\n\n lines.push(text)\n } else if (block.type === \"table\" && block.table) {\n lines.push(tableToMarkdown(block.table))\n }\n }\n\n return lines.join(\"\\n\").trim()\n}\n\nfunction tableToMarkdown(table: IRTable): string {\n if (table.rows === 0 || table.cols === 0) return \"\"\n\n const { cells, rows: numRows, cols: numCols } = table\n\n // 1행 1열 → 구조화된 텍스트\n if (numRows === 1 && numCols === 1) {\n const content = cells[0][0].text\n return content\n .split(/\\n/)\n .map(line => {\n const trimmed = line.trim()\n if (!trimmed) return \"\"\n if (/^\\d+\\.\\s/.test(trimmed)) return `**${trimmed}**`\n if (/^[가-힣]\\.\\s/.test(trimmed)) return ` ${trimmed}`\n return trimmed\n })\n .filter(Boolean)\n .join(\"\\n\")\n }\n\n // 병합 셀: 행/열 병합된 셀은 빈 칸으로\n const display: string[][] = Array.from({ length: numRows }, () => Array(numCols).fill(\"\"))\n const skip = new Set<string>()\n\n for (let r = 0; r < numRows; r++) {\n for (let c = 0; c < numCols; c++) {\n if (skip.has(`${r},${c}`)) continue\n const cell = cells[r][c]\n display[r][c] = cell.text.replace(/\\n/g, \"<br>\")\n\n for (let dr = 0; dr < cell.rowSpan; dr++) {\n for (let dc = 0; dc < cell.colSpan; dc++) {\n if (dr === 0 && dc === 0) continue\n if (r + dr < numRows && c + dc < numCols) {\n skip.add(`${r + dr},${c + dc}`)\n }\n }\n }\n }\n }\n\n // rowSpan에 의해 생긴 빈 placeholder 행만 제거 (내용이 동일한 실제 데이터 행은 유지)\n const uniqueRows: string[][] = []\n for (const row of display) {\n const isEmptyPlaceholder = row.every(cell => cell === \"\")\n if (!isEmptyPlaceholder) uniqueRows.push(row)\n }\n\n if (uniqueRows.length === 0) return \"\"\n\n const md: string[] = []\n md.push(\"| \" + uniqueRows[0].join(\" | \") + \" |\")\n md.push(\"| \" + uniqueRows[0].map(() => \"---\").join(\" | \") + \" |\")\n for (let i = 1; i < uniqueRows.length; i++) {\n md.push(\"| \" + uniqueRows[i].join(\" | \") + \" |\")\n }\n return md.join(\"\\n\")\n}\n","/** HWP 5.x 레코드 리더, UTF-16LE 텍스트 추출, 스트림 압축해제 */\n\nimport { inflateRawSync, inflateSync } from \"zlib\"\nimport { KordocError } from \"../utils.js\"\n\n// ─── 레코드 태그 상수 ────────────────────────────────\n\nexport const TAG_PARA_HEADER = 0x0042\nexport const TAG_PARA_TEXT = 0x0043\nexport const TAG_CTRL_HEADER = 0x0047\nexport const TAG_LIST_HEADER = 0x0048\nexport const TAG_TABLE = 0x004d\n\n// 특수 문자 코드 (UTF-16LE)\n// HWP 스펙에서 0x0000은 NUL이 아닌 줄바꿈(line break)으로 정의됨\nconst CHAR_LINE = 0x0000\nconst CHAR_PARA = 0x000d\nconst CHAR_TAB = 0x0009\nconst CHAR_HYPHEN = 0x001e\nconst CHAR_NBSP = 0x001f\nconst CHAR_FIXED_NBSP = 0x0018\n\n// FileHeader 플래그\nexport const FLAG_COMPRESSED = 1 << 0\nexport const FLAG_ENCRYPTED = 1 << 1\nexport const FLAG_DRM = 1 << 4\n\n// ─── 레코드 구조 ─────────────────────────────────────\n\nexport interface HwpRecord {\n tagId: number\n level: number\n size: number\n data: Buffer\n}\n\nexport interface HwpFileHeader {\n signature: string\n versionMajor: number\n flags: number\n}\n\n// ─── 레코드 리더 ─────────────────────────────────────\n\n/** 최대 레코드 수 — 비정상 파일에 의한 메모리 폭주 방지 */\nconst MAX_RECORDS = 500_000\n\nexport function readRecords(data: Buffer): HwpRecord[] {\n const records: HwpRecord[] = []\n let offset = 0\n\n while (offset + 4 <= data.length && records.length < MAX_RECORDS) {\n const header = data.readUInt32LE(offset)\n offset += 4\n\n const tagId = header & 0x3ff\n const level = (header >> 10) & 0x3ff\n let size = (header >> 20) & 0xfff\n\n // 확장 크기\n if (size === 0xfff) {\n if (offset + 4 > data.length) break\n size = data.readUInt32LE(offset)\n offset += 4\n }\n\n if (offset + size > data.length) break\n records.push({ tagId, level, size, data: data.subarray(offset, offset + size) })\n offset += size\n }\n\n return records\n}\n\n// ─── 스트림 압축 해제 ────────────────────────────────\n\n/** 압축 해제 최대 크기 (100MB) — decompression bomb 방지 */\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\n\nexport function decompressStream(data: Buffer): Buffer {\n const opts = { maxOutputLength: MAX_DECOMPRESS_SIZE }\n if (data.length >= 2 && data[0] === 0x78) {\n try { return inflateSync(data, opts) } catch { /* fallback to raw */ }\n }\n return inflateRawSync(data, opts)\n}\n\n// ─── FileHeader 파싱 ─────────────────────────────────\n\nexport function parseFileHeader(data: Buffer): HwpFileHeader {\n if (data.length < 40) throw new KordocError(\"FileHeader가 너무 짧습니다 (최소 40바이트)\")\n const sig = data.subarray(0, 32).toString(\"utf8\").replace(/\\0+$/, \"\")\n return {\n signature: sig,\n versionMajor: data[35],\n flags: data.readUInt32LE(36),\n }\n}\n\n// ─── UTF-16LE 텍스트 추출 (21가지 제어문자 처리) ─────\n\nexport function extractText(data: Buffer): string {\n let result = \"\"\n let i = 0\n\n while (i + 1 < data.length) {\n const ch = data.readUInt16LE(i)\n i += 2\n\n switch (ch) {\n case CHAR_LINE: result += \"\\n\"; break\n case CHAR_PARA: break\n case CHAR_TAB: result += \"\\t\"; break\n case CHAR_HYPHEN: result += \"-\"; break\n case CHAR_NBSP: case CHAR_FIXED_NBSP: result += \" \"; break\n default:\n if (ch >= 0x0001 && ch <= 0x001f) {\n const isExt = (ch >= 1 && ch <= 3) || (ch >= 10 && ch <= 18) || (ch >= 21 && ch <= 23)\n const isInline = (ch >= 4 && ch <= 9) || (ch >= 19 && ch <= 20)\n if ((isExt || isInline) && i + 14 <= data.length) i += 14\n } else if (ch >= 0x0020) {\n // UTF-16 surrogate pair 처리 (BMP 외 문자: 이모지, CJK 확장 등)\n if (ch >= 0xd800 && ch <= 0xdbff && i + 1 < data.length) {\n const lo = data.readUInt16LE(i)\n if (lo >= 0xdc00 && lo <= 0xdfff) {\n i += 2\n const codePoint = ((ch - 0xd800) << 10) + (lo - 0xdc00) + 0x10000\n result += String.fromCodePoint(codePoint)\n break\n }\n }\n result += String.fromCharCode(ch)\n }\n break\n }\n }\n\n return result\n}\n","/** HWP 5.x 바이너리 파서 — OLE2 컨테이너 → 섹션 → Markdown */\n\nimport {\n readRecords, decompressStream, parseFileHeader, extractText,\n TAG_PARA_HEADER, TAG_PARA_TEXT, TAG_CTRL_HEADER, TAG_LIST_HEADER, TAG_TABLE,\n FLAG_COMPRESSED, FLAG_ENCRYPTED, FLAG_DRM,\n type HwpRecord,\n} from \"./record.js\"\nimport { buildTable, blocksToMarkdown, MAX_COLS, MAX_ROWS } from \"../table/builder.js\"\nimport type { CellContext, IRBlock } from \"../types.js\"\nimport { KordocError } from \"../utils.js\"\n\nimport { createRequire } from \"module\"\nconst require = createRequire(import.meta.url)\nconst CFB: CfbModule = require(\"cfb\")\n\ninterface CfbEntry { name?: string; content?: Buffer | Uint8Array }\ninterface CfbContainer { FileIndex?: CfbEntry[] }\ninterface CfbModule {\n parse(data: Buffer): CfbContainer\n find(cfb: CfbContainer, path: string): CfbEntry | null\n}\n\n/** 최대 섹션 수 — 비정상 파일에 의한 무한 루프 방지 */\nconst MAX_SECTIONS = 100\n/** 누적 압축 해제 최대 크기 (100MB) */\nconst MAX_TOTAL_DECOMPRESS = 100 * 1024 * 1024\n\nexport function parseHwp5Document(buffer: Buffer): string {\n const cfb = CFB.parse(buffer)\n\n const headerEntry = CFB.find(cfb, \"/FileHeader\")\n if (!headerEntry?.content) throw new KordocError(\"FileHeader 스트림 없음\")\n const header = parseFileHeader(Buffer.from(headerEntry.content))\n if (header.signature !== \"HWP Document File\") throw new KordocError(\"HWP 시그니처 불일치\")\n if (header.flags & FLAG_ENCRYPTED) throw new KordocError(\"암호화된 HWP는 지원하지 않습니다\")\n if (header.flags & FLAG_DRM) throw new KordocError(\"DRM 보호된 HWP는 지원하지 않습니다\")\n const compressed = (header.flags & FLAG_COMPRESSED) !== 0\n\n const sections = findSections(cfb)\n if (sections.length === 0) throw new KordocError(\"섹션 스트림을 찾을 수 없습니다\")\n\n const blocks: IRBlock[] = []\n let totalDecompressed = 0\n for (const sectionData of sections) {\n const data = compressed ? decompressStream(Buffer.from(sectionData)) : Buffer.from(sectionData)\n totalDecompressed += data.length\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\n const records = readRecords(data)\n blocks.push(...parseSection(records))\n }\n\n return blocksToMarkdown(blocks)\n}\n\nfunction findSections(cfb: CfbContainer): Buffer[] {\n const sections: Array<{ idx: number; content: Buffer }> = []\n\n for (let i = 0; i < MAX_SECTIONS; i++) {\n const entry = CFB.find(cfb, `/BodyText/Section${i}`)\n if (!entry?.content) break\n sections.push({ idx: i, content: Buffer.from(entry.content) })\n }\n\n if (sections.length === 0 && cfb.FileIndex) {\n for (const entry of cfb.FileIndex) {\n if (sections.length >= MAX_SECTIONS) break\n if (entry.name?.startsWith(\"Section\") && entry.content) {\n const idx = parseInt(entry.name.replace(\"Section\", \"\"), 10) || 0\n sections.push({ idx, content: Buffer.from(entry.content) })\n }\n }\n }\n\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\n}\n\nfunction parseSection(records: HwpRecord[]): IRBlock[] {\n const blocks: IRBlock[] = []\n let i = 0\n\n while (i < records.length) {\n const rec = records[i]\n\n if (rec.tagId === TAG_PARA_HEADER && rec.level === 0) {\n const { paragraph, tables, nextIdx } = parseParagraphWithTables(records, i)\n if (paragraph) blocks.push({ type: \"paragraph\", text: paragraph })\n for (const t of tables) blocks.push({ type: \"table\", table: t })\n i = nextIdx\n continue\n }\n\n if (rec.tagId === TAG_CTRL_HEADER && rec.level <= 1 && rec.data.length >= 4) {\n const ctrlId = rec.data.subarray(0, 4).toString(\"ascii\")\n if (ctrlId === \" lbt\" || ctrlId === \"tbl \") {\n const { table, nextIdx } = parseTableBlock(records, i)\n if (table) blocks.push({ type: \"table\", table })\n i = nextIdx\n continue\n }\n }\n\n i++\n }\n\n return blocks\n}\n\nfunction parseParagraphWithTables(records: HwpRecord[], startIdx: number) {\n const startLevel = records[startIdx].level\n let text = \"\"\n const tables: ReturnType<typeof buildTable>[] = []\n let i = startIdx + 1\n\n while (i < records.length) {\n const rec = records[i]\n if (rec.tagId === TAG_PARA_HEADER && rec.level <= startLevel) break\n\n if (rec.tagId === TAG_PARA_TEXT) {\n text = extractText(rec.data)\n }\n\n if (rec.tagId === TAG_CTRL_HEADER && rec.data.length >= 4) {\n const ctrlId = rec.data.subarray(0, 4).toString(\"ascii\")\n if (ctrlId === \" lbt\" || ctrlId === \"tbl \") {\n const { table, nextIdx } = parseTableBlock(records, i)\n if (table) tables.push(table)\n i = nextIdx\n continue\n }\n }\n i++\n }\n\n const trimmed = text.trim()\n return { paragraph: trimmed || null, tables, nextIdx: i }\n}\n\nfunction parseTableBlock(records: HwpRecord[], startIdx: number) {\n const tableLevel = records[startIdx].level\n let i = startIdx + 1\n let rows = 0, cols = 0\n const cells: CellContext[] = []\n\n while (i < records.length) {\n const rec = records[i]\n if (rec.tagId === TAG_PARA_HEADER && rec.level <= tableLevel) break\n if (rec.tagId === TAG_CTRL_HEADER && rec.level <= tableLevel) break\n\n if (rec.tagId === TAG_TABLE && rec.data.length >= 8) {\n rows = Math.min(rec.data.readUInt16LE(4), MAX_ROWS)\n cols = Math.min(rec.data.readUInt16LE(6), MAX_COLS)\n }\n\n if (rec.tagId === TAG_LIST_HEADER) {\n const { cell, nextIdx } = parseCellBlock(records, i, tableLevel)\n if (cell) cells.push(cell)\n i = nextIdx\n continue\n }\n i++\n }\n\n if (rows === 0 || cols === 0 || cells.length === 0) return { table: null, nextIdx: i }\n\n const cellRows = arrangeCells(rows, cols, cells)\n return { table: buildTable(cellRows), nextIdx: i }\n}\n\nfunction parseCellBlock(records: HwpRecord[], startIdx: number, tableLevel: number) {\n const rec = records[startIdx]\n const cellLevel = rec.level\n const texts: string[] = []\n\n // LIST_HEADER에서 셀 병합 정보 추출\n // HWP5 셀 LIST_HEADER 구조: paraCount(u16) + flags(u32) + colAddr(u16) + rowAddr(u16) + colSpan(u16) + rowSpan(u16)\n let colSpan = 1\n let rowSpan = 1\n if (rec.data.length >= 14) {\n const cs = rec.data.readUInt16LE(10)\n const rs = rec.data.readUInt16LE(12)\n if (cs > 0) colSpan = Math.min(cs, MAX_COLS)\n if (rs > 0) rowSpan = Math.min(rs, MAX_ROWS)\n }\n\n let i = startIdx + 1\n\n while (i < records.length) {\n const r = records[i]\n if (r.tagId === TAG_LIST_HEADER && r.level <= cellLevel) break\n if (r.level <= tableLevel && (r.tagId === TAG_PARA_HEADER || r.tagId === TAG_CTRL_HEADER)) break\n\n if (r.tagId === TAG_PARA_TEXT) {\n const t = extractText(r.data).trim()\n if (t) texts.push(t)\n }\n i++\n }\n\n return { cell: { text: texts.join(\"\\n\"), colSpan, rowSpan } as CellContext, nextIdx: i }\n}\n\nfunction arrangeCells(rows: number, cols: number, cells: CellContext[]): CellContext[][] {\n const grid: (CellContext | null)[][] = Array.from({ length: rows }, () => Array(cols).fill(null))\n let cellIdx = 0\n\n for (let r = 0; r < rows && cellIdx < cells.length; r++) {\n for (let c = 0; c < cols && cellIdx < cells.length; c++) {\n if (grid[r][c] !== null) continue\n const cell = cells[cellIdx++]\n grid[r][c] = cell\n\n for (let dr = 0; dr < cell.rowSpan; dr++) {\n for (let dc = 0; dc < cell.colSpan; dc++) {\n if (dr === 0 && dc === 0) continue\n if (r + dr < rows && c + dc < cols)\n grid[r + dr][c + dc] = { text: \"\", colSpan: 1, rowSpan: 1 }\n }\n }\n }\n }\n\n return grid.map(row => row.map(c => c || { text: \"\", colSpan: 1, rowSpan: 1 }))\n}\n","/**\n * 서버리스/Node.js 환경에서 pdfjs-dist가 요구하는 브라우저 API polyfill.\n * pdfjs-dist import보다 먼저 실행되어야 함 (ES 모듈 호이스팅 대응으로 별도 파일 분리).\n *\n * 1. DOMMatrix / Path2D polyfill — pdfjs-dist가 참조하지만 Node.js에 없음\n * 2. pdfjsWorker 사전 주입 — fake worker의 동적 import를 우회\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst g = globalThis as any\n\nif (typeof g.DOMMatrix === \"undefined\") {\n g.DOMMatrix = class DOMMatrix {\n m: number[] = [1, 0, 0, 1, 0, 0]\n constructor(init?: number[]) { if (init) this.m = init }\n }\n}\n\nif (typeof g.Path2D === \"undefined\") {\n g.Path2D = class Path2D {}\n}\n\n// worker 모듈 static import → fake worker가 동적 import를 건너뜀\n// @ts-expect-error pdfjs-dist worker has no type declarations\nimport * as pdfjsWorker from \"pdfjs-dist/legacy/build/pdf.worker.mjs\"\ng.pdfjsWorker = pdfjsWorker\n","/**\n * PDF 텍스트 추출 (pdfjs-dist static import 기반)\n *\n * polyfill을 먼저 import해야 DOMMatrix/Path2D/pdfjsWorker가 주입됨.\n * ES 모듈 호이스팅 때문에 별도 파일로 분리되어 있음.\n */\n\nimport type { ParseResult } from \"../types.js\"\nimport { KordocError } from \"../utils.js\"\n// polyfill 먼저 (ES 모듈 호이스팅되므로 별도 파일 필수)\nimport \"./polyfill.js\"\nimport { getDocument, GlobalWorkerOptions } from \"pdfjs-dist/legacy/build/pdf.mjs\"\n\n// worker 비활성화 (polyfill에서 pdfjsWorker를 이미 주입했으므로)\nGlobalWorkerOptions.workerSrc = \"\"\n\nconst MAX_PAGES = 5000\nconst MAX_TOTAL_TEXT = 100 * 1024 * 1024\n\ninterface PdfTextItem {\n str: string\n transform: number[]\n width: number\n height: number\n}\n\ninterface NormItem {\n text: string\n x: number\n y: number\n w: number\n h: number\n}\n\nexport async function parsePdfDocument(buffer: ArrayBuffer): Promise<ParseResult> {\n const doc = await getDocument({\n data: new Uint8Array(buffer),\n useSystemFonts: true,\n disableFontFace: true,\n isEvalSupported: false,\n }).promise\n\n try {\n const pageCount = doc.numPages\n if (pageCount === 0) return { success: false, fileType: \"pdf\", pageCount: 0, error: \"PDF에 페이지가 없습니다.\" }\n\n const pageTexts: string[] = []\n let totalChars = 0\n let totalTextBytes = 0\n const effectivePageCount = Math.min(pageCount, MAX_PAGES)\n\n for (let i = 1; i <= effectivePageCount; i++) {\n const page = await doc.getPage(i)\n const tc = await page.getTextContent()\n const pageText = extractPageContent(tc.items as PdfTextItem[])\n totalChars += pageText.replace(/\\s/g, \"\").length\n totalTextBytes += pageText.length * 2\n if (totalTextBytes > MAX_TOTAL_TEXT) throw new KordocError(\"텍스트 추출 크기 초과\")\n pageTexts.push(pageText)\n }\n\n if (totalChars / effectivePageCount < 10) {\n return { success: false, fileType: \"pdf\", pageCount, isImageBased: true, error: `이미지 기반 PDF (${pageCount}페이지, ${totalChars}자)` }\n }\n\n let markdown = pageTexts.filter(t => t.trim()).join(\"\\n\\n\")\n markdown = cleanPdfText(markdown)\n\n return { success: true, fileType: \"pdf\", markdown, pageCount: effectivePageCount }\n } finally {\n await doc.destroy().catch(() => {})\n }\n}\n\n// ═══════════════════════════════════════════════════════\n// 페이지 콘텐츠 추출 (열 경계 학습 기반 테이블 감지)\n// ═══════════════════════════════════════════════════════\n\nfunction extractPageContent(rawItems: PdfTextItem[]): string {\n const items = normalizeItems(rawItems)\n if (items.length === 0) return \"\"\n\n const yLines = groupByY(items)\n const columns = detectColumns(yLines)\n\n if (columns && columns.length >= 3) {\n return extractWithColumns(yLines, columns)\n }\n\n // 테이블 없으면 기존 방식\n return yLines.map(line => mergeLineSimple(line)).join(\"\\n\")\n}\n\nfunction normalizeItems(rawItems: PdfTextItem[]): NormItem[] {\n return rawItems\n .filter(i => typeof i.str === \"string\" && i.str.trim() !== \"\")\n .map(i => ({\n text: i.str.trim(),\n x: Math.round(i.transform[4]),\n y: Math.round(i.transform[5]),\n w: Math.round(i.width),\n h: Math.round(i.height),\n }))\n .sort((a, b) => b.y - a.y || a.x - b.x)\n}\n\nfunction groupByY(items: NormItem[]): NormItem[][] {\n if (items.length === 0) return []\n const lines: NormItem[][] = []\n let curY = items[0].y\n let curLine: NormItem[] = [items[0]]\n\n for (let i = 1; i < items.length; i++) {\n if (Math.abs(items[i].y - curY) > 3) {\n lines.push(curLine)\n curLine = []\n curY = items[i].y\n }\n curLine.push(items[i])\n }\n if (curLine.length > 0) lines.push(curLine)\n return lines\n}\n\n// ═══════════════════════════════════════════════════════\n// 열 경계 감지 — 빈도 기반 x-히스토그램 클러스터링\n// ═══════════════════════════════════════════════════════\n\n/** prose 라인 판별: 아이템 간 gap이 모두 작으면 문장 (단어 나열) */\nfunction isProseSpread(items: NormItem[]): boolean {\n if (items.length < 4) return false\n const sorted = [...items].sort((a, b) => a.x - b.x)\n const gaps: number[] = []\n for (let i = 1; i < sorted.length; i++) {\n gaps.push(sorted[i].x - (sorted[i - 1].x + sorted[i - 1].w))\n }\n // gap의 표준편차가 작고 최대 gap이 작으면 prose\n const maxGap = Math.max(...gaps)\n const avgLen = items.reduce((s, i) => s + i.text.length, 0) / items.length\n // 짧은 단어들이 좁은 간격으로 나열 = prose (예: \"위 표 제3호나목에서 남은 유효기간...\")\n return maxGap < 40 && avgLen < 5\n}\n\nfunction detectColumns(yLines: NormItem[][]): number[] | null {\n const allItems = yLines.flat()\n if (allItems.length === 0) return null\n const pageWidth = Math.max(...allItems.map(i => i.x + i.w)) - Math.min(...allItems.map(i => i.x))\n if (pageWidth < 100) return null\n\n // \"비고\" 이전 아이템만 사용 (비고 이후는 prose)\n let bigoLineIdx = -1\n for (let i = 0; i < yLines.length; i++) {\n if (yLines[i].length <= 2 && yLines[i].some(item => item.text === \"비고\")) {\n bigoLineIdx = i\n break\n }\n }\n const tableYLines = bigoLineIdx >= 0 ? yLines.slice(0, bigoLineIdx) : yLines\n\n // Step 1: 모든 아이템의 x를 수집 (prose 라인 제외)\n const CLUSTER_TOL = 22\n const xClusters: { center: number; count: number; minX: number }[] = []\n\n for (const line of tableYLines) {\n if (isProseSpread(line)) continue\n for (const item of line) {\n let found = false\n for (const c of xClusters) {\n if (Math.abs(item.x - c.center) <= CLUSTER_TOL) {\n c.center = Math.round((c.center * c.count + item.x) / (c.count + 1))\n c.minX = Math.min(c.minX, item.x)\n c.count++\n found = true\n break\n }\n }\n if (!found) {\n xClusters.push({ center: item.x, count: 1, minX: item.x })\n }\n }\n }\n\n // Step 2: 빈도 피크 — 최소 3회 이상 등장 (노이즈 제거)\n const peaks = xClusters\n .filter(c => c.count >= 3)\n .sort((a, b) => a.minX - b.minX)\n\n if (peaks.length < 3) return null\n\n // Step 3: 가까운 피크 병합 (간격 < 30)\n const MERGE_TOL = 30\n const merged: { center: number; count: number; minX: number }[] = [peaks[0]]\n for (let i = 1; i < peaks.length; i++) {\n const prev = merged[merged.length - 1]\n if (peaks[i].minX - prev.minX < MERGE_TOL) {\n // 빈도 높은 쪽 유지, 최소 x는 작은 값\n if (peaks[i].count > prev.count) {\n prev.center = peaks[i].center\n }\n prev.count += peaks[i].count\n prev.minX = Math.min(prev.minX, peaks[i].minX)\n } else {\n merged.push({ ...peaks[i] })\n }\n }\n\n // 열 경계 = 각 클러스터의 minX (왼쪽 정렬 기준)\n const columns = merged.filter(c => c.count >= 3).map(c => c.minX)\n return columns.length >= 3 ? columns : null\n}\n\nfunction findColumn(x: number, columns: number[]): number {\n for (let i = columns.length - 1; i >= 0; i--) {\n if (x >= columns[i] - 10) return i\n }\n return 0\n}\n\n// ═══════════════════════════════════════════════════════\n// 열 기반 추출 — 테이블/텍스트 영역 분리\n// ═══════════════════════════════════════════════════════\n\nfunction extractWithColumns(yLines: NormItem[][], columns: number[]): string {\n const result: string[] = []\n const colMin = columns[0]\n const colMax = columns[columns.length - 1]\n\n // \"비고\" 라인 감지 — 이후는 텍스트로 처리\n let bigoIdx = -1\n for (let i = 0; i < yLines.length; i++) {\n if (yLines[i].length <= 2 && yLines[i].some(item => item.text === \"비고\")) {\n bigoIdx = i\n break\n }\n }\n\n // 테이블 시작: 첫 번째 다열(3+ 열 사용) 라인\n let tableStart = -1\n for (let i = 0; i < (bigoIdx >= 0 ? bigoIdx : yLines.length); i++) {\n const usedCols = new Set(yLines[i].map(item => findColumn(item.x, columns)))\n if (usedCols.size >= 3) {\n tableStart = i\n break\n }\n }\n\n const tableEnd = bigoIdx >= 0 ? bigoIdx : yLines.length\n\n // 테이블 시작 이전 = 텍스트\n for (let i = 0; i < (tableStart >= 0 ? tableStart : tableEnd); i++) {\n result.push(mergeLineSimple(yLines[i]))\n }\n\n // 테이블 영역: 모든 라인을 그리드에 포함 (단일 아이템 라인도)\n if (tableStart >= 0) {\n const tableLines = yLines.slice(tableStart, tableEnd)\n // 테이블 x범위 밖의 라인만 텍스트로 분리\n const gridLines: NormItem[][] = []\n for (const line of tableLines) {\n const inRange = line.some(item =>\n item.x >= colMin - 20 && item.x <= colMax + 200\n )\n if (inRange && !isProseSpread(line)) {\n gridLines.push(line)\n } else {\n // 그리드 밖 라인은 현재까지 축적된 그리드 출력 후 텍스트로\n if (gridLines.length > 0) {\n result.push(buildGridTable(gridLines.splice(0), columns))\n }\n result.push(mergeLineSimple(line))\n }\n }\n if (gridLines.length > 0) {\n result.push(buildGridTable(gridLines, columns))\n }\n }\n\n // 비고 영역\n if (bigoIdx >= 0) {\n result.push(\"\")\n for (let i = bigoIdx; i < yLines.length; i++) {\n result.push(mergeLineSimple(yLines[i]))\n }\n }\n\n return result.join(\"\\n\")\n}\n\n// ═══════════════════════════════════════════════════════\n// 그리드 테이블 빌더 — y-라인을 열에 배치 후 행 병합\n// ═══════════════════════════════════════════════════════\n\nfunction buildGridTable(lines: NormItem[][], columns: number[]): string {\n const numCols = columns.length\n\n // Step 1: 각 y-라인을 열에 배치\n const yRows: string[][] = lines.map(items => {\n const row = Array(numCols).fill(\"\")\n for (const item of items) {\n const col = findColumn(item.x, columns)\n row[col] = row[col] ? row[col] + \" \" + item.text : item.text\n }\n return row\n })\n\n // Step 2: 행 병합 — 새 논리적 행 판별\n // 데이터 열 기준점 (가격 등이 들어가는 오른쪽 열들)\n const dataColStart = Math.max(2, Math.floor(numCols / 2))\n const merged: string[][] = []\n\n for (const row of yRows) {\n if (row.every(c => c === \"\")) continue\n\n if (merged.length === 0) {\n merged.push([...row])\n continue\n }\n\n const prev = merged[merged.length - 1]\n const filledCols = row.map((c, i) => c ? i : -1).filter(i => i >= 0)\n const filledCount = filledCols.length\n\n let isNewRow = false\n\n // Rule 1: col 0에 텍스트 (3글자 이상) → 새 행 (단, \"권\"처럼 짧은 건 continuation)\n if (row[0] && row[0].length >= 3) {\n isNewRow = true\n }\n\n // Rule 2: col 1에 텍스트 → 항상 새 행 (새 항목 시작)\n if (!isNewRow && numCols > 1 && row[1]) {\n isNewRow = true\n }\n\n // Rule 3: 데이터 열(3+)에 새 값이 있고 이전 행 데이터 열에도 이미 값 있음 → 새 가격 행\n if (!isNewRow) {\n const hasData = row.slice(dataColStart).some(c => c !== \"\")\n const prevHasData = prev.slice(dataColStart).some(c => c !== \"\")\n if (hasData && prevHasData) {\n isNewRow = true\n }\n }\n\n // Exception: filledCount=1이고 col 0에 짧은 텍스트(≤2자) → word continuation (예: \"권\", \"여권\")\n if (isNewRow && filledCount === 1 && row[0] && row[0].length <= 2) {\n isNewRow = false\n }\n\n if (isNewRow) {\n merged.push([...row])\n } else {\n for (let c = 0; c < numCols; c++) {\n if (row[c]) {\n prev[c] = prev[c] ? prev[c] + \" \" + row[c] : row[c]\n }\n }\n }\n }\n\n if (merged.length < 2) {\n return merged.map(r => r.filter(c => c).join(\" \")).join(\"\\n\")\n }\n\n // Step 3: 헤더 행 병합 — 첫 N행이 모두 데이터열(dataColStart+)에 값이 없으면 헤더\n let headerEnd = 0\n for (let r = 0; r < merged.length; r++) {\n const hasDataValues = merged[r].slice(dataColStart).some(c => c && /\\d/.test(c))\n if (hasDataValues) break\n headerEnd = r + 1\n }\n\n if (headerEnd > 1) {\n // 헤더 행들을 하나로 합침\n const headerRow = Array(numCols).fill(\"\")\n for (let r = 0; r < headerEnd; r++) {\n for (let c = 0; c < numCols; c++) {\n if (merged[r][c]) {\n headerRow[c] = headerRow[c] ? headerRow[c] + \" \" + merged[r][c] : merged[r][c]\n }\n }\n }\n merged.splice(0, headerEnd, headerRow)\n }\n\n // Step 4: 마크다운 테이블\n const md: string[] = []\n md.push(\"| \" + merged[0].join(\" | \") + \" |\")\n md.push(\"| \" + merged[0].map(() => \"---\").join(\" | \") + \" |\")\n for (let r = 1; r < merged.length; r++) {\n md.push(\"| \" + merged[r].join(\" | \") + \" |\")\n }\n return md.join(\"\\n\")\n}\n\n// ═══════════════════════════════════════════════════════\n// 유틸\n// ═══════════════════════════════════════════════════════\n\nfunction mergeLineSimple(items: NormItem[]): string {\n if (items.length <= 1) return items[0]?.text || \"\"\n const sorted = [...items].sort((a, b) => a.x - b.x)\n let result = sorted[0].text\n for (let i = 1; i < sorted.length; i++) {\n const gap = sorted[i].x - (sorted[i - 1].x + sorted[i - 1].w)\n if (gap > 15) result += \"\\t\"\n else if (gap > 3) result += \" \"\n result += sorted[i].text\n }\n return result\n}\n\nexport function cleanPdfText(text: string): string {\n return mergeKoreanLines(\n text\n .replace(/^[\\s]*[-–—]\\s*\\d+\\s*[-–—][\\s]*$/gm, \"\")\n .replace(/^\\s*\\d+\\s*\\/\\s*\\d+\\s*$/gm, \"\")\n )\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim()\n}\n\nfunction startsWithMarker(line: string): boolean {\n const t = line.trimStart()\n return /^[가-힣ㄱ-ㅎ][.)]/.test(t) || /^\\d+[.)]/.test(t) || /^\\([가-힣ㄱ-ㅎ\\d]+\\)/.test(t) ||\n /^[○●※▶▷◆◇■□★☆\\-·]\\s/.test(t) || /^제\\d+[조항호장절]/.test(t)\n}\n\nfunction isStandaloneHeader(line: string): boolean {\n return /^제\\d+[조항호장절](\\([^)]*\\))?(\\s+\\S+){0,7}$/.test(line.trim())\n}\n\nfunction mergeKoreanLines(text: string): string {\n if (!text) return \"\"\n const lines = text.split(\"\\n\")\n if (lines.length <= 1) return text\n const result: string[] = [lines[0]]\n\n for (let i = 1; i < lines.length; i++) {\n const prev = result[result.length - 1]\n const curr = lines[i]\n if (/[가-힣·,\\-]$/.test(prev) && /^[가-힣(]/.test(curr) && !startsWithMarker(curr) && !isStandaloneHeader(prev)) {\n result[result.length - 1] = prev + \" \" + curr\n } else {\n result.push(curr)\n }\n }\n return result.join(\"\\n\")\n}\n","/**\n * kordoc — 모두 파싱해버리겠다\n *\n * HWP, HWPX, PDF → Markdown 변환 통합 라이브러리\n */\n\nimport { detectFormat, isHwpxFile, isOldHwpFile, isPdfFile } from \"./detect.js\"\nimport { parseHwpxDocument } from \"./hwpx/parser.js\"\nimport { parseHwp5Document } from \"./hwp5/parser.js\"\nimport { parsePdfDocument } from \"./pdf/parser.js\"\nimport type { ParseResult } from \"./types.js\"\n\n// ─── 메인 API ────────────────────────────────────────\n\n/**\n * 파일 버퍼를 자동 감지하여 Markdown으로 변환\n *\n * @example\n * ```ts\n * import { parse } from \"kordoc\"\n * const result = await parse(buffer)\n * if (result.success) console.log(result.markdown)\n * ```\n */\nexport async function parse(buffer: ArrayBuffer): Promise<ParseResult> {\n if (!buffer || buffer.byteLength === 0) {\n return { success: false, fileType: \"unknown\", error: \"빈 버퍼이거나 유효하지 않은 입력입니다.\" }\n }\n const format = detectFormat(buffer)\n\n switch (format) {\n case \"hwpx\":\n return parseHwpx(buffer)\n case \"hwp\":\n return parseHwp(buffer)\n case \"pdf\":\n return parsePdf(buffer)\n default:\n return { success: false, fileType: \"unknown\", error: \"지원하지 않는 파일 형식입니다.\" }\n }\n}\n\n// ─── 포맷별 API ──────────────────────────────────────\n\n/** HWPX 파일을 Markdown으로 변환 */\nexport async function parseHwpx(buffer: ArrayBuffer): Promise<ParseResult> {\n try {\n const markdown = await parseHwpxDocument(buffer)\n return { success: true, fileType: \"hwpx\", markdown }\n } catch (err) {\n return { success: false, fileType: \"hwpx\", error: err instanceof Error ? err.message : \"HWPX 파싱 실패\" }\n }\n}\n\n/** HWP 5.x 바이너리 파일을 Markdown으로 변환 */\nexport async function parseHwp(buffer: ArrayBuffer): Promise<ParseResult> {\n try {\n const markdown = parseHwp5Document(Buffer.from(buffer))\n return { success: true, fileType: \"hwp\", markdown }\n } catch (err) {\n return { success: false, fileType: \"hwp\", error: err instanceof Error ? err.message : \"HWP 파싱 실패\" }\n }\n}\n\n/** PDF 파일에서 텍스트를 추출하여 Markdown으로 변환 */\nexport async function parsePdf(buffer: ArrayBuffer): Promise<ParseResult> {\n try {\n return await parsePdfDocument(buffer)\n } catch (err) {\n return { success: false, fileType: \"pdf\", error: err instanceof Error ? err.message : \"PDF 파싱 실패\" }\n }\n}\n\n// ─── Re-exports ──────────────────────────────────────\n\nexport { detectFormat, isHwpxFile, isOldHwpFile, isPdfFile } from \"./detect.js\"\nexport type { ParseResult, ParseSuccess, ParseFailure, FileType } from \"./types.js\"\n// buildTable, blocksToMarkdown, convertTableToText는 내부 전용 — public API에서 제거 (v1.1.1)\nexport { VERSION } from \"./utils.js\"\n"],"mappings":"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","names":["inflateRawSync","MAX_DECOMPRESS_SIZE","inflateRawSync","require"]}
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -201,7 +201,7 @@ function tableToMarkdown(table) {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
// src/utils.ts
|
|
204
|
-
var VERSION = true ? "1.
|
|
204
|
+
var VERSION = true ? "1.3.0" : "0.0.0-dev";
|
|
205
205
|
var KordocError = class extends Error {
|
|
206
206
|
constructor(message) {
|
|
207
207
|
super(message);
|
|
@@ -772,34 +772,30 @@ function arrangeCells(rows, cols, cells) {
|
|
|
772
772
|
return grid.map((row) => row.map((c) => c || { text: "", colSpan: 1, rowSpan: 1 }));
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
+
// src/pdf/polyfill.ts
|
|
776
|
+
var pdfjsWorker = __toESM(require("pdfjs-dist/legacy/build/pdf.worker.mjs"), 1);
|
|
777
|
+
var g = globalThis;
|
|
778
|
+
if (typeof g.DOMMatrix === "undefined") {
|
|
779
|
+
g.DOMMatrix = class DOMMatrix {
|
|
780
|
+
m = [1, 0, 0, 1, 0, 0];
|
|
781
|
+
constructor(init) {
|
|
782
|
+
if (init) this.m = init;
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
if (typeof g.Path2D === "undefined") {
|
|
787
|
+
g.Path2D = class Path2D {
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
g.pdfjsWorker = pdfjsWorker;
|
|
791
|
+
|
|
775
792
|
// src/pdf/parser.ts
|
|
776
|
-
var
|
|
777
|
-
|
|
778
|
-
var import_meta2 = {};
|
|
793
|
+
var import_pdf = require("pdfjs-dist/legacy/build/pdf.mjs");
|
|
794
|
+
import_pdf.GlobalWorkerOptions.workerSrc = "";
|
|
779
795
|
var MAX_PAGES = 5e3;
|
|
780
796
|
var MAX_TOTAL_TEXT = 100 * 1024 * 1024;
|
|
781
|
-
var pdfjsModule = null;
|
|
782
|
-
async function loadPdfjs() {
|
|
783
|
-
if (pdfjsModule) return pdfjsModule;
|
|
784
|
-
try {
|
|
785
|
-
const mod = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
786
|
-
const req = (0, import_module2.createRequire)(import_meta2.url);
|
|
787
|
-
const workerPath = req.resolve("pdfjs-dist/legacy/build/pdf.worker.mjs");
|
|
788
|
-
mod.GlobalWorkerOptions.workerSrc = (0, import_url.pathToFileURL)(workerPath).href;
|
|
789
|
-
pdfjsModule = mod;
|
|
790
|
-
return mod;
|
|
791
|
-
} catch (err) {
|
|
792
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
793
|
-
if (msg.includes("Cannot find") || msg.includes("MODULE_NOT_FOUND")) return null;
|
|
794
|
-
throw new KordocError(`pdfjs-dist \uB85C\uB529 \uC2E4\uD328: ${msg}`);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
797
|
async function parsePdfDocument(buffer) {
|
|
798
|
-
const
|
|
799
|
-
if (!pdfjs) {
|
|
800
|
-
return { success: false, fileType: "pdf", pageCount: 0, error: "pdfjs-dist\uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. npm install pdfjs-dist" };
|
|
801
|
-
}
|
|
802
|
-
const doc = await pdfjs.getDocument({
|
|
798
|
+
const doc = await (0, import_pdf.getDocument)({
|
|
803
799
|
data: new Uint8Array(buffer),
|
|
804
800
|
useSystemFonts: true,
|
|
805
801
|
disableFontFace: true,
|