kordoc 2.4.0 → 2.5.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 +56 -1
- package/dist/{chunk-VLSATRNQ.cjs → chunk-5HWLDNT5.cjs} +2 -2
- package/dist/{chunk-VLSATRNQ.cjs.map → chunk-5HWLDNT5.cjs.map} +1 -1
- package/dist/{chunk-KSBPABBQ.js → chunk-JU7NRDCV.js} +95 -10
- package/dist/chunk-JU7NRDCV.js.map +1 -0
- package/dist/{chunk-XG5CQUSC.js → chunk-UOBENOSJ.js} +2 -2
- package/dist/{chunk-VJPDY4YT.js → chunk-V6STPG3I.js} +2 -2
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +179 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +94 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/{parser-STAOZMUC.cjs → parser-BYHUJ5GP.cjs} +16 -15
- package/dist/{parser-STAOZMUC.cjs.map → parser-BYHUJ5GP.cjs.map} +1 -1
- package/dist/{parser-XRUZEFZT.js → parser-W4P5VX7T.js} +3 -2
- package/dist/parser-W4P5VX7T.js.map +1 -0
- package/dist/{parser-4275GJRB.js → parser-YHW6R62S.js} +3 -2
- package/dist/parser-YHW6R62S.js.map +1 -0
- package/dist/setup-57FB3LSP.js +201 -0
- package/dist/setup-57FB3LSP.js.map +1 -0
- package/dist/{watch-BFLNFJBE.js → watch-Z2YSFSQ3.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-KSBPABBQ.js.map +0 -1
- package/dist/parser-4275GJRB.js.map +0 -1
- package/dist/parser-XRUZEFZT.js.map +0 -1
- /package/dist/{chunk-XG5CQUSC.js.map → chunk-UOBENOSJ.js.map} +0 -0
- /package/dist/{chunk-VJPDY4YT.js.map → chunk-V6STPG3I.js.map} +0 -0
- /package/dist/{watch-BFLNFJBE.js.map → watch-Z2YSFSQ3.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hwpx/parser.ts","../src/hwpx/com-fallback.ts","../src/hwp5/record.ts","../src/hwp5/aes.ts","../src/hwp5/crypto.ts","../src/hwp5/cfb-lenient.ts","../src/hwp5/parser.ts","../src/form/recognize.ts","../src/form/match.ts","../src/form/filler.ts","../src/form/filler-hwpx.ts","../src/hwpx/generator.ts","../src/index.ts","../src/hwp5/sentinel.ts","../src/xlsx/parser.ts","../src/docx/parser.ts","../src/hwpml/parser.ts","../src/diff/text-diff.ts","../src/diff/compare.ts"],"sourcesContent":["/**\r\n * HWPX 파서 — manifest 멀티섹션, colSpan/rowSpan, 중첩테이블\r\n *\r\n * lexdiff 기반 + edu-facility-ai 손상ZIP 복구\r\n */\r\n\r\nimport JSZip from \"jszip\"\r\nimport { inflateRawSync } from \"zlib\"\r\nimport { DOMParser } from \"@xmldom/xmldom\"\r\nimport { buildTable, convertTableToText, blocksToMarkdown, MAX_COLS, MAX_ROWS } from \"../table/builder.js\"\r\nimport type { CellContext, IRBlock, DocumentMetadata, InternalParseResult, ParseOptions, ParseWarning, OutlineItem, InlineStyle, ExtractedImage } from \"../types.js\"\r\nimport { HEADING_RATIO_H1, HEADING_RATIO_H2, HEADING_RATIO_H3 } from \"../types.js\"\r\nimport { KordocError, isPathTraversal, sanitizeHref, precheckZipSize, stripDtd } from \"../utils.js\"\r\n// 테스트 호환성 re-export\r\nexport { precheckZipSize } from \"../utils.js\"\r\nimport { parsePageRange } from \"../page-range.js\"\r\nimport { isComFallbackAvailable, isEncryptedHwpx, extractTextViaCom, comResultToParseResult } from \"./com-fallback.js\"\r\n\r\n/** 압축 해제 최대 크기 (100MB) — ZIP bomb 방지 */\r\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\r\n/** 손상 ZIP 복구 시 최대 엔트리 수 */\r\nconst MAX_ZIP_ENTRIES = 500\r\n\r\n/** colSpan/rowSpan을 안전한 범위로 클램핑 */\r\nfunction clampSpan(val: number, max: number): number {\r\n return Math.max(1, Math.min(val, max))\r\n}\r\n\r\n/** XML DOM 재귀 최대 깊이 — 악성 파일의 스택 오버플로 방지 */\r\nconst MAX_XML_DEPTH = 200\r\n\r\ninterface TableState { rows: CellContext[][]; currentRow: CellContext[]; cell: CellContext | null }\r\n\r\n/** walk 함수들이 공유하는 파싱 컨텍스트 — 개별 optional 파라미터를 하나로 묶어 시그니처 안정화 */\r\ninterface WalkCtx {\r\n styleMap?: HwpxStyleMap\r\n warnings?: ParseWarning[]\r\n sectionNum?: number\r\n counter?: { count: number }\r\n}\r\n\r\n/** xmldom DOMParser 생성 — onError 콜백으로 malformed XML 경고 수집 */\r\nfunction createXmlParser(warnings?: ParseWarning[]): DOMParser {\r\n return new DOMParser({\r\n onError(level: \"warn\" | \"error\" | \"fatalError\", msg: string) {\r\n if (level === \"fatalError\") throw new KordocError(`XML 파싱 실패: ${msg}`)\r\n warnings?.push({ code: \"MALFORMED_XML\", message: `XML ${level === \"warn\" ? \"경고\" : \"오류\"}: ${msg}` })\r\n },\r\n })\r\n}\r\n\r\n// ─── HWPX 스타일 정보 ──────────────────────────────\r\n\r\ninterface HwpxCharProperty {\r\n fontSize?: number // 단위: pt (hwpx는 centi-pt → /100)\r\n bold?: boolean\r\n italic?: boolean\r\n fontName?: string\r\n}\r\n\r\ninterface HwpxStyleMap {\r\n charProperties: Map<string, HwpxCharProperty> // id → property\r\n styles: Map<string, { name: string; charPrId?: string; paraPrId?: string }> // id → style\r\n}\r\n\r\n/** head.xml 또는 header.xml에서 스타일 정보 추출 */\r\nasync function extractHwpxStyles(zip: JSZip, decompressed?: { total: number }): Promise<HwpxStyleMap> {\r\n const result: HwpxStyleMap = {\r\n charProperties: new Map(),\r\n styles: new Map(),\r\n }\r\n\r\n const headerPaths = [\"Contents/header.xml\", \"header.xml\", \"Contents/head.xml\", \"head.xml\"]\r\n for (const hp of headerPaths) {\r\n const hpLower = hp.toLowerCase()\r\n const file = zip.file(hp) || Object.values(zip.files).find(f => f.name.toLowerCase() === hpLower) || null\r\n if (!file) continue\r\n\r\n try {\r\n const xml = await file.async(\"text\")\r\n if (decompressed) {\r\n decompressed.total += xml.length * 2\r\n if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError(\"ZIP 압축 해제 크기 초과 (ZIP bomb 의심)\")\r\n }\r\n const parser = createXmlParser()\r\n const doc = parser.parseFromString(stripDtd(xml), \"text/xml\")\r\n if (!doc.documentElement) continue\r\n\r\n // charProperties 파싱\r\n parseCharProperties(doc, result.charProperties)\r\n // styles 파싱\r\n parseStyleElements(doc, result.styles)\r\n break\r\n } catch { continue }\r\n }\r\n\r\n return result\r\n}\r\n\r\nfunction parseCharProperties(doc: Document, map: Map<string, HwpxCharProperty>): void {\r\n // <hh:charPr> 또는 <charPr> 요소 탐색\r\n const tagNames = [\"hh:charPr\", \"charPr\", \"hp:charPr\"]\r\n for (const tagName of tagNames) {\r\n const elements = doc.getElementsByTagName(tagName)\r\n for (let i = 0; i < elements.length; i++) {\r\n const el = elements[i]\r\n const id = el.getAttribute(\"id\") || el.getAttribute(\"IDRef\") || \"\"\r\n if (!id) continue\r\n\r\n const prop: HwpxCharProperty = {}\r\n\r\n // height 속성 (centi-pt 단위)\r\n const height = el.getAttribute(\"height\")\r\n if (height) {\r\n const parsedHeight = parseInt(height, 10)\r\n if (!isNaN(parsedHeight) && parsedHeight > 0) {\r\n prop.fontSize = parsedHeight / 100\r\n }\r\n }\r\n\r\n // bold/italic\r\n const bold = el.getAttribute(\"bold\")\r\n if (bold === \"true\" || bold === \"1\") prop.bold = true\r\n const italic = el.getAttribute(\"italic\")\r\n if (italic === \"true\" || italic === \"1\") prop.italic = true\r\n\r\n // 하위 요소에서 fontface 탐색\r\n const fontFaces = el.getElementsByTagName(\"*\")\r\n for (let j = 0; j < fontFaces.length; j++) {\r\n const ff = fontFaces[j]\r\n const localTag = (ff.tagName || \"\").replace(/^[^:]+:/, \"\")\r\n if (localTag === \"fontface\" || localTag === \"fontRef\") {\r\n const face = ff.getAttribute(\"face\") || ff.getAttribute(\"FontFace\")\r\n if (face) { prop.fontName = face; break }\r\n }\r\n }\r\n\r\n map.set(id, prop)\r\n }\r\n }\r\n}\r\n\r\nfunction parseStyleElements(doc: Document, map: Map<string, { name: string; charPrId?: string; paraPrId?: string }>): void {\r\n const tagNames = [\"hh:style\", \"style\", \"hp:style\"]\r\n for (const tagName of tagNames) {\r\n const elements = doc.getElementsByTagName(tagName)\r\n for (let i = 0; i < elements.length; i++) {\r\n const el = elements[i]\r\n const id = el.getAttribute(\"id\") || el.getAttribute(\"IDRef\") || String(i)\r\n const name = el.getAttribute(\"name\") || el.getAttribute(\"engName\") || \"\"\r\n const charPrId = el.getAttribute(\"charPrIDRef\") || undefined\r\n const paraPrId = el.getAttribute(\"paraPrIDRef\") || undefined\r\n map.set(id, { name, charPrId, paraPrId })\r\n }\r\n }\r\n}\r\n\r\n// stripDtd는 utils.js에서 import\r\n\r\nexport async function parseHwpxDocument(buffer: ArrayBuffer, options?: ParseOptions): Promise<InternalParseResult> {\r\n // Best-effort 사전 검증 — CD 선언 크기 기반 (위조 가능, 실제 방어는 per-file 누적 체크)\r\n precheckZipSize(buffer, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES)\r\n\r\n let zip: JSZip\r\n\r\n try {\r\n zip = await JSZip.loadAsync(buffer)\r\n } catch {\r\n return extractFromBrokenZip(buffer)\r\n }\r\n\r\n // loadAsync 후 실제 엔트리 수 검증 — CD 위조와 무관한 진짜 방어선\r\n const actualEntryCount = Object.keys(zip.files).length\r\n if (actualEntryCount > MAX_ZIP_ENTRIES) {\r\n throw new KordocError(\"ZIP 엔트리 수 초과 (ZIP bomb 의심)\")\r\n }\r\n\r\n // ── DRM 감지: manifest.xml에 encryption-data가 있으면 COM fallback ──\r\n const manifestFile = zip.file(\"META-INF/manifest.xml\")\r\n if (manifestFile) {\r\n const manifestXml = await manifestFile.async(\"text\")\r\n if (isEncryptedHwpx(manifestXml)) {\r\n // 파일 경로가 options에 있으면 COM fallback 시도\r\n if (isComFallbackAvailable() && options?.filePath) {\r\n const { pages, pageCount, warnings } = extractTextViaCom(options.filePath)\r\n if (pages.some(p => p && p.trim().length > 0)) {\r\n return comResultToParseResult(pages, pageCount, warnings)\r\n }\r\n }\r\n throw new KordocError(\"DRM 암호화된 HWPX 파일입니다. Windows + 한컴 오피스 설치 시 자동 추출됩니다.\")\r\n }\r\n }\r\n\r\n // ZIP 전체 파일 누적 압축해제 크기 추적 (비섹션 파일 포함)\r\n const decompressed = { total: 0 }\r\n\r\n // 메타데이터 추출 (best-effort)\r\n const metadata: DocumentMetadata = {}\r\n await extractHwpxMetadata(zip, metadata, decompressed)\r\n\r\n // 스타일 정보 추출 (best-effort)\r\n const styleMap = await extractHwpxStyles(zip, decompressed)\r\n const warnings: ParseWarning[] = []\r\n\r\n const sectionPaths = await resolveSectionPaths(zip)\r\n if (sectionPaths.length === 0) throw new KordocError(\"HWPX에서 섹션 파일을 찾을 수 없습니다\")\r\n\r\n metadata.pageCount = sectionPaths.length\r\n\r\n // 페이지 범위 필터링 (섹션 단위 근사치)\r\n const pageFilter = options?.pages ? parsePageRange(options.pages, sectionPaths.length) : null\r\n const totalTarget = pageFilter ? pageFilter.size : sectionPaths.length\r\n const blocks: IRBlock[] = []\r\n const nestedTableCounter = { count: 0 }\r\n let parsedSections = 0\r\n for (let si = 0; si < sectionPaths.length; si++) {\r\n if (pageFilter && !pageFilter.has(si + 1)) continue\r\n const file = zip.file(sectionPaths[si])\r\n if (!file) continue\r\n try {\r\n const xml = await file.async(\"text\")\r\n decompressed.total += xml.length * 2\r\n if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError(\"ZIP 압축 해제 크기 초과 (ZIP bomb 의심)\")\r\n blocks.push(...parseSectionXml(xml, styleMap, warnings, si + 1, nestedTableCounter))\r\n parsedSections++\r\n options?.onProgress?.(parsedSections, totalTarget)\r\n } catch (secErr) {\r\n if (secErr instanceof KordocError) throw secErr\r\n warnings.push({ page: si + 1, message: `섹션 ${si + 1} 파싱 실패: ${secErr instanceof Error ? secErr.message : \"알 수 없는 오류\"}`, code: \"PARTIAL_PARSE\" })\r\n }\r\n }\r\n\r\n // 이미지 블록에서 ZIP 바이너리 추출\r\n const images = await extractImagesFromZip(zip, blocks, decompressed, warnings)\r\n\r\n // 스타일 기반 헤딩 감지\r\n detectHwpxHeadings(blocks, styleMap)\r\n\r\n // outline 구축\r\n const outline: OutlineItem[] = blocks\r\n .filter(b => b.type === \"heading\" && b.level && b.text)\r\n .map(b => ({ level: b.level!, text: b.text!, pageNumber: b.pageNumber }))\r\n\r\n const markdown = blocksToMarkdown(blocks)\r\n return { markdown, blocks, metadata, outline: outline.length > 0 ? outline : undefined, warnings: warnings.length > 0 ? warnings : undefined, images: images.length > 0 ? images : undefined }\r\n}\r\n\r\n// ─── 이미지 추출 ───────────────────────────────────\r\n\r\n/** 확장자 → MIME 타입 */\r\nfunction imageExtToMime(ext: string): string {\r\n switch (ext.toLowerCase()) {\r\n case \"jpg\": case \"jpeg\": return \"image/jpeg\"\r\n case \"png\": return \"image/png\"\r\n case \"gif\": return \"image/gif\"\r\n case \"bmp\": return \"image/bmp\"\r\n case \"tif\": case \"tiff\": return \"image/tiff\"\r\n case \"wmf\": return \"image/wmf\"\r\n case \"emf\": return \"image/emf\"\r\n case \"svg\": return \"image/svg+xml\"\r\n default: return \"application/octet-stream\"\r\n }\r\n}\r\n\r\n/** MIME → 확장자 */\r\nfunction mimeToExt(mime: string): string {\r\n if (mime.includes(\"jpeg\")) return \"jpg\"\r\n if (mime.includes(\"png\")) return \"png\"\r\n if (mime.includes(\"gif\")) return \"gif\"\r\n if (mime.includes(\"bmp\")) return \"bmp\"\r\n if (mime.includes(\"tiff\")) return \"tif\"\r\n if (mime.includes(\"wmf\")) return \"wmf\"\r\n if (mime.includes(\"emf\")) return \"emf\"\r\n if (mime.includes(\"svg\")) return \"svg\"\r\n return \"bin\"\r\n}\r\n\r\n/** blocks에서 type=\"image\" 블록의 참조를 ZIP에서 실제 바이너리로 변환 */\r\nasync function extractImagesFromZip(\r\n zip: JSZip,\r\n blocks: IRBlock[],\r\n decompressed: { total: number },\r\n warnings?: ParseWarning[],\r\n): Promise<ExtractedImage[]> {\r\n const images: ExtractedImage[] = []\r\n let imageIndex = 0\r\n\r\n for (const block of blocks) {\r\n if (block.type !== \"image\" || !block.text) continue\r\n\r\n const ref = block.text\r\n // BinData/ 폴더 내에서 참조 파일 찾기\r\n // HWPX binaryItemIDRef는 확장자 없이 오는 경우가 많음 (예: \"image1\" → \"BinData/image1.bmp\")\r\n const candidates = [\r\n `BinData/${ref}`,\r\n `Contents/BinData/${ref}`,\r\n ref, // 절대 경로일 수도 있음\r\n ]\r\n\r\n // 확장자 없는 ref인 경우 ZIP에서 매칭 파일 탐색\r\n let resolvedPath: string | null = null\r\n if (!ref.includes(\".\")) {\r\n const prefixes = [`BinData/${ref}`, `Contents/BinData/${ref}`]\r\n for (const prefix of prefixes) {\r\n const match = zip.file(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\.[a-zA-Z0-9]+$`))\r\n if (match.length > 0) { resolvedPath = match[0].name; break }\r\n }\r\n }\r\n\r\n let found = false\r\n const allCandidates = resolvedPath ? [resolvedPath, ...candidates] : candidates\r\n for (const path of allCandidates) {\r\n if (isPathTraversal(path)) continue\r\n const file = zip.file(path)\r\n if (!file) continue\r\n\r\n try {\r\n const data = await file.async(\"uint8array\")\r\n decompressed.total += data.length\r\n if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError(\"ZIP 압축 해제 크기 초과 (ZIP bomb 의심)\")\r\n\r\n const actualPath = path\r\n const ext = actualPath.includes(\".\") ? (actualPath.split(\".\").pop() || \"png\") : \"png\"\r\n const mimeType = imageExtToMime(ext)\r\n imageIndex++\r\n const filename = `image_${String(imageIndex).padStart(3, \"0\")}.${mimeToExt(mimeType)}`\r\n\r\n images.push({ filename, data, mimeType })\r\n // 블록 텍스트를 참조 파일명으로 교체\r\n block.text = filename\r\n block.imageData = { data, mimeType, filename: ref }\r\n found = true\r\n break\r\n } catch (err) {\r\n if (err instanceof KordocError) throw err\r\n // 개별 이미지 실패는 경고로 처리\r\n }\r\n }\r\n\r\n if (!found) {\r\n warnings?.push({ page: block.pageNumber, message: `이미지 파일 없음: ${ref}`, code: \"SKIPPED_IMAGE\" })\r\n // image 블록을 paragraph로 전환 (참조만 남김)\r\n block.type = \"paragraph\"\r\n block.text = `[이미지: ${ref}]`\r\n }\r\n }\r\n\r\n return images\r\n}\r\n\r\n// ─── 메타데이터 추출 (best-effort) ───────────────────\r\n\r\n/**\r\n * HWPX ZIP 내 메타데이터 파일에서 Dublin Core 정보 추출.\r\n * 표준 경로: meta.xml, docProps/core.xml, META-INF/container.xml\r\n */\r\nasync function extractHwpxMetadata(zip: JSZip, metadata: DocumentMetadata, decompressed?: { total: number }): Promise<void> {\r\n try {\r\n // meta.xml (HWPX 표준) 또는 docProps/core.xml (OOXML 호환)\r\n const metaPaths = [\"meta.xml\", \"META-INF/meta.xml\", \"docProps/core.xml\"]\r\n for (const mp of metaPaths) {\r\n const file = zip.file(mp) || Object.values(zip.files).find(f => f.name.toLowerCase() === mp.toLowerCase()) || null\r\n if (!file) continue\r\n const xml = await file.async(\"text\")\r\n if (decompressed) {\r\n decompressed.total += xml.length * 2\r\n if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new KordocError(\"ZIP 압축 해제 크기 초과 (ZIP bomb 의심)\")\r\n }\r\n parseDublinCoreMetadata(xml, metadata)\r\n if (metadata.title || metadata.author) return\r\n }\r\n } catch {\r\n // best-effort\r\n }\r\n}\r\n\r\n/** Dublin Core (dc:) 메타데이터 XML 파싱 */\r\nfunction parseDublinCoreMetadata(xml: string, metadata: DocumentMetadata): void {\r\n const parser = createXmlParser()\r\n const doc = parser.parseFromString(stripDtd(xml), \"text/xml\")\r\n if (!doc.documentElement) return\r\n\r\n const getText = (tagNames: string[]): string | undefined => {\r\n for (const tag of tagNames) {\r\n const els = doc.getElementsByTagName(tag)\r\n if (els.length > 0) {\r\n const text = els[0].textContent?.trim()\r\n if (text) return text\r\n }\r\n }\r\n return undefined\r\n }\r\n\r\n metadata.title = metadata.title || getText([\"dc:title\", \"title\"])\r\n metadata.author = metadata.author || getText([\"dc:creator\", \"creator\", \"cp:lastModifiedBy\"])\r\n metadata.description = metadata.description || getText([\"dc:description\", \"description\", \"dc:subject\", \"subject\"])\r\n metadata.createdAt = metadata.createdAt || getText([\"dcterms:created\", \"meta:creation-date\"])\r\n metadata.modifiedAt = metadata.modifiedAt || getText([\"dcterms:modified\", \"meta:date\"])\r\n\r\n const keywords = getText([\"dc:keyword\", \"cp:keywords\", \"meta:keyword\"])\r\n if (keywords && !metadata.keywords) {\r\n metadata.keywords = keywords.split(/[,;]/).map(k => k.trim()).filter(Boolean)\r\n }\r\n}\r\n\r\n/** 메타데이터만 추출 (전체 파싱 없이) — MCP parse_metadata용 */\r\nexport async function extractHwpxMetadataOnly(buffer: ArrayBuffer): Promise<DocumentMetadata> {\r\n let zip: JSZip\r\n try {\r\n zip = await JSZip.loadAsync(buffer)\r\n } catch {\r\n throw new KordocError(\"HWPX ZIP을 열 수 없습니다\")\r\n }\r\n\r\n const metadata: DocumentMetadata = {}\r\n await extractHwpxMetadata(zip, metadata)\r\n\r\n const sectionPaths = await resolveSectionPaths(zip)\r\n metadata.pageCount = sectionPaths.length\r\n\r\n return metadata\r\n}\r\n\r\n// ─── 손상 ZIP 복구 (edu-facility-ai에서 포팅) ──────────\r\n\r\nfunction extractFromBrokenZip(buffer: ArrayBuffer): InternalParseResult {\r\n const data = new Uint8Array(buffer)\r\n const view = new DataView(buffer)\r\n let pos = 0\r\n const blocks: IRBlock[] = []\r\n const warnings: ParseWarning[] = [\r\n { code: \"BROKEN_ZIP_RECOVERY\", message: \"손상된 ZIP 구조 — Local File Header 기반 복구 모드\" },\r\n ]\r\n let totalDecompressed = 0\r\n let entryCount = 0\r\n let sectionNum = 0\r\n const nestedTableCounter = { count: 0 }\r\n\r\n while (pos < data.length - 30) {\r\n // PK\\x03\\x04 시그니처 확인 — 미매칭 시 다음 PK 시그니처까지 스캔 (중간 손상 복구)\r\n if (data[pos] !== 0x50 || data[pos + 1] !== 0x4b || data[pos + 2] !== 0x03 || data[pos + 3] !== 0x04) {\r\n pos++\r\n while (pos < data.length - 30) {\r\n if (data[pos] === 0x50 && data[pos + 1] === 0x4b && data[pos + 2] === 0x03 && data[pos + 3] === 0x04) break\r\n pos++\r\n }\r\n continue\r\n }\r\n\r\n if (++entryCount > MAX_ZIP_ENTRIES) break\r\n\r\n const method = view.getUint16(pos + 8, true)\r\n const compSize = view.getUint32(pos + 18, true)\r\n const nameLen = view.getUint16(pos + 26, true)\r\n const extraLen = view.getUint16(pos + 28, true)\r\n\r\n // nameLen 상한 — 비정상 값에 의한 대규모 버퍼 할당 방지\r\n if (nameLen > 1024 || extraLen > 65535) { pos += 30 + nameLen + extraLen; continue }\r\n\r\n const fileStart = pos + 30 + nameLen + extraLen\r\n // 범위 초과 검증 — OOB 및 무한 루프 방지\r\n if (fileStart + compSize > data.length) break\r\n if (compSize === 0 && method !== 0) { pos = fileStart; continue }\r\n\r\n const nameBytes = data.slice(pos + 30, pos + 30 + nameLen)\r\n const name = new TextDecoder().decode(nameBytes)\r\n\r\n // 경로 순회 방지 — 상위 디렉토리 참조 및 절대 경로 차단\r\n if (isPathTraversal(name)) { pos = fileStart + compSize; continue }\r\n const fileData = data.slice(fileStart, fileStart + compSize)\r\n pos = fileStart + compSize\r\n\r\n if (!name.toLowerCase().includes(\"section\") || !name.endsWith(\".xml\")) continue\r\n\r\n try {\r\n let content: string\r\n if (method === 0) {\r\n content = new TextDecoder().decode(fileData)\r\n } else if (method === 8) {\r\n const decompressed = inflateRawSync(Buffer.from(fileData), { maxOutputLength: MAX_DECOMPRESS_SIZE })\r\n content = new TextDecoder().decode(decompressed)\r\n } else {\r\n continue\r\n }\r\n totalDecompressed += content.length * 2\r\n if (totalDecompressed > MAX_DECOMPRESS_SIZE) throw new KordocError(\"압축 해제 크기 초과\")\r\n sectionNum++\r\n blocks.push(...parseSectionXml(content, undefined, warnings, sectionNum, nestedTableCounter))\r\n } catch {\r\n continue\r\n }\r\n }\r\n\r\n if (blocks.length === 0) throw new KordocError(\"손상된 HWPX에서 섹션 데이터를 복구할 수 없습니다\")\r\n const markdown = blocksToMarkdown(blocks)\r\n return { markdown, blocks, warnings: warnings.length > 0 ? warnings : undefined }\r\n}\r\n\r\n// ─── Manifest 해석 ───────────────────────────────────\r\n\r\nasync function resolveSectionPaths(zip: JSZip): Promise<string[]> {\r\n const manifestPaths = [\"Contents/content.hpf\", \"content.hpf\"]\r\n for (const mp of manifestPaths) {\r\n const mpLower = mp.toLowerCase()\r\n const file = zip.file(mp) || Object.values(zip.files).find(f => f.name.toLowerCase() === mpLower) || null\r\n if (!file) continue\r\n const xml = await file.async(\"text\")\r\n const paths = parseSectionPathsFromManifest(xml)\r\n if (paths.length > 0) return paths\r\n }\r\n\r\n // fallback: section*.xml 직접 검색\r\n const sectionFiles = zip.file(/[Ss]ection\\d+\\.xml$/)\r\n return sectionFiles.map(f => f.name).sort()\r\n}\r\n\r\nfunction parseSectionPathsFromManifest(xml: string): string[] {\r\n const parser = createXmlParser()\r\n const doc = parser.parseFromString(stripDtd(xml), \"text/xml\")\r\n const items = doc.getElementsByTagName(\"opf:item\")\r\n const spine = doc.getElementsByTagName(\"opf:itemref\")\r\n\r\n const isSectionId = (id: string) => /^s/i.test(id) || id.toLowerCase().includes(\"section\")\r\n const idToHref = new Map<string, string>()\r\n for (let i = 0; i < items.length; i++) {\r\n const item = items[i]\r\n const id = item.getAttribute(\"id\") || \"\"\r\n let href = item.getAttribute(\"href\") || \"\"\r\n const mediaType = item.getAttribute(\"media-type\") || \"\"\r\n if (!isSectionId(id) && !mediaType.includes(\"xml\")) continue\r\n if (!href.startsWith(\"/\") && !href.startsWith(\"Contents/\") && isSectionId(id))\r\n href = \"Contents/\" + href\r\n idToHref.set(id, href)\r\n }\r\n\r\n if (spine.length > 0) {\r\n const ordered: string[] = []\r\n for (let i = 0; i < spine.length; i++) {\r\n const href = idToHref.get(spine[i].getAttribute(\"idref\") || \"\")\r\n if (href) ordered.push(href)\r\n }\r\n if (ordered.length > 0) return ordered\r\n }\r\n return Array.from(idToHref.entries())\r\n .filter(([id]) => isSectionId(id))\r\n .sort((a, b) => a[0].localeCompare(b[0]))\r\n .map(([, href]) => href)\r\n}\r\n\r\n// ─── 헤딩 감지 (스타일 기반) ────────────────────────\r\n\r\n/** HWPX 스타일 기반 헤딩 감지 */\r\nfunction detectHwpxHeadings(blocks: IRBlock[], styleMap: HwpxStyleMap): void {\r\n // 본문 폰트 크기 결정\r\n let baseFontSize = 0\r\n const sizeFreq = new Map<number, number>()\r\n for (const b of blocks) {\r\n if (b.style?.fontSize) {\r\n sizeFreq.set(b.style.fontSize, (sizeFreq.get(b.style.fontSize) || 0) + 1)\r\n }\r\n }\r\n let maxCount = 0\r\n for (const [size, count] of sizeFreq) {\r\n if (count > maxCount) { maxCount = count; baseFontSize = size }\r\n }\r\n\r\n for (const block of blocks) {\r\n if (block.type !== \"paragraph\" || !block.text) continue\r\n const text = block.text.trim()\r\n if (text.length === 0 || text.length > 200 || /^\\d+$/.test(text)) continue\r\n\r\n let level = 0\r\n\r\n // 폰트 크기 기반\r\n if (baseFontSize > 0 && block.style?.fontSize) {\r\n const ratio = block.style.fontSize / baseFontSize\r\n if (ratio >= HEADING_RATIO_H1) level = 1\r\n else if (ratio >= HEADING_RATIO_H2) level = 2\r\n else if (ratio >= HEADING_RATIO_H3) level = 3\r\n }\r\n\r\n // \"제N조/장/절\" 패턴 — 균등배분 공백 허용 (\"제 1 장\" → \"제1장\")\r\n const compactText = text.replace(/\\s+/g, \"\")\r\n if (/^제\\d+[조장절편]/.test(compactText) && text.length <= 50) {\r\n if (level === 0) level = 3\r\n }\r\n\r\n if (level > 0) {\r\n block.type = \"heading\"\r\n block.level = level\r\n }\r\n }\r\n}\r\n\r\n// ─── 섹션 XML 파싱 ──────────────────────────────────\r\n\r\n/** 중첩 테이블 마커 생성 — 순번 + 첫 행 힌트(있는 경우) */\r\nfunction makeNestedTableMarker(counter: { count: number }, rows: CellContext[][]): string {\r\n counter.count++\r\n const firstRow = rows[0] ?? []\r\n const hint = firstRow.map(c => c.text.trim().replace(/\\n/g, \" \")).filter(Boolean).join(\" | \")\r\n const hintChars = [...hint]\r\n const truncated = hintChars.length > 60 ? hintChars.slice(0, 60).join(\"\") + \"…\" : hint\r\n return truncated\r\n ? `[중첩 테이블 #${counter.count}: ${truncated}]`\r\n : `[중첩 테이블 #${counter.count}]`\r\n}\r\n\r\n/**\r\n * 중첩 테이블 처리 공통 로직 — walkSection/walkParagraphChildren 중복 제거\r\n * 큰 중첩 테이블(≥3행, ≥2열)은 별도 블록으로 분리, 작은 것은 텍스트로 평탄화.\r\n * 두 경우 모두 부모 셀에 마커 삽입. parentTable을 반환하여 tableCtx 갱신에 사용.\r\n */\r\nfunction handleNestedTable(\r\n newTable: TableState,\r\n tableStack: TableState[],\r\n blocks: IRBlock[],\r\n ctx: WalkCtx\r\n): TableState {\r\n const parentTable = tableStack.pop()!\r\n let nestedCols = 0\r\n for (const r of newTable.rows) if (r.length > nestedCols) nestedCols = r.length\r\n if (newTable.rows.length >= 3 && nestedCols >= 2) {\r\n blocks.push({ type: \"table\", table: buildTable(newTable.rows), pageNumber: ctx.sectionNum })\r\n if (parentTable.cell) {\r\n const marker = ctx.counter ? makeNestedTableMarker(ctx.counter, newTable.rows) : \"[중첩 테이블]\"\r\n parentTable.cell.text += (parentTable.cell.text ? \"\\n\" : \"\") + marker\r\n }\r\n } else {\r\n const nestedText = convertTableToText(newTable.rows)\r\n if (parentTable.cell) {\r\n const marker = ctx.counter ? makeNestedTableMarker(ctx.counter, newTable.rows) : \"[중첩 테이블]\"\r\n parentTable.cell.text += (parentTable.cell.text ? \"\\n\" : \"\") + marker + \"\\n\" + nestedText\r\n }\r\n }\r\n return parentTable\r\n}\r\n\r\nfunction parseSectionXml(xml: string, styleMap?: HwpxStyleMap, warnings?: ParseWarning[], sectionNum?: number, counter?: { count: number }): IRBlock[] {\r\n const parser = createXmlParser(warnings)\r\n const doc = parser.parseFromString(stripDtd(xml), \"text/xml\")\r\n if (!doc.documentElement) return []\r\n\r\n const blocks: IRBlock[] = []\r\n const ctx: WalkCtx = { styleMap, warnings, sectionNum, counter }\r\n walkSection(doc.documentElement, blocks, null, [], ctx)\r\n return blocks\r\n}\r\n\r\n/** pic/shape 요소에서 이미지 참조 경로 추출 (binaryItemIDRef 또는 href) */\r\nfunction extractImageRef(el: Element): string | null {\r\n // HWPX: <hp:imgRect> 또는 <hp:img> 내 binaryItemIDRef 속성\r\n // 또는 하위에서 img 관련 속성 탐색\r\n const children = el.childNodes\r\n if (!children) return null\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i] as Element\r\n if (child.nodeType !== 1) continue\r\n const tag = (child.tagName || child.localName || \"\").replace(/^[^:]+:/, \"\")\r\n if (tag === \"imgRect\" || tag === \"img\" || tag === \"imgClip\") {\r\n const ref = child.getAttribute(\"binaryItemIDRef\") || child.getAttribute(\"href\") || \"\"\r\n if (ref) return ref\r\n }\r\n // lineShape > imgRect 같은 중첩 구조\r\n const nested = extractImageRef(child)\r\n if (nested) return nested\r\n }\r\n // 직접 속성 체크\r\n const directRef = el.getAttribute(\"binaryItemIDRef\") || \"\"\r\n if (directRef) return directRef\r\n return null\r\n}\r\n\r\nfunction walkSection(\r\n node: Node, blocks: IRBlock[],\r\n tableCtx: TableState | null, tableStack: TableState[],\r\n ctx: WalkCtx, depth: number = 0\r\n): void {\r\n if (depth > MAX_XML_DEPTH) return\r\n const children = node.childNodes\r\n if (!children) return\r\n\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType !== 1) continue\r\n\r\n const tag = el.tagName || el.localName || \"\"\r\n const localTag = tag.replace(/^[^:]+:/, \"\")\r\n\r\n switch (localTag) {\r\n case \"tbl\": {\r\n if (tableCtx) tableStack.push(tableCtx)\r\n const newTable: TableState = { rows: [], currentRow: [], cell: null }\r\n walkSection(el, blocks, newTable, tableStack, ctx, depth + 1)\r\n\r\n if (newTable.rows.length > 0) {\r\n if (tableStack.length > 0) {\r\n tableCtx = handleNestedTable(newTable, tableStack, blocks, ctx)\r\n } else {\r\n blocks.push({ type: \"table\", table: buildTable(newTable.rows), pageNumber: ctx.sectionNum })\r\n tableCtx = null\r\n }\r\n } else {\r\n tableCtx = tableStack.length > 0 ? tableStack.pop()! : null\r\n }\r\n break\r\n }\r\n\r\n case \"tr\":\r\n if (tableCtx) {\r\n tableCtx.currentRow = []\r\n walkSection(el, blocks, tableCtx, tableStack, ctx, depth + 1)\r\n if (tableCtx.currentRow.length > 0) tableCtx.rows.push(tableCtx.currentRow)\r\n tableCtx.currentRow = []\r\n }\r\n break\r\n\r\n case \"tc\":\r\n if (tableCtx) {\r\n tableCtx.cell = { text: \"\", colSpan: 1, rowSpan: 1 }\r\n walkSection(el, blocks, tableCtx, tableStack, ctx, depth + 1)\r\n if (tableCtx.cell) {\r\n tableCtx.currentRow.push(tableCtx.cell)\r\n tableCtx.cell = null\r\n }\r\n }\r\n break\r\n\r\n case \"cellAddr\":\r\n if (tableCtx?.cell) {\r\n const ca = parseInt(el.getAttribute(\"colAddr\") || \"\", 10)\r\n const ra = parseInt(el.getAttribute(\"rowAddr\") || \"\", 10)\r\n if (!isNaN(ca)) tableCtx.cell.colAddr = ca\r\n if (!isNaN(ra)) tableCtx.cell.rowAddr = ra\r\n }\r\n break\r\n\r\n case \"cellSpan\":\r\n if (tableCtx?.cell) {\r\n const rawCs = parseInt(el.getAttribute(\"colSpan\") || \"1\", 10)\r\n const cs = isNaN(rawCs) ? 1 : rawCs\r\n const rawRs = parseInt(el.getAttribute(\"rowSpan\") || \"1\", 10)\r\n const rs = isNaN(rawRs) ? 1 : rawRs\r\n tableCtx.cell.colSpan = clampSpan(cs, MAX_COLS)\r\n tableCtx.cell.rowSpan = clampSpan(rs, MAX_ROWS)\r\n }\r\n break\r\n\r\n case \"p\": {\r\n const { text, href, footnote, style } = extractParagraphInfo(el, ctx.styleMap)\r\n if (text) {\r\n if (tableCtx?.cell) {\r\n tableCtx.cell.text += (tableCtx.cell.text ? \"\\n\" : \"\") + text\r\n } else if (!tableCtx) {\r\n const block: IRBlock = { type: \"paragraph\", text, pageNumber: ctx.sectionNum }\r\n if (style) block.style = style\r\n if (href) block.href = href\r\n if (footnote) block.footnoteText = footnote\r\n blocks.push(block)\r\n }\r\n }\r\n // <p> 내부의 <tbl>만 별도 처리 — extractParagraphInfo가 이미 텍스트를 추출했으므로\r\n // 전체 walkSection 재귀 대신 테이블/이미지 자식만 선택적으로 처리\r\n tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1)\r\n break\r\n }\r\n\r\n // 이미지/그림 — 경로 추출 또는 경고\r\n case \"pic\": case \"shape\": case \"drawingObject\": {\r\n const imgRef = extractImageRef(el)\r\n if (imgRef) {\r\n blocks.push({ type: \"image\", text: imgRef, pageNumber: ctx.sectionNum })\r\n } else if (ctx.warnings && ctx.sectionNum) {\r\n ctx.warnings.push({ page: ctx.sectionNum, message: `스킵된 요소: ${localTag}`, code: \"SKIPPED_IMAGE\" })\r\n }\r\n break\r\n }\r\n\r\n default:\r\n walkSection(el, blocks, tableCtx, tableStack, ctx, depth + 1)\r\n break\r\n }\r\n }\r\n}\r\n\r\n/** <p> 내부에서 텍스트가 아닌 구조적 자식만 처리 (tbl, pic, shape). tableCtx 반환으로 상태 전파 */\r\nfunction walkParagraphChildren(\r\n node: Node, blocks: IRBlock[],\r\n tableCtx: TableState | null, tableStack: TableState[],\r\n ctx: WalkCtx, depth: number = 0\r\n): TableState | null {\r\n if (depth > MAX_XML_DEPTH) return tableCtx\r\n const children = node.childNodes\r\n if (!children) return tableCtx\r\n const walkChildren = (parent: Node, d: number) => {\r\n if (d > MAX_XML_DEPTH) return\r\n const kids = parent.childNodes\r\n if (!kids) return\r\n for (let i = 0; i < kids.length; i++) {\r\n const el = kids[i] as Element\r\n if (el.nodeType !== 1) continue\r\n const tag = el.tagName || el.localName || \"\"\r\n const localTag = tag.replace(/^[^:]+:/, \"\")\r\n\r\n if (localTag === \"tbl\") {\r\n // 테이블은 walkSection으로 위임\r\n if (tableCtx) tableStack.push(tableCtx)\r\n const newTable: TableState = { rows: [], currentRow: [], cell: null }\r\n walkSection(el, blocks, newTable, tableStack, ctx, d + 1)\r\n if (newTable.rows.length > 0) {\r\n if (tableStack.length > 0) {\r\n tableCtx = handleNestedTable(newTable, tableStack, blocks, ctx)\r\n } else {\r\n blocks.push({ type: \"table\", table: buildTable(newTable.rows), pageNumber: ctx.sectionNum })\r\n tableCtx = null\r\n }\r\n } else {\r\n tableCtx = tableStack.length > 0 ? tableStack.pop()! : null\r\n }\r\n } else if (localTag === \"pic\" || localTag === \"shape\" || localTag === \"drawingObject\") {\r\n // 도형/이미지 안에 drawText(글상자)가 있으면 텍스트 추출 우선\r\n const drawTextChild = findDescendant(el, \"drawText\")\r\n if (drawTextChild) {\r\n extractDrawTextBlocks(drawTextChild, blocks, ctx.styleMap, ctx.sectionNum)\r\n } else {\r\n const imgRef = extractImageRef(el)\r\n if (imgRef) {\r\n blocks.push({ type: \"image\", text: imgRef, pageNumber: ctx.sectionNum })\r\n } else if (ctx.warnings && ctx.sectionNum) {\r\n ctx.warnings.push({ page: ctx.sectionNum, message: `스킵된 요소: ${localTag}`, code: \"SKIPPED_IMAGE\" })\r\n }\r\n }\r\n } else if (localTag === \"drawText\") {\r\n // 글상자(TextBox) 안 텍스트 추출 — <hp:p> 순회\r\n extractDrawTextBlocks(el, blocks, ctx.styleMap, ctx.sectionNum)\r\n } else if (localTag === \"r\" || localTag === \"run\" || localTag === \"ctrl\"\r\n || localTag === \"rect\" || localTag === \"ellipse\" || localTag === \"polygon\"\r\n || localTag === \"line\" || localTag === \"arc\" || localTag === \"curve\"\r\n || localTag === \"connectLine\" || localTag === \"container\") {\r\n // <hp:run>, <hp:ctrl>, 도형 요소 내부에 테이블/이미지/글상자가 포함될 수 있음 — 재귀\r\n walkChildren(el, d + 1)\r\n } else if (localTag === \"run\") {\r\n tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1)\r\n }\r\n }\r\n }\r\n walkChildren(node, depth)\r\n return tableCtx\r\n}\r\n\r\n/** 자손에서 특정 태그명의 첫 번째 요소 탐색 (최대 깊이 5) */\r\nfunction findDescendant(node: Node, targetTag: string, depth = 0): Element | null {\r\n if (depth > 5) return null\r\n const children = node.childNodes\r\n if (!children) return null\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i] as Element\r\n if (child.nodeType !== 1) continue\r\n const tag = (child.tagName || child.localName || \"\").replace(/^[^:]+:/, \"\")\r\n if (tag === targetTag) return child\r\n const found = findDescendant(child, targetTag, depth + 1)\r\n if (found) return found\r\n }\r\n return null\r\n}\r\n\r\n/** drawText(글상자) 내부의 <p> 요소들에서 텍스트를 추출하여 paragraph 블록 생성 */\r\nfunction extractDrawTextBlocks(drawTextNode: Node, blocks: IRBlock[], styleMap?: HwpxStyleMap, sectionNum?: number): void {\r\n const children = drawTextNode.childNodes\r\n if (!children) return\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i] as Element\r\n if (child.nodeType !== 1) continue\r\n const tag = (child.tagName || child.localName || \"\").replace(/^[^:]+:/, \"\")\r\n if (tag === \"subList\" || tag === \"p\" || tag === \"para\") {\r\n // subList 안의 <p>들을 순회\r\n if (tag === \"subList\") {\r\n extractDrawTextBlocks(child, blocks, styleMap, sectionNum)\r\n } else {\r\n const info = extractParagraphInfo(child, styleMap)\r\n const text = info.text.trim()\r\n if (text) {\r\n blocks.push({ type: \"paragraph\", text, style: info.style ?? undefined, pageNumber: sectionNum })\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\ninterface ParagraphInfo {\r\n text: string\r\n href?: string\r\n footnote?: string\r\n style?: InlineStyle\r\n}\r\n\r\nfunction extractParagraphInfo(para: Element, styleMap?: HwpxStyleMap): ParagraphInfo {\r\n let text = \"\"\r\n let href: string | undefined\r\n let footnote: string | undefined\r\n let charPrId: string | undefined\r\n\r\n // 문단의 스타일 참조 → charPr로 간접 조회\r\n // HWPX <p>에는 paraPrIDRef/styleIDRef가 있고, charPrIDRef는 <r> 요소에 있음\r\n // 여기서는 일단 null — <r> 요소에서 charPrIDRef를 가져옴\r\n\r\n const walk = (node: Node) => {\r\n const children = node.childNodes\r\n if (!children) return\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i] as Element\r\n if (child.nodeType === 3) { text += child.textContent || \"\"; continue }\r\n if (child.nodeType !== 1) continue\r\n\r\n const tag = (child.tagName || child.localName || \"\").replace(/^[^:]+:/, \"\")\r\n switch (tag) {\r\n case \"t\": walk(child); break // 자식 순회 (tab 등 하위 요소 처리)\r\n case \"tab\": {\r\n const leader = child.getAttribute(\"leader\")\r\n if (leader && leader !== \"0\") {\r\n // 목차 리더 탭 (점선/실선 등) — 뒤에 페이지번호가 오므로 이후 텍스트 무시\r\n text += \"\\x1F\" // 특수 마커: 이후 텍스트 제거용\r\n } else {\r\n text += \"\\t\"\r\n }\r\n break\r\n }\r\n case \"br\":\r\n if ((child.getAttribute(\"type\") || \"line\") === \"line\") text += \"\\n\"\r\n break\r\n case \"fwSpace\": case \"hwSpace\": text += \" \"; break\r\n case \"tbl\": break // 테이블은 walkSection에서 처리\r\n\r\n // 하이퍼링크\r\n case \"hyperlink\": {\r\n const url = child.getAttribute(\"url\") || child.getAttribute(\"href\") || \"\"\r\n if (url) {\r\n // XSS 방지: 추출 시점에서 href 살균\r\n const safe = sanitizeHref(url)\r\n if (safe) href = safe\r\n }\r\n // 하이퍼링크 내 텍스트 추출\r\n walk(child)\r\n break\r\n }\r\n\r\n // 각주/미주\r\n case \"footNote\": case \"endNote\": case \"fn\": case \"en\": {\r\n const noteText = extractTextFromNode(child)\r\n if (noteText) footnote = (footnote ? footnote + \"; \" : \"\") + noteText\r\n break\r\n }\r\n\r\n // 제어 요소 — 필드, 컨트롤, 매개변수 등 스킵\r\n case \"ctrl\": case \"fieldBegin\": case \"fieldEnd\":\r\n case \"parameters\": case \"stringParam\": case \"integerParam\":\r\n case \"boolParam\": case \"floatParam\":\r\n case \"secPr\": // 섹션 속성 (페이지 설정 등)\r\n case \"colPr\": // 다단 속성\r\n case \"linesegarray\": case \"lineseg\": // 레이아웃 정보\r\n // 도형/이미지 요소 — 대체텍스트(\"사각형입니다.\" 등) 누출 방지\r\n case \"pic\": case \"shape\": case \"drawingObject\":\r\n case \"shapeComment\": case \"drawText\":\r\n break\r\n\r\n // run 요소에서 charPrIDRef 추출\r\n case \"r\": {\r\n const runCharPr = child.getAttribute(\"charPrIDRef\")\r\n if (runCharPr && !charPrId) charPrId = runCharPr\r\n walk(child)\r\n break\r\n }\r\n\r\n default: walk(child); break\r\n }\r\n }\r\n }\r\n walk(para)\r\n\r\n // 목차 리더 마커(\\x1F) 이후 텍스트(페이지번호) 제거\r\n const leaderIdx = text.indexOf(\"\\x1F\")\r\n if (leaderIdx >= 0) text = text.substring(0, leaderIdx)\r\n\r\n let cleanText = text.replace(/[ \\t]+/g, \" \").trim()\r\n\r\n // 한글 이미지 OLE 대체 텍스트 필터링 (\"그림입니다. 원본 그림의 이름: ...\")\r\n if (/^그림입니다\\.?\\s*원본\\s*그림의\\s*(이름|크기)/.test(cleanText)) cleanText = \"\"\r\n // 멀티라인으로 삽입된 OLE 대체 텍스트도 제거\r\n cleanText = cleanText.replace(/그림입니다\\.?\\s*원본\\s*그림의\\s*(이름|크기)[^\\n]*(\\n[^\\n]*원본\\s*그림의\\s*(이름|크기)[^\\n]*)*/g, \"\").trim()\r\n // HWP 도형/개체 대체텍스트 제거 (\"사각형입니다.\", \"개체 입니다.\" 등)\r\n cleanText = cleanText.replace(/(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|선|직선|곡선|화살표|오각형|육각형|팔각형|별|십자|구름|마름모|도넛|평행사변형|사다리꼴|개체|그리기\\s?개체|묶음\\s?개체|글상자|수식|표|그림|OLE\\s?개체)\\s?입니다\\.?/g, \"\").trim()\r\n\r\n // 스타일 정보 조회\r\n let style: InlineStyle | undefined\r\n if (styleMap && charPrId) {\r\n const charProp = styleMap.charProperties.get(charPrId)\r\n if (charProp) {\r\n style = {}\r\n if (charProp.fontSize) style.fontSize = charProp.fontSize\r\n if (charProp.bold) style.bold = true\r\n if (charProp.italic) style.italic = true\r\n if (charProp.fontName) style.fontName = charProp.fontName\r\n if (!style.fontSize && !style.bold && !style.italic) style = undefined\r\n }\r\n }\r\n\r\n return { text: cleanText, href, footnote, style }\r\n}\r\n\r\n/** 노드 내 모든 텍스트를 재귀적으로 추출 */\r\nfunction extractTextFromNode(node: Node): string {\r\n let result = \"\"\r\n const children = node.childNodes\r\n if (!children) return result\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i]\r\n if (child.nodeType === 3) result += child.textContent || \"\"\r\n else if (child.nodeType === 1) result += extractTextFromNode(child)\r\n }\r\n return result.trim()\r\n}\r\n","/**\r\n * HWPX DRM 문서 COM fallback — 한컴 오피스 COM API (GetPageText) 활용\r\n *\r\n * DRM 암호화된 HWPX 파일을 한컴 오피스의 HWPFrame.HwpObject COM으로 열어\r\n * 페이지별 텍스트를 추출한다. Windows + 한컴 오피스 설치 필수.\r\n *\r\n * 흐름: manifest.xml에 encryption-data 발견 → COM으로 Open → GetPageText(1..N) → Markdown\r\n */\r\n\r\nimport { execFileSync } from \"child_process\"\r\nimport { platform } from \"os\"\r\nimport type { InternalParseResult, ParseWarning, DocumentMetadata, IRBlock } from \"../types.js\"\r\n\r\n/** COM fallback 사용 가능 여부 (Windows만) */\r\nexport function isComFallbackAvailable(): boolean {\r\n return platform() === \"win32\"\r\n}\r\n\r\n/** manifest.xml 내용에서 encryption-data 존재 여부 확인 */\r\nexport function isEncryptedHwpx(manifestXml: string): boolean {\r\n return manifestXml.includes(\"encryption-data\")\r\n}\r\n\r\n/**\r\n * COM API로 DRM HWPX 파일의 텍스트를 추출\r\n * @param filePath 디스크 상의 HWPX 파일 절대 경로\r\n */\r\nexport function extractTextViaCom(filePath: string): { pages: string[]; pageCount: number; warnings: ParseWarning[] } {\r\n if (!isComFallbackAvailable()) {\r\n throw new Error(\"COM fallback은 Windows에서만 사용 가능합니다\")\r\n }\r\n\r\n // PowerShell 스크립트를 인라인으로 실행\r\n //\r\n // 핵심 우회: 파일을 %TEMP%로 복사한 뒤 해당 경로로 Open.\r\n // 한컴 오피스는 파일 경로가 \"신뢰 영역\"(사용자 Temp/AppData 등) 밖일 때\r\n // FilePathChecker가 경고 팝업을 띄운다. FilePathCheckerModuleExample DLL이\r\n // 등록된 개발 환경에서는 RegisterModule이 작동해 경고가 억제되지만, 일반\r\n // 사용자 환경에는 해당 DLL이 없다.\r\n // %TEMP% 하위 경로로 복사하면 신뢰 영역 규칙에 의해 경고가 나오지 않아\r\n // DLL 등록 없이도 안정적으로 DRM 텍스트를 추출할 수 있다.\r\n //\r\n // - RegisterModule: 보안 경고 1차 억제(DLL 없어도 해가 없음)\r\n // - GetPageText: DRM 우회 텍스트 추출\r\n // filePath를 single-quote로 이스케이프 (내부 ' → '')\r\n const escaped = filePath.replace(/'/g, \"''\")\r\n const ps1 = `\r\n[Console]::OutputEncoding = [System.Text.Encoding]::UTF8\r\n$ErrorActionPreference = 'Stop'\r\n\r\n$src = '${escaped}'\r\n$tmpDir = Join-Path $env:TEMP ('hwp-com-' + [guid]::NewGuid().ToString('N'))\r\n[void](New-Item -ItemType Directory -Path $tmpDir -Force)\r\n$tmpFile = Join-Path $tmpDir (Split-Path $src -Leaf)\r\nCopy-Item -LiteralPath $src -Destination $tmpFile -Force\r\n\r\ntry {\r\n $hwp = New-Object -ComObject HWPFrame.HwpObject\r\n $hwp.RegisterModule('FilePathCheckerModule', 'FilePathCheckerModuleExample') | Out-Null\r\n $hwp.Open($tmpFile, '', '') | Out-Null\r\n $pc = $hwp.PageCount\r\n $result = @{ pageCount = $pc; pages = @() }\r\n for ($p = 1; $p -le $pc; $p++) {\r\n $t = $hwp.GetPageText($p, 0)\r\n $result.pages += @($t)\r\n }\r\n $hwp.Clear(1) | Out-Null\r\n try { $hwp.Quit() | Out-Null } catch { }\r\n [System.Runtime.InteropServices.Marshal]::ReleaseComObject($hwp) | Out-Null\r\n [GC]::Collect()\r\n [GC]::WaitForPendingFinalizers()\r\n $result | ConvertTo-Json -Depth 3 -Compress\r\n} catch {\r\n @{ error = $_.Exception.Message } | ConvertTo-Json -Compress\r\n} finally {\r\n # 임시 파일 정리 + 좀비 Hwp.exe 방지용 garbage collect\r\n try { Remove-Item -LiteralPath $tmpDir -Recurse -Force -ErrorAction SilentlyContinue } catch { }\r\n [GC]::Collect()\r\n [GC]::WaitForPendingFinalizers()\r\n}\r\n`\r\n\r\n const stdout = execFileSync(\"powershell\", [\r\n \"-NoProfile\",\r\n \"-NonInteractive\",\r\n \"-ExecutionPolicy\", \"Bypass\",\r\n \"-Command\", ps1,\r\n ], {\r\n encoding: \"utf-8\",\r\n timeout: 120_000, // 2분 타임아웃\r\n windowsHide: true,\r\n maxBuffer: 50 * 1024 * 1024, // 50MB\r\n })\r\n\r\n // COM 메서드 반환값 등 JSON 앞의 garbage 제거\r\n const trimmed = stdout.trim()\r\n const jsonStart = trimmed.indexOf(\"{\")\r\n if (jsonStart < 0) throw new Error(`COM 출력에 JSON이 없습니다: ${trimmed.slice(0, 200)}`)\r\n const json = JSON.parse(trimmed.slice(jsonStart))\r\n if (json.error) {\r\n throw new Error(`COM 텍스트 추출 실패: ${json.error}`)\r\n }\r\n\r\n const warnings: ParseWarning[] = []\r\n const pages: string[] = Array.isArray(json.pages) ? json.pages : []\r\n const pageCount: number = json.pageCount ?? pages.length\r\n\r\n if (pages.length === 0) {\r\n warnings.push({ message: \"COM으로 텍스트를 추출하지 못했습니다\", code: \"COM_EMPTY\" })\r\n }\r\n\r\n return { pages, pageCount, warnings }\r\n}\r\n\r\n/**\r\n * COM 추출 결과를 InternalParseResult로 변환\r\n */\r\nexport function comResultToParseResult(\r\n pages: string[],\r\n pageCount: number,\r\n warnings: ParseWarning[],\r\n): InternalParseResult {\r\n const blocks: IRBlock[] = []\r\n const lines: string[] = []\r\n\r\n for (let i = 0; i < pages.length; i++) {\r\n const text = (pages[i] ?? \"\").trim()\r\n if (!text) continue\r\n\r\n // 페이지 텍스트를 paragraph 블록들로 변환\r\n const paragraphs = text.split(/\\n/)\r\n for (const para of paragraphs) {\r\n const trimmed = para.trim()\r\n if (!trimmed) continue\r\n blocks.push({ type: \"paragraph\", text: trimmed, pageNumber: i + 1 })\r\n lines.push(trimmed)\r\n }\r\n }\r\n\r\n const markdown = lines.join(\"\\n\\n\")\r\n const metadata: DocumentMetadata = { pageCount }\r\n\r\n warnings.push({\r\n message: \"DRM 문서: 한컴 COM API로 텍스트 추출 (서식/표 정보 제한적)\",\r\n code: \"DRM_COM_FALLBACK\",\r\n })\r\n\r\n return {\r\n markdown,\r\n blocks,\r\n metadata,\r\n warnings: warnings.length > 0 ? warnings : undefined,\r\n }\r\n}\r\n","/** HWP 5.x 레코드 리더, UTF-16LE 텍스트 추출, 스트림 압축해제 */\r\n\r\nimport { inflateRawSync, inflateSync } from \"zlib\"\r\nimport { KordocError } from \"../utils.js\"\r\n\r\n// ─── 레코드 태그 상수 ────────────────────────────────\r\n\r\nexport const TAG_PARA_HEADER = 0x0042\r\nexport const TAG_PARA_TEXT = 0x0043\r\nexport const TAG_CHAR_SHAPE = 0x0044\r\nexport const TAG_PARA_SHAPE = 0x0045\r\nexport const TAG_CTRL_HEADER = 0x0047\r\nexport const TAG_LIST_HEADER = 0x0048\r\nexport const TAG_TABLE = 0x004d\r\n\r\n// DocInfo 태그 (스타일 정보 해석용) — HWPTAG_BEGIN(0x0010) 기준\r\nexport const TAG_ID_MAPPINGS = 0x0011 // HWPTAG_BEGIN + 1\r\nexport const TAG_FACE_NAME = 0x0013 // HWPTAG_BEGIN + 3\r\nexport const TAG_DOC_CHAR_SHAPE = 0x0015 // HWPTAG_BEGIN + 5\r\nexport const TAG_DOC_PARA_SHAPE = 0x0019 // HWPTAG_BEGIN + 9\r\nexport const TAG_DOC_STYLE = 0x001a // HWPTAG_BEGIN + 10\r\n\r\n// 특수 문자 코드 (UTF-16LE) — HWP 5.0 바이너리 스펙 + rhwp 검증\r\n// 3가지 카테고리: char(2바이트), inline(16바이트), extended(16바이트)\r\n// char: 0, 13, 24-31 — 제어문자만, 확장 데이터 없음\r\n// inline: 4-9, 19-20 — 제어문자(2) + 확장(14) = 16바이트\r\n// extended: 1-3, 10-12, 14-18, 21-23 — 제어문자(2) + 확장(14) = 16바이트\r\nconst CHAR_LINE = 0x0000 // char: 줄바꿈\r\nconst CHAR_SECTION_BREAK = 0x000a // extended: 구역/단 정의 (14바이트 확장 데이터)\r\nconst CHAR_PARA = 0x000d // char: 문단 끝\r\nconst CHAR_TAB = 0x0009 // inline: 탭\r\nconst CHAR_HYPHEN = 0x001e // char: 하이픈\r\nconst CHAR_NBSP = 0x001f // char: 비분리 공백\r\nconst CHAR_FIXED_NBSP = 0x0018 // char: 고정 비분리 공백\r\nconst CHAR_FIXED_WIDTH = 0x0019 // char: 고정폭 공백\r\n\r\n// FileHeader 플래그\r\nexport const FLAG_COMPRESSED = 1 << 0\r\nexport const FLAG_ENCRYPTED = 1 << 1\r\nexport const FLAG_DISTRIBUTION = 1 << 2\r\nexport const FLAG_DRM = 1 << 4\r\n\r\n// ─── 레코드 구조 ─────────────────────────────────────\r\n\r\nexport interface HwpRecord {\r\n tagId: number\r\n level: number\r\n size: number\r\n data: Buffer\r\n}\r\n\r\nexport interface HwpFileHeader {\r\n signature: string\r\n versionMajor: number\r\n flags: number\r\n}\r\n\r\n// ─── 레코드 리더 ─────────────────────────────────────\r\n\r\n/** 최대 레코드 수 — 비정상 파일에 의한 메모리 폭주 방지 */\r\nconst MAX_RECORDS = 500_000\r\n\r\nexport function readRecords(data: Buffer): HwpRecord[] {\r\n const records: HwpRecord[] = []\r\n let offset = 0\r\n\r\n while (offset + 4 <= data.length && records.length < MAX_RECORDS) {\r\n const header = data.readUInt32LE(offset)\r\n offset += 4\r\n\r\n const tagId = header & 0x3ff\r\n const level = (header >> 10) & 0x3ff\r\n let size = (header >> 20) & 0xfff\r\n\r\n // 확장 크기\r\n if (size === 0xfff) {\r\n if (offset + 4 > data.length) break\r\n size = data.readUInt32LE(offset)\r\n offset += 4\r\n }\r\n\r\n if (offset + size > data.length) break\r\n records.push({ tagId, level, size, data: data.subarray(offset, offset + size) })\r\n offset += size\r\n }\r\n\r\n return records\r\n}\r\n\r\n// ─── 스트림 압축 해제 ────────────────────────────────\r\n\r\n/** 압축 해제 최대 크기 (100MB) — decompression bomb 방지 */\r\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\r\n\r\nexport function decompressStream(data: Buffer): Buffer {\r\n const opts = { maxOutputLength: MAX_DECOMPRESS_SIZE }\r\n if (data.length >= 2 && data[0] === 0x78) {\r\n try { return inflateSync(data, opts) } catch { /* fallback to raw */ }\r\n }\r\n return inflateRawSync(data, opts)\r\n}\r\n\r\n// ─── FileHeader 파싱 ─────────────────────────────────\r\n\r\nexport function parseFileHeader(data: Buffer): HwpFileHeader {\r\n if (data.length < 40) throw new KordocError(\"FileHeader가 너무 짧습니다 (최소 40바이트)\")\r\n const sig = data.subarray(0, 32).toString(\"utf8\").replace(/\\0+$/, \"\")\r\n return {\r\n signature: sig,\r\n versionMajor: data[35],\r\n flags: data.readUInt32LE(36),\r\n }\r\n}\r\n\r\n// ─── 스타일 정보 구조 ────────────────────────────────\r\n\r\n/** DocInfo에서 추출한 문단 모양 (PARA_SHAPE) */\r\nexport interface HwpParaShape {\r\n /** 개요 수준: 0=본문, 1-7=개요수준 1-7 (heading 계층) */\r\n outlineLevel: number\r\n}\r\n\r\n/** DocInfo에서 추출한 글자 모양 (CHAR_SHAPE) */\r\nexport interface HwpCharShape {\r\n /** 글꼴 크기 (단위: 0.1pt, 예: 100 = 10pt) */\r\n fontSize: number\r\n /**\r\n * 속성 플래그 (HWP5 바이너리 스펙 1.1 기준):\r\n * bit 0 = italic, bit 1 = bold, bit 2 = underline, bit 3 = outline\r\n * 검증 완료: 공식 스펙 + pyhwp/hwp.js 등 오픈소스 파서와 일치 (v1.7)\r\n */\r\n attrFlags: number\r\n}\r\n\r\n/** DocInfo에서 추출한 스타일 */\r\nexport interface HwpStyle {\r\n name: string\r\n /** 한글 이름 (UTF-16LE) */\r\n nameKo: string\r\n /** 연결된 charShape 인덱스 */\r\n charShapeId: number\r\n /** 연결된 paraShape 인덱스 */\r\n paraShapeId: number\r\n /** 스타일 타입: 0=paragraph, 1=character */\r\n type: number\r\n}\r\n\r\n/** DocInfo 파싱 결과 */\r\nexport interface HwpDocInfo {\r\n charShapes: HwpCharShape[]\r\n paraShapes: HwpParaShape[]\r\n styles: HwpStyle[]\r\n}\r\n\r\n/** DocInfo 레코드들에서 스타일 정보 추출 */\r\nexport function parseDocInfo(records: HwpRecord[]): HwpDocInfo {\r\n const charShapes: HwpCharShape[] = []\r\n const paraShapes: HwpParaShape[] = []\r\n const styles: HwpStyle[] = []\r\n\r\n for (const rec of records) {\r\n // PARA_SHAPE — 문단 모양 (개요 수준 추출)\r\n // 첫 4바이트(u32) 비트 팩: bits 25-27 = 개요 수준 (0=본문, 1-7=heading)\r\n if (rec.tagId === TAG_DOC_PARA_SHAPE && rec.data.length >= 4) {\r\n const flags = rec.data.readUInt32LE(0)\r\n const outlineLevel = (flags >> 25) & 0x07 // bits 25-27 → 3bit (0-7)\r\n paraShapes.push({ outlineLevel })\r\n }\r\n\r\n if (rec.tagId === TAG_DOC_CHAR_SHAPE && rec.data.length >= 18) {\r\n // HWP5 CHAR_SHAPE 구조 (바이너리 스펙 1.1 기준):\r\n // faceId: 7개 언어 * u16 = 14바이트 (offset 0-13)\r\n // ratio: 7개 언어 * u8 = 7바이트 (offset 14-20)\r\n // spacing: 7개 언어 * s8 = 7바이트 (offset 21-27)\r\n // relSize: 7개 언어 * u8 = 7바이트 (offset 28-34)\r\n // charOffset: 7개 언어 * s8 = 7바이트 (offset 35-41)\r\n // baseSize: u32 at offset 42 (단위: 0.1pt)\r\n // attrFlags: u32 at offset 46 (bit0=italic, bit1=bold) — 공식 스펙 검증 완료\r\n if (rec.data.length >= 50) {\r\n const fontSize = rec.data.readUInt32LE(42) // 단위: 0.1pt\r\n const attrFlags = rec.data.readUInt32LE(46)\r\n charShapes.push({ fontSize, attrFlags })\r\n } else {\r\n // 짧은 레코드 — 스타일 정보 없음\r\n charShapes.push({ fontSize: 0, attrFlags: 0 })\r\n }\r\n }\r\n\r\n if (rec.tagId === TAG_DOC_STYLE && rec.data.length >= 8) {\r\n try {\r\n // STYLE 구조: nameLen(u16) + name(UTF-16LE) + nameKoLen(u16) + nameKo(UTF-16LE)\r\n // + type(u8) + nextStyleId(u16) + langId(s16) + paraShapeId(u16) + charShapeId(u16)\r\n let offset = 0\r\n const nameLen = rec.data.readUInt16LE(offset); offset += 2\r\n const nameBytes = nameLen * 2\r\n const name = nameBytes > 0 && offset + nameBytes <= rec.data.length\r\n ? rec.data.subarray(offset, offset + nameBytes).toString(\"utf16le\")\r\n : \"\"\r\n offset += nameBytes\r\n\r\n let nameKo = \"\"\r\n if (offset + 2 <= rec.data.length) {\r\n const nameKoLen = rec.data.readUInt16LE(offset); offset += 2\r\n const nameKoBytes = nameKoLen * 2\r\n if (nameKoBytes > 0 && offset + nameKoBytes <= rec.data.length) {\r\n nameKo = rec.data.subarray(offset, offset + nameKoBytes).toString(\"utf16le\")\r\n }\r\n offset += nameKoBytes\r\n }\r\n\r\n // type(u8) + nextStyleId(u16) + langId(s16) + paraShapeId(u16) + charShapeId(u16)\r\n const type = offset < rec.data.length ? rec.data.readUInt8(offset) : 0; offset += 1\r\n offset += 2 // nextStyleId\r\n offset += 2 // langId\r\n const paraShapeId = offset + 2 <= rec.data.length ? rec.data.readUInt16LE(offset) : 0; offset += 2\r\n const charShapeId = offset + 2 <= rec.data.length ? rec.data.readUInt16LE(offset) : 0\r\n\r\n styles.push({ name, nameKo, charShapeId, paraShapeId, type })\r\n } catch {\r\n // 파싱 실패 — 스킵\r\n }\r\n }\r\n }\r\n\r\n return { charShapes, paraShapes, styles }\r\n}\r\n\r\n// ─── UTF-16LE 텍스트 추출 (21가지 제어문자 처리) ─────\r\n\r\nexport function extractText(data: Buffer): string {\r\n let result = \"\"\r\n let i = 0\r\n\r\n while (i + 1 < data.length) {\r\n const ch = data.readUInt16LE(i)\r\n i += 2\r\n\r\n switch (ch) {\r\n // ── char 타입 (2바이트만, 확장 데이터 없음) ──\r\n case CHAR_LINE: result += \"\\n\"; break\r\n case CHAR_SECTION_BREAK: // 구역/단 정의 — extended(14바이트 스킵)\r\n result += \"\\n\"\r\n if (i + 14 <= data.length) i += 14\r\n break\r\n case CHAR_PARA: break // 문단 끝\r\n case CHAR_HYPHEN: result += \"-\"; break\r\n case CHAR_NBSP: result += \" \"; break\r\n case CHAR_FIXED_NBSP: result += \"\\u00a0\"; break // 진짜 NBSP\r\n case CHAR_FIXED_WIDTH: result += \" \"; break // 고정폭 공백\r\n\r\n // ── inline 타입 (2바이트 + 14바이트 확장) ──\r\n case CHAR_TAB:\r\n result += \"\\t\"\r\n if (i + 14 <= data.length) i += 14\r\n break\r\n\r\n default:\r\n if (ch >= 0x0001 && ch <= 0x001f) {\r\n // rhwp 기준 3-카테고리 분류:\r\n // extended(1-3, 11-12, 14-18, 21-23) + inline(4-9, 19-20) → 14바이트 스킵\r\n // char(24-31) → 스킵 없음 (이미 switch에서 24,25,30,31 처리됨)\r\n const isExtended = (ch >= 1 && ch <= 3) || (ch >= 11 && ch <= 12) || (ch >= 14 && ch <= 18) || (ch >= 21 && ch <= 23)\r\n const isInline = (ch >= 4 && ch <= 9) || (ch >= 19 && ch <= 20)\r\n if ((isExtended || isInline) && i + 14 <= data.length) i += 14\r\n } else if (ch >= 0x0020) {\r\n // UTF-16 surrogate pair 처리 (BMP 외 문자: 이모지, CJK 확장 등)\r\n if (ch >= 0xd800 && ch <= 0xdbff && i + 1 < data.length) {\r\n const lo = data.readUInt16LE(i)\r\n if (lo >= 0xdc00 && lo <= 0xdfff) {\r\n i += 2\r\n const codePoint = ((ch - 0xd800) << 10) + (lo - 0xdc00) + 0x10000\r\n result += String.fromCodePoint(codePoint)\r\n break\r\n }\r\n }\r\n result += String.fromCharCode(ch)\r\n }\r\n break\r\n }\r\n }\r\n\r\n return result\r\n}\r\n","/**\n * AES-128 ECB 순수 JS 구현 — 배포용 HWP 복호화 전용.\n * 외부 의존성 없음 (kordoc 제로 네이티브 의존성 원칙 유지).\n * 참조: rhwp (MIT) src/parser/crypto.rs + FIPS-197 (AES 표준)\n */\n\n// ── S-Box & 역 S-Box ──\n\nconst S_BOX = new Uint8Array([\n 0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,\n 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,\n 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,\n 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,\n 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,\n 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,\n 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,\n 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,\n 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,\n 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,\n 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,\n 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,\n 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,\n 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,\n 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,\n 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,\n])\n\nconst INV_S_BOX = new Uint8Array([\n 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,\n 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,\n 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,\n 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,\n 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,\n 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,\n 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,\n 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,\n 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,\n 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,\n 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,\n 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,\n 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,\n 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,\n 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,\n 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d,\n])\n\n// ── RCON (라운드 상수) ──\n\nconst RCON = new Uint8Array([0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36])\n\n// ── GF(2^8) 곱셈 ──\n\nfunction gmul(a: number, b: number): number {\n let p = 0\n for (let i = 0; i < 8; i++) {\n if (b & 1) p ^= a\n const hi = a & 0x80\n a = (a << 1) & 0xff\n if (hi) a ^= 0x1b\n b >>= 1\n }\n return p\n}\n\n// ── 키 확장 (AES-128: 4 words → 44 words) ──\n\nfunction expandKey(key: Uint8Array): Uint32Array {\n const w = new Uint32Array(44)\n\n // 첫 4 words: 원본 키\n for (let i = 0; i < 4; i++) {\n w[i] = (key[4 * i] << 24) | (key[4 * i + 1] << 16) | (key[4 * i + 2] << 8) | key[4 * i + 3]\n }\n\n for (let i = 4; i < 44; i++) {\n let temp = w[i - 1]\n if (i % 4 === 0) {\n // RotWord + SubWord + RCON\n temp = ((temp << 8) | (temp >>> 24)) >>> 0\n temp = (S_BOX[(temp >>> 24) & 0xff] << 24) |\n (S_BOX[(temp >>> 16) & 0xff] << 16) |\n (S_BOX[(temp >>> 8) & 0xff] << 8) |\n S_BOX[temp & 0xff]\n temp = (temp ^ (RCON[i / 4 - 1] << 24)) >>> 0\n }\n w[i] = (w[i - 4] ^ temp) >>> 0\n }\n\n return w\n}\n\n// ── AES-128 단일 블록 복호화 (16바이트) ──\n\nfunction decryptBlock(block: Uint8Array, roundKeys: Uint32Array): Uint8Array {\n // state를 4x4 column-major 배열로 로드\n const s = new Uint8Array(16)\n for (let i = 0; i < 16; i++) s[i] = block[i]\n\n // AddRoundKey (round 10)\n addRoundKey(s, roundKeys, 10)\n\n // Rounds 9 → 1\n for (let round = 9; round >= 1; round--) {\n invShiftRows(s)\n invSubBytes(s)\n addRoundKey(s, roundKeys, round)\n invMixColumns(s)\n }\n\n // Final round (round 0)\n invShiftRows(s)\n invSubBytes(s)\n addRoundKey(s, roundKeys, 0)\n\n return s\n}\n\nfunction addRoundKey(s: Uint8Array, w: Uint32Array, round: number): void {\n const base = round * 4\n for (let c = 0; c < 4; c++) {\n const k = w[base + c]\n s[c * 4] ^= (k >>> 24) & 0xff\n s[c * 4 + 1] ^= (k >>> 16) & 0xff\n s[c * 4 + 2] ^= (k >>> 8) & 0xff\n s[c * 4 + 3] ^= k & 0xff\n }\n}\n\nfunction invSubBytes(s: Uint8Array): void {\n for (let i = 0; i < 16; i++) s[i] = INV_S_BOX[s[i]]\n}\n\nfunction invShiftRows(s: Uint8Array): void {\n // Row 0: no shift\n // Row 1: shift right 1\n let t = s[13]; s[13] = s[9]; s[9] = s[5]; s[5] = s[1]; s[1] = t\n // Row 2: shift right 2\n t = s[2]; s[2] = s[10]; s[10] = t\n t = s[6]; s[6] = s[14]; s[14] = t\n // Row 3: shift right 3 (= left 1)\n t = s[3]; s[3] = s[7]; s[7] = s[11]; s[11] = s[15]; s[15] = t\n}\n\nfunction invMixColumns(s: Uint8Array): void {\n for (let c = 0; c < 4; c++) {\n const i = c * 4\n const a0 = s[i], a1 = s[i + 1], a2 = s[i + 2], a3 = s[i + 3]\n s[i] = gmul(a0, 0x0e) ^ gmul(a1, 0x0b) ^ gmul(a2, 0x0d) ^ gmul(a3, 0x09)\n s[i + 1] = gmul(a0, 0x09) ^ gmul(a1, 0x0e) ^ gmul(a2, 0x0b) ^ gmul(a3, 0x0d)\n s[i + 2] = gmul(a0, 0x0d) ^ gmul(a1, 0x09) ^ gmul(a2, 0x0e) ^ gmul(a3, 0x0b)\n s[i + 3] = gmul(a0, 0x0b) ^ gmul(a1, 0x0d) ^ gmul(a2, 0x09) ^ gmul(a3, 0x0e)\n }\n}\n\n// ── 공개 API ──\n\n/** AES-128 ECB 복호화. data 길이는 16의 배수여야 함. */\nexport function aes128EcbDecrypt(data: Uint8Array, key: Uint8Array): Uint8Array {\n if (key.length !== 16) throw new Error(\"AES-128 키는 16바이트여야 합니다\")\n if (data.length % 16 !== 0) throw new Error(\"AES ECB 입력은 16바이트의 배수여야 합니다\")\n\n const roundKeys = expandKey(key)\n const out = new Uint8Array(data.length)\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const block = data.subarray(offset, offset + 16)\n const decrypted = decryptBlock(block, roundKeys)\n out.set(decrypted, offset)\n }\n\n return out\n}\n","/**\n * HWP 배포용(distribution) 문서 복호화.\n *\n * 배포용 HWP는 ViewText/Section{N} 스트림에 암호화된 본문을 저장.\n * 첫 레코드(HWPTAG_DISTRIBUTE_DOC_DATA)의 256바이트 payload에서 AES 키를 추출한 뒤\n * 나머지 데이터를 AES-128 ECB로 복호화.\n *\n * 알고리즘 참조: rhwp (MIT) src/parser/crypto.rs\n * 포맷 참조: HWP 5.0 바이너리 스펙 — 배포용 문서 구조\n */\n\nimport { aes128EcbDecrypt } from \"./aes.js\"\nimport { decompressStream } from \"./record.js\"\n\n// ── MSVC LCG (Linear Congruential Generator) ──\n\n/** MSVC CRT rand() 호환 LCG */\nclass MsvcLcg {\n private seed: number\n\n constructor(seed: number) {\n this.seed = seed >>> 0 // u32로 강제\n }\n\n /** 0 ~ 0x7FFF 범위 난수 반환 (MSVC rand() 호환) */\n rand(): number {\n // MSVC LCG: seed = seed * 214013 + 2531011\n // JS에서 32bit 정수 오버플로우를 정확히 재현하기 위해 Math.imul 사용\n this.seed = (Math.imul(this.seed, 214013) + 2531011) >>> 0\n return (this.seed >>> 16) & 0x7fff\n }\n}\n\n// ── 배포용 문서 256바이트 payload 복호화 ──\n\n/**\n * DISTRIBUTE_DOC_DATA 레코드의 256바이트 payload를 LCG+XOR로 복호화.\n *\n * 구조:\n * - bytes[0..4]: LCG seed (u32 LE)\n * - bytes[4..256]: XOR 암호화된 데이터\n *\n * XOR 규칙: LCG에서 키 바이트를 뽑고, n = (lcg.rand() & 0xF) + 1 바이트마다 키 교체\n */\nfunction decryptDistributePayload(payload: Uint8Array): Uint8Array {\n if (payload.length < 256) throw new Error(\"배포용 payload가 256바이트 미만입니다\")\n\n const seed = (payload[0] | (payload[1] << 8) | (payload[2] << 16) | (payload[3] << 24)) >>> 0\n const lcg = new MsvcLcg(seed)\n\n const result = new Uint8Array(payload.subarray(0, 256)) // 원본 복사\n\n // rhwp 호환: i=0부터 시작하여 n 카운터를 소비하되, i<4는 XOR 스킵 (seed 보존)\n // i=4부터 시작하면 LCG 시퀀스가 어긋남 — i=0~3에서도 n을 소비해야 정확함\n let i = 0\n let n = 0\n let key = 0\n\n while (i < 256) {\n if (n === 0) {\n key = lcg.rand() & 0xff\n n = (lcg.rand() & 0x0f) + 1\n }\n if (i >= 4) {\n result[i] ^= key\n }\n i++\n n--\n }\n\n return result\n}\n\n// ── AES 키 추출 ──\n\n/**\n * 복호화된 256바이트 payload에서 AES-128 키(16바이트) 추출.\n * offset = 4 + (decrypted[0] & 0x0F)\n */\nfunction extractAesKey(decryptedPayload: Uint8Array): Uint8Array {\n const offset = 4 + (decryptedPayload[0] & 0x0f)\n if (offset + 16 > decryptedPayload.length) {\n throw new Error(\"AES 키 추출 실패: 오프셋이 payload 범위를 초과합니다\")\n }\n return decryptedPayload.slice(offset, offset + 16)\n}\n\n// ── 레코드 헤더 파싱 ──\n\n/** HWP 레코드 헤더에서 tag_id와 size 추출 */\nfunction parseRecordHeader(data: Uint8Array, offset: number): { tagId: number; size: number; headerSize: number } {\n if (offset + 4 > data.length) throw new Error(\"레코드 헤더 파싱 실패: 데이터 부족\")\n\n const header = (data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)) >>> 0\n const tagId = header & 0x3ff\n let size = (header >>> 20) & 0xfff\n let headerSize = 4\n\n if (size === 0xfff) {\n if (offset + 8 > data.length) throw new Error(\"확장 레코드 크기 파싱 실패: 데이터 부족\")\n size = (data[offset + 4] | (data[offset + 5] << 8) | (data[offset + 6] << 16) | (data[offset + 7] << 24)) >>> 0\n headerSize = 8\n }\n\n return { tagId, size, headerSize }\n}\n\n// ── 공개 API ──\n\n/** HWPTAG_DISTRIBUTE_DOC_DATA 태그 ID (HWPTAG_BEGIN + 12 = 0x10 + 12 = 0x1C = 28) */\nconst TAG_DISTRIBUTE_DOC_DATA = 0x10 + 12 // = 28\n\n/**\n * ViewText 스트림을 복호화하여 일반 BodyText 레코드 데이터로 변환.\n *\n * @param viewTextRaw ViewText/Section{N} 스트림의 원본 바이트\n * @param compressed FileHeader의 compressed 플래그\n * @returns 복호화된 레코드 데이터 (readRecords()로 파싱 가능)\n */\nexport function decryptViewText(viewTextRaw: Buffer, compressed: boolean): Buffer {\n const data = new Uint8Array(viewTextRaw)\n\n // 1. 첫 레코드 파싱 (DISTRIBUTE_DOC_DATA)\n const rec = parseRecordHeader(data, 0)\n if (rec.tagId !== TAG_DISTRIBUTE_DOC_DATA) {\n throw new Error(`배포용 문서의 첫 레코드가 DISTRIBUTE_DOC_DATA(${TAG_DISTRIBUTE_DOC_DATA})가 아닙니다 (실제: ${rec.tagId})`)\n }\n\n const payloadStart = rec.headerSize\n const payloadEnd = payloadStart + rec.size\n if (payloadEnd > data.length || rec.size < 256) {\n throw new Error(\"배포용 payload가 유효하지 않습니다\")\n }\n\n // 2. 256바이트 payload 복호화 (LCG + XOR)\n const payload = data.subarray(payloadStart, payloadStart + 256)\n const decryptedPayload = decryptDistributePayload(payload)\n\n // 3. AES-128 키 추출\n const aesKey = extractAesKey(decryptedPayload)\n\n // 4. 나머지 데이터를 AES-128 ECB 복호화\n const encryptedStart = payloadEnd\n const encryptedData = data.subarray(encryptedStart)\n\n if (encryptedData.length === 0) {\n throw new Error(\"배포용 문서에 암호화된 본문 데이터가 없습니다\")\n }\n\n // AES ECB는 16바이트 블록 단위 — 패딩 처리\n const alignedLen = encryptedData.length - (encryptedData.length % 16)\n if (alignedLen === 0) {\n throw new Error(\"암호화된 데이터가 너무 짧습니다 (16바이트 미만)\")\n }\n\n const alignedData = encryptedData.subarray(0, alignedLen)\n const decrypted = aes128EcbDecrypt(alignedData, aesKey)\n\n // 5. 압축 해제 (compressed 플래그가 설정된 경우)\n if (compressed) {\n try {\n return decompressStream(Buffer.from(decrypted))\n } catch {\n // 압축이 아닐 수도 있음 — 그대로 반환\n return Buffer.from(decrypted)\n }\n }\n\n return Buffer.from(decrypted)\n}\n\n// 테스트용 내부 함수 export\nexport { MsvcLcg as _MsvcLcg, decryptDistributePayload as _decryptDistributePayload, extractAesKey as _extractAesKey }\n","/**\n * Lenient CFB (Compound File Binary / OLE2) 파서.\n *\n * 표준 cfb 모듈이 FAT 검증 실패로 거부하는 손상된 HWP 파일을 열기 위한 폴백.\n * 직접 헤더/FAT/디렉토리를 파싱하여 스트림 데이터를 추출.\n *\n * 참조: rhwp (MIT) src/parser/cfb_reader.rs (LenientCfbReader)\n * 참조: MS-CFB spec (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb)\n */\n\nimport { decompressStream } from \"./record.js\"\n\n// ── 상수 ──\n\nconst CFB_MAGIC = Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1])\nconst END_OF_CHAIN = 0xfffffffe\nconst FREE_SECT = 0xffffffff\n\n/** 순환 감지용 최대 체인 길이 */\nconst MAX_CHAIN_LENGTH = 1_000_000\n/** 최대 디렉토리 엔트리 수 */\nconst MAX_DIR_ENTRIES = 100_000\n/** 최대 스트림 크기 (100MB) */\nconst MAX_STREAM_SIZE = 100 * 1024 * 1024\n\n// ── 디렉토리 엔트리 ──\n\ninterface DirEntry {\n name: string\n type: number // 0=unknown, 1=storage, 2=stream, 5=root\n startSector: number\n size: number\n}\n\n// ── CFB 컨테이너 ──\n\nexport interface LenientCfbContainer {\n /** 이름 기반 스트림 탐색 */\n findStream(path: string): Buffer | null\n /** 디렉토리 엔트리 목록 */\n entries(): DirEntry[]\n}\n\n// ── 구현 ──\n\nexport function parseLenientCfb(data: Buffer): LenientCfbContainer {\n if (data.length < 512) throw new Error(\"CFB 파일이 너무 짧습니다 (최소 512바이트)\")\n if (!data.subarray(0, 8).equals(CFB_MAGIC)) throw new Error(\"CFB 매직 바이트 불일치\")\n\n // ── 헤더 파싱 ──\n\n const sectorSizeShift = data.readUInt16LE(30)\n if (sectorSizeShift < 7 || sectorSizeShift > 16) throw new Error(\"유효하지 않은 섹터 크기 시프트: \" + sectorSizeShift)\n const sectorSize = 1 << sectorSizeShift // 보통 512\n const miniSectorSizeShift = data.readUInt16LE(32)\n if (miniSectorSizeShift > 16) throw new Error(\"유효하지 않은 미니 섹터 크기 시프트: \" + miniSectorSizeShift)\n const miniSectorSize = 1 << miniSectorSizeShift // 보통 64\n\n const fatSectorCount = data.readUInt32LE(44)\n if (fatSectorCount > 10000) throw new Error(\"FAT 섹터 수가 너무 많습니다: \" + fatSectorCount)\n const firstDirSector = data.readUInt32LE(48)\n const miniStreamCutoff = data.readUInt32LE(56) // 보통 4096\n const firstMiniFatSector = data.readUInt32LE(60)\n const miniFatSectorCount = data.readUInt32LE(64)\n const firstDifatSector = data.readUInt32LE(68)\n const difatSectorCount = data.readUInt32LE(72)\n\n // ── 유틸 ──\n\n function sectorOffset(id: number): number {\n return 512 + id * sectorSize\n }\n\n function readSectorData(id: number): Buffer {\n const off = sectorOffset(id)\n if (off + sectorSize > data.length) return Buffer.alloc(0)\n return data.subarray(off, off + sectorSize)\n }\n\n // ── DIFAT → FAT 섹터 목록 ──\n\n const fatSectors: number[] = []\n\n // 헤더 내 DIFAT (최대 109개)\n for (let i = 0; i < 109 && fatSectors.length < fatSectorCount; i++) {\n const sid = data.readUInt32LE(76 + i * 4)\n if (sid === FREE_SECT || sid === END_OF_CHAIN) break\n fatSectors.push(sid)\n }\n\n // 추가 DIFAT 섹터 체인\n let difatSector = firstDifatSector\n const visitedDifat = new Set<number>()\n for (let d = 0; d < difatSectorCount && difatSector !== END_OF_CHAIN && difatSector !== FREE_SECT; d++) {\n if (visitedDifat.has(difatSector)) break\n visitedDifat.add(difatSector)\n\n const buf = readSectorData(difatSector)\n const entriesPerSector = (sectorSize / 4) - 1 // 마지막 4바이트는 다음 DIFAT 포인터\n for (let i = 0; i < entriesPerSector && fatSectors.length < fatSectorCount; i++) {\n const sid = buf.readUInt32LE(i * 4)\n if (sid === FREE_SECT || sid === END_OF_CHAIN) continue\n fatSectors.push(sid)\n }\n difatSector = buf.readUInt32LE(entriesPerSector * 4)\n }\n\n // ── FAT 테이블 구축 ──\n\n const entriesPerFatSector = sectorSize / 4\n const fatTable = new Uint32Array(fatSectors.length * entriesPerFatSector)\n\n for (let fi = 0; fi < fatSectors.length; fi++) {\n const buf = readSectorData(fatSectors[fi])\n for (let i = 0; i < entriesPerFatSector; i++) {\n fatTable[fi * entriesPerFatSector + i] = i * 4 + 3 < buf.length\n ? buf.readUInt32LE(i * 4)\n : FREE_SECT\n }\n }\n\n // ── 체인 리더 (순환 방지) ──\n\n function readChain(startSector: number, maxBytes: number): Buffer {\n if (startSector === END_OF_CHAIN || startSector === FREE_SECT) return Buffer.alloc(0)\n if (maxBytes > MAX_STREAM_SIZE) throw new Error(\"스트림이 너무 큽니다\")\n\n const chunks: Buffer[] = []\n let current = startSector\n let totalRead = 0\n const visited = new Set<number>()\n\n while (current !== END_OF_CHAIN && current !== FREE_SECT && totalRead < maxBytes) {\n if (visited.has(current)) break // 순환 감지\n if (visited.size > MAX_CHAIN_LENGTH) break\n visited.add(current)\n\n const buf = readSectorData(current)\n const remaining = maxBytes - totalRead\n chunks.push(remaining < sectorSize ? buf.subarray(0, remaining) : buf)\n totalRead += Math.min(buf.length, remaining)\n\n current = current < fatTable.length ? fatTable[current] : END_OF_CHAIN\n }\n\n return Buffer.concat(chunks)\n }\n\n // ── Mini-FAT 테이블 ──\n\n let miniFatTable: Uint32Array | null = null\n\n function getMiniFatTable(): Uint32Array {\n if (miniFatTable) return miniFatTable\n\n if (miniFatSectorCount === 0 || firstMiniFatSector === END_OF_CHAIN) {\n miniFatTable = new Uint32Array(0)\n return miniFatTable\n }\n\n const miniFatData = readChain(firstMiniFatSector, miniFatSectorCount * sectorSize)\n const entries = miniFatData.length / 4\n miniFatTable = new Uint32Array(entries)\n for (let i = 0; i < entries; i++) {\n miniFatTable[i] = miniFatData.readUInt32LE(i * 4)\n }\n return miniFatTable\n }\n\n // ── 디렉토리 엔트리 파싱 ──\n\n const dirData = readChain(firstDirSector, MAX_DIR_ENTRIES * 128)\n const dirEntries: DirEntry[] = []\n\n for (let offset = 0; offset + 128 <= dirData.length && dirEntries.length < MAX_DIR_ENTRIES; offset += 128) {\n const nameLen = dirData.readUInt16LE(offset + 64) // 바이트 수 (null 포함)\n if (nameLen <= 0 || nameLen > 64) {\n dirEntries.push({ name: \"\", type: 0, startSector: 0, size: 0 })\n continue\n }\n\n const nameBytes = nameLen - 2 // null terminator 제외\n const name = nameBytes > 0\n ? dirData.subarray(offset, offset + nameBytes).toString(\"utf16le\")\n : \"\"\n\n const type = dirData[offset + 66]\n const startSector = dirData.readUInt32LE(offset + 116)\n // CFBv3에서는 size가 u32 (offset 120), v4에서는 u64\n const size = dirData.readUInt32LE(offset + 120)\n\n dirEntries.push({ name, type, startSector, size })\n }\n\n // ── Root 엔트리에서 미니 스트림 추출 ──\n\n let miniStreamData: Buffer | null = null\n\n function getMiniStream(): Buffer {\n if (miniStreamData) return miniStreamData\n const root = dirEntries[0]\n if (!root || root.type !== 5) {\n miniStreamData = Buffer.alloc(0)\n return miniStreamData\n }\n miniStreamData = readChain(root.startSector, root.size || MAX_STREAM_SIZE)\n return miniStreamData\n }\n\n // ── 미니 스트림에서 읽기 ──\n\n function readMiniStream(startSector: number, size: number): Buffer {\n const mft = getMiniFatTable()\n const ms = getMiniStream()\n if (mft.length === 0 || ms.length === 0) return Buffer.alloc(0)\n\n const chunks: Buffer[] = []\n let current = startSector\n let totalRead = 0\n const visited = new Set<number>()\n\n while (current !== END_OF_CHAIN && current !== FREE_SECT && totalRead < size) {\n if (visited.has(current)) break\n if (visited.size > MAX_CHAIN_LENGTH) break\n visited.add(current)\n\n const off = current * miniSectorSize\n const remaining = size - totalRead\n const chunkSize = Math.min(miniSectorSize, remaining)\n if (off + chunkSize <= ms.length) {\n chunks.push(ms.subarray(off, off + chunkSize))\n }\n totalRead += chunkSize\n\n current = current < mft.length ? mft[current] : END_OF_CHAIN\n }\n\n return Buffer.concat(chunks)\n }\n\n // ── 스트림 읽기 (일반/미니 자동 분기) ──\n\n function readStreamData(entry: DirEntry): Buffer {\n if (entry.size === 0) return Buffer.alloc(0)\n if (entry.size < miniStreamCutoff) {\n const miniResult = readMiniStream(entry.startSector, entry.size)\n // 미니스트림이 비어있으면 일반 체인으로 폴백 (lenient)\n if (miniResult.length > 0) return miniResult\n }\n return readChain(entry.startSector, entry.size)\n }\n\n // ── 경로 기반 탐색 ──\n\n // 전체 경로 맵 구축 (간이: 이름 기반 flat lookup)\n // HWP 파일의 디렉토리 구조는 보통 1~2 depth이므로 이름 매칭으로 충분\n function findEntryByPath(path: string): DirEntry | null {\n // \"/FileHeader\" → \"FileHeader\"\n // \"/BodyText/Section0\" → path component matching\n const parts = path.replace(/^\\//, \"\").split(\"/\")\n\n if (parts.length === 1) {\n // 단일 이름 매칭\n return dirEntries.find(e => e.name === parts[0] && e.type === 2) ?? null\n }\n\n // 2-depth: storage/stream\n // HWP 구조: Root/BodyText/Section0, Root/DocInfo, Root/BinData/BIN0001 등\n const storageName = parts[0]\n const streamName = parts.slice(1).join(\"/\")\n\n // 디렉토리 구조 대신 이름 패턴으로 찾기 (lenient)\n for (const e of dirEntries) {\n if (e.type === 2 && e.name === streamName) {\n // 부모 확인은 생략 (lenient) — 중복 이름 시 첫 번째 반환\n return e\n }\n }\n\n // 정확한 이름이 아닌 경우 (ViewText/Section0 등)\n const lastPart = parts[parts.length - 1]\n return dirEntries.find(e => e.type === 2 && e.name === lastPart) ?? null\n }\n\n // ── 공개 API ──\n\n return {\n findStream(path: string): Buffer | null {\n // \\005 prefix 처리 (SummaryInformation)\n const normalized = path.replace(/^\\//, \"\")\n const entry = findEntryByPath(normalized)\n if (!entry || entry.type !== 2) return null\n const stream = readStreamData(entry)\n return stream.length > 0 ? stream : null\n },\n\n entries(): DirEntry[] {\n return dirEntries.filter(e => e.type === 2) // stream만\n },\n }\n}\n","/** HWP 5.x 바이너리 파서 — OLE2 컨테이너 → 섹션 → Markdown */\r\n\r\nimport {\r\n readRecords, decompressStream, parseFileHeader, extractText, parseDocInfo,\r\n TAG_PARA_HEADER, TAG_PARA_TEXT, TAG_CHAR_SHAPE, TAG_CTRL_HEADER, TAG_LIST_HEADER, TAG_TABLE,\r\n FLAG_COMPRESSED, FLAG_ENCRYPTED, FLAG_DISTRIBUTION, FLAG_DRM,\r\n type HwpRecord, type HwpDocInfo, type HwpCharShape, type HwpParaShape,\r\n} from \"./record.js\"\r\nimport { decryptViewText } from \"./crypto.js\"\r\nimport { parseLenientCfb, type LenientCfbContainer } from \"./cfb-lenient.js\"\r\nimport { buildTable, blocksToMarkdown, flattenLayoutTables, MAX_COLS, MAX_ROWS } from \"../table/builder.js\"\r\nimport type { CellContext, IRBlock, IRTable, DocumentMetadata, InternalParseResult, ParseOptions, ParseWarning, OutlineItem, InlineStyle, ExtractedImage } from \"../types.js\"\r\nimport { HEADING_RATIO_H1, HEADING_RATIO_H2, HEADING_RATIO_H3 } from \"../types.js\"\r\nimport { KordocError, sanitizeHref } from \"../utils.js\"\r\nimport { parsePageRange } from \"../page-range.js\"\r\n\r\nimport { createRequire } from \"module\"\r\nconst require = createRequire(import.meta.url)\r\nconst CFB: CfbModule = require(\"cfb\")\r\n\r\ninterface CfbEntry { name?: string; content?: Buffer | Uint8Array }\r\ninterface CfbContainer { FileIndex?: CfbEntry[] }\r\ninterface CfbModule {\r\n parse(data: Buffer): CfbContainer\r\n find(cfb: CfbContainer, path: string): CfbEntry | null\r\n}\r\n\r\n/** 최대 섹션 수 — 비정상 파일에 의한 무한 루프 방지 */\r\nconst MAX_SECTIONS = 100\r\n/** 누적 압축 해제 최대 크기 (100MB) */\r\nconst MAX_TOTAL_DECOMPRESS = 100 * 1024 * 1024\r\n\r\nexport function parseHwp5Document(buffer: Buffer, options?: ParseOptions): InternalParseResult {\r\n // CFB 파싱: strict 먼저, 실패 시 lenient 폴백\r\n let cfb: CfbContainer | null = null\r\n let lenientCfb: LenientCfbContainer | null = null\r\n const warnings: ParseWarning[] = []\r\n\r\n try {\r\n cfb = CFB.parse(buffer)\r\n } catch {\r\n try {\r\n lenientCfb = parseLenientCfb(buffer)\r\n warnings.push({ message: \"손상된 CFB 컨테이너 — lenient 모드로 복구\", code: \"LENIENT_CFB_RECOVERY\" })\r\n } catch {\r\n throw new KordocError(\"CFB 컨테이너 파싱 실패 (strict 및 lenient 모두)\")\r\n }\r\n }\r\n\r\n // CFB 래퍼: strict/lenient 통합 인터페이스\r\n const findStream = (path: string): Buffer | null => {\r\n if (cfb) {\r\n const entry = CFB.find(cfb, path)\r\n return entry?.content ? Buffer.from(entry.content) : null\r\n }\r\n return lenientCfb!.findStream(path)\r\n }\r\n\r\n const headerData = findStream(\"/FileHeader\")\r\n if (!headerData) throw new KordocError(\"FileHeader 스트림 없음\")\r\n const header = parseFileHeader(headerData)\r\n if (header.signature !== \"HWP Document File\") throw new KordocError(\"HWP 시그니처 불일치\")\r\n if (header.flags & FLAG_ENCRYPTED) throw new KordocError(\"암호화된 HWP는 지원하지 않습니다\")\r\n if (header.flags & FLAG_DRM) throw new KordocError(\"DRM 보호된 HWP는 지원하지 않습니다\")\r\n const compressed = (header.flags & FLAG_COMPRESSED) !== 0\r\n const distribution = (header.flags & FLAG_DISTRIBUTION) !== 0\r\n\r\n const metadata: DocumentMetadata = {\r\n version: `${header.versionMajor}.x`,\r\n }\r\n if (cfb) extractHwp5Metadata(cfb, metadata)\r\n\r\n // DocInfo 파싱 (스타일 정보 추출)\r\n const docInfo = cfb\r\n ? parseDocInfoStream(cfb, compressed)\r\n : parseDocInfoFromStream(findStream(\"/DocInfo\"), compressed)\r\n\r\n const sections = distribution\r\n ? (cfb ? findViewTextSections(cfb, compressed) : findViewTextSectionsLenient(lenientCfb!, compressed))\r\n : (cfb ? findSections(cfb) : findSectionsLenient(lenientCfb!, compressed))\r\n if (sections.length === 0) throw new KordocError(\"섹션 스트림을 찾을 수 없습니다\")\r\n\r\n metadata.pageCount = sections.length\r\n\r\n // 페이지 범위 필터링 (섹션 단위 근사치)\r\n const pageFilter = options?.pages ? parsePageRange(options.pages, sections.length) : null\r\n const totalTarget = pageFilter ? pageFilter.size : sections.length\r\n\r\n const blocks: IRBlock[] = []\r\n const nestedTableCounter = { count: 0 }\r\n let totalDecompressed = 0\r\n let parsedSections = 0\r\n for (let si = 0; si < sections.length; si++) {\r\n if (pageFilter && !pageFilter.has(si + 1)) continue\r\n try {\r\n const sectionData = sections[si]\r\n // 배포용 문서는 findViewTextSections에서 이미 복호화+압축해제 완료\r\n const data = (!distribution && compressed) ? decompressStream(Buffer.from(sectionData)) : Buffer.from(sectionData)\r\n totalDecompressed += data.length\r\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\r\n const records = readRecords(data)\r\n const sectionBlocks = parseSection(records, docInfo, warnings, si + 1, nestedTableCounter)\r\n blocks.push(...sectionBlocks)\r\n parsedSections++\r\n options?.onProgress?.(parsedSections, totalTarget)\r\n } catch (secErr) {\r\n if (secErr instanceof KordocError) throw secErr\r\n warnings.push({ page: si + 1, message: `섹션 ${si + 1} 파싱 실패: ${secErr instanceof Error ? secErr.message : \"알 수 없는 오류\"}`, code: \"PARTIAL_PARSE\" })\r\n }\r\n }\r\n\r\n // BinData에서 이미지 추출\r\n const images = cfb\r\n ? extractHwp5Images(cfb, blocks, compressed, warnings)\r\n : extractHwp5ImagesLenient(lenientCfb!, blocks, compressed, warnings)\r\n\r\n // 레이아웃 테이블 해체 (heading 감지 전에 수행하여 해체된 텍스트도 heading 감지 대상)\r\n const flatBlocks = flattenLayoutTables(blocks)\r\n\r\n // 스타일 기반 헤딩 감지\r\n if (docInfo) {\r\n detectHwp5Headings(flatBlocks, docInfo)\r\n }\r\n\r\n // outline 구축\r\n const outline: OutlineItem[] = flatBlocks\r\n .filter(b => b.type === \"heading\" && b.level && b.text)\r\n .map(b => ({ level: b.level!, text: b.text!, pageNumber: b.pageNumber }))\r\n\r\n const markdown = blocksToMarkdown(flatBlocks)\r\n return { markdown, blocks: flatBlocks, metadata, outline: outline.length > 0 ? outline : undefined, warnings: warnings.length > 0 ? warnings : undefined, images: images.length > 0 ? images : undefined }\r\n}\r\n\r\n/** DocInfo 스트림 파싱 (best-effort) */\r\nfunction parseDocInfoStream(cfb: CfbContainer, compressed: boolean): HwpDocInfo | null {\r\n try {\r\n const entry = CFB.find(cfb, \"/DocInfo\")\r\n if (!entry?.content) return null\r\n const data = compressed ? decompressStream(Buffer.from(entry.content)) : Buffer.from(entry.content)\r\n const records = readRecords(data)\r\n return parseDocInfo(records)\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/** DocInfo — Buffer에서 직접 파싱 (lenient용) */\r\nfunction parseDocInfoFromStream(raw: Buffer | null, compressed: boolean): HwpDocInfo | null {\r\n if (!raw) return null\r\n try {\r\n const data = compressed ? decompressStream(raw) : raw\r\n return parseDocInfo(readRecords(data))\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/** 스타일 기반 헤딩 감지 — 큰 폰트 + 짧은 텍스트 → heading */\r\nfunction detectHwp5Headings(blocks: IRBlock[], docInfo: HwpDocInfo): void {\r\n // 기본 폰트 크기 결정 (본문 스타일 또는 가장 많이 사용되는 크기)\r\n let baseFontSize = 0\r\n\r\n // \"바탕글\", \"본문\" 등 본문 스타일 찾기\r\n for (const style of docInfo.styles) {\r\n const name = (style.nameKo || style.name).toLowerCase()\r\n if (name.includes(\"바탕\") || name.includes(\"본문\") || name === \"normal\" || name === \"body\") {\r\n const cs = docInfo.charShapes[style.charShapeId]\r\n // cs.fontSize는 0.1pt 단위 → pt로 변환 (블록의 style.fontSize와 동일 단위)\r\n if (cs?.fontSize > 0) { baseFontSize = cs.fontSize / 10; break }\r\n }\r\n }\r\n\r\n // 본문 스타일 못 찾으면 블록의 폰트 크기 중 최빈값 사용\r\n if (baseFontSize === 0) {\r\n const sizeFreq = new Map<number, number>()\r\n for (const b of blocks) {\r\n if (b.style?.fontSize) {\r\n sizeFreq.set(b.style.fontSize, (sizeFreq.get(b.style.fontSize) || 0) + 1)\r\n }\r\n }\r\n let maxCount = 0\r\n for (const [size, count] of sizeFreq) {\r\n if (count > maxCount) { maxCount = count; baseFontSize = size }\r\n }\r\n }\r\n\r\n if (baseFontSize <= 0) return\r\n\r\n for (const block of blocks) {\r\n // 개요 수준(outlineLevel)으로 이미 heading이 된 블록은 스킵\r\n if (block.type === \"heading\") continue\r\n if (block.type !== \"paragraph\" || !block.text) continue\r\n const text = block.text.trim()\r\n if (text.length === 0 || text.length > 200) continue\r\n if (/^\\d+$/.test(text)) continue\r\n\r\n let level = 0\r\n\r\n // 폰트 크기 비율 기반 헤딩 감지 (스타일 정보가 있을 때만)\r\n if (block.style?.fontSize && baseFontSize > 0) {\r\n const ratio = block.style.fontSize / baseFontSize\r\n if (ratio >= HEADING_RATIO_H1) level = 1\r\n else if (ratio >= HEADING_RATIO_H2) level = 2\r\n else if (ratio >= HEADING_RATIO_H3) level = 3\r\n }\r\n\r\n // \"제N장/절/편\" 패턴 → H2, \"제N조\" 패턴 → H3 (스타일 유무 무관)\r\n if (/^제\\d+[장절편]\\s/.test(text) && text.length <= 50) {\r\n if (level === 0) level = 2\r\n } else if (/^제\\d+(조의?\\d*)\\s*[\\((]/.test(text) && text.length <= 80) {\r\n if (level === 0) level = 3\r\n }\r\n\r\n if (level > 0) {\r\n block.type = \"heading\"\r\n block.level = level\r\n }\r\n }\r\n}\r\n\r\n// ─── 메타데이터 추출 (best-effort) ───────────────────\r\n\r\n/**\r\n * OLE2 SummaryInformation 스트림에서 제목/작성자 추출.\r\n * HWP5는 \\005HwpSummaryInformation 또는 \\005SummaryInformation에 저장.\r\n * OLE2 Property Set 포맷의 간이 파싱 — 실패 시 조용히 무시.\r\n */\r\nfunction extractHwp5Metadata(cfb: CfbContainer, metadata: DocumentMetadata): void {\r\n try {\r\n // HWP 전용 SummaryInformation 먼저, 없으면 표준 OLE2\r\n const summaryEntry =\r\n CFB.find(cfb, \"/\\x05HwpSummaryInformation\") ||\r\n CFB.find(cfb, \"/\\x05SummaryInformation\")\r\n if (!summaryEntry?.content) return\r\n\r\n const data = Buffer.from(summaryEntry.content)\r\n if (data.length < 48) return\r\n\r\n // OLE2 Property Set Header: byte order(2) + version(2) + OS(4) + CLSID(16) + numSets(4) = 28\r\n // Then FMTID(16) + offset(4)\r\n const numSets = data.readUInt32LE(24)\r\n if (numSets === 0) return\r\n\r\n const setOffset = data.readUInt32LE(44)\r\n if (setOffset >= data.length - 8) return\r\n\r\n // Property Set: size(4) + numProperties(4) + [propertyId(4) + offset(4)] * N\r\n const numProps = data.readUInt32LE(setOffset + 4)\r\n if (numProps === 0 || numProps > 100) return\r\n\r\n for (let i = 0; i < numProps; i++) {\r\n const entryOffset = setOffset + 8 + i * 8\r\n if (entryOffset + 8 > data.length) break\r\n\r\n const propId = data.readUInt32LE(entryOffset)\r\n const propOffset = setOffset + data.readUInt32LE(entryOffset + 4)\r\n if (propOffset + 8 > data.length) continue\r\n\r\n // Property ID: 2=Title, 4=Author, 6=Subject/Description\r\n if (propId !== 2 && propId !== 4 && propId !== 6) continue\r\n\r\n const propType = data.readUInt32LE(propOffset)\r\n // Type 0x1E = VT_LPSTR (ANSI string)\r\n if (propType !== 0x1e) continue\r\n\r\n const strLen = data.readUInt32LE(propOffset + 4)\r\n if (strLen === 0 || strLen > 10000 || propOffset + 8 + strLen > data.length) continue\r\n\r\n const str = data.subarray(propOffset + 8, propOffset + 8 + strLen).toString(\"utf8\").replace(/\\0+$/, \"\").trim()\r\n if (!str) continue\r\n\r\n if (propId === 2) metadata.title = str\r\n else if (propId === 4) metadata.author = str\r\n else if (propId === 6) metadata.description = str\r\n }\r\n } catch {\r\n // best-effort — 실패 시 조용히 무시\r\n }\r\n}\r\n\r\n/** 메타데이터만 추출 (전체 파싱 없이) — MCP parse_metadata용 */\r\nexport function extractHwp5MetadataOnly(buffer: Buffer): DocumentMetadata {\r\n const cfb = CFB.parse(buffer)\r\n const headerEntry = CFB.find(cfb, \"/FileHeader\")\r\n if (!headerEntry?.content) throw new KordocError(\"FileHeader 스트림 없음\")\r\n const header = parseFileHeader(Buffer.from(headerEntry.content))\r\n if (header.signature !== \"HWP Document File\") throw new KordocError(\"HWP 시그니처 불일치\")\r\n\r\n const metadata: DocumentMetadata = {\r\n version: `${header.versionMajor}.x`,\r\n }\r\n extractHwp5Metadata(cfb, metadata)\r\n\r\n const sections = findSections(cfb)\r\n metadata.pageCount = sections.length\r\n\r\n return metadata\r\n}\r\n\r\n/** 배포용 문서: ViewText/Section{N} 스트림을 복호화하여 반환 */\r\nfunction findViewTextSections(cfb: CfbContainer, compressed: boolean): Buffer[] {\r\n const sections: Array<{ idx: number; content: Buffer }> = []\r\n\r\n for (let i = 0; i < MAX_SECTIONS; i++) {\r\n const entry = CFB.find(cfb, `/ViewText/Section${i}`)\r\n if (!entry?.content) break\r\n try {\r\n const decrypted = decryptViewText(Buffer.from(entry.content), compressed)\r\n sections.push({ idx: i, content: decrypted })\r\n } catch {\r\n // 복호화 실패 시 해당 섹션 스킵\r\n break\r\n }\r\n }\r\n\r\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\r\n}\r\n\r\nfunction findSections(cfb: CfbContainer): Buffer[] {\r\n const sections: Array<{ idx: number; content: Buffer }> = []\r\n\r\n for (let i = 0; i < MAX_SECTIONS; i++) {\r\n const entry = CFB.find(cfb, `/BodyText/Section${i}`)\r\n if (!entry?.content) break\r\n sections.push({ idx: i, content: Buffer.from(entry.content) })\r\n }\r\n\r\n if (sections.length === 0 && cfb.FileIndex) {\r\n for (const entry of cfb.FileIndex) {\r\n if (sections.length >= MAX_SECTIONS) break\r\n if (entry.name?.startsWith(\"Section\") && entry.content) {\r\n const idx = parseInt(entry.name.replace(\"Section\", \"\"), 10) || 0\r\n sections.push({ idx, content: Buffer.from(entry.content) })\r\n }\r\n }\r\n }\r\n\r\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\r\n}\r\n\r\n/** Lenient CFB: BodyText/Section{N} 탐색 — 누적 압축해제 크기 추적 */\r\nfunction findSectionsLenient(lcfb: LenientCfbContainer, compressed: boolean): Buffer[] {\r\n const sections: Array<{ idx: number; content: Buffer }> = []\r\n let totalDecompressed = 0\r\n for (let i = 0; i < MAX_SECTIONS; i++) {\r\n const raw = lcfb.findStream(`/BodyText/Section${i}`) ?? lcfb.findStream(`Section${i}`)\r\n if (!raw) break\r\n const content = compressed ? decompressStream(raw) : raw\r\n totalDecompressed += content.length\r\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\r\n sections.push({ idx: i, content })\r\n }\r\n if (sections.length === 0) {\r\n // fallback: 이름에 \"Section\" 포함된 스트림\r\n for (const e of lcfb.entries()) {\r\n if (sections.length >= MAX_SECTIONS) break\r\n if (e.name.startsWith(\"Section\")) {\r\n const idx = parseInt(e.name.replace(\"Section\", \"\"), 10) || 0\r\n const raw = lcfb.findStream(e.name)\r\n if (raw) {\r\n const content = compressed ? decompressStream(raw) : raw\r\n totalDecompressed += content.length\r\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\r\n sections.push({ idx, content })\r\n }\r\n }\r\n }\r\n }\r\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\r\n}\r\n\r\n/** Lenient CFB: ViewText/Section{N} 복호화 — 누적 크기 추적 */\r\nfunction findViewTextSectionsLenient(lcfb: LenientCfbContainer, compressed: boolean): Buffer[] {\r\n const sections: Array<{ idx: number; content: Buffer }> = []\r\n let totalDecompressed = 0\r\n for (let i = 0; i < MAX_SECTIONS; i++) {\r\n const raw = lcfb.findStream(`/ViewText/Section${i}`) ?? lcfb.findStream(`Section${i}`)\r\n if (!raw) break\r\n try {\r\n const content = decryptViewText(raw, compressed)\r\n totalDecompressed += content.length\r\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\r\n sections.push({ idx: i, content })\r\n } catch { break }\r\n }\r\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\r\n}\r\n\r\n// ─── BinData ���미지 추출 ─────��─────────────────��────\r\n\r\n/** SHAPE_COMPONENT 태그 — HWP5 스펙 */\r\nconst TAG_SHAPE_COMPONENT = 0x004a\r\n\r\n/** gso 제어 뒤의 하위 레코드에서 binDataId 추출 (best-effort) */\r\nfunction extractBinDataId(records: HwpRecord[], ctrlIdx: number): number {\r\n const ctrlLevel = records[ctrlIdx].level\r\n // CTRL_HEADER 이후의 하위 레코드들을 순회\r\n for (let j = ctrlIdx + 1; j < records.length && j < ctrlIdx + 50; j++) {\r\n const r = records[j]\r\n if (r.level <= ctrlLevel) break // 같은/상위 레벨이면 이 제어 블록 끝\r\n // SHAPE_COMPONENT에서 picture 타입이면 binDataId 추출\r\n // picture 데이터는 SHAPE_COMPONENT 뒤에 오는 하위 레코드에 있음\r\n // HWP5에서 그림 정보는 level이 높은 하위 레코드에 binDataId가 uint16LE로 저장\r\n if (r.data.length >= 2) {\r\n // 매직바이트로 이미지인지 확인하는 대신, SHAPE_COMPONENT 뒤의 하위 레코드에서 binDataId를 읽음\r\n // HWP5 picture 구조: CTRL_HEADER(gso) → LIST_HEADER → SHAPE_COMPONENT → [picture data record]\r\n // picture data record에서 offset 0부터 uint16LE = binDataId\r\n if (r.tagId > TAG_SHAPE_COMPONENT && r.level > ctrlLevel + 1 && r.data.length >= 4) {\r\n const possibleId = r.data.readUInt16LE(0)\r\n if (possibleId < 10000) return possibleId // 합리적 범위\r\n }\r\n }\r\n }\r\n return -1\r\n}\r\n\r\n/** MIME 타입 매직바이트 판별 */\r\nfunction detectImageMime(data: Buffer | Uint8Array): string | null {\r\n if (data.length < 4) return null\r\n if (data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47) return \"image/png\"\r\n if (data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) return \"image/jpeg\"\r\n if (data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46) return \"image/gif\"\r\n if (data[0] === 0x42 && data[1] === 0x4d) return \"image/bmp\"\r\n if (data[0] === 0xd7 && data[1] === 0xcd && data[2] === 0xc6 && data[3] === 0x9a) return \"image/wmf\"\r\n if (data[0] === 0x01 && data[1] === 0x00 && data[2] === 0x00 && data[3] === 0x00) return \"image/emf\"\r\n return null\r\n}\r\n\r\n/** OLE2 BinData 스토리지에서 이미지 추출, blocks의 image 블록과 매핑 */\r\nfunction extractHwp5Images(\r\n cfb: CfbContainer,\r\n blocks: IRBlock[],\r\n compressed: boolean,\r\n warnings: ParseWarning[],\r\n): ExtractedImage[] {\r\n // BinData 스토리지의 모든 파일을 FileIndex 순회로 수집 (O(n), 기존 O(20000) CFB.find 제거)\r\n const binDataMap = new Map<number, { data: Buffer; name: string }>()\r\n const binDataRe = /\\/BinData\\/[Bb][Ii][Nn](\\d{4})$/\r\n if (cfb.FileIndex) {\r\n for (const entry of cfb.FileIndex) {\r\n if (!entry?.name || !entry.content) continue\r\n const match = entry.name.match(binDataRe)\r\n if (!match) continue\r\n const idx = parseInt(match[1], 10)\r\n let data = Buffer.from(entry.content)\r\n if (compressed) {\r\n try { data = decompressStream(data) } catch { /* 이미 비압축일 수 있음 */ }\r\n }\r\n binDataMap.set(idx, { data, name: entry.name })\r\n }\r\n }\r\n\r\n if (binDataMap.size === 0) return []\r\n\r\n const images: ExtractedImage[] = []\r\n let imageIndex = 0\r\n\r\n for (const block of blocks) {\r\n if (block.type !== \"image\" || !block.text) continue\r\n const binId = parseInt(block.text, 10)\r\n if (isNaN(binId)) continue\r\n\r\n const bin = binDataMap.get(binId)\r\n if (!bin) {\r\n warnings.push({ page: block.pageNumber, message: `BinData ${binId} 없음`, code: \"SKIPPED_IMAGE\" })\r\n block.type = \"paragraph\"\r\n block.text = `[이미지: BinData ${binId}]`\r\n continue\r\n }\r\n\r\n const mime = detectImageMime(bin.data)\r\n if (!mime) {\r\n warnings.push({ page: block.pageNumber, message: `BinData ${binId}: 알 수 없는 이미지 형식`, code: \"SKIPPED_IMAGE\" })\r\n block.type = \"paragraph\"\r\n block.text = `[이미지: ${bin.name}]`\r\n continue\r\n }\r\n\r\n imageIndex++\r\n const ext = mime.includes(\"jpeg\") ? \"jpg\" : mime.includes(\"png\") ? \"png\" : mime.includes(\"gif\") ? \"gif\" : mime.includes(\"bmp\") ? \"bmp\" : \"bin\"\r\n const filename = `image_${String(imageIndex).padStart(3, \"0\")}.${ext}`\r\n\r\n images.push({ filename, data: new Uint8Array(bin.data), mimeType: mime })\r\n block.text = filename\r\n block.imageData = { data: new Uint8Array(bin.data), mimeType: mime, filename: bin.name }\r\n }\r\n\r\n return images\r\n}\r\n\r\n/** Lenient CFB: BinData 이미지 추출 */\r\nfunction extractHwp5ImagesLenient(\r\n lcfb: LenientCfbContainer,\r\n blocks: IRBlock[],\r\n compressed: boolean,\r\n warnings: ParseWarning[],\r\n): ExtractedImage[] {\r\n // BinData 엔트리 수집\r\n const binDataMap = new Map<number, { data: Buffer; name: string }>()\r\n const binRe = /^BIN(\\d{4})/i\r\n for (const e of lcfb.entries()) {\r\n const match = e.name.match(binRe)\r\n if (!match) continue\r\n const idx = parseInt(match[1], 10)\r\n let raw = lcfb.findStream(e.name)\r\n if (!raw) continue\r\n if (compressed) {\r\n try { raw = decompressStream(raw) } catch { /* 이미 비압축일 수 있음 */ }\r\n }\r\n binDataMap.set(idx, { data: raw, name: e.name })\r\n }\r\n if (binDataMap.size === 0) return []\r\n\r\n const images: ExtractedImage[] = []\r\n let imageIndex = 0\r\n for (const block of blocks) {\r\n if (block.type !== \"image\" || !block.text) continue\r\n const binId = parseInt(block.text, 10)\r\n if (isNaN(binId)) continue\r\n const bin = binDataMap.get(binId)\r\n if (!bin) {\r\n warnings.push({ page: block.pageNumber, message: `BinData ${binId} ���음`, code: \"SKIPPED_IMAGE\" })\r\n block.type = \"paragraph\"; block.text = `[이미지: BinData ${binId}]`; continue\r\n }\r\n const mime = detectImageMime(bin.data)\r\n if (!mime) {\r\n warnings.push({ page: block.pageNumber, message: `BinData ${binId}: 알 수 없는 이미지 형식`, code: \"SKIPPED_IMAGE\" })\r\n block.type = \"paragraph\"; block.text = `[이미지: ${bin.name}]`; continue\r\n }\r\n imageIndex++\r\n const ext = mime.includes(\"jpeg\") ? \"jpg\" : mime.includes(\"png\") ? \"png\" : mime.includes(\"gif\") ? \"gif\" : mime.includes(\"bmp\") ? \"bmp\" : \"bin\"\r\n const filename = `image_${String(imageIndex).padStart(3, \"0\")}.${ext}`\r\n images.push({ filename, data: new Uint8Array(bin.data), mimeType: mime })\r\n block.text = filename\r\n block.imageData = { data: new Uint8Array(bin.data), mimeType: mime, filename: bin.name }\r\n }\r\n return images\r\n}\r\n\r\nfunction parseSection(records: HwpRecord[], docInfo: HwpDocInfo | null, warnings: ParseWarning[], sectionNum: number, counter?: { count: number }): IRBlock[] {\r\n const blocks: IRBlock[] = []\r\n let i = 0\r\n\r\n while (i < records.length) {\r\n const rec = records[i]\r\n\r\n if (rec.tagId === TAG_PARA_HEADER && rec.level === 0) {\r\n const { paragraph, tables, nextIdx, charShapeIds, paraShapeId } = parseParagraphWithTables(records, i, counter)\r\n if (paragraph) {\r\n const block: IRBlock = { type: \"paragraph\", text: paragraph, pageNumber: sectionNum }\r\n // CHAR_SHAPE 기반 스타일 정보 추가\r\n if (docInfo && charShapeIds.length > 0) {\r\n const style = resolveCharStyle(charShapeIds, docInfo)\r\n if (style) block.style = style\r\n }\r\n // PARA_SHAPE 개요 수준으로 heading 즉시 설정\r\n if (docInfo && paraShapeId >= 0 && paraShapeId < docInfo.paraShapes.length) {\r\n const ol = docInfo.paraShapes[paraShapeId].outlineLevel\r\n if (ol >= 1 && ol <= 6) {\r\n block.type = \"heading\"\r\n block.level = ol\r\n }\r\n }\r\n blocks.push(block)\r\n }\r\n for (const t of tables) blocks.push({ type: \"table\", table: t, pageNumber: sectionNum })\r\n i = nextIdx\r\n continue\r\n }\r\n\r\n if (rec.tagId === TAG_CTRL_HEADER && rec.level <= 1 && rec.data.length >= 4) {\r\n const ctrlId = rec.data.subarray(0, 4).toString(\"ascii\")\r\n if (ctrlId === \" lbt\" || ctrlId === \"tbl \") {\r\n const { table, nextIdx } = parseTableBlock(records, i, counter)\r\n if (table) blocks.push({ type: \"table\", table, pageNumber: sectionNum })\r\n i = nextIdx\r\n continue\r\n }\r\n // 그리기 객체(gso) — 이미지 또는 글상자\r\n if (ctrlId === \"gso \" || ctrlId === \" osg\") {\r\n const binId = extractBinDataId(records, i)\r\n if (binId >= 0) {\r\n blocks.push({ type: \"image\", text: String(binId), pageNumber: sectionNum })\r\n } else {\r\n // 이미지가 아니면 글상자(TextBox) 텍스트 추출 시도\r\n const boxText = extractTextBoxText(records, i)\r\n if (boxText) {\r\n blocks.push({ type: \"paragraph\", text: boxText, pageNumber: sectionNum })\r\n }\r\n // 텍스트도 없으면 조용히 스킵 (장식용 도형)\r\n }\r\n } else if (ctrlId === \" elo\" || ctrlId === \"ole \") {\r\n warnings.push({ page: sectionNum, message: `스킵된 제어 요소: ${ctrlId.trim()}`, code: \"SKIPPED_IMAGE\" })\r\n }\r\n // 각주/미주 — CTRL_HEADER 아래의 텍스트를 추출하여 footnoteText로 연결\r\n else if (ctrlId === \"fn \" || ctrlId === \" nf \" || ctrlId === \"en \" || ctrlId === \" ne \") {\r\n const noteText = extractNoteText(records, i)\r\n if (noteText && blocks.length > 0) {\r\n // 직전 paragraph 블록에 footnoteText 연결\r\n const lastBlock = blocks[blocks.length - 1]\r\n if (lastBlock.type === \"paragraph\") {\r\n lastBlock.footnoteText = lastBlock.footnoteText\r\n ? lastBlock.footnoteText + \"; \" + noteText\r\n : noteText\r\n }\r\n }\r\n }\r\n // 하이퍼링크 — CTRL_HEADER 데이터에서 URL 추출\r\n else if (ctrlId === \"%tok\" || ctrlId === \"klnk\") {\r\n const url = extractHyperlinkUrl(rec.data)\r\n if (url && blocks.length > 0) {\r\n const lastBlock = blocks[blocks.length - 1]\r\n if (lastBlock.type === \"paragraph\" && !lastBlock.href) {\r\n lastBlock.href = sanitizeHref(url) ?? undefined\r\n }\r\n }\r\n }\r\n }\r\n\r\n i++\r\n }\r\n\r\n return blocks\r\n}\r\n\r\n/** 각주/미주 CTRL_HEADER 아래의 본문 텍스트 추출 */\r\nfunction extractNoteText(records: HwpRecord[], ctrlIdx: number): string | null {\r\n const ctrlLevel = records[ctrlIdx].level\r\n const texts: string[] = []\r\n\r\n for (let j = ctrlIdx + 1; j < records.length && j < ctrlIdx + 100; j++) {\r\n const r = records[j]\r\n if (r.level <= ctrlLevel) break // 상위 레벨 도달 → 이 컨트롤 블록 끝\r\n\r\n if (r.tagId === TAG_PARA_TEXT) {\r\n const t = extractText(r.data).trim()\r\n if (t) texts.push(t)\r\n }\r\n }\r\n\r\n return texts.length > 0 ? texts.join(\" \") : null\r\n}\r\n\r\n/** 글상자(TextBox) 제어 요소 아래의 텍스트 추출 — extractNoteText와 동일 패턴 */\r\nfunction extractTextBoxText(records: HwpRecord[], ctrlIdx: number): string | null {\r\n const ctrlLevel = records[ctrlIdx].level\r\n const texts: string[] = []\r\n\r\n for (let j = ctrlIdx + 1; j < records.length && j < ctrlIdx + 200; j++) {\r\n const r = records[j]\r\n if (r.level <= ctrlLevel) break\r\n\r\n if (r.tagId === TAG_PARA_TEXT) {\r\n const t = extractText(r.data).trim()\r\n if (t) texts.push(t)\r\n }\r\n }\r\n\r\n return texts.length > 0 ? texts.join(\"\\n\") : null\r\n}\r\n\r\n/** 하이퍼링크 CTRL_HEADER에서 URL 추출 (best-effort) */\r\nfunction extractHyperlinkUrl(data: Buffer): string | null {\r\n // HWP5 하이퍼링크 CTRL_HEADER 구조:\r\n // ctrlId(4) + 기타 필드들... + URL 문자열 (UTF-16LE, length-prefixed)\r\n // 정확한 오프셋은 버전마다 다를 수 있으므로 URL 패턴 스캔으로 폴백\r\n try {\r\n // UTF-16LE에서 \"http\" 시그니처 스캔\r\n const httpSig = Buffer.from(\"http\", \"utf16le\") // \"h\\0t\\0t\\0p\\0\"\r\n const idx = data.indexOf(httpSig)\r\n if (idx >= 0) {\r\n // null terminator(0x0000 0x0000)까지 UTF-16LE로 읽기\r\n let end = idx\r\n while (end + 1 < data.length) {\r\n const ch = data.readUInt16LE(end)\r\n if (ch === 0) break\r\n end += 2\r\n }\r\n const url = data.subarray(idx, end).toString(\"utf16le\")\r\n // 기본 URL 검증\r\n if (/^https?:\\/\\/.+/.test(url) && url.length < 2000) {\r\n return url\r\n }\r\n }\r\n } catch { /* best-effort */ }\r\n return null\r\n}\r\n\r\n/** CHAR_SHAPE ID 배열에서 대표 스타일 결정 (최빈값) */\r\nfunction resolveCharStyle(charShapeIds: number[], docInfo: HwpDocInfo): InlineStyle | undefined {\r\n if (charShapeIds.length === 0 || docInfo.charShapes.length === 0) return undefined\r\n\r\n // 가장 많이 나타나는 charShapeId 사용\r\n const freq = new Map<number, number>()\r\n let maxCount = 0, dominantId = charShapeIds[0]\r\n for (const id of charShapeIds) {\r\n const count = (freq.get(id) || 0) + 1\r\n freq.set(id, count)\r\n if (count > maxCount) { maxCount = count; dominantId = id }\r\n }\r\n\r\n const cs = docInfo.charShapes[dominantId]\r\n if (!cs) return undefined\r\n\r\n const style: InlineStyle = {}\r\n if (cs.fontSize > 0) style.fontSize = cs.fontSize / 10 // 0.1pt → pt\r\n if (cs.attrFlags & 0x01) style.italic = true\r\n if (cs.attrFlags & 0x02) style.bold = true\r\n\r\n return (style.fontSize || style.bold || style.italic) ? style : undefined\r\n}\r\n\r\nfunction parseParagraphWithTables(records: HwpRecord[], startIdx: number, counter?: { count: number }) {\r\n const startLevel = records[startIdx].level\r\n let text = \"\"\r\n const tables: ReturnType<typeof buildTable>[] = []\r\n const charShapeIds: number[] = []\r\n\r\n // PARA_HEADER에서 paraShapeId 추출 (offset 8-9, u16)\r\n const paraHeaderData = records[startIdx].data\r\n const paraShapeId = paraHeaderData.length >= 10 ? paraHeaderData.readUInt16LE(8) : -1\r\n\r\n let i = startIdx + 1\r\n\r\n while (i < records.length) {\r\n const rec = records[i]\r\n if (rec.tagId === TAG_PARA_HEADER && rec.level <= startLevel) break\r\n\r\n if (rec.tagId === TAG_PARA_TEXT) {\r\n text = extractText(rec.data)\r\n }\r\n\r\n // CHAR_SHAPE 레코드 — 문단 내 글자 모양 인덱스 배열\r\n if (rec.tagId === TAG_CHAR_SHAPE && rec.data.length >= 8) {\r\n // 구조: [position(u32) + charShapeId(u32)] * N\r\n for (let offset = 0; offset + 7 < rec.data.length; offset += 8) {\r\n charShapeIds.push(rec.data.readUInt32LE(offset + 4))\r\n }\r\n }\r\n\r\n if (rec.tagId === TAG_CTRL_HEADER && rec.data.length >= 4) {\r\n const ctrlId = rec.data.subarray(0, 4).toString(\"ascii\")\r\n if (ctrlId === \" lbt\" || ctrlId === \"tbl \") {\r\n const { table, nextIdx } = parseTableBlock(records, i, counter)\r\n if (table) tables.push(table)\r\n i = nextIdx\r\n continue\r\n }\r\n }\r\n i++\r\n }\r\n\r\n const trimmed = text.trim()\r\n return { paragraph: trimmed || null, tables, nextIdx: i, charShapeIds, paraShapeId }\r\n}\r\n\r\nfunction parseTableBlock(records: HwpRecord[], startIdx: number, counter?: { count: number }) {\r\n const tableLevel = records[startIdx].level\r\n let i = startIdx + 1\r\n let rows = 0, cols = 0\r\n const cells: CellContext[] = []\r\n\r\n while (i < records.length) {\r\n const rec = records[i]\r\n if (rec.tagId === TAG_PARA_HEADER && rec.level <= tableLevel) break\r\n if (rec.tagId === TAG_CTRL_HEADER && rec.level <= tableLevel) break\r\n\r\n if (rec.tagId === TAG_TABLE && rec.data.length >= 8) {\r\n rows = Math.min(rec.data.readUInt16LE(4), MAX_ROWS)\r\n cols = Math.min(rec.data.readUInt16LE(6), MAX_COLS)\r\n }\r\n\r\n if (rec.tagId === TAG_LIST_HEADER) {\r\n const { cell, nextIdx } = parseCellBlock(records, i, tableLevel, counter)\r\n if (cell) cells.push(cell)\r\n i = nextIdx\r\n continue\r\n }\r\n i++\r\n }\r\n\r\n if (rows === 0 || cols === 0 || cells.length === 0) return { table: null, nextIdx: i }\r\n\r\n // colAddr/rowAddr가 있으면 arrangeCells가 이미 완성된 그리드를 반환하므로\r\n // buildTable(2-pass) 없이 직접 IRTable 생성 — 이중 colSpan 확장 방지\r\n const hasAddr = cells.some(c => c.colAddr !== undefined && c.rowAddr !== undefined)\r\n if (hasAddr) {\r\n const cellRows = arrangeCells(rows, cols, cells)\r\n const irCells = cellRows.map(row => row.map(c => ({\r\n text: c.text.trim(),\r\n colSpan: c.colSpan,\r\n rowSpan: c.rowSpan,\r\n })))\r\n return { table: { rows, cols, cells: irCells, hasHeader: rows > 1 }, nextIdx: i }\r\n }\r\n\r\n const cellRows = arrangeCells(rows, cols, cells)\r\n return { table: buildTable(cellRows), nextIdx: i }\r\n}\r\n\r\nfunction parseCellBlock(records: HwpRecord[], startIdx: number, tableLevel: number, counter?: { count: number }) {\r\n const rec = records[startIdx]\r\n const cellLevel = rec.level\r\n const texts: string[] = []\r\n\r\n // LIST_HEADER에서 셀 위치 및 병합 정보 추출\r\n // HWP5 셀 LIST_HEADER 구조:\r\n // paraCount(u16) + flags(u32) + width(u16) + colAddr(u16) + rowAddr(u16) + colSpan(u16) + rowSpan(u16)\r\n // offset: 0 2 6 8 10 12 14\r\n let colSpan = 1\r\n let rowSpan = 1\r\n let colAddr: number | undefined\r\n let rowAddr: number | undefined\r\n if (rec.data.length >= 16) {\r\n colAddr = rec.data.readUInt16LE(8)\r\n rowAddr = rec.data.readUInt16LE(10)\r\n const cs = rec.data.readUInt16LE(12)\r\n const rs = rec.data.readUInt16LE(14)\r\n if (cs > 0) colSpan = Math.min(cs, MAX_COLS)\r\n if (rs > 0) rowSpan = Math.min(rs, MAX_ROWS)\r\n }\r\n\r\n let i = startIdx + 1\r\n\r\n while (i < records.length) {\r\n const r = records[i]\r\n if (r.tagId === TAG_LIST_HEADER && r.level <= cellLevel) break\r\n if (r.level <= tableLevel && (r.tagId === TAG_PARA_HEADER || r.tagId === TAG_CTRL_HEADER)) break\r\n\r\n if (r.tagId === TAG_PARA_TEXT) {\r\n const t = extractText(r.data).trim()\r\n if (t) texts.push(t)\r\n }\r\n\r\n // 셀 내부 중첩 테이블 감지 (HWP5에서는 내용 파싱 없이 마커만 표시)\r\n // 힌트 없음 — HWP5는 이 단계에서 중첩 테이블 내부를 파싱하지 않으므로 첫 행 추출 불가\r\n if (r.tagId === TAG_CTRL_HEADER && r.data.length >= 4) {\r\n const ctrlId = r.data.subarray(0, 4).toString(\"ascii\")\r\n if (ctrlId === \" lbt\" || ctrlId === \"tbl \") {\r\n if (counter) {\r\n counter.count++\r\n texts.push(`[중첩 테이블 #${counter.count}]`)\r\n } else {\r\n texts.push(\"[중첩 테이블]\")\r\n }\r\n }\r\n }\r\n\r\n i++\r\n }\r\n\r\n return { cell: { text: texts.join(\"\\n\"), colSpan, rowSpan, colAddr, rowAddr } as CellContext, nextIdx: i }\r\n}\r\n\r\nfunction arrangeCells(rows: number, cols: number, cells: CellContext[]): CellContext[][] {\r\n const grid: (CellContext | null)[][] = Array.from({ length: rows }, () => Array(cols).fill(null))\r\n\r\n // colAddr/rowAddr가 있으면 직접 배치 (HWP5 병합 테이블 정확도 향상)\r\n const hasAddr = cells.some(c => c.colAddr !== undefined && c.rowAddr !== undefined)\r\n\r\n if (hasAddr) {\r\n for (const cell of cells) {\r\n const r = cell.rowAddr ?? 0\r\n const c = cell.colAddr ?? 0\r\n if (r >= rows || c >= cols) continue\r\n grid[r][c] = cell\r\n\r\n for (let dr = 0; dr < cell.rowSpan; dr++) {\r\n for (let dc = 0; dc < cell.colSpan; dc++) {\r\n if (dr === 0 && dc === 0) continue\r\n if (r + dr < rows && c + dc < cols)\r\n grid[r + dr][c + dc] = { text: \"\", colSpan: 1, rowSpan: 1 }\r\n }\r\n }\r\n }\r\n } else {\r\n // fallback: 순차 배치 (colAddr 없는 경우)\r\n let cellIdx = 0\r\n for (let r = 0; r < rows && cellIdx < cells.length; r++) {\r\n for (let c = 0; c < cols && cellIdx < cells.length; c++) {\r\n if (grid[r][c] !== null) continue\r\n const cell = cells[cellIdx++]\r\n grid[r][c] = cell\r\n\r\n for (let dr = 0; dr < cell.rowSpan; dr++) {\r\n for (let dc = 0; dc < cell.colSpan; dc++) {\r\n if (dr === 0 && dc === 0) continue\r\n if (r + dr < rows && c + dc < cols)\r\n grid[r + dr][c + dc] = { text: \"\", colSpan: 1, rowSpan: 1 }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return grid.map(row => row.map(c => c || { text: \"\", colSpan: 1, rowSpan: 1 }))\r\n}\r\n","/** 양식(서식) 필드 인식 — 테이블 기반 label-value 패턴 매칭 */\r\n\r\nimport type { IRBlock, IRTable, FormField, FormResult } from \"../types.js\"\r\n\r\n/** 한국 공문서 필드 라벨 키워드 */\r\nexport const LABEL_KEYWORDS = new Set([\r\n \"성명\", \"이름\", \"주소\", \"전화\", \"전화번호\", \"휴대폰\", \"핸드폰\", \"연락처\",\r\n \"생년월일\", \"주민등록번호\", \"소속\", \"직위\", \"직급\", \"부서\",\r\n \"이메일\", \"팩스\", \"학교\", \"학년\", \"반\", \"번호\",\r\n \"신청인\", \"대표자\", \"담당자\", \"작성자\", \"확인자\", \"승인자\",\r\n \"일시\", \"날짜\", \"기간\", \"장소\", \"목적\", \"사유\", \"비고\",\r\n \"금액\", \"수량\", \"단가\", \"합계\", \"계\", \"소계\",\r\n \"등록기준지\", \"본적\", \"위임인\", \"청구사유\", \"소명자료\",\r\n])\r\n\r\n/** 라벨처럼 보이는 셀인지 판별 */\r\nexport function isLabelCell(text: string): boolean {\r\n // 각주 번호/특수문자 제거 후 판별 (예: \"등록기준지²\" → \"등록기준지\")\r\n const trimmed = text.trim().replace(/[¹²³⁴⁵⁶⁷⁸⁹⁰*※]+$/g, \"\").trim()\r\n if (!trimmed || trimmed.length > 30) return false\r\n // 키워드 매칭\r\n for (const kw of LABEL_KEYWORDS) {\r\n if (trimmed.includes(kw)) return true\r\n }\r\n // 짧은 한글 텍스트 (2-8자) + 숫자 없음 (공백/괄호/특수기호 허용)\r\n if (/^[가-힣\\s()()·::]+$/.test(trimmed) && trimmed.replace(/\\s/g, \"\").length >= 2 && trimmed.replace(/\\s/g, \"\").length <= 8 && !/\\d/.test(trimmed)) return true\r\n // \"라벨:\" 패턴\r\n if (/^[가-힣A-Za-z\\s]+[::]$/.test(trimmed)) return true\r\n return false\r\n}\r\n\r\n/**\r\n * IRBlock[]에서 양식 필드를 인식하여 추출.\r\n * 테이블의 label-value 패턴을 감지.\r\n */\r\nexport function extractFormFields(blocks: IRBlock[]): FormResult {\r\n const fields: FormField[] = []\r\n let totalTables = 0\r\n let formTables = 0\r\n\r\n for (const block of blocks) {\r\n if (block.type !== \"table\" || !block.table) continue\r\n totalTables++\r\n\r\n const tableFields = extractFromTable(block.table)\r\n if (tableFields.length > 0) {\r\n formTables++\r\n fields.push(...tableFields)\r\n }\r\n }\r\n\r\n // 인라인 \"라벨: 값\" 패턴도 검사 (paragraph만 — heading/list는 양식 필드가 아님)\r\n for (const block of blocks) {\r\n if (block.type === \"paragraph\" && block.text) {\r\n const inlineFields = extractInlineFields(block.text)\r\n fields.push(...inlineFields)\r\n }\r\n }\r\n\r\n const confidence = totalTables > 0 ? formTables / totalTables : (fields.length > 0 ? 0.3 : 0)\r\n return { fields, confidence: Math.min(confidence, 1) }\r\n}\r\n\r\nfunction extractFromTable(table: IRTable): FormField[] {\r\n const fields: FormField[] = []\r\n\r\n // 전략 1: 인접셀 label-value (2열 이상 테이블)\r\n if (table.cols >= 2) {\r\n for (let r = 0; r < table.rows; r++) {\r\n for (let c = 0; c < table.cols - 1; c++) {\r\n const labelCell = table.cells[r][c]\r\n const valueCell = table.cells[r][c + 1]\r\n if (isLabelCell(labelCell.text)) {\r\n fields.push({\r\n label: labelCell.text.trim().replace(/[::]\\s*$/, \"\"),\r\n value: valueCell.text.trim(),\r\n row: r,\r\n col: c,\r\n })\r\n }\r\n }\r\n }\r\n }\r\n\r\n // 전략 2: 헤더+데이터 행 (첫 행이 전부 라벨이면)\r\n if (fields.length === 0 && table.rows >= 2 && table.cols >= 2) {\r\n const headerRow = table.cells[0]\r\n const allLabels = headerRow.every(cell => {\r\n const t = cell.text.trim()\r\n return t.length > 0 && t.length <= 20\r\n })\r\n if (allLabels) {\r\n for (let r = 1; r < table.rows; r++) {\r\n for (let c = 0; c < table.cols; c++) {\r\n const label = headerRow[c].text.trim()\r\n const value = table.cells[r][c].text.trim()\r\n if (label && value) {\r\n fields.push({ label, value, row: r, col: c })\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return fields\r\n}\r\n\r\nfunction extractInlineFields(text: string): FormField[] {\r\n const fields: FormField[] = []\r\n // \"라벨: 값\" 또는 \"라벨 : 값\" 패턴\r\n const pattern = /([가-힣A-Za-z]{2,10})\\s*[::]\\s*([^\\n,;]{1,100})/g\r\n let match\r\n while ((match = pattern.exec(text)) !== null) {\r\n const label = match[1].trim()\r\n const value = match[2].trim()\r\n if (value) {\r\n fields.push({ label, value, row: -1, col: -1 })\r\n }\r\n }\r\n return fields\r\n}\r\n","/** 양식 필드 매칭 공용 유틸 — filler.ts, filler-hwpx.ts에서 공유 */\n\nimport { LABEL_KEYWORDS } from \"./recognize.js\"\n\n/** 라벨 정규화 — 콜론/공백/특수문자 제거, 비교용 */\nexport function normalizeLabel(label: string): string {\n return label.trim().replace(/[::\\s()()·]/g, \"\")\n}\n\n/**\n * 정규화된 셀 라벨과 입력 값 맵에서 최적 매칭 키를 찾음.\n *\n * 매칭 우선순위:\n * 1. 정확 매칭 (normalizedCellLabel === key)\n * 2. 접두사 기반 매칭 (60% 이상 겹침 필요)\n */\nexport function findMatchingKey(\n cellLabel: string,\n values: Map<string, string>,\n): string | undefined {\n // 1) 정확 매칭\n if (values.has(cellLabel)) return cellLabel\n\n // 2) 접두사 기반 매칭 — 가장 긴 매칭 우선 (= 가장 구체적)\n // 단, 길이 비율 60% 이상 겹쳐야 매칭 (오탐 방지)\n let bestKey: string | undefined\n let bestLen = 0\n\n for (const key of values.keys()) {\n if (cellLabel.startsWith(key)) {\n if (key.length >= cellLabel.length * 0.6 && key.length > bestLen) {\n bestLen = key.length\n bestKey = key\n }\n } else if (key.startsWith(cellLabel)) {\n if (cellLabel.length >= key.length * 0.6 && cellLabel.length > bestLen) {\n bestLen = cellLabel.length\n bestKey = key\n }\n }\n }\n\n return bestKey\n}\n\n/**\n * 값 셀이 키워드 라벨(섹션 헤더의 하위 라벨)인지 판별.\n * \"성명\", \"주소\" 같은 키워드 라벨이면 true → 스킵 대상.\n * \"(한자:)\" 같은 어노테이션이면 false → 채울 수 있음.\n */\nexport function isKeywordLabel(text: string): boolean {\n const trimmed = text.trim().replace(/[¹²³⁴⁵⁶⁷⁸⁹⁰*※]+$/g, \"\").trim()\n if (!trimmed || trimmed.length > 15) return false\n for (const kw of LABEL_KEYWORDS) {\n if (trimmed.includes(kw)) return true\n }\n return false\n}\n\n/**\n * 셀 텍스트에서 인셀 패턴을 찾아 교체 — 체크박스 + 괄호 빈칸.\n *\n * 지원 패턴:\n * 1. 괄호 빈칸: `일반( )통` → 키 \"일반통\" 또는 \"일반\" 매칭 시 → `일반(값)통`\n * 2. 체크박스: `□부` → 키 \"부\" 매칭 시 → `☑부` (값이 \"☑\",\"✓\",\"v\",\"V\",\"true\",\"1\" 등)\n *\n * @returns 교체된 텍스트 + 매칭된 키 목록. null이면 교체 없음.\n */\nexport function fillInCellPatterns(\n cellText: string,\n values: Map<string, string>,\n matchedLabels: Set<string>,\n): { text: string; matches: Array<{ key: string; label: string; value: string }> } | null {\n let text = cellText\n const matches: Array<{ key: string; label: string; value: string }> = []\n\n // 1) 괄호 빈칸: keyword(\\s+)suffix → keyword(value)suffix\n text = text.replace(\n /([가-힣A-Za-z]+)\\(\\s{1,}\\)([가-힣A-Za-z]*)/g,\n (match, prefix: string, suffix: string) => {\n const label = prefix + suffix // \"일반\" + \"통\" = \"일반통\"\n const normalizedLabel = normalizeLabel(label)\n // 정확 매칭 → 접두사만 매칭 순\n const matchKey = values.has(normalizedLabel)\n ? normalizedLabel\n : values.has(normalizeLabel(prefix))\n ? normalizeLabel(prefix)\n : undefined\n if (matchKey === undefined) return match\n\n const newValue = values.get(matchKey)!\n matchedLabels.add(matchKey)\n matches.push({ key: matchKey, label, value: newValue })\n return `${prefix}(${newValue})${suffix}`\n },\n )\n\n // 2) 체크박스: □keyword → ☑keyword (값이 truthy)\n text = text.replace(\n /□([가-힣A-Za-z]+)/g,\n (match, keyword: string) => {\n const normalizedKw = normalizeLabel(keyword)\n const matchKey = values.has(normalizedKw) ? normalizedKw : undefined\n if (matchKey === undefined) return match\n\n const val = values.get(matchKey)!\n const isTruthy = [\"☑\", \"✓\", \"✔\", \"v\", \"V\", \"true\", \"1\", \"yes\", \"o\", \"O\"].includes(val.trim()) || val.trim() === \"\"\n if (!isTruthy) return match\n\n matchedLabels.add(matchKey)\n matches.push({ key: matchKey, label: `□${keyword}`, value: \"☑\" })\n return `☑${keyword}`\n },\n )\n\n // 3) 어노테이션 빈칸: (keyword:\\s+) → (keyword:value)\n // 예: \"(한자: )\" → \"(한자:金民秀)\"\n // 예: \"(성명: )\" → \"(성명: 홍길동)\"\n text = text.replace(\n /\\(([가-힣A-Za-z]+)[::]\\s{1,}\\)/g,\n (match, keyword: string) => {\n const normalizedKw = normalizeLabel(keyword)\n const matchKey = values.has(normalizedKw) ? normalizedKw : undefined\n if (matchKey === undefined) return match\n\n const newValue = values.get(matchKey)!\n matchedLabels.add(matchKey)\n matches.push({ key: matchKey, label: keyword, value: newValue })\n return `(${keyword}:${newValue})`\n },\n )\n\n return matches.length > 0 ? { text, matches } : null\n}\n\n/** 입력 values 맵을 정규화된 키로 변환 */\nexport function normalizeValues(values: Record<string, string>): Map<string, string> {\n const map = new Map<string, string>()\n for (const [label, value] of Object.entries(values)) {\n map.set(normalizeLabel(label), value)\n }\n return map\n}\n\n/** 매칭 안 된 라벨을 원본 키로 복원 */\nexport function resolveUnmatched(\n normalizedValues: Map<string, string>,\n matchedLabels: Set<string>,\n originalValues: Record<string, string>,\n): string[] {\n return [...normalizedValues.keys()]\n .filter(k => !matchedLabels.has(k))\n .map(k => {\n for (const orig of Object.keys(originalValues)) {\n if (normalizeLabel(orig) === k) return orig\n }\n return k\n })\n}\n","/** 양식 서식 필드 값 채우기 — IRBlock[] 기반 in-place 교체 */\n\nimport type { IRBlock, IRTable, FormField } from \"../types.js\"\nimport { isLabelCell } from \"./recognize.js\"\nimport { normalizeLabel, findMatchingKey, normalizeValues, resolveUnmatched, isKeywordLabel, fillInCellPatterns } from \"./match.js\"\n\n/** 필드 채우기 결과 */\nexport interface FillResult {\n /** 값이 교체된 IRBlock[] */\n blocks: IRBlock[]\n /** 실제 채워진 필드 목록 */\n filled: FormField[]\n /** 매칭 실패한 라벨 (입력에는 있지만 서식에서 못 찾은 것) */\n unmatched: string[]\n}\n\n/**\n * IRBlock[]에서 양식 필드를 찾아 값을 교체.\n *\n * @param blocks 원본 IRBlock[] (변경하지 않음 — deep clone)\n * @param values 채울 값 맵 (라벨 → 새 값). 라벨은 접두사 매칭 지원.\n * @returns FillResult\n *\n * @example\n * ```ts\n * const result = await parse(\"신청서.hwp\")\n * if (!result.success) throw new Error(result.error)\n * const { blocks, filled } = fillFormFields(result.blocks, {\n * \"성명\": \"홍길동\",\n * \"전화번호\": \"010-1234-5678\",\n * \"주소\": \"서울시 강남구\",\n * })\n * ```\n */\nexport function fillFormFields(\n blocks: IRBlock[],\n values: Record<string, string>,\n): FillResult {\n // deep clone — 원본 불변\n const cloned = structuredClone(blocks)\n const filled: FormField[] = []\n const matchedLabels = new Set<string>()\n\n const normalizedValues = normalizeValues(values)\n\n // 1) 인셀 패턴 먼저 (체크박스, 괄호 빈칸, 어노테이션) — 전략 2가 덮어쓰기 전에\n const patternFilledCells = new Set<string>() // \"r,c\" 키\n for (const block of cloned) {\n if (block.type !== \"table\" || !block.table) continue\n for (let r = 0; r < block.table.rows; r++) {\n for (let c = 0; c < block.table.cols; c++) {\n const cell = block.table.cells[r]?.[c]\n if (!cell) continue\n const result = fillInCellPatterns(cell.text, normalizedValues, matchedLabels)\n if (result) {\n cell.text = result.text\n patternFilledCells.add(`${r},${c}`)\n for (const m of result.matches) {\n filled.push({ label: m.label, value: m.value, row: r, col: c })\n }\n }\n }\n }\n }\n\n // 2) 테이블 기반 필드 교체 (라벨-값 셀 패턴)\n for (const block of cloned) {\n if (block.type !== \"table\" || !block.table) continue\n fillTable(block.table, normalizedValues, filled, matchedLabels, patternFilledCells)\n }\n\n // 3) 인라인 \"라벨: 값\" 패턴 교체\n for (const block of cloned) {\n if (block.type !== \"paragraph\" || !block.text) continue\n const newText = fillInlineFields(block.text, normalizedValues, filled, matchedLabels)\n if (newText !== block.text) block.text = newText\n }\n\n const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values)\n return { blocks: cloned, filled, unmatched }\n}\n\n/** 테이블 셀에서 라벨-값 패턴을 찾아 값 교체 */\nfunction fillTable(\n table: IRTable,\n values: Map<string, string>,\n filled: FormField[],\n matchedLabels: Set<string>,\n patternFilledCells?: Set<string>,\n): void {\n if (table.cols < 2) return\n\n // 전략 1: 인접 라벨-값 셀 패턴\n for (let r = 0; r < table.rows; r++) {\n for (let c = 0; c < table.cols - 1; c++) {\n const labelCell = table.cells[r][c]\n const valueCell = table.cells[r][c + 1]\n if (!labelCell || !valueCell) continue\n\n if (!isLabelCell(labelCell.text)) continue\n\n if (isKeywordLabel(valueCell.text)) continue\n\n const normalizedCellLabel = normalizeLabel(labelCell.text)\n if (!normalizedCellLabel) continue\n\n const matchKey = findMatchingKey(normalizedCellLabel, values)\n if (matchKey === undefined) continue\n\n const newValue = values.get(matchKey)!\n // 이미 인셀 패턴이 처리된 셀이면 앞에 삽입 (어노테이션 보존)\n if (patternFilledCells?.has(`${r},${c + 1}`)) {\n valueCell.text = newValue + \" \" + valueCell.text\n } else {\n valueCell.text = newValue\n }\n matchedLabels.add(matchKey)\n filled.push({\n label: labelCell.text.trim().replace(/[::]\\s*$/, \"\"),\n value: newValue,\n row: r,\n col: c,\n })\n }\n }\n\n // 전략 2: 헤더+데이터 행 패턴 (첫 행이 전부 라벨이면)\n // 전략 1에서 이미 채운 필드는 스킵 (matchedLabels 검사)\n if (table.rows >= 2 && table.cols >= 2) {\n const headerRow = table.cells[0]\n const allLabels = headerRow.every(cell => {\n const t = cell.text.trim()\n return t.length > 0 && t.length <= 20 && isLabelCell(t)\n })\n if (!allLabels) return\n\n for (let r = 1; r < table.rows; r++) {\n for (let c = 0; c < table.cols; c++) {\n const headerLabel = normalizeLabel(headerRow[c].text)\n const matchKey = findMatchingKey(headerLabel, values)\n if (matchKey === undefined) continue\n if (matchedLabels.has(matchKey)) continue\n\n const newValue = values.get(matchKey)!\n table.cells[r][c].text = newValue\n matchedLabels.add(matchKey)\n filled.push({\n label: headerRow[c].text.trim(),\n value: newValue,\n row: r,\n col: c,\n })\n }\n }\n }\n}\n\n/** 인라인 \"라벨: 값\" 패턴 교체 */\nfunction fillInlineFields(\n text: string,\n values: Map<string, string>,\n filled: FormField[],\n matchedLabels: Set<string>,\n): string {\n return text.replace(\n /([가-힣A-Za-z]{2,10})\\s*[::]\\s*([^\\n,;]{0,100})/g,\n (match, rawLabel: string, _oldValue: string) => {\n const normalized = normalizeLabel(rawLabel)\n const matchKey = findMatchingKey(normalized, values)\n if (matchKey === undefined) return match\n\n const newValue = values.get(matchKey)!\n matchedLabels.add(matchKey)\n filled.push({\n label: rawLabel.trim(),\n value: newValue,\n row: -1,\n col: -1,\n })\n return `${rawLabel}: ${newValue}`\n },\n )\n}\n","/**\n * HWPX 원본 서식 유지 채우기 — ZIP 내 section XML 직접 수정\n *\n * IRBlock 중간 표현을 거치지 않고, 원본 HWPX ZIP의 section XML에서\n * 테이블 셀 텍스트(<hp:t>)만 교체하여 모든 스타일을 보존합니다.\n */\n\nimport JSZip from \"jszip\"\nimport { DOMParser, XMLSerializer } from \"@xmldom/xmldom\"\nimport { isLabelCell } from \"../form/recognize.js\"\nimport { KordocError, stripDtd } from \"../utils.js\"\nimport { normalizeLabel, findMatchingKey, normalizeValues, resolveUnmatched, isKeywordLabel, fillInCellPatterns } from \"./match.js\"\nimport type { FormField } from \"../types.js\"\n\n/** 채우기 결과 */\nexport interface HwpxFillResult {\n /** 채워진 HWPX 바이너리 */\n buffer: ArrayBuffer\n /** 실제 채워진 필드 목록 */\n filled: FormField[]\n /** 매칭 실패한 라벨 */\n unmatched: string[]\n}\n\n/**\n * HWPX 원본을 직접 수정하여 서식 필드를 채움 — 스타일 100% 보존.\n *\n * @param hwpxBuffer 원본 HWPX 파일 버퍼\n * @param values 채울 값 맵 (라벨 → 값)\n * @returns HwpxFillResult\n */\nexport async function fillHwpx(\n hwpxBuffer: ArrayBuffer,\n values: Record<string, string>,\n): Promise<HwpxFillResult> {\n const zip = await JSZip.loadAsync(hwpxBuffer)\n const filled: FormField[] = []\n const matchedLabels = new Set<string>()\n\n const normalizedValues = normalizeValues(values)\n\n // section XML 파일 찾기\n const sectionFiles = Object.keys(zip.files)\n .filter(name => /[Ss]ection\\d+\\.xml$/i.test(name))\n .sort()\n\n if (sectionFiles.length === 0) {\n throw new KordocError(\"HWPX에서 섹션 파일을 찾을 수 없습니다\")\n }\n\n const xmlParser = new DOMParser()\n const xmlSerializer = new XMLSerializer()\n\n for (const sectionPath of sectionFiles) {\n const zipEntry = zip.file(sectionPath)\n if (!zipEntry) continue // null 방어\n\n const rawXml = await zipEntry.async(\"text\")\n const doc = xmlParser.parseFromString(stripDtd(rawXml), \"text/xml\")\n if (!doc.documentElement) continue\n\n let modified = false\n\n // 모든 테이블 요소 탐색\n const tables = findAllElements(doc.documentElement as unknown as Node, \"tbl\")\n\n // 전략 0: 인셀 패턴 채우기 — 전략 1보다 먼저 실행\n // (체크박스 □→☑, 괄호 빈칸 ( )→(값), 어노테이션 (한자:)→(한자:값))\n // 이렇게 해야 전략 1이 셀을 덮어쓸 때 어노테이션이 보존됨\n const cellPatternApplied = new Set<Element>()\n for (const tblEl of tables) {\n const allCells = findAllElements(tblEl, \"tc\")\n for (const tcEl of allCells) {\n const tNodes = collectCellTextNodes(tcEl)\n const fullText = tNodes.map(n => n.text).join(\"\")\n const result = fillInCellPatterns(fullText, normalizedValues, matchedLabels)\n if (!result) continue\n\n applyTextReplacements(tNodes, fullText, result.text)\n cellPatternApplied.add(tcEl)\n for (const m of result.matches) {\n filled.push({ label: m.label, value: m.value, row: -1, col: -1 })\n }\n modified = true\n }\n }\n\n for (const tblEl of tables) {\n const rows = findDirectChildren(tblEl, \"tr\")\n\n // 전략 1: 인접 라벨-값 셀 (label | value 패턴)\n for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {\n const trEl = rows[rowIdx]\n const cells = findDirectChildren(trEl, \"tc\")\n\n for (let colIdx = 0; colIdx < cells.length - 1; colIdx++) {\n const labelText = extractCellText(cells[colIdx])\n if (!isLabelCell(labelText)) continue\n\n const valueCell = cells[colIdx + 1]\n const valueText = extractCellText(valueCell)\n if (isKeywordLabel(valueText)) continue\n\n const normalizedCellLabel = normalizeLabel(labelText)\n if (!normalizedCellLabel) continue\n\n const matchKey = findMatchingKey(normalizedCellLabel, normalizedValues)\n if (matchKey === undefined) continue\n\n const newValue = normalizedValues.get(matchKey)!\n\n // 전략 0이 이미 어노테이션을 채웠다면, 값을 앞에 삽입 (어노테이션 보존)\n if (cellPatternApplied.has(valueCell)) {\n prependCellText(valueCell, newValue)\n } else {\n replaceCellText(valueCell, newValue)\n }\n matchedLabels.add(matchKey)\n filled.push({\n label: labelText.trim().replace(/[::]\\s*$/, \"\"),\n value: newValue,\n row: rowIdx,\n col: colIdx,\n })\n modified = true\n }\n }\n\n // 전략 2: 헤더+데이터 행 패턴 (첫 행이 전부 라벨이면)\n if (rows.length >= 2) {\n const headerCells = findDirectChildren(rows[0], \"tc\")\n const allLabels = headerCells.every(cell => {\n const t = extractCellText(cell).trim()\n return t.length > 0 && t.length <= 20 && isLabelCell(t)\n })\n\n if (allLabels) {\n for (let rowIdx = 1; rowIdx < rows.length; rowIdx++) {\n const dataCells = findDirectChildren(rows[rowIdx], \"tc\")\n for (let colIdx = 0; colIdx < Math.min(headerCells.length, dataCells.length); colIdx++) {\n const headerLabel = normalizeLabel(extractCellText(headerCells[colIdx]))\n const matchKey = findMatchingKey(headerLabel, normalizedValues)\n if (matchKey === undefined) continue\n if (matchedLabels.has(matchKey)) continue\n\n const newValue = normalizedValues.get(matchKey)!\n replaceCellText(dataCells[colIdx], newValue)\n matchedLabels.add(matchKey)\n filled.push({\n label: extractCellText(headerCells[colIdx]).trim(),\n value: newValue,\n row: rowIdx,\n col: colIdx,\n })\n modified = true\n }\n }\n }\n }\n }\n\n // 인라인 \"라벨: 값\" 패턴도 처리 (테이블 밖 paragraph)\n const allParagraphs = findAllElements(doc.documentElement as unknown as Node, \"p\")\n for (const pEl of allParagraphs) {\n if (isInsideTable(pEl)) continue\n\n // 모든 <hp:t> 텍스트를 위치 정보와 함께 수집\n const tNodes = collectTextNodes(pEl)\n const fullText = tNodes.map(n => n.text).join(\"\")\n\n const pattern = /([가-힣A-Za-z]{2,10})\\s*[::]\\s*([^\\n,;]{0,100})/g\n let match\n while ((match = pattern.exec(fullText)) !== null) {\n const rawLabel = match[1]\n const normalized = normalizeLabel(rawLabel)\n const matchKey = findMatchingKey(normalized, normalizedValues)\n if (matchKey === undefined) continue\n\n const newValue = normalizedValues.get(matchKey)!\n // 값 부분의 시작/끝 오프셋 계산\n const valueStart = match.index + match[0].length - match[2].length\n const valueEnd = match.index + match[0].length\n\n replaceTextRange(tNodes, valueStart, valueEnd, newValue)\n matchedLabels.add(matchKey)\n filled.push({ label: rawLabel.trim(), value: newValue, row: -1, col: -1 })\n modified = true\n // 교체 후 패턴 인덱스 조정 (텍스트 길이 변경됨) — 다음 매칭 건너뜀\n break\n }\n }\n\n if (modified) {\n const newXml = xmlSerializer.serializeToString(doc)\n zip.file(sectionPath, newXml)\n }\n }\n\n const unmatched = resolveUnmatched(normalizedValues, matchedLabels, values)\n const buffer = await zip.generateAsync({ type: \"arraybuffer\" })\n return { buffer, filled, unmatched }\n}\n\n// ─── XML 탐색 헬퍼 ──────────────────────────────────\n\n/** 로컬 태그명 추출 (네임스페이스 프리픽스 제거) */\nfunction localName(el: Element): string {\n return (el.tagName || el.localName || \"\").replace(/^[^:]+:/, \"\")\n}\n\n/** 문서 전체에서 특정 로컬 태그명의 요소를 재귀 탐색 */\nfunction findAllElements(node: Node, tagLocalName: string): Element[] {\n const result: Element[] = []\n const walk = (n: Node) => {\n const children = n.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 !== 1) continue\n if (localName(child) === tagLocalName) result.push(child)\n walk(child)\n }\n }\n walk(node)\n return result\n}\n\n/** 직계 자식 중 특정 로컬 태그명 요소만 반환 */\nfunction findDirectChildren(parent: Node, tagLocalName: string): Element[] {\n const result: Element[] = []\n const children = parent.childNodes\n if (!children) return result\n for (let i = 0; i < children.length; i++) {\n const child = children[i] as Element\n if (child.nodeType === 1 && localName(child) === tagLocalName) {\n result.push(child)\n }\n }\n return result\n}\n\n/** 요소가 <tbl> 안에 있는지 확인 (부모 체인 탐색) */\nfunction isInsideTable(el: Element): boolean {\n let parent = el.parentNode as Element | null\n while (parent) {\n if (parent.nodeType === 1 && localName(parent) === \"tbl\") return true\n parent = parent.parentNode as Element | null\n }\n return false\n}\n\n// ─── 셀 텍스트 추출/교체 ────────────────────────────\n\n/** 셀(<hp:tc>) 내 모든 <hp:t> 텍스트를 합쳐 반환 */\nfunction extractCellText(tcEl: Element): string {\n const parts: string[] = []\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) {\n parts.push(child.textContent || \"\")\n } else if (child.nodeType === 1) {\n const tag = localName(child)\n // subList는 tc 아래 p를 감싸는 컨테이너 — 반드시 순회\n if (tag === \"t\") walk(child)\n else if (tag === \"run\" || tag === \"r\" || tag === \"p\" || tag === \"subList\") walk(child)\n else if (tag === \"tab\") parts.push(\"\\t\")\n else if (tag === \"br\") parts.push(\"\\n\")\n }\n }\n }\n walk(tcEl)\n return parts.join(\"\")\n}\n\n/**\n * 셀(<hp:tc>)의 첫 번째 <hp:t> 앞에 텍스트를 삽입 — 어노테이션 보존.\n * 예: \"(한자:金民秀)\" → \"김민수 (한자:金民秀)\"\n */\nfunction prependCellText(tcEl: Element, text: string): void {\n const tElements = findAllElements(tcEl, \"t\")\n if (tElements.length === 0) return\n\n const firstT = tElements[0]\n const existing = firstT.textContent || \"\"\n clearChildren(firstT)\n firstT.appendChild(firstT.ownerDocument!.createTextNode(text + \" \" + existing))\n}\n\n/**\n * 셀(<hp:tc>) 내 텍스트를 새 값으로 교체 — 스타일 보존 전략:\n *\n * 1) 첫 번째 <hp:run>의 <hp:t>에 새 텍스트 설정\n * 2) 나머지 <hp:run>의 <hp:t>는 빈 문자열로\n * 3) 두 번째 이후 <hp:p>는 내용만 비움 (요소 유지 — HWPX 뷰어 호환)\n *\n * 이렇게 하면 첫 번째 run의 charPrIDRef(글꼴, 크기, 굵기 등)가 보존됨\n */\nfunction replaceCellText(tcEl: Element, newValue: string): void {\n const paragraphs = findAllElements(tcEl, \"p\")\n if (paragraphs.length === 0) return\n\n const firstP = paragraphs[0]\n const runs = findAllElements(firstP, \"run\").concat(findAllElements(firstP, \"r\"))\n\n if (runs.length > 0) {\n setRunText(runs[0], newValue)\n for (let i = 1; i < runs.length; i++) {\n setRunText(runs[i], \"\")\n }\n } else {\n const tElements = findAllElements(firstP, \"t\")\n if (tElements.length > 0) {\n clearChildren(tElements[0])\n tElements[0].appendChild(tElements[0].ownerDocument!.createTextNode(newValue))\n for (let i = 1; i < tElements.length; i++) {\n clearChildren(tElements[i])\n }\n }\n }\n\n // 두 번째 이후 paragraph — 내용만 비움\n for (let i = 1; i < paragraphs.length; i++) {\n const p = paragraphs[i]\n if (p.parentNode) {\n const pRuns = findAllElements(p, \"run\").concat(findAllElements(p, \"r\"))\n for (const run of pRuns) setRunText(run, \"\")\n const pTs = findAllElements(p, \"t\")\n for (const t of pTs) clearChildren(t)\n }\n }\n}\n\n/** <hp:run> 요소의 <hp:t> 텍스트를 교체 */\nfunction setRunText(runEl: Element, text: string): void {\n const tElements = findAllElements(runEl, \"t\")\n if (tElements.length > 0) {\n clearChildren(tElements[0])\n tElements[0].appendChild(tElements[0].ownerDocument!.createTextNode(text))\n for (let i = 1; i < tElements.length; i++) {\n clearChildren(tElements[i])\n }\n }\n}\n\n/** 요소의 모든 자식 노드 제거 */\nfunction clearChildren(el: Element): void {\n while (el.firstChild) el.removeChild(el.firstChild)\n}\n\n// ─── 인라인 텍스트 교체 (분리된 <hp:t> 대응) ─────────\n\n/** <hp:t> 텍스트 노드와 글로벌 오프셋 정보 */\ninterface TextNodeInfo {\n /** <hp:t> 요소 */\n element: Element\n /** 이 요소의 텍스트 */\n text: string\n /** 전체 합산 텍스트에서의 시작 오프셋 */\n offset: number\n}\n\n/** paragraph 내 모든 <hp:t> 텍스트 노드를 오프셋과 함께 수집 */\nfunction collectTextNodes(pEl: Element): TextNodeInfo[] {\n const tElements = findAllElements(pEl, \"t\")\n const result: TextNodeInfo[] = []\n let offset = 0\n for (const t of tElements) {\n const text = t.textContent || \"\"\n result.push({ element: t, text, offset })\n offset += text.length\n }\n return result\n}\n\n/**\n * 여러 <hp:t>에 걸친 텍스트 범위를 새 값으로 교체.\n * 첫 번째 걸리는 <hp:t>에 교체 텍스트를 넣고, 나머지는 해당 범위만큼 잘라냄.\n */\nfunction replaceTextRange(\n tNodes: TextNodeInfo[],\n globalStart: number,\n globalEnd: number,\n newValue: string,\n): void {\n let replaced = false\n for (const node of tNodes) {\n const nodeStart = node.offset\n const nodeEnd = node.offset + node.text.length\n\n if (nodeEnd <= globalStart || nodeStart >= globalEnd) continue\n\n const localStart = Math.max(0, globalStart - nodeStart)\n const localEnd = Math.min(node.text.length, globalEnd - nodeStart)\n\n if (!replaced) {\n const before = node.text.slice(0, localStart)\n const after = node.text.slice(localEnd)\n const newText = before + newValue + after\n clearChildren(node.element)\n node.element.appendChild(node.element.ownerDocument!.createTextNode(newText))\n replaced = true\n } else {\n const before = node.text.slice(0, localStart)\n const after = node.text.slice(localEnd)\n const newText = before + after\n clearChildren(node.element)\n node.element.appendChild(node.element.ownerDocument!.createTextNode(newText))\n }\n }\n}\n\n// ─── 인셀 패턴 교체 (체크박스/괄호 빈칸) ─────────\n\n/** 셀(<hp:tc>) 내 모든 <hp:t> 텍스트 노드를 오프셋과 함께 수집 (subList 순회 포함) */\nfunction collectCellTextNodes(tcEl: Element): TextNodeInfo[] {\n const tElements = findAllElements(tcEl, \"t\")\n const result: TextNodeInfo[] = []\n let offset = 0\n for (const t of tElements) {\n const text = t.textContent || \"\"\n result.push({ element: t, text, offset })\n offset += text.length\n }\n return result\n}\n\n/**\n * 셀 내 <hp:t> 노드들의 텍스트를 원본→교체 결과에 맞춰 반영.\n * 각 노드가 원본 텍스트에서 차지하는 범위를 추적하고,\n * 교체된 텍스트에서 같은 비율의 영역을 할당.\n */\nfunction applyTextReplacements(\n tNodes: TextNodeInfo[],\n originalFull: string,\n replacedFull: string,\n): void {\n if (originalFull === replacedFull) return\n\n // 단일 <hp:t> 노드면 간단히 전체 교체\n if (tNodes.length === 1) {\n clearChildren(tNodes[0].element)\n tNodes[0].element.appendChild(\n tNodes[0].element.ownerDocument!.createTextNode(replacedFull),\n )\n return\n }\n\n // 여러 노드: diff를 노드 경계에 맞춰 적용\n // 변경된 부분의 시작 오프셋을 찾아서 해당 노드만 교체\n let diffStart = 0\n while (diffStart < originalFull.length && diffStart < replacedFull.length &&\n originalFull[diffStart] === replacedFull[diffStart]) {\n diffStart++\n }\n let diffEndOrig = originalFull.length\n let diffEndRepl = replacedFull.length\n while (diffEndOrig > diffStart && diffEndRepl > diffStart &&\n originalFull[diffEndOrig - 1] === replacedFull[diffEndRepl - 1]) {\n diffEndOrig--\n diffEndRepl--\n }\n\n // 변경된 범위를 포함하는 노드에 교체 적용\n const newPart = replacedFull.slice(diffStart, diffEndRepl)\n replaceTextRange(tNodes, diffStart, diffEndOrig, newPart)\n}\n","/**\r\n * Markdown → HWPX 역변환\r\n *\r\n * 지원: 헤딩(h1~h6), 단락, 볼드, 이탤릭, 인라인코드, 코드블록,\r\n * 순서/비순서 리스트, 수평선, 인용문, 테이블\r\n * jszip으로 HWPX ZIP 패키징.\r\n */\r\n\r\nimport JSZip from \"jszip\"\r\n\r\nconst NS_SECTION = \"http://www.hancom.co.kr/hwpml/2011/section\"\r\nconst NS_PARA = \"http://www.hancom.co.kr/hwpml/2011/paragraph\"\r\nconst NS_HEAD = \"http://www.hancom.co.kr/hwpml/2011/head\"\r\nconst NS_OPF = \"http://www.idpf.org/2007/opf/\"\r\nconst NS_HPF = \"http://www.hancom.co.kr/schema/2011/hpf\"\r\nconst NS_OCF = \"urn:oasis:names:tc:opendocument:xmlns:container\"\r\n\r\n// ─── 스타일 ID 매핑 ─────────────────────────────────\r\n// charPr: 0=본문, 1=볼드, 2=이탤릭, 3=볼드이탤릭, 4=인라인코드, 5=h1, 6=h2, 7=h3, 8=h4~h6\r\n// paraPr: 0=본문, 1=h1, 2=h2, 3=h3, 4=h4~h6, 5=코드블록, 6=인용문, 7=리스트\r\n\r\nconst CHAR_NORMAL = 0\r\nconst CHAR_BOLD = 1\r\nconst CHAR_ITALIC = 2\r\nconst CHAR_BOLD_ITALIC = 3\r\nconst CHAR_CODE = 4\r\nconst CHAR_H1 = 5\r\nconst CHAR_H2 = 6\r\nconst CHAR_H3 = 7\r\nconst CHAR_H4 = 8\r\n\r\nconst PARA_NORMAL = 0\r\nconst PARA_H1 = 1\r\nconst PARA_H2 = 2\r\nconst PARA_H3 = 3\r\nconst PARA_H4 = 4\r\nconst PARA_CODE = 5\r\nconst PARA_QUOTE = 6\r\nconst PARA_LIST = 7\r\n\r\n/**\r\n * 마크다운 텍스트를 HWPX (ArrayBuffer)로 변환.\r\n */\r\nexport async function markdownToHwpx(markdown: string): Promise<ArrayBuffer> {\r\n const blocks = parseMarkdownToBlocks(markdown)\r\n const sectionXml = blocksToSectionXml(blocks)\r\n\r\n const zip = new JSZip()\r\n zip.file(\"mimetype\", \"application/hwp+zip\", { compression: \"STORE\" })\r\n zip.file(\"META-INF/container.xml\", generateContainerXml())\r\n zip.file(\"Contents/content.hpf\", generateManifest())\r\n zip.file(\"Contents/header.xml\", generateHeaderXml())\r\n zip.file(\"Contents/section0.xml\", sectionXml)\r\n // Preview/ — 한글 프로그램의 일부 버전(특히 macOS)이 존재 여부를 확인함\r\n zip.file(\"Preview/PrvText.txt\", buildPrvText(blocks))\r\n\r\n return await zip.generateAsync({ type: \"arraybuffer\" })\r\n}\r\n\r\n/** Preview/PrvText.txt — 문서 앞부분 텍스트 스냅샷 (최대 1KB) */\r\nfunction buildPrvText(blocks: MdBlock[]): string {\r\n const lines: string[] = []\r\n let bytes = 0\r\n for (const b of blocks) {\r\n const text = b.text || (b.rows ? b.rows.map(r => r.join(\" \")).join(\"\\n\") : \"\")\r\n if (!text) continue\r\n lines.push(text)\r\n bytes += text.length * 3\r\n if (bytes > 1024) break\r\n }\r\n return lines.join(\"\\n\").slice(0, 1024)\r\n}\r\n\r\n// ─── 마크다운 파싱 ───────────────────────────────────\r\n\r\ninterface MdBlock {\r\n type: \"paragraph\" | \"heading\" | \"table\" | \"code_block\" | \"hr\" | \"blockquote\" | \"list_item\"\r\n text?: string\r\n level?: number\r\n rows?: string[][]\r\n lang?: string\r\n ordered?: boolean\r\n indent?: number\r\n}\r\n\r\nfunction parseMarkdownToBlocks(md: string): MdBlock[] {\r\n const lines = md.split(\"\\n\")\r\n const blocks: MdBlock[] = []\r\n let i = 0\r\n\r\n while (i < lines.length) {\r\n const line = lines[i]\r\n\r\n // 빈 줄 스킵\r\n if (!line.trim()) { i++; continue }\r\n\r\n // 코드블록\r\n const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)$/)\r\n if (fenceMatch) {\r\n const fence = fenceMatch[1]\r\n const lang = fenceMatch[2].trim()\r\n const codeLines: string[] = []\r\n i++\r\n while (i < lines.length && !lines[i].startsWith(fence)) {\r\n codeLines.push(lines[i])\r\n i++\r\n }\r\n if (i < lines.length) i++ // 닫는 fence\r\n blocks.push({ type: \"code_block\", text: codeLines.join(\"\\n\"), lang })\r\n continue\r\n }\r\n\r\n // 수평선\r\n if (/^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(line.trim())) {\r\n blocks.push({ type: \"hr\" })\r\n i++; continue\r\n }\r\n\r\n // 헤딩\r\n const headingMatch = line.match(/^(#{1,6})\\s+(.+)$/)\r\n if (headingMatch) {\r\n blocks.push({ type: \"heading\", text: headingMatch[2].trim(), level: headingMatch[1].length })\r\n i++; continue\r\n }\r\n\r\n // 테이블\r\n if (line.trimStart().startsWith(\"|\")) {\r\n const tableRows: string[][] = []\r\n while (i < lines.length && lines[i].trimStart().startsWith(\"|\")) {\r\n const row = lines[i]\r\n if (/^[\\s|:\\-]+$/.test(row)) { i++; continue }\r\n const cells = row.split(\"|\").slice(1, -1).map(c => c.trim())\r\n if (cells.length > 0) tableRows.push(cells)\r\n i++\r\n }\r\n if (tableRows.length > 0) blocks.push({ type: \"table\", rows: tableRows })\r\n continue\r\n }\r\n\r\n // 인용문\r\n if (line.trimStart().startsWith(\"> \")) {\r\n const quoteLines: string[] = []\r\n while (i < lines.length && (lines[i].trimStart().startsWith(\"> \") || lines[i].trimStart().startsWith(\">\"))) {\r\n quoteLines.push(lines[i].replace(/^>\\s?/, \"\"))\r\n i++\r\n }\r\n for (const ql of quoteLines) {\r\n blocks.push({ type: \"blockquote\", text: ql.trim() || \"\" })\r\n }\r\n continue\r\n }\r\n\r\n // 리스트\r\n const listMatch = line.match(/^(\\s*)([-*+]|\\d+[.)]) (.+)$/)\r\n if (listMatch) {\r\n const indent = Math.floor(listMatch[1].length / 2)\r\n const ordered = /\\d/.test(listMatch[2])\r\n blocks.push({ type: \"list_item\", text: listMatch[3].trim(), ordered, indent })\r\n i++; continue\r\n }\r\n\r\n // 일반 단락\r\n blocks.push({ type: \"paragraph\", text: line.trim() })\r\n i++\r\n }\r\n\r\n return blocks\r\n}\r\n\r\n// ─── 인라인 마크다운 → 멀티 run ─────────────────────\r\n\r\ninterface InlineSpan {\r\n text: string\r\n bold: boolean\r\n italic: boolean\r\n code: boolean\r\n}\r\n\r\nfunction parseInlineMarkdown(text: string): InlineSpan[] {\r\n // 전처리: 마크다운 링크/이미지 → 텍스트만 추출\r\n text = text.replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\") //  → alt\r\n text = text.replace(/\\[([^\\]]*)\\]\\(([^)]*)\\)/g, (_, t, u) => t || u) // [text](url) → text or url\r\n // 전처리: ~~취소선~~ → 텍스트만\r\n text = text.replace(/~~([^~]+)~~/g, \"$1\")\r\n\r\n const spans: InlineSpan[] = []\r\n // 패턴: `code`, ***bolditalic***, **bold**, *italic*, __bold__, _italic_\r\n const regex = /(`[^`]+`|\\*{3}[^*]+\\*{3}|\\*{2}[^*]+\\*{2}|\\*[^*]+\\*|_{2}[^_]+_{2}|_[^_]+_)/g\r\n let lastIdx = 0\r\n\r\n for (const match of text.matchAll(regex)) {\r\n const idx = match.index!\r\n if (idx > lastIdx) {\r\n spans.push({ text: text.slice(lastIdx, idx), bold: false, italic: false, code: false })\r\n }\r\n const raw = match[0]\r\n if (raw.startsWith(\"`\")) {\r\n spans.push({ text: raw.slice(1, -1), bold: false, italic: false, code: true })\r\n } else if (raw.startsWith(\"***\") || raw.startsWith(\"___\")) {\r\n spans.push({ text: raw.slice(3, -3), bold: true, italic: true, code: false })\r\n } else if (raw.startsWith(\"**\") || raw.startsWith(\"__\")) {\r\n spans.push({ text: raw.slice(2, -2), bold: true, italic: false, code: false })\r\n } else {\r\n spans.push({ text: raw.slice(1, -1), bold: false, italic: true, code: false })\r\n }\r\n lastIdx = idx + raw.length\r\n }\r\n if (lastIdx < text.length) {\r\n spans.push({ text: text.slice(lastIdx), bold: false, italic: false, code: false })\r\n }\r\n if (spans.length === 0) {\r\n spans.push({ text, bold: false, italic: false, code: false })\r\n }\r\n return spans\r\n}\r\n\r\nfunction spanToCharPrId(span: InlineSpan): number {\r\n if (span.code) return CHAR_CODE\r\n if (span.bold && span.italic) return CHAR_BOLD_ITALIC\r\n if (span.bold) return CHAR_BOLD\r\n if (span.italic) return CHAR_ITALIC\r\n return CHAR_NORMAL\r\n}\r\n\r\n// ─── XML 생성 헬퍼 ───────────────────────────────────\r\n\r\nfunction escapeXml(text: string): string {\r\n return text\r\n .replace(/&/g, \"&\")\r\n .replace(/</g, \"<\")\r\n .replace(/>/g, \">\")\r\n .replace(/\"/g, \""\")\r\n}\r\n\r\nfunction generateRuns(text: string, defaultCharPr: number = CHAR_NORMAL): string {\r\n const spans = parseInlineMarkdown(text)\r\n return spans.map(span => {\r\n const charId = span.code || span.bold || span.italic ? spanToCharPrId(span) : defaultCharPr\r\n return `<hp:run charPrIDRef=\"${charId}\"><hp:t>${escapeXml(span.text)}</hp:t></hp:run>`\r\n }).join(\"\")\r\n}\r\n\r\nfunction generateParagraph(text: string, paraPrId: number = PARA_NORMAL, charPrId: number = CHAR_NORMAL): string {\r\n if (paraPrId === PARA_CODE) {\r\n // 코드블록은 인라인 파싱 안 함\r\n return `<hp:p paraPrIDRef=\"${paraPrId}\" styleIDRef=\"0\"><hp:run charPrIDRef=\"${CHAR_CODE}\"><hp:t>${escapeXml(text)}</hp:t></hp:run></hp:p>`\r\n }\r\n const runs = generateRuns(text, charPrId)\r\n return `<hp:p paraPrIDRef=\"${paraPrId}\" styleIDRef=\"0\">${runs}</hp:p>`\r\n}\r\n\r\nfunction headingParaPrId(level: number): number {\r\n if (level === 1) return PARA_H1\r\n if (level === 2) return PARA_H2\r\n if (level === 3) return PARA_H3\r\n return PARA_H4\r\n}\r\n\r\nfunction headingCharPrId(level: number): number {\r\n if (level === 1) return CHAR_H1\r\n if (level === 2) return CHAR_H2\r\n if (level === 3) return CHAR_H3\r\n return CHAR_H4\r\n}\r\n\r\n// ─── HWPX 구조 파일 생성 ─────────────────────────────\r\n\r\nfunction generateContainerXml(): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\r\n<ocf:container xmlns:ocf=\"${NS_OCF}\" xmlns:hpf=\"${NS_HPF}\">\r\n <ocf:rootfiles>\r\n <ocf:rootfile full-path=\"Contents/content.hpf\" media-type=\"application/hwpml-package+xml\"/>\r\n </ocf:rootfiles>\r\n</ocf:container>`\r\n}\r\n\r\nfunction generateManifest(): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\r\n<opf:package xmlns:opf=\"${NS_OPF}\" xmlns:hpf=\"${NS_HPF}\" xmlns:hh=\"${NS_HEAD}\">\r\n <opf:manifest>\r\n <opf:item id=\"header\" href=\"Contents/header.xml\" media-type=\"application/xml\"/>\r\n <opf:item id=\"section0\" href=\"Contents/section0.xml\" media-type=\"application/xml\"/>\r\n </opf:manifest>\r\n <opf:spine>\r\n <opf:itemref idref=\"header\" linear=\"no\"/>\r\n <opf:itemref idref=\"section0\" linear=\"yes\"/>\r\n </opf:spine>\r\n</opf:package>`\r\n}\r\n\r\n// ─── charPr 생성 헬퍼 ───────────────────────────────\r\n\r\nfunction charPr(id: number, height: number, bold: boolean, italic: boolean, fontId: number = 0): string {\r\n const boldAttr = bold ? ` bold=\"1\"` : \"\"\r\n const italicAttr = italic ? ` italic=\"1\"` : \"\"\r\n return ` <hh:charPr id=\"${id}\" height=\"${height}\" textColor=\"#000000\" shadeColor=\"none\" useFontSpace=\"0\" useKerning=\"0\" symMark=\"NONE\" borderFillIDRef=\"0\"${boldAttr}${italicAttr}>\r\n <hh:fontRef hangul=\"${fontId}\" latin=\"${fontId}\" hanja=\"${fontId}\" japanese=\"${fontId}\" other=\"${fontId}\" symbol=\"${fontId}\" user=\"${fontId}\"/>\r\n <hh:ratio hangul=\"100\" latin=\"100\" hanja=\"100\" japanese=\"100\" other=\"100\" symbol=\"100\" user=\"100\"/>\r\n <hh:spacing hangul=\"0\" latin=\"0\" hanja=\"0\" japanese=\"0\" other=\"0\" symbol=\"0\" user=\"0\"/>\r\n <hh:relSz hangul=\"100\" latin=\"100\" hanja=\"100\" japanese=\"100\" other=\"100\" symbol=\"100\" user=\"100\"/>\r\n <hh:offset hangul=\"0\" latin=\"0\" hanja=\"0\" japanese=\"0\" other=\"0\" symbol=\"0\" user=\"0\"/>\r\n </hh:charPr>`\r\n}\r\n\r\n// ─── paraPr 생성 헬퍼 ───────────────────────────────\r\n\r\nfunction paraPr(id: number, opts: { align?: string; spaceBefore?: number; spaceAfter?: number; lineSpacing?: number; indent?: number } = {}): string {\r\n const { align = \"JUSTIFY\", spaceBefore = 0, spaceAfter = 0, lineSpacing = 160, indent = 0 } = opts\r\n return ` <hh:paraPr id=\"${id}\" tabPrIDRef=\"0\" condense=\"0\" fontLineHeight=\"0\" snapToGrid=\"1\" suppressLineNumbers=\"0\" checked=\"0\" textDir=\"AUTO\">\r\n <hh:align horizontal=\"${align}\" vertical=\"BASELINE\"/>\r\n <hh:heading type=\"NONE\" idRef=\"0\" level=\"0\"/>\r\n <hh:breakSetting breakLatinWord=\"KEEP_WORD\" breakNonLatinWord=\"BREAK_WORD\" widowOrphan=\"0\" keepWithNext=\"0\" keepLines=\"0\" pageBreakBefore=\"0\" lineWrap=\"BREAK\"/>\r\n <hh:autoSpacing eAsianEng=\"0\" eAsianNum=\"0\"/>\r\n <hh:margin indent=\"${indent}\" left=\"0\" right=\"0\" prev=\"${spaceBefore}\" next=\"${spaceAfter}\"/>\r\n <hh:lineSpacing type=\"PERCENT\" value=\"${lineSpacing}\"/>\r\n <hh:border borderFillIDRef=\"0\" offsetLeft=\"0\" offsetRight=\"0\" offsetTop=\"0\" offsetBottom=\"0\" connect=\"0\" ignoreMargin=\"0\"/>\r\n </hh:paraPr>`\r\n}\r\n\r\nfunction generateHeaderXml(): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\r\n<hh:head xmlns:hh=\"${NS_HEAD}\" xmlns:hp=\"${NS_PARA}\" version=\"1.4\" secCnt=\"1\">\r\n <hh:beginNum page=\"1\" footnote=\"1\" endnote=\"1\" pic=\"1\" tbl=\"1\" equation=\"1\"/>\r\n <hh:refList>\r\n <hh:fontfaces itemCnt=\"7\">\r\n <hh:fontface lang=\"HANGUL\" fontCnt=\"2\">\r\n <hh:font id=\"0\" face=\"함초롬바탕\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"4\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n <hh:font id=\"1\" face=\"함초롬돋움\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"4\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n <hh:fontface lang=\"LATIN\" fontCnt=\"2\">\r\n <hh:font id=\"0\" face=\"Times New Roman\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_OLDSTYLE\" weight=\"5\" proportion=\"4\" contrast=\"2\" strokeVariation=\"0\" armStyle=\"0\" letterform=\"0\" midline=\"0\" xHeight=\"4\"/>\r\n </hh:font>\r\n <hh:font id=\"1\" face=\"Consolas\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_MODERN\" weight=\"5\" proportion=\"0\" contrast=\"0\" strokeVariation=\"0\" armStyle=\"0\" letterform=\"0\" midline=\"0\" xHeight=\"0\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n <hh:fontface lang=\"HANJA\" fontCnt=\"1\">\r\n <hh:font id=\"0\" face=\"함초롬바탕\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"4\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n <hh:fontface lang=\"JAPANESE\" fontCnt=\"1\">\r\n <hh:font id=\"0\" face=\"굴림\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"0\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n <hh:fontface lang=\"OTHER\" fontCnt=\"1\">\r\n <hh:font id=\"0\" face=\"굴림\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"0\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n <hh:fontface lang=\"SYMBOL\" fontCnt=\"1\">\r\n <hh:font id=\"0\" face=\"Symbol\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"0\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n <hh:fontface lang=\"USER\" fontCnt=\"1\">\r\n <hh:font id=\"0\" face=\"굴림\" type=\"TTF\" isEmbedded=\"0\">\r\n <hh:typeInfo familyType=\"FCAT_GOTHIC\" weight=\"6\" proportion=\"0\" contrast=\"0\" strokeVariation=\"1\" armStyle=\"1\" letterform=\"1\" midline=\"1\" xHeight=\"1\"/>\r\n </hh:font>\r\n </hh:fontface>\r\n </hh:fontfaces>\r\n <hh:borderFills itemCnt=\"2\">\r\n <hh:borderFill id=\"0\" threeD=\"0\" shadow=\"0\" centerLine=\"0\" breakCellSeparateLine=\"0\">\r\n <hh:slash type=\"NONE\" Crooked=\"0\" isCounter=\"0\"/>\r\n <hh:backSlash type=\"NONE\" Crooked=\"0\" isCounter=\"0\"/>\r\n <hh:leftBorder type=\"NONE\" width=\"0.1mm\" color=\"#000000\"/>\r\n <hh:rightBorder type=\"NONE\" width=\"0.1mm\" color=\"#000000\"/>\r\n <hh:topBorder type=\"NONE\" width=\"0.1mm\" color=\"#000000\"/>\r\n <hh:bottomBorder type=\"NONE\" width=\"0.1mm\" color=\"#000000\"/>\r\n <hh:diagonal type=\"NONE\" width=\"0.1mm\" color=\"#000000\"/>\r\n <hh:fillInfo/>\r\n </hh:borderFill>\r\n <hh:borderFill id=\"1\" threeD=\"0\" shadow=\"0\" centerLine=\"0\" breakCellSeparateLine=\"0\">\r\n <hh:slash type=\"NONE\" Crooked=\"0\" isCounter=\"0\"/>\r\n <hh:backSlash type=\"NONE\" Crooked=\"0\" isCounter=\"0\"/>\r\n <hh:leftBorder type=\"SOLID\" width=\"0.12mm\" color=\"#000000\"/>\r\n <hh:rightBorder type=\"SOLID\" width=\"0.12mm\" color=\"#000000\"/>\r\n <hh:topBorder type=\"SOLID\" width=\"0.12mm\" color=\"#000000\"/>\r\n <hh:bottomBorder type=\"SOLID\" width=\"0.12mm\" color=\"#000000\"/>\r\n <hh:diagonal type=\"NONE\" width=\"0.1mm\" color=\"#000000\"/>\r\n <hh:fillInfo/>\r\n </hh:borderFill>\r\n </hh:borderFills>\r\n <hh:charProperties itemCnt=\"9\">\r\n${charPr(0, 1000, false, false)}\r\n${charPr(1, 1000, true, false)}\r\n${charPr(2, 1000, false, true)}\r\n${charPr(3, 1000, true, true)}\r\n${charPr(4, 900, false, false, 1)}\r\n${charPr(5, 1800, true, false, 1)}\r\n${charPr(6, 1400, true, false, 1)}\r\n${charPr(7, 1200, true, false, 1)}\r\n${charPr(8, 1100, true, false, 1)}\r\n </hh:charProperties>\r\n <hh:tabProperties itemCnt=\"0\"/>\r\n <hh:numberings itemCnt=\"0\"/>\r\n <hh:bullets itemCnt=\"0\"/>\r\n <hh:paraProperties itemCnt=\"8\">\r\n${paraPr(0)}\r\n${paraPr(1, { align: \"LEFT\", spaceBefore: 800, spaceAfter: 200, lineSpacing: 180 })}\r\n${paraPr(2, { align: \"LEFT\", spaceBefore: 600, spaceAfter: 150, lineSpacing: 170 })}\r\n${paraPr(3, { align: \"LEFT\", spaceBefore: 400, spaceAfter: 100, lineSpacing: 160 })}\r\n${paraPr(4, { align: \"LEFT\", spaceBefore: 300, spaceAfter: 100, lineSpacing: 160 })}\r\n${paraPr(5, { align: \"LEFT\", lineSpacing: 130, indent: 400 })}\r\n${paraPr(6, { align: \"LEFT\", lineSpacing: 150, indent: 600 })}\r\n${paraPr(7, { align: \"LEFT\", lineSpacing: 160, indent: 600 })}\r\n </hh:paraProperties>\r\n <hh:styles itemCnt=\"1\">\r\n <hh:style id=\"0\" type=\"PARA\" name=\"바탕글\" engName=\"Normal\" paraPrIDRef=\"0\" charPrIDRef=\"0\" nextStyleIDRef=\"0\" langIDRef=\"1042\" lockForm=\"0\"/>\r\n </hh:styles>\r\n </hh:refList>\r\n <hh:compatibleDocument targetProgram=\"HWP2018\"/>\r\n</hh:head>`\r\n}\r\n\r\n// ─── 섹션 속성 (공문서 표준 여백) ────────────────────\r\n\r\nfunction generateSecPr(): string {\r\n // A4: 210mm × 297mm → 59528 × 84188 HWPUNIT (1mm ≈ 283.46 HWPUNIT)\r\n // 공문서 표준: 위 30mm(8504), 아래 15mm(4252), 왼쪽 20mm(5670), 오른쪽 15mm(4252)\r\n // 머리말 10mm(2835), 꼬리말 10mm(2835)\r\n return `<hp:secPr textDirection=\"HORIZONTAL\" spaceColumns=\"1134\" tabStop=\"8000\" outlineShapeIDRef=\"0\" memoShapeIDRef=\"0\" textVerticalWidthHead=\"0\" masterPageCnt=\"0\">` +\r\n `<hp:grid lineGrid=\"0\" charGrid=\"0\" wonggojiFormat=\"0\"/>` +\r\n `<hp:startNum pageStartsOn=\"BOTH\" page=\"0\" pic=\"0\" tbl=\"0\" equation=\"0\"/>` +\r\n `<hp:visibility hideFirstHeader=\"0\" hideFirstFooter=\"0\" hideFirstMasterPage=\"0\" border=\"SHOW_ALL\" fill=\"SHOW_ALL\" hideFirstPageNum=\"0\" hideFirstEmptyLine=\"0\" showLineNumber=\"0\"/>` +\r\n `<hp:pagePr landscape=\"WIDELY\" width=\"59528\" height=\"84188\" gutterType=\"LEFT_ONLY\">` +\r\n `<hp:margin header=\"2835\" footer=\"2835\" gutter=\"0\" left=\"5670\" right=\"4252\" top=\"8504\" bottom=\"4252\"/>` +\r\n `</hp:pagePr>` +\r\n `<hp:footNotePr><hp:autoNumFormat type=\"DIGIT\" userChar=\"\" prefixChar=\"\" suffixChar=\")\" supscript=\"0\"/><hp:noteLine length=\"-1\" type=\"SOLID\" width=\"0.12 mm\" color=\"#000000\"/><hp:noteSpacing betweenNotes=\"283\" belowLine=\"567\" aboveLine=\"850\"/><hp:numbering type=\"CONTINUOUS\" newNum=\"1\"/><hp:placement place=\"EACH_COLUMN\" beneathText=\"0\"/></hp:footNotePr>` +\r\n `<hp:endNotePr><hp:autoNumFormat type=\"DIGIT\" userChar=\"\" prefixChar=\"\" suffixChar=\")\" supscript=\"0\"/><hp:noteLine length=\"14692344\" type=\"SOLID\" width=\"0.12 mm\" color=\"#000000\"/><hp:noteSpacing betweenNotes=\"0\" belowLine=\"567\" aboveLine=\"850\"/><hp:numbering type=\"CONTINUOUS\" newNum=\"1\"/><hp:placement place=\"END_OF_DOCUMENT\" beneathText=\"0\"/></hp:endNotePr>` +\r\n `</hp:secPr>`\r\n}\r\n\r\n// ─── 테이블 생성 ─────────────────────────────────────\r\n//\r\n// HWPX 스펙 완전 준수 버전 — 한글 프로그램(Windows/macOS)이 문서를 거부하지 않으려면\r\n// <hp:tbl> 필수 속성 + <hp:sz>/<hp:pos>/<hp:outMargin>/<hp:inMargin> + 각 cell의\r\n// <hp:subList> 래퍼, <hp:cellAddr>, <hp:cellSz>, <hp:cellMargin>이 전부 있어야 함.\r\n// 또한 테이블은 paragraph 안의 <hp:run><hp:ctrl>... 로 감싸야 한다.\r\n//\r\n// 이슈 #4 참고: v2.4.1 이전엔 최소 스켈레톤만 내서 macOS 한글이 \"파일이 깨졌다\"며 거부.\r\n\r\n// 기본 셀 크기 (HWPUnit) — A4 기준 적당한 기본값\r\nconst TABLE_ID_BASE = 1000\r\nlet tableIdCounter = TABLE_ID_BASE\r\nfunction nextTableId(): number { return ++tableIdCounter }\r\n\r\nfunction generateTable(rows: string[][]): string {\r\n const rowCnt = rows.length\r\n const colCnt = Math.max(...rows.map(r => r.length), 1)\r\n // A4 portrait: 폭 약 44000 HWPUnit 사용 가능 → colCnt로 균등 분배\r\n const cellW = Math.floor(44000 / colCnt)\r\n const cellH = 1500 // 기본 행 높이\r\n const tblW = cellW * colCnt\r\n const tblH = cellH * rowCnt\r\n\r\n const tblId = nextTableId()\r\n\r\n const trElements = rows.map((row, rowIdx) => {\r\n // 부족한 셀은 빈 문자열로 채워 colCnt 맞춤\r\n const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill(\"\")] : row\r\n const tdElements = cells.map((cell, colIdx) => {\r\n const runs = generateRuns(cell)\r\n const p = `<hp:p paraPrIDRef=\"0\" styleIDRef=\"0\">${runs}</hp:p>`\r\n // <hp:tc> 필수 속성 + subList + cellAddr + cellSpan + cellSz + cellMargin\r\n return `<hp:tc name=\"\" header=\"${rowIdx === 0 ? 1 : 0}\" hasMargin=\"0\" protect=\"0\" editable=\"1\" dirty=\"0\" borderFillIDRef=\"1\">`\r\n + `<hp:subList id=\"\" textDirection=\"HORIZONTAL\" lineWrap=\"BREAK\" vertAlign=\"TOP\" linkListIDRef=\"0\" linkListNextIDRef=\"0\" textWidth=\"0\" textHeight=\"0\" hasTextRef=\"0\" hasNumRef=\"0\">${p}</hp:subList>`\r\n + `<hp:cellAddr colAddr=\"${colIdx}\" rowAddr=\"${rowIdx}\"/>`\r\n + `<hp:cellSpan colSpan=\"1\" rowSpan=\"1\"/>`\r\n + `<hp:cellSz width=\"${cellW}\" height=\"${cellH}\"/>`\r\n + `<hp:cellMargin left=\"141\" right=\"141\" top=\"141\" bottom=\"141\"/>`\r\n + `</hp:tc>`\r\n }).join(\"\")\r\n return `<hp:tr>${tdElements}</hp:tr>`\r\n }).join(\"\")\r\n\r\n // <hp:tbl>에 필수 속성 + <hp:sz>/<hp:outMargin>/<hp:inMargin> (pos는 inline-level 기준)\r\n const tblInner = `<hp:sz width=\"${tblW}\" widthRelTo=\"ABSOLUTE\" height=\"${tblH}\" heightRelTo=\"ABSOLUTE\" protect=\"0\"/>`\r\n + `<hp:pos treatAsChar=\"1\" affectLSpacing=\"0\" flowWithText=\"0\" allowOverlap=\"0\" holdAnchorAndSO=\"0\" vertRelTo=\"PARA\" horzRelTo=\"PARA\" vertAlign=\"TOP\" horzAlign=\"LEFT\" vertOffset=\"0\" horzOffset=\"0\"/>`\r\n + `<hp:outMargin left=\"0\" right=\"0\" top=\"0\" bottom=\"0\"/>`\r\n + `<hp:inMargin left=\"510\" right=\"510\" top=\"141\" bottom=\"141\"/>`\r\n + trElements\r\n\r\n const tbl = `<hp:tbl id=\"${tblId}\" zOrder=\"0\" numberingType=\"TABLE\" pageBreak=\"CELL\" repeatHeader=\"0\" rowCnt=\"${rowCnt}\" colCnt=\"${colCnt}\" cellSpacing=\"0\" borderFillIDRef=\"1\" noShading=\"0\">${tblInner}</hp:tbl>`\r\n\r\n // 테이블은 paragraph 안의 run → 가 아니라 별도 p로 감쌈 (block-level inline-anchored)\r\n return `<hp:p paraPrIDRef=\"0\" styleIDRef=\"0\"><hp:run charPrIDRef=\"0\">${tbl}</hp:run></hp:p>`\r\n}\r\n\r\n// ─── 섹션 XML 생성 ──────────────────────────────────\r\n\r\nfunction blocksToSectionXml(blocks: MdBlock[]): string {\r\n const paraXmls: string[] = []\r\n let isFirst = true\r\n\r\n for (const block of blocks) {\r\n let xml = \"\"\r\n switch (block.type) {\r\n case \"heading\": {\r\n const pId = headingParaPrId(block.level || 1)\r\n const cId = headingCharPrId(block.level || 1)\r\n xml = generateParagraph(block.text || \"\", pId, cId)\r\n break\r\n }\r\n case \"paragraph\":\r\n xml = generateParagraph(block.text || \"\")\r\n break\r\n case \"code_block\": {\r\n const codeLines = (block.text || \"\").split(\"\\n\")\r\n xml = codeLines.map(line => generateParagraph(line || \" \", PARA_CODE)).join(\"\\n \")\r\n break\r\n }\r\n case \"blockquote\":\r\n xml = generateParagraph(block.text || \"\", PARA_QUOTE)\r\n break\r\n case \"list_item\": {\r\n const marker = block.ordered ? `${(block.indent || 0) + 1}. ` : \"· \"\r\n const indentPrefix = \" \".repeat(block.indent || 0)\r\n xml = generateParagraph(indentPrefix + marker + (block.text || \"\"), PARA_LIST)\r\n break\r\n }\r\n case \"hr\":\r\n // 수평선 — 긴 대시로 대체\r\n xml = `<hp:p paraPrIDRef=\"0\" styleIDRef=\"0\"><hp:run charPrIDRef=\"0\"><hp:t>────────────────────────────────────────</hp:t></hp:run></hp:p>`\r\n break\r\n case \"table\":\r\n if (block.rows) {\r\n if (isFirst) {\r\n // 테이블이 첫 블록이면 빈 단락에 secPr\r\n const secRun = `<hp:run charPrIDRef=\"0\">${generateSecPr()}<hp:t></hp:t></hp:run>`\r\n paraXmls.push(`<hp:p paraPrIDRef=\"0\" styleIDRef=\"0\">${secRun}</hp:p>`)\r\n isFirst = false\r\n }\r\n xml = generateTable(block.rows)\r\n }\r\n break\r\n }\r\n\r\n if (!xml) continue\r\n\r\n // 첫 번째 단락에 secPr 주입\r\n if (isFirst && block.type !== \"table\") {\r\n xml = xml.replace(\r\n /<hp:run charPrIDRef=\"(\\d+)\">/,\r\n `<hp:run charPrIDRef=\"$1\">${generateSecPr()}`\r\n )\r\n isFirst = false\r\n }\r\n\r\n paraXmls.push(xml)\r\n }\r\n\r\n // 블록이 없으면 빈 단락\r\n if (paraXmls.length === 0) {\r\n paraXmls.push(`<hp:p paraPrIDRef=\"0\" styleIDRef=\"0\"><hp:run charPrIDRef=\"0\">${generateSecPr()}<hp:t></hp:t></hp:run></hp:p>`)\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\r\n<hs:sec xmlns:hs=\"${NS_SECTION}\" xmlns:hp=\"${NS_PARA}\">\r\n ${paraXmls.join(\"\\n \")}\r\n</hs:sec>`\r\n}\r\n","/**\r\n * kordoc — 모두 파싱해버리겠다\r\n *\r\n * HWP, HWPX, PDF → Markdown 변환 통합 라이브러리\r\n */\r\n\r\nimport { readFile } from \"fs/promises\"\r\nimport { detectFormat, detectZipFormat, isHwpxFile, isOldHwpFile, isPdfFile, isZipFile } from \"./detect.js\"\r\nimport { parseHwpxDocument } from \"./hwpx/parser.js\"\r\nimport { parseHwp5Document } from \"./hwp5/parser.js\"\r\nimport { isComFallbackAvailable, extractTextViaCom, comResultToParseResult } from \"./hwpx/com-fallback.js\"\r\nimport { isDistributionSentinel } from \"./hwp5/sentinel.js\"\r\n// pdfjs-dist는 optional peer dep (37MB) — PDF 안 쓰는 사용자를 위해 dynamic import\r\n// import { parsePdfDocument } from \"./pdf/parser.js\"\r\nimport { parseXlsxDocument } from \"./xlsx/parser.js\"\r\nimport { parseDocxDocument } from \"./docx/parser.js\"\r\nimport { parseHwpmlDocument } from \"./hwpml/parser.js\"\r\nimport type { ParseResult, ParseOptions } from \"./types.js\"\r\nimport { classifyError, toArrayBuffer } from \"./utils.js\"\r\nimport { fillFormFields } from \"./form/filler.js\"\r\nimport type { FillResult } from \"./form/filler.js\"\r\nimport { fillHwpx } from \"./form/filler-hwpx.js\"\r\nimport type { HwpxFillResult } from \"./form/filler-hwpx.js\"\r\nimport { blocksToMarkdown } from \"./table/builder.js\"\r\nimport { markdownToHwpx } from \"./hwpx/generator.js\"\r\n\r\n// ─── 메인 API ────────────────────────────────────────\r\n\r\n/**\r\n * 파일 버퍼를 자동 감지하여 Markdown으로 변환\r\n *\r\n * @example\r\n * ```ts\r\n * import { parse } from \"kordoc\"\r\n * // 파일 경로로 파싱\r\n * const result = await parse(\"document.hwp\")\r\n * // 또는 Buffer로 파싱\r\n * const result = await parse(buffer)\r\n * ```\r\n */\r\nexport async function parse(input: string | ArrayBuffer | Buffer, options?: ParseOptions): Promise<ParseResult> {\r\n let buffer: ArrayBuffer\r\n // 파일 경로 입력 시 filePath를 options에 자동 설정 (DRM COM fallback에 필요)\r\n const opts = typeof input === \"string\" && !options?.filePath\r\n ? { ...options, filePath: input }\r\n : options\r\n if (typeof input === \"string\") {\r\n try {\r\n const buf = await readFile(input)\r\n buffer = toArrayBuffer(buf)\r\n } catch (err) {\r\n const msg = err instanceof Error && \"code\" in err && (err as NodeJS.ErrnoException).code === \"ENOENT\"\r\n ? `파일을 찾을 수 없습니다: ${input}`\r\n : `파일 읽기 실패: ${input}`\r\n return { success: false, fileType: \"unknown\", error: msg, code: \"PARSE_ERROR\" }\r\n }\r\n } else if (Buffer.isBuffer(input)) {\r\n buffer = toArrayBuffer(input)\r\n } else {\r\n buffer = input\r\n }\r\n\r\n if (!buffer || buffer.byteLength === 0) {\r\n return { success: false, fileType: \"unknown\", error: \"빈 버퍼이거나 유효하지 않은 입력입니다.\", code: \"EMPTY_INPUT\" }\r\n }\r\n const format = detectFormat(buffer)\r\n\r\n switch (format) {\r\n case \"hwpx\": {\r\n // ZIP 기반 포맷 세분화: HWPX, XLSX, DOCX 구분\r\n const zipFormat = await detectZipFormat(buffer)\r\n if (zipFormat === \"xlsx\") return parseXlsx(buffer, opts)\r\n if (zipFormat === \"docx\") return parseDocx(buffer, opts)\r\n return parseHwpx(buffer, opts)\r\n }\r\n case \"hwp\":\r\n return parseHwp(buffer, opts)\r\n case \"hwpml\":\r\n return parseHwpml(buffer, opts)\r\n case \"pdf\":\r\n return parsePdf(buffer, opts)\r\n default:\r\n return { success: false, fileType: \"unknown\", error: \"지원하지 않는 파일 형식입니다.\", code: \"UNSUPPORTED_FORMAT\" }\r\n }\r\n}\r\n\r\n// ─── 포맷별 API ──────────────────────────────────────\r\n\r\n/** HWPX 파일을 Markdown으로 변환 */\r\nexport async function parseHwpx(buffer: ArrayBuffer, options?: ParseOptions): Promise<ParseResult> {\r\n try {\r\n const { markdown, blocks, metadata, outline, warnings, images } = await parseHwpxDocument(buffer, options)\r\n return { success: true, fileType: \"hwpx\", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : undefined }\r\n } catch (err) {\r\n return { success: false, fileType: \"hwpx\", error: err instanceof Error ? err.message : \"HWPX 파싱 실패\", code: classifyError(err) }\r\n }\r\n}\r\n\r\n/** HWP 5.x 바이너리 파일을 Markdown으로 변환 */\r\nexport async function parseHwp(buffer: ArrayBuffer, options?: ParseOptions): Promise<ParseResult> {\r\n try {\r\n const { markdown, blocks, metadata, outline, warnings, images } = parseHwp5Document(Buffer.from(buffer), options)\r\n\r\n // 배포용 HWP 5.x 감지 — 본문이 \"상위 버전의 배포용 문서입니다...\" 플레이스홀더뿐이면\r\n // COM fallback으로 재시도 (Windows + 한컴오피스 환경에서만). 이슈 #25 대응.\r\n if (isDistributionSentinel(markdown) && isComFallbackAvailable() && options?.filePath) {\r\n try {\r\n const { pages, pageCount, warnings: comWarns } = extractTextViaCom(options.filePath)\r\n if (pages.some(p => p && p.trim().length > 0)) {\r\n const com = comResultToParseResult(pages, pageCount, comWarns)\r\n return {\r\n success: true,\r\n fileType: \"hwp\",\r\n markdown: com.markdown,\r\n blocks: com.blocks,\r\n metadata: com.metadata,\r\n warnings: com.warnings,\r\n }\r\n }\r\n } catch {\r\n // COM 실패 시 기존 결과(경고 문자열 포함) 그대로 반환\r\n }\r\n }\r\n\r\n return { success: true, fileType: \"hwp\", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : undefined }\r\n } catch (err) {\r\n return { success: false, fileType: \"hwp\", error: err instanceof Error ? err.message : \"HWP 파싱 실패\", code: classifyError(err) }\r\n }\r\n}\r\n\r\n/** PDF 파일에서 텍스트를 추출하여 Markdown으로 변환 */\r\nexport async function parsePdf(buffer: ArrayBuffer, options?: ParseOptions): Promise<ParseResult> {\r\n let parsePdfDocument: typeof import(\"./pdf/parser.js\").parsePdfDocument\r\n try {\r\n const mod = await import(\"./pdf/parser.js\")\r\n parsePdfDocument = mod.parsePdfDocument\r\n } catch {\r\n return {\r\n success: false, fileType: \"pdf\",\r\n error: \"PDF 파싱에 pdfjs-dist가 필요합니다. 설치: npm install pdfjs-dist\",\r\n code: \"MISSING_DEPENDENCY\",\r\n }\r\n }\r\n try {\r\n const { markdown, blocks, metadata, outline, warnings, isImageBased } = await parsePdfDocument(buffer, options)\r\n return { success: true, fileType: \"pdf\", markdown, blocks, metadata, outline, warnings, isImageBased }\r\n } catch (err) {\r\n const isImageBased = err instanceof Error && \"isImageBased\" in err ? true : undefined\r\n return { success: false, fileType: \"pdf\", error: err instanceof Error ? err.message : \"PDF 파싱 실패\", code: classifyError(err), isImageBased }\r\n }\r\n}\r\n\r\n/** XLSX 파일을 Markdown으로 변환 */\r\nexport async function parseXlsx(buffer: ArrayBuffer, options?: ParseOptions): Promise<ParseResult> {\r\n try {\r\n const { markdown, blocks, metadata, warnings } = await parseXlsxDocument(buffer, options)\r\n return { success: true, fileType: \"xlsx\", markdown, blocks, metadata, warnings }\r\n } catch (err) {\r\n return { success: false, fileType: \"xlsx\", error: err instanceof Error ? err.message : \"XLSX 파싱 실패\", code: classifyError(err) }\r\n }\r\n}\r\n\r\n/** DOCX 파일을 Markdown으로 변환 */\r\nexport async function parseDocx(buffer: ArrayBuffer, options?: ParseOptions): Promise<ParseResult> {\r\n try {\r\n const { markdown, blocks, metadata, outline, warnings, images } = await parseDocxDocument(buffer, options)\r\n return { success: true, fileType: \"docx\", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : undefined }\r\n } catch (err) {\r\n return { success: false, fileType: \"docx\", error: err instanceof Error ? err.message : \"DOCX 파싱 실패\", code: classifyError(err) }\r\n }\r\n}\r\n\r\n/** HWPML (XML 기반 한컴 문서) 파일을 Markdown으로 변환 */\r\nexport async function parseHwpml(buffer: ArrayBuffer, options?: ParseOptions): Promise<ParseResult> {\r\n try {\r\n const { markdown, blocks, metadata, outline, warnings } = parseHwpmlDocument(buffer, options)\r\n return { success: true, fileType: \"hwpml\", markdown, blocks, metadata, outline, warnings }\r\n } catch (err) {\r\n return { success: false, fileType: \"hwpml\", error: err instanceof Error ? err.message : \"HWPML 파싱 실패\", code: classifyError(err) }\r\n }\r\n}\r\n\r\n// ─── 서식 채우기 API ────────────────────────────────\r\n\r\n/**\r\n * 서식 채우기 출력 포맷\r\n * - \"markdown\": 마크다운 텍스트\r\n * - \"hwpx\": 새로 생성한 HWPX (스타일 초기화)\r\n * - \"hwpx-preserve\": 원본 HWPX ZIP 직접 수정 (스타일 100% 보존, HWPX 입력만 가능)\r\n */\r\nexport type FillOutputFormat = \"markdown\" | \"hwpx\" | \"hwpx-preserve\"\r\n\r\n/** 서식 채우기 결과 */\r\nexport interface FillFormOutput {\r\n /** 채워진 문서 (markdown: string, hwpx/hwpx-preserve: ArrayBuffer) */\r\n output: string | ArrayBuffer\r\n /** 출력 포맷 */\r\n format: FillOutputFormat\r\n /** 채우기 상세 — filled 필드 목록 + unmatched 라벨 */\r\n fill: { filled: import(\"./types.js\").FormField[]; unmatched: string[] }\r\n}\r\n\r\n/**\r\n * 서식 문서를 파싱하여 필드를 채우고, 원하는 포맷으로 출력.\r\n *\r\n * - \"hwpx-preserve\": HWPX 입력 → 원본 ZIP XML 직접 수정 (테두리/폰트/병합 등 100% 보존)\r\n * - \"hwpx\": 아무 포맷 → IRBlock → Markdown → HWPX 생성 (스타일 초기화됨)\r\n * - \"markdown\": 아무 포맷 → IRBlock → Markdown\r\n *\r\n * @example\r\n * ```ts\r\n * // HWPX 원본 스타일 보존 채우기\r\n * const result = await fillForm(\"신청서.hwpx\", { \"성명\": \"홍길동\" }, \"hwpx-preserve\")\r\n * writeFileSync(\"결과.hwpx\", Buffer.from(result.output as ArrayBuffer))\r\n *\r\n * // 아무 포맷 → 마크다운 채우기\r\n * const result = await fillForm(\"신청서.hwp\", { \"성명\": \"홍길동\" })\r\n * console.log(result.output) // 채워진 마크다운\r\n * ```\r\n */\r\nexport async function fillForm(\r\n input: string | ArrayBuffer | Buffer,\r\n values: Record<string, string>,\r\n outputFormat: FillOutputFormat = \"markdown\",\r\n): Promise<FillFormOutput> {\r\n // 입력 버퍼 준비\r\n let buffer: ArrayBuffer\r\n if (typeof input === \"string\") {\r\n const buf = await readFile(input)\r\n buffer = toArrayBuffer(buf)\r\n } else if (Buffer.isBuffer(input)) {\r\n buffer = toArrayBuffer(input)\r\n } else {\r\n buffer = input\r\n }\r\n\r\n // hwpx-preserve: 원본 HWPX ZIP 직접 수정 (스타일 보존)\r\n if (outputFormat === \"hwpx-preserve\") {\r\n const format = detectFormat(buffer)\r\n // detectFormat은 ZIP이면 \"hwpx\" 반환 (XLSX/DOCX 포함), 세분화 필요\r\n if (format === \"hwpx\") {\r\n const zipFormat = await detectZipFormat(buffer)\r\n if (zipFormat !== \"hwpx\") {\r\n throw new Error(`hwpx-preserve 포맷은 HWPX 입력만 지원합니다 (감지된 포맷: ${zipFormat})`)\r\n }\r\n } else {\r\n throw new Error(`hwpx-preserve 포맷은 HWPX 입력만 지원합니다 (감지된 포맷: ${format})`)\r\n }\r\n const hwpxResult = await fillHwpx(buffer, values)\r\n return {\r\n output: hwpxResult.buffer,\r\n format: \"hwpx-preserve\",\r\n fill: { filled: hwpxResult.filled, unmatched: hwpxResult.unmatched },\r\n }\r\n }\r\n\r\n // 일반 경로: parse → IRBlock → fill → output\r\n const parsed = await parse(buffer)\r\n if (!parsed.success) {\r\n throw new Error(`서식 파싱 실패: ${parsed.error}`)\r\n }\r\n\r\n const fill = fillFormFields(parsed.blocks, values)\r\n const markdown = blocksToMarkdown(fill.blocks)\r\n\r\n if (outputFormat === \"hwpx\") {\r\n const hwpxBuffer = await markdownToHwpx(markdown)\r\n return { output: hwpxBuffer, format: \"hwpx\", fill }\r\n }\r\n\r\n return { output: markdown, format: \"markdown\", fill }\r\n}\r\n\r\n// ─── 게임체인저 API ─────────────────────────────────\r\n\r\nexport { compare, diffBlocks } from \"./diff/compare.js\"\r\nexport { extractFormFields, isLabelCell } from \"./form/recognize.js\"\r\nexport { fillFormFields } from \"./form/filler.js\"\r\nexport type { FillResult } from \"./form/filler.js\"\r\nexport { fillHwpx } from \"./form/filler-hwpx.js\"\r\nexport type { HwpxFillResult } from \"./form/filler-hwpx.js\"\r\nexport { markdownToHwpx } from \"./hwpx/generator.js\"\r\n\r\n// ─── Re-exports ──────────────────────────────────────\r\n\r\nexport { detectFormat, detectZipFormat, isHwpxFile, isOldHwpFile, isPdfFile, isZipFile } from \"./detect.js\"\r\nexport type {\r\n ParseResult, ParseSuccess, ParseFailure, FileType,\r\n IRBlock, IRBlockType, IRTable, IRCell, CellContext,\r\n BoundingBox, InlineStyle, ImageData, ExtractedImage,\r\n DocumentMetadata, ParseOptions, ErrorCode,\r\n ParseWarning, WarningCode, OutlineItem,\r\n DiffResult, BlockDiff, CellDiff, DiffChangeType,\r\n FormField, FormResult,\r\n OcrProvider, WatchOptions,\r\n} from \"./types.js\"\r\nexport { blocksToMarkdown } from \"./table/builder.js\"\r\nexport { VERSION } from \"./utils.js\"\r\n","/**\n * HWP 5.x 배포용 문서 \"상위 버전\" 경고 플레이스홀더 감지.\n *\n * 배포용(열람 제한) HWP 파일은 본문을 암호화하고, 복호화 실패 시 한컴에서 삽입한\n * 고정 경고 문자열이 노출된다. 이 문자열이 파싱 결과의 대부분을 차지하면 COM API\n * fallback으로 전환해야 한다.\n *\n * 이슈 #25 참고.\n */\n\n// 배포용 HWP의 고정 경고 문구 (버전·지역에 따라 미세 차이 가능 → 핵심 키만 매칭)\nconst SENTINEL_PATTERNS: RegExp[] = [\n /상위\\s*버전의\\s*배포용\\s*문서/,\n /최신\\s*버전의\\s*한글.*뷰어/,\n /문서를\\s*읽으려면/,\n]\n\n/**\n * 본문이 배포용 플레이스홀더로만 채워졌는지 판정.\n *\n * 기준:\n * - 패턴이 한 번 이상 매치\n * - 패턴을 제거한 나머지 의미 있는 텍스트가 매우 짧음 (공백·개행 제외 120자 미만)\n *\n * → 정상 본문이 섞여 있으면 false (COM fallback 불필요)\n */\nexport function isDistributionSentinel(markdown: string): boolean {\n if (!markdown) return false\n const hit = SENTINEL_PATTERNS.some(p => p.test(markdown))\n if (!hit) return false\n\n // 경고 문구 라인 제거 후 실질 내용이 짧은지\n const stripped = markdown\n .split(/\\r?\\n/)\n .filter(line => !SENTINEL_PATTERNS.some(p => p.test(line)))\n .join(\"\")\n .replace(/\\s+/g, \"\")\n\n return stripped.length < 120\n}\n","/**\n * XLSX (Office Open XML Spreadsheet) 파서\n *\n * ZIP + XML 구조를 jszip + xmldom으로 파싱하여 IRBlock[]로 변환.\n * 각 시트 → heading(시트명) + table(데이터) 블록.\n */\n\nimport JSZip from \"jszip\"\nimport { DOMParser } from \"@xmldom/xmldom\"\nimport type {\n IRBlock, IRTable, IRCell, CellContext, DocumentMetadata, InternalParseResult,\n ParseOptions, ParseWarning, ExtractedImage,\n} from \"../types.js\"\nimport { KordocError, precheckZipSize, stripDtd } from \"../utils.js\"\nimport { buildTable, blocksToMarkdown } from \"../table/builder.js\"\n\n// ─── 상수 ────────────────────────────────────────────\n\nconst MAX_SHEETS = 100\n/** ZIP 압축 해제 누적 최대 크기 (100MB) — ZIP bomb 방지 */\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\nconst MAX_ROWS = 10000\nconst MAX_COLS = 200\n\n// ─── 숫자값 정리 ──────────────────────────────────────\n\n/** 부동소수점 아티팩트 정리 (132.30000000000001 → 132.3) */\nfunction cleanNumericValue(raw: string): string {\n if (!/^-?\\d+\\.\\d+$/.test(raw)) return raw\n const num = parseFloat(raw)\n if (!isFinite(num)) return raw\n // toPrecision(15)로 IEEE 754 오차 제거 후 불필요한 후행 0 제거\n const cleaned = parseFloat(num.toPrecision(15)).toString()\n return cleaned\n}\n\n// ─── 셀 참조 파싱 ──────────────────────────────────────\n\n/** \"A1\" → { col: 0, row: 0 }, \"AB123\" → { col: 27, row: 122 } */\nfunction parseCellRef(ref: string): { col: number; row: number } | null {\n const m = ref.match(/^([A-Z]+)(\\d+)$/)\n if (!m) return null\n let col = 0\n for (const ch of m[1]) col = col * 26 + (ch.charCodeAt(0) - 64)\n return { col: col - 1, row: parseInt(m[2], 10) - 1 }\n}\n\n/** \"A1:C3\" → { startCol, startRow, endCol, endRow } */\nfunction parseMergeRef(ref: string): { startCol: number; startRow: number; endCol: number; endRow: number } | null {\n const parts = ref.split(\":\")\n if (parts.length !== 2) return null\n const start = parseCellRef(parts[0])\n const end = parseCellRef(parts[1])\n if (!start || !end) return null\n return { startCol: start.col, startRow: start.row, endCol: end.col, endRow: end.row }\n}\n\n// ─── XML 헬퍼 ──────────────────────────────────────────\n\nfunction getElements(parent: Element, tagName: string): Element[] {\n const nodes = parent.getElementsByTagName(tagName)\n const result: Element[] = []\n for (let i = 0; i < nodes.length; i++) result.push(nodes[i] as Element)\n return result\n}\n\nfunction getTextContent(el: Element): string {\n return el.textContent?.trim() ?? \"\"\n}\n\nfunction parseXml(text: string): Document {\n return new DOMParser().parseFromString(stripDtd(text), \"text/xml\")\n}\n\n// ─── 공유 문자열 파싱 ──────────────────────────────────\n\nfunction parseSharedStrings(xml: string): string[] {\n const doc = parseXml(xml)\n const strings: string[] = []\n const siList = getElements(doc.documentElement, \"si\")\n for (const si of siList) {\n // <si><t>text</t></si> 또는 <si><r><t>text</t></r>...</si>\n const tElements = getElements(si, \"t\")\n strings.push(tElements.map(t => t.textContent ?? \"\").join(\"\"))\n }\n return strings\n}\n\n// ─── 시트 목록 파싱 ─────────────────────────────────────\n\ninterface SheetInfo {\n name: string\n sheetId: string\n rId: string\n}\n\nfunction parseWorkbook(xml: string): SheetInfo[] {\n const doc = parseXml(xml)\n const sheets: SheetInfo[] = []\n const sheetElements = getElements(doc.documentElement, \"sheet\")\n for (const el of sheetElements) {\n sheets.push({\n name: el.getAttribute(\"name\") ?? `Sheet${sheets.length + 1}`,\n sheetId: el.getAttribute(\"sheetId\") ?? \"\",\n rId: el.getAttribute(\"r:id\") ?? \"\",\n })\n }\n return sheets\n}\n\n/** workbook.xml.rels 파싱 → rId → target 매핑 */\nfunction parseRels(xml: string): Map<string, string> {\n const doc = parseXml(xml)\n const map = new Map<string, string>()\n const rels = getElements(doc.documentElement, \"Relationship\")\n for (const rel of rels) {\n const id = rel.getAttribute(\"Id\")\n const target = rel.getAttribute(\"Target\")\n if (id && target) map.set(id, target)\n }\n return map\n}\n\n// ─── 워크시트 파싱 ──────────────────────────────────────\n\ninterface MergeInfo {\n startCol: number\n startRow: number\n endCol: number\n endRow: number\n}\n\nfunction parseWorksheet(\n xml: string,\n sharedStrings: string[],\n): { grid: string[][]; merges: MergeInfo[]; maxRow: number; maxCol: number } {\n const doc = parseXml(xml)\n const grid: string[][] = []\n let maxRow = 0\n let maxCol = 0\n\n // 데이터 행 파싱\n const rows = getElements(doc.documentElement, \"row\")\n for (const rowEl of rows) {\n const rowNum = parseInt(rowEl.getAttribute(\"r\") ?? \"0\", 10) - 1\n if (rowNum < 0 || rowNum >= MAX_ROWS) continue\n\n const cells = getElements(rowEl, \"c\")\n for (const cellEl of cells) {\n const ref = cellEl.getAttribute(\"r\")\n if (!ref) continue\n const pos = parseCellRef(ref)\n if (!pos || pos.col >= MAX_COLS) continue\n\n // 값 추출\n const type = cellEl.getAttribute(\"t\")\n const vElements = getElements(cellEl, \"v\")\n const fElements = getElements(cellEl, \"f\")\n let value = \"\"\n\n if (vElements.length > 0) {\n const raw = getTextContent(vElements[0])\n if (type === \"s\") {\n // shared string\n const idx = parseInt(raw, 10)\n value = sharedStrings[idx] ?? \"\"\n } else if (type === \"b\") {\n value = raw === \"1\" ? \"TRUE\" : \"FALSE\"\n } else {\n // 숫자값 부동소수점 아티팩트 정리 (9895607.8000000007 → 9895607.8)\n value = cleanNumericValue(raw)\n }\n } else if (type === \"inlineStr\") {\n // <is><t>text</t></is>\n const isEl = getElements(cellEl, \"is\")\n if (isEl.length > 0) {\n const tElements = getElements(isEl[0], \"t\")\n value = tElements.map(t => t.textContent ?? \"\").join(\"\")\n }\n }\n\n // 수식이 있고 값이 없으면 수식 표시\n if (!value && fElements.length > 0) {\n value = `=${getTextContent(fElements[0])}`\n }\n\n // 그리드 확장\n while (grid.length <= pos.row) grid.push([])\n while (grid[pos.row].length <= pos.col) grid[pos.row].push(\"\")\n grid[pos.row][pos.col] = value\n\n if (pos.row > maxRow) maxRow = pos.row\n if (pos.col > maxCol) maxCol = pos.col\n }\n }\n\n // 병합 셀 파싱\n const merges: MergeInfo[] = []\n const mergeCellElements = getElements(doc.documentElement, \"mergeCell\")\n for (const el of mergeCellElements) {\n const ref = el.getAttribute(\"ref\")\n if (!ref) continue\n const m = parseMergeRef(ref)\n if (m) merges.push(m)\n }\n\n return { grid, merges, maxRow, maxCol }\n}\n\n// ─── 시트 → IRBlock[] 변환 ────────────────────────────\n\nfunction sheetToBlocks(\n sheetName: string,\n grid: string[][],\n merges: MergeInfo[],\n maxRow: number,\n maxCol: number,\n sheetIndex: number,\n): IRBlock[] {\n const blocks: IRBlock[] = []\n\n // 시트명 = heading\n if (sheetName) {\n blocks.push({\n type: \"heading\",\n text: sheetName,\n level: 2,\n pageNumber: sheetIndex + 1,\n })\n }\n\n // 빈 시트\n if (maxRow < 0 || maxCol < 0 || grid.length === 0) return blocks\n\n // 병합 맵: \"row,col\" → { colSpan, rowSpan }\n const mergeMap = new Map<string, { colSpan: number; rowSpan: number }>()\n const mergeSkip = new Set<string>()\n for (const m of merges) {\n const colSpan = m.endCol - m.startCol + 1\n const rowSpan = m.endRow - m.startRow + 1\n mergeMap.set(`${m.startRow},${m.startCol}`, { colSpan, rowSpan })\n for (let r = m.startRow; r <= m.endRow; r++) {\n for (let c = m.startCol; c <= m.endCol; c++) {\n if (r !== m.startRow || c !== m.startCol) {\n mergeSkip.add(`${r},${c}`)\n }\n }\n }\n }\n\n // 유효 행 범위 감지 (앞뒤 빈 행 제거)\n let firstRow = -1\n let lastRow = -1\n for (let r = 0; r <= maxRow; r++) {\n const row = grid[r]\n if (row && row.some(cell => cell !== \"\")) {\n if (firstRow === -1) firstRow = r\n lastRow = r\n }\n }\n if (firstRow === -1) return blocks\n\n // CellContext[][] → buildTable로 IRTable 생성 (2-pass 알고리즘 재사용)\n const cellRows: CellContext[][] = []\n\n for (let r = firstRow; r <= lastRow; r++) {\n const row: CellContext[] = []\n for (let c = 0; c <= maxCol; c++) {\n const key = `${r},${c}`\n if (mergeSkip.has(key)) continue\n\n const text = (grid[r] && grid[r][c]) ?? \"\"\n const merge = mergeMap.get(key)\n row.push({\n text,\n colSpan: merge?.colSpan ?? 1,\n rowSpan: merge?.rowSpan ?? 1,\n })\n }\n cellRows.push(row)\n }\n\n if (cellRows.length > 0) {\n const table = buildTable(cellRows)\n if (table.rows > 0) {\n blocks.push({ type: \"table\", table, pageNumber: sheetIndex + 1 })\n }\n }\n\n return blocks\n}\n\n// ─── 메인 파서 ─────────────────────────────────────────\n\nexport async function parseXlsxDocument(\n buffer: ArrayBuffer,\n options?: ParseOptions,\n): Promise<InternalParseResult> {\n // ZIP bomb 사전 검사\n precheckZipSize(buffer, MAX_DECOMPRESS_SIZE)\n\n const zip = await JSZip.loadAsync(buffer)\n const warnings: ParseWarning[] = []\n\n // XLSX 구조 검증\n const workbookFile = zip.file(\"xl/workbook.xml\")\n if (!workbookFile) {\n throw new KordocError(\"유효하지 않은 XLSX 파일: xl/workbook.xml이 없습니다\")\n }\n\n // 1. 공유 문자열 로드\n let sharedStrings: string[] = []\n const ssFile = zip.file(\"xl/sharedStrings.xml\")\n if (ssFile) {\n sharedStrings = parseSharedStrings(await ssFile.async(\"text\"))\n }\n\n // 2. 시트 목록 로드\n const sheets = parseWorkbook(await workbookFile.async(\"text\"))\n if (sheets.length === 0) {\n throw new KordocError(\"XLSX 파일에 시트가 없습니다\")\n }\n\n // 3. 관계 매핑 (rId → 파일 경로)\n let relsMap = new Map<string, string>()\n const relsFile = zip.file(\"xl/_rels/workbook.xml.rels\")\n if (relsFile) {\n relsMap = parseRels(await relsFile.async(\"text\"))\n }\n\n // 4. 페이지 필터\n let pageFilter: Set<number> | null = null\n if (options?.pages) {\n const { parsePageRange } = await import(\"../page-range.js\")\n pageFilter = parsePageRange(options.pages, sheets.length)\n }\n\n // 5. 각 시트 파싱\n const blocks: IRBlock[] = []\n const processedSheets = Math.min(sheets.length, MAX_SHEETS)\n\n for (let i = 0; i < processedSheets; i++) {\n if (pageFilter && !pageFilter.has(i + 1)) continue\n\n const sheet = sheets[i]\n options?.onProgress?.(i + 1, processedSheets)\n\n // 시트 파일 경로 결정\n let sheetPath = relsMap.get(sheet.rId)\n if (sheetPath) {\n // 상대 경로 → 절대 경로\n if (!sheetPath.startsWith(\"xl/\") && !sheetPath.startsWith(\"/\")) {\n sheetPath = `xl/${sheetPath}`\n } else if (sheetPath.startsWith(\"/\")) {\n sheetPath = sheetPath.slice(1)\n }\n } else {\n sheetPath = `xl/worksheets/sheet${i + 1}.xml`\n }\n\n const sheetFile = zip.file(sheetPath)\n if (!sheetFile) {\n warnings.push({\n page: i + 1,\n message: `시트 \"${sheet.name}\" 파일을 찾을 수 없습니다: ${sheetPath}`,\n code: \"PARTIAL_PARSE\",\n })\n continue\n }\n\n try {\n const sheetXml = await sheetFile.async(\"text\")\n const { grid, merges, maxRow, maxCol } = parseWorksheet(sheetXml, sharedStrings)\n const sheetBlocks = sheetToBlocks(sheet.name, grid, merges, maxRow, maxCol, i)\n blocks.push(...sheetBlocks)\n } catch (err) {\n warnings.push({\n page: i + 1,\n message: `시트 \"${sheet.name}\" 파싱 실패: ${err instanceof Error ? err.message : \"알 수 없는 오류\"}`,\n code: \"PARTIAL_PARSE\",\n })\n }\n }\n\n // 6. 메타데이터 추출\n const metadata: DocumentMetadata = {\n pageCount: processedSheets,\n }\n const coreFile = zip.file(\"docProps/core.xml\")\n if (coreFile) {\n try {\n const coreXml = await coreFile.async(\"text\")\n const doc = parseXml(coreXml)\n const getFirst = (tag: string) => {\n const els = doc.getElementsByTagName(tag)\n return els.length > 0 ? (els[0].textContent ?? \"\").trim() : undefined\n }\n metadata.title = getFirst(\"dc:title\") || getFirst(\"dcterms:title\")\n metadata.author = getFirst(\"dc:creator\")\n metadata.description = getFirst(\"dc:description\")\n const created = getFirst(\"dcterms:created\")\n if (created) metadata.createdAt = created\n const modified = getFirst(\"dcterms:modified\")\n if (modified) metadata.modifiedAt = modified\n } catch { /* 메타데이터 실패는 무시 */ }\n }\n\n const markdown = blocksToMarkdown(blocks)\n\n return { markdown, blocks, metadata, warnings: warnings.length > 0 ? warnings : undefined }\n}\n","/**\n * DOCX (Office Open XML Document) 파서\n *\n * ZIP + XML 구조를 jszip + xmldom으로 파싱하여 IRBlock[]로 변환.\n * w:p → paragraph/heading, w:tbl → table, w:drawing → image.\n */\n\nimport JSZip from \"jszip\"\nimport { DOMParser } from \"@xmldom/xmldom\"\nimport type {\n IRBlock, IRTable, IRCell, DocumentMetadata, InternalParseResult,\n ParseOptions, ParseWarning, ExtractedImage, InlineStyle,\n} from \"../types.js\"\nimport { KordocError, precheckZipSize, stripDtd } from \"../utils.js\"\nimport { blocksToMarkdown } from \"../table/builder.js\"\n\n/** ZIP 압축 해제 누적 최대 크기 (100MB) — ZIP bomb 방지 */\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\n\n// ─── XML 헬퍼 ──────────────────────────────────────────\n\n/** 네임스페이스 무시 태그 검색 — DOCX는 네임스페이스가 많음 */\nfunction getChildElements(parent: Element | Document, localName: string): Element[] {\n const result: Element[] = []\n const children = parent.childNodes\n for (let i = 0; i < children.length; i++) {\n const node = children[i]\n if (node.nodeType === 1) {\n const el = node as Element\n if (el.localName === localName || el.tagName?.endsWith(`:${localName}`)) {\n result.push(el)\n }\n }\n }\n return result\n}\n\n/** 재귀적으로 localName 매칭 — getElementsByTagName 대안 */\nfunction findElements(parent: Element | Document, localName: string): Element[] {\n const result: Element[] = []\n const walk = (node: Element | Document) => {\n const children = node.childNodes\n for (let i = 0; i < children.length; i++) {\n const child = children[i]\n if (child.nodeType === 1) {\n const el = child as Element\n if (el.localName === localName || el.tagName?.endsWith(`:${localName}`)) {\n result.push(el)\n }\n walk(el)\n }\n }\n }\n walk(parent)\n return result\n}\n\nfunction getAttr(el: Element, localName: string): string | null {\n // w:val, r:id 등 네임스페이스 포함 속성\n const attrs = el.attributes\n for (let i = 0; i < attrs.length; i++) {\n const attr = attrs[i]\n if (attr.localName === localName || attr.name === localName) return attr.value\n }\n return null\n}\n\nfunction parseXml(text: string): Document {\n return new DOMParser().parseFromString(stripDtd(text), \"text/xml\")\n}\n\n// ─── 스타일 파싱 ────────────────────────────────────────\n\ninterface StyleInfo {\n name: string\n basedOn?: string\n outlineLevel?: number\n}\n\nfunction parseStyles(xml: string): Map<string, StyleInfo> {\n const doc = parseXml(xml)\n const styles = new Map<string, StyleInfo>()\n const styleElements = findElements(doc, \"style\")\n\n for (const el of styleElements) {\n const styleId = getAttr(el, \"styleId\")\n if (!styleId) continue\n\n const nameEls = getChildElements(el, \"name\")\n const name = nameEls.length > 0 ? (getAttr(nameEls[0], \"val\") ?? \"\") : \"\"\n const basedOnEls = getChildElements(el, \"basedOn\")\n const basedOn = basedOnEls.length > 0 ? (getAttr(basedOnEls[0], \"val\") ?? undefined) : undefined\n\n // outlineLevel으로 heading 감지\n const pPrEls = getChildElements(el, \"pPr\")\n let outlineLevel: number | undefined\n if (pPrEls.length > 0) {\n const outlineEls = getChildElements(pPrEls[0], \"outlineLvl\")\n if (outlineEls.length > 0) {\n const val = getAttr(outlineEls[0], \"val\")\n if (val !== null) outlineLevel = parseInt(val, 10)\n }\n }\n\n // Heading 패턴 매칭\n if (outlineLevel === undefined) {\n const headingMatch = name.match(/^(?:heading|Heading)\\s*(\\d+)$/i)\n if (headingMatch) outlineLevel = parseInt(headingMatch[1], 10) - 1\n }\n\n styles.set(styleId, { name, basedOn, outlineLevel })\n }\n return styles\n}\n\n// ─── 번호 매기기 파싱 ──────────────────────────────────\n\ninterface NumberingInfo {\n numFmt: string // \"decimal\", \"bullet\", etc.\n level: number\n}\n\nfunction parseNumbering(xml: string): Map<string, Map<number, NumberingInfo>> {\n const doc = parseXml(xml)\n const abstractNums = new Map<string, Map<number, NumberingInfo>>()\n\n // abstractNum 파싱\n const abstractElements = findElements(doc, \"abstractNum\")\n for (const el of abstractElements) {\n const abstractNumId = getAttr(el, \"abstractNumId\")\n if (!abstractNumId) continue\n const levels = new Map<number, NumberingInfo>()\n const lvlElements = getChildElements(el, \"lvl\")\n for (const lvl of lvlElements) {\n const ilvl = parseInt(getAttr(lvl, \"ilvl\") ?? \"0\", 10)\n const numFmtEls = getChildElements(lvl, \"numFmt\")\n const numFmt = numFmtEls.length > 0 ? (getAttr(numFmtEls[0], \"val\") ?? \"bullet\") : \"bullet\"\n levels.set(ilvl, { numFmt, level: ilvl })\n }\n abstractNums.set(abstractNumId, levels)\n }\n\n // num → abstractNum 매핑\n const nums = new Map<string, Map<number, NumberingInfo>>()\n const numElements = findElements(doc, \"num\")\n for (const el of numElements) {\n const numId = getAttr(el, \"numId\")\n if (!numId) continue\n const abstractRefs = getChildElements(el, \"abstractNumId\")\n if (abstractRefs.length > 0) {\n const ref = getAttr(abstractRefs[0], \"val\")\n if (ref && abstractNums.has(ref)) {\n nums.set(numId, abstractNums.get(ref)!)\n }\n }\n }\n return nums\n}\n\n// ─── 관계 파싱 ─────────────────────────────────────────\n\nfunction parseRels(xml: string): Map<string, string> {\n const doc = parseXml(xml)\n const map = new Map<string, string>()\n const rels = findElements(doc, \"Relationship\")\n for (const rel of rels) {\n const id = getAttr(rel, \"Id\")\n const target = getAttr(rel, \"Target\")\n if (id && target) map.set(id, target)\n }\n return map\n}\n\n// ─── 각주 파싱 ─────────────────────────────────────────\n\nfunction parseFootnotes(xml: string): Map<string, string> {\n const doc = parseXml(xml)\n const notes = new Map<string, string>()\n const fnElements = findElements(doc, \"footnote\")\n for (const fn of fnElements) {\n const id = getAttr(fn, \"id\")\n if (!id || id === \"0\" || id === \"-1\") continue // 0=separator, -1=continuation\n const texts: string[] = []\n const pElements = findElements(fn, \"p\")\n for (const p of pElements) {\n const runs = findElements(p, \"r\")\n for (const r of runs) {\n const tElements = getChildElements(r, \"t\")\n for (const t of tElements) texts.push(t.textContent ?? \"\")\n }\n }\n notes.set(id, texts.join(\"\").trim())\n }\n return notes\n}\n\n// ─── Run 텍스트 추출 ──────────────────────────────────\n\ninterface RunResult {\n text: string\n bold: boolean\n italic: boolean\n}\n\nfunction extractRun(r: Element): RunResult {\n const tElements = getChildElements(r, \"t\")\n const text = tElements.map(t => t.textContent ?? \"\").join(\"\")\n\n let bold = false\n let italic = false\n const rPrEls = getChildElements(r, \"rPr\")\n if (rPrEls.length > 0) {\n bold = getChildElements(rPrEls[0], \"b\").length > 0\n italic = getChildElements(rPrEls[0], \"i\").length > 0\n }\n\n return { text, bold, italic }\n}\n\n// ─── 단락 파싱 ─────────────────────────────────────────\n\nfunction parseParagraph(\n p: Element,\n styles: Map<string, StyleInfo>,\n numbering: Map<string, Map<number, NumberingInfo>>,\n footnotes: Map<string, string>,\n rels: Map<string, string>,\n): IRBlock | null {\n // 스타일 확인\n const pPrEls = getChildElements(p, \"pPr\")\n let styleId = \"\"\n let numId = \"\"\n let ilvl = 0\n\n if (pPrEls.length > 0) {\n const pStyleEls = getChildElements(pPrEls[0], \"pStyle\")\n if (pStyleEls.length > 0) styleId = getAttr(pStyleEls[0], \"val\") ?? \"\"\n\n const numPrEls = getChildElements(pPrEls[0], \"numPr\")\n if (numPrEls.length > 0) {\n const numIdEls = getChildElements(numPrEls[0], \"numId\")\n const ilvlEls = getChildElements(numPrEls[0], \"ilvl\")\n numId = numIdEls.length > 0 ? (getAttr(numIdEls[0], \"val\") ?? \"\") : \"\"\n ilvl = ilvlEls.length > 0 ? parseInt(getAttr(ilvlEls[0], \"val\") ?? \"0\", 10) : 0\n }\n }\n\n // 텍스트 수집\n const parts: string[] = []\n let hasBold = false\n let hasItalic = false\n let href: string | undefined\n let footnoteText: string | undefined\n\n // 하이퍼링크 처리\n const hyperlinks = getChildElements(p, \"hyperlink\")\n const hyperlinkTexts = new Set<string>()\n\n for (const hl of hyperlinks) {\n const rId = getAttr(hl, \"id\")\n const hlText: string[] = []\n const runs = findElements(hl, \"r\")\n for (const r of runs) {\n const result = extractRun(r)\n hlText.push(result.text)\n }\n const text = hlText.join(\"\")\n if (text) {\n hyperlinkTexts.add(text)\n if (rId && rels.has(rId)) {\n href = rels.get(rId)\n parts.push(text)\n } else {\n parts.push(text)\n }\n }\n }\n\n // 일반 run 처리\n const runs = getChildElements(p, \"r\")\n for (const r of runs) {\n // 하이퍼링크 내부 run은 이미 처리됨 — 부모가 hyperlink이면 스킵\n if (r.parentNode && (r.parentNode as Element).localName === \"hyperlink\") continue\n\n const result = extractRun(r)\n if (result.bold) hasBold = true\n if (result.italic) hasItalic = true\n\n // 각주 참조 확인\n const fnRefEls = getChildElements(r, \"footnoteReference\")\n if (fnRefEls.length > 0) {\n const fnId = getAttr(fnRefEls[0], \"id\")\n if (fnId && footnotes.has(fnId)) {\n footnoteText = footnotes.get(fnId)\n }\n }\n\n if (result.text) parts.push(result.text)\n }\n\n const text = parts.join(\"\").trim()\n if (!text) return null\n\n // Heading 판별\n const style = styles.get(styleId)\n if (style?.outlineLevel !== undefined && style.outlineLevel >= 0 && style.outlineLevel <= 5) {\n return {\n type: \"heading\",\n text,\n level: style.outlineLevel + 1,\n }\n }\n\n // 리스트 판별\n if (numId && numId !== \"0\") {\n const numDef = numbering.get(numId)\n const levelInfo = numDef?.get(ilvl)\n const listType = levelInfo?.numFmt === \"bullet\" ? \"unordered\" : \"ordered\"\n return { type: \"list\", text, listType }\n }\n\n // 일반 단락\n const block: IRBlock = { type: \"paragraph\", text }\n if (hasBold || hasItalic) {\n block.style = { bold: hasBold || undefined, italic: hasItalic || undefined }\n }\n if (href) block.href = href\n if (footnoteText) block.footnoteText = footnoteText\n return block\n}\n\n// ─── 테이블 파싱 ────────────────────────────────────────\n\nfunction parseTable(\n tbl: Element,\n styles: Map<string, StyleInfo>,\n numbering: Map<string, Map<number, NumberingInfo>>,\n footnotes: Map<string, string>,\n rels: Map<string, string>,\n): IRBlock | null {\n const trElements = getChildElements(tbl, \"tr\")\n if (trElements.length === 0) return null\n\n const rows: IRCell[][] = []\n let maxCols = 0\n\n for (const tr of trElements) {\n const tcElements = getChildElements(tr, \"tc\")\n const row: IRCell[] = []\n\n for (const tc of tcElements) {\n // 셀 속성\n let colSpan = 1\n let rowSpan = 1\n const tcPrEls = getChildElements(tc, \"tcPr\")\n if (tcPrEls.length > 0) {\n const gridSpanEls = getChildElements(tcPrEls[0], \"gridSpan\")\n if (gridSpanEls.length > 0) {\n colSpan = parseInt(getAttr(gridSpanEls[0], \"val\") ?? \"1\", 10)\n }\n const vMergeEls = getChildElements(tcPrEls[0], \"vMerge\")\n if (vMergeEls.length > 0) {\n const val = getAttr(vMergeEls[0], \"val\")\n if (val !== \"restart\" && val !== null) {\n // 병합 계속 셀 — 스킵 마커\n row.push({ text: \"\", colSpan, rowSpan: 0 })\n continue\n }\n }\n }\n\n // 셀 텍스트\n const cellTexts: string[] = []\n const pElements = getChildElements(tc, \"p\")\n for (const p of pElements) {\n const block = parseParagraph(p, styles, numbering, footnotes, rels)\n if (block?.text) cellTexts.push(block.text)\n }\n\n row.push({ text: cellTexts.join(\"\\n\"), colSpan, rowSpan })\n }\n rows.push(row)\n if (row.length > maxCols) maxCols = row.length\n }\n\n // vMerge rowSpan 후처리: restart에서 아래로 연속되는 rowSpan=0 카운트\n for (let c = 0; c < maxCols; c++) {\n for (let r = 0; r < rows.length; r++) {\n const cell = rows[r][c]\n if (!cell || cell.rowSpan === 0) continue\n let span = 1\n for (let nr = r + 1; nr < rows.length; nr++) {\n if (rows[nr][c]?.rowSpan === 0) span++\n else break\n }\n cell.rowSpan = span\n }\n }\n\n // rowSpan=0인 placeholder 제거\n const cleanRows: IRCell[][] = []\n for (const row of rows) {\n const clean = row.filter(cell => cell.rowSpan !== 0)\n cleanRows.push(clean)\n }\n\n // 빈 테이블 체크\n if (cleanRows.length === 0) return null\n\n // 컬럼 수 재계산\n let cols = 0\n for (const row of cleanRows) {\n let c = 0\n for (const cell of row) c += cell.colSpan\n if (c > cols) cols = c\n }\n\n const table: IRTable = {\n rows: cleanRows.length,\n cols,\n cells: cleanRows,\n hasHeader: cleanRows.length > 1,\n }\n return { type: \"table\", table }\n}\n\n// ─── 이미지 추출 ────────────────────────────────────────\n\nasync function extractImages(\n zip: JSZip,\n rels: Map<string, string>,\n doc: Document,\n): Promise<{ blocks: IRBlock[]; images: ExtractedImage[] }> {\n const blocks: IRBlock[] = []\n const images: ExtractedImage[] = []\n\n const drawingElements = findElements(doc.documentElement, \"drawing\")\n let imgIdx = 0\n\n for (const drawing of drawingElements) {\n // a:blip → r:embed\n const blips = findElements(drawing, \"blip\")\n for (const blip of blips) {\n const embedId = getAttr(blip, \"embed\")\n if (!embedId) continue\n const target = rels.get(embedId)\n if (!target) continue\n\n const imgPath = target.startsWith(\"/\") ? target.slice(1)\n : target.startsWith(\"word/\") ? target\n : `word/${target}`\n\n const imgFile = zip.file(imgPath)\n if (!imgFile) continue\n\n try {\n const data = await imgFile.async(\"uint8array\")\n imgIdx++\n const ext = imgPath.split(\".\").pop()?.toLowerCase() ?? \"png\"\n const mimeMap: Record<string, string> = {\n png: \"image/png\", jpg: \"image/jpeg\", jpeg: \"image/jpeg\",\n gif: \"image/gif\", bmp: \"image/bmp\", wmf: \"image/wmf\", emf: \"image/emf\",\n }\n const filename = `image_${String(imgIdx).padStart(3, \"0\")}.${ext}`\n images.push({ filename, data, mimeType: mimeMap[ext] ?? \"image/png\" })\n blocks.push({ type: \"image\", text: filename })\n } catch { /* 이미지 실패 무시 */ }\n }\n }\n return { blocks, images }\n}\n\n// ─── 메인 파서 ─────────────────────────────────────────\n\nexport async function parseDocxDocument(\n buffer: ArrayBuffer,\n options?: ParseOptions,\n): Promise<InternalParseResult> {\n // ZIP bomb 사전 검사\n precheckZipSize(buffer, MAX_DECOMPRESS_SIZE)\n\n const zip = await JSZip.loadAsync(buffer)\n const warnings: ParseWarning[] = []\n\n // DOCX 구조 검증\n const docFile = zip.file(\"word/document.xml\")\n if (!docFile) {\n throw new KordocError(\"유효하지 않은 DOCX 파일: word/document.xml이 없습니다\")\n }\n\n // 1. 관계 로드\n let rels = new Map<string, string>()\n const relsFile = zip.file(\"word/_rels/document.xml.rels\")\n if (relsFile) {\n rels = parseRels(await relsFile.async(\"text\"))\n }\n\n // 2. 스타일 로드\n let styles = new Map<string, StyleInfo>()\n const stylesFile = zip.file(\"word/styles.xml\")\n if (stylesFile) {\n try {\n styles = parseStyles(await stylesFile.async(\"text\"))\n } catch { /* 스타일 실패 무시 */ }\n }\n\n // 3. 번호 매기기 로드\n let numbering = new Map<string, Map<number, NumberingInfo>>()\n const numFile = zip.file(\"word/numbering.xml\")\n if (numFile) {\n try {\n numbering = parseNumbering(await numFile.async(\"text\"))\n } catch { /* 번호 매기기 실패 무시 */ }\n }\n\n // 4. 각주 로드\n let footnotes = new Map<string, string>()\n const fnFile = zip.file(\"word/footnotes.xml\")\n if (fnFile) {\n try {\n footnotes = parseFootnotes(await fnFile.async(\"text\"))\n } catch { /* 각주 실패 무시 */ }\n }\n\n // 5. 본문 파싱\n const docXml = await docFile.async(\"text\")\n const doc = parseXml(docXml)\n const body = findElements(doc, \"body\")\n if (body.length === 0) {\n throw new KordocError(\"DOCX 본문(w:body)을 찾을 수 없습니다\")\n }\n\n const blocks: IRBlock[] = []\n const bodyEl = body[0]\n const children = bodyEl.childNodes\n\n for (let i = 0; i < children.length; i++) {\n const node = children[i]\n if (node.nodeType !== 1) continue\n const el = node as Element\n const localName = el.localName ?? el.tagName?.split(\":\").pop()\n\n if (localName === \"p\") {\n const block = parseParagraph(el, styles, numbering, footnotes, rels)\n if (block) blocks.push(block)\n } else if (localName === \"tbl\") {\n const block = parseTable(el, styles, numbering, footnotes, rels)\n if (block) blocks.push(block)\n }\n }\n\n // 6. 이미지 추출\n const { blocks: imgBlocks, images } = await extractImages(zip, rels, doc)\n // 이미지 블록은 본문에 이미 포함되어야 하지만, 누락된 것 추가\n // (drawing이 paragraph 내에 있으므로 대부분 이미 포함됨)\n\n // 7. 메타데이터\n const metadata: DocumentMetadata = {}\n const coreFile = zip.file(\"docProps/core.xml\")\n if (coreFile) {\n try {\n const coreXml = await coreFile.async(\"text\")\n const coreDoc = parseXml(coreXml)\n const getFirst = (tag: string) => {\n const els = coreDoc.getElementsByTagName(tag)\n return els.length > 0 ? (els[0].textContent ?? \"\").trim() : undefined\n }\n metadata.title = getFirst(\"dc:title\") || getFirst(\"dcterms:title\")\n metadata.author = getFirst(\"dc:creator\")\n metadata.description = getFirst(\"dc:description\")\n const created = getFirst(\"dcterms:created\")\n if (created) metadata.createdAt = created\n const modified = getFirst(\"dcterms:modified\")\n if (modified) metadata.modifiedAt = modified\n } catch { /* 메타데이터 실패 무시 */ }\n }\n\n // 8. 아웃라인\n const outline = blocks\n .filter(b => b.type === \"heading\")\n .map(b => ({ level: b.level ?? 2, text: b.text ?? \"\" }))\n\n const markdown = blocksToMarkdown(blocks)\n\n return {\n markdown,\n blocks,\n metadata,\n outline: outline.length > 0 ? outline : undefined,\n warnings: warnings.length > 0 ? warnings : undefined,\n images: images.length > 0 ? images : undefined,\n }\n}\n","/** HWPML 2.x 파서 — XML 기반 한컴 문서 (.hwp with XML content) */\r\n\r\nimport { DOMParser } from \"@xmldom/xmldom\"\r\nimport type { IRBlock, InternalParseResult, ParseOptions, ParseWarning, DocumentMetadata, OutlineItem } from \"../types.js\"\r\nimport { blocksToMarkdown, buildTable } from \"../table/builder.js\"\r\nimport { parsePageRange } from \"../page-range.js\"\r\nimport { stripDtd } from \"../utils.js\"\r\nimport type { CellContext } from \"../types.js\"\r\n\r\nconst MAX_XML_DEPTH = 200\r\nconst MAX_TABLE_ROWS = 5000\r\nconst MAX_TABLE_COLS = 500\r\nconst MAX_HWPML_BYTES = 50 * 1024 * 1024 // 50MB 상한\r\n\r\n/** ParaShape 헤딩 정보 */\r\ninterface ParaShapeInfo {\r\n headingLevel: number | null // null = 일반 단락, 1-6 = 헤딩 레벨\r\n}\r\n\r\n/** HWPML 문서 파싱 진입점 */\r\nexport function parseHwpmlDocument(buffer: ArrayBuffer, options?: ParseOptions): InternalParseResult {\r\n if (buffer.byteLength > MAX_HWPML_BYTES) {\r\n throw new Error(`HWPML 파일 크기 초과 (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB > 50MB)`)\r\n }\r\n const text = new TextDecoder(\"utf-8\").decode(buffer).replace(/^\\uFEFF/, \"\")\r\n\r\n // 엔티티 치환 (DOCTYPE 제거 전에 처리)\r\n const normalized = text.replace(/ /g, \" \")\r\n const xml = stripDtd(normalized)\r\n\r\n const warnings: ParseWarning[] = []\r\n const parser = new DOMParser({\r\n onError: (_level: string, msg: string) => {\r\n warnings.push({ message: `HWPML XML 파싱 경고: ${msg}`, code: \"MALFORMED_XML\" })\r\n },\r\n } as ConstructorParameters<typeof DOMParser>[0])\r\n\r\n const doc = parser.parseFromString(xml, \"text/xml\")\r\n if (!doc.documentElement) {\r\n return { markdown: \"\", blocks: [], warnings }\r\n }\r\n\r\n const root = doc.documentElement\r\n\r\n // ─── 메타데이터 추출 ──────────────────────────────────\r\n const metadata: DocumentMetadata = {}\r\n const docSummary = findChild(root, \"DOCSUMMARY\")\r\n if (docSummary) {\r\n const title = findChild(docSummary, \"TITLE\")\r\n const author = findChild(docSummary, \"AUTHOR\")\r\n const date = findChild(docSummary, \"DATE\")\r\n if (title) metadata.title = textContent(title).trim()\r\n if (author) metadata.author = textContent(author).trim()\r\n if (date) metadata.createdAt = textContent(date).trim() || undefined\r\n }\r\n\r\n // ─── HEAD: ParaShape 맵 구축 ──────────────────────────\r\n const paraShapeMap = buildParaShapeMap(root)\r\n\r\n // ─── BODY 파싱 ────────────────────────────────────────\r\n const body = findChild(root, \"BODY\")\r\n if (!body) {\r\n return { markdown: \"\", blocks: [], metadata, warnings }\r\n }\r\n\r\n const blocks: IRBlock[] = []\r\n const pageFilter = options?.pages ? parsePageRange(options.pages, countSections(body)) : null\r\n let sectionIdx = 0\r\n\r\n const children = body.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType !== 1) continue\r\n if (localName(el) !== \"SECTION\") continue\r\n\r\n sectionIdx++\r\n if (pageFilter && !pageFilter.has(sectionIdx)) continue\r\n\r\n parseSection(el, blocks, paraShapeMap, sectionIdx, warnings)\r\n }\r\n\r\n // ─── 헤딩 트리(outline) 구성 ──────────────────────────\r\n const outline: OutlineItem[] = blocks\r\n .filter(b => b.type === \"heading\" && b.text)\r\n .map(b => ({ level: b.level ?? 1, text: b.text!, pageNumber: b.pageNumber }))\r\n\r\n const markdown = blocksToMarkdown(blocks)\r\n return {\r\n markdown,\r\n blocks,\r\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\r\n outline: outline.length > 0 ? outline : undefined,\r\n warnings: warnings.length > 0 ? warnings : undefined,\r\n }\r\n}\r\n\r\n// ─── ParaShape 맵 ────────────────────────────────────────\r\n\r\nfunction buildParaShapeMap(root: Element): Map<string, ParaShapeInfo> {\r\n const map = new Map<string, ParaShapeInfo>()\r\n const head = findChild(root, \"HEAD\")\r\n if (!head) return map\r\n\r\n const mappingTable = findChild(head, \"MAPPINGTABLE\")\r\n if (!mappingTable) return map\r\n\r\n const paraShapeList = findChild(mappingTable, \"PARASHAPELIST\")\r\n if (!paraShapeList) return map\r\n\r\n const children = paraShapeList.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType !== 1 || localName(el) !== \"PARASHAPE\") continue\r\n const id = el.getAttribute(\"Id\") ?? \"\"\r\n const headingType = el.getAttribute(\"HeadingType\") ?? \"None\"\r\n const level = parseInt(el.getAttribute(\"Level\") ?? \"0\", 10)\r\n let headingLevel: number | null = null\r\n if (headingType === \"Outline\") {\r\n const safeLevel = isNaN(level) ? 0 : Math.max(0, level)\r\n headingLevel = Math.min(safeLevel + 1, 6) // Level 0→H1, 1→H2, ..., 5→H6\r\n }\r\n map.set(id, { headingLevel })\r\n }\r\n\r\n return map\r\n}\r\n\r\n// ─── 섹션 파싱 ───────────────────────────────────────────\r\n\r\nfunction parseSection(\r\n section: Element,\r\n blocks: IRBlock[],\r\n paraShapeMap: Map<string, ParaShapeInfo>,\r\n sectionNum: number,\r\n warnings: ParseWarning[],\r\n): void {\r\n walkContent(section, blocks, paraShapeMap, sectionNum, warnings, false)\r\n}\r\n\r\n/**\r\n * 콘텐츠 노드를 재귀적으로 순회하여 IRBlock 생성.\r\n * inHeaderFooter=true일 때 단락/표 블록 출력 억제.\r\n */\r\nfunction walkContent(\r\n node: Element,\r\n blocks: IRBlock[],\r\n paraShapeMap: Map<string, ParaShapeInfo>,\r\n sectionNum: number,\r\n warnings: ParseWarning[],\r\n inHeaderFooter: boolean,\r\n depth: number = 0,\r\n): void {\r\n if (depth > MAX_XML_DEPTH) return\r\n const children = node.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType !== 1) continue\r\n const tag = localName(el)\r\n\r\n if (tag === \"HEADER\" || tag === \"FOOTER\") {\r\n // 머리글/바닥글 — 텍스트 콘텐츠 무시\r\n continue\r\n }\r\n\r\n if (tag === \"P\") {\r\n if (!inHeaderFooter) {\r\n parseParagraph(el, blocks, paraShapeMap, sectionNum)\r\n }\r\n continue\r\n }\r\n\r\n if (tag === \"TABLE\") {\r\n if (!inHeaderFooter) {\r\n parseTable(el, blocks, paraShapeMap, sectionNum, warnings)\r\n }\r\n continue\r\n }\r\n\r\n // PARALIST, SUBLIST, SECTION 내부 등 — 재귀\r\n if (tag === \"PARALIST\" || tag === \"SECTION\" || tag === \"COLDEF\") {\r\n walkContent(el, blocks, paraShapeMap, sectionNum, warnings, inHeaderFooter, depth + 1)\r\n continue\r\n }\r\n\r\n // TEXT, SECDEF 등 내부에서도 P/TABLE이 중첩될 수 있음 — 재귀\r\n walkContent(el, blocks, paraShapeMap, sectionNum, warnings, inHeaderFooter, depth + 1)\r\n }\r\n}\r\n\r\n// ─── 단락 파싱 ───────────────────────────────────────────\r\n\r\nfunction parseParagraph(\r\n el: Element,\r\n blocks: IRBlock[],\r\n paraShapeMap: Map<string, ParaShapeInfo>,\r\n sectionNum: number,\r\n): void {\r\n const paraShapeId = el.getAttribute(\"ParaShape\") ?? \"\"\r\n const shapeInfo = paraShapeMap.get(paraShapeId)\r\n\r\n const text = extractParagraphText(el)\r\n if (!text) return\r\n\r\n if (shapeInfo?.headingLevel != null) {\r\n blocks.push({ type: \"heading\", text, level: shapeInfo.headingLevel, pageNumber: sectionNum })\r\n } else {\r\n blocks.push({ type: \"paragraph\", text, pageNumber: sectionNum })\r\n }\r\n}\r\n\r\n/** <P> 에서 텍스트 추출 — <TEXT><CHAR> 순회 */\r\nfunction extractParagraphText(p: Element): string {\r\n const parts: string[] = []\r\n collectCharText(p, parts)\r\n return parts.join(\"\").trim()\r\n}\r\n\r\nfunction collectCharText(node: Element, parts: string[], depth: number = 0): void {\r\n if (depth > MAX_XML_DEPTH) return\r\n const children = node.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType !== 1) continue\r\n const tag = localName(el)\r\n\r\n if (tag === \"CHAR\") {\r\n // textContent — 자식 텍스트 노드 직접 수집\r\n const t = textContent(el)\r\n if (t) parts.push(t)\r\n } else if (tag === \"TABLE\" || tag === \"PICTURE\" || tag === \"SHAPEOBJECT\") {\r\n // 단락 내 테이블/이미지는 별도 블록으로 처리되므로 스킵\r\n } else if (tag === \"AUTONUM\") {\r\n // 자동 번호 (페이지 번호 등) 스킵\r\n } else {\r\n collectCharText(el, parts, depth + 1)\r\n }\r\n }\r\n}\r\n\r\n// ─── 테이블 파싱 ─────────────────────────────────────────\r\n\r\nfunction parseTable(\r\n el: Element,\r\n blocks: IRBlock[],\r\n paraShapeMap: Map<string, ParaShapeInfo>,\r\n sectionNum: number,\r\n warnings: ParseWarning[],\r\n): void {\r\n const cells: CellContext[] = []\r\n const rowCount = parseInt(el.getAttribute(\"RowCount\") ?? \"0\", 10)\r\n const colCount = parseInt(el.getAttribute(\"ColCount\") ?? \"0\", 10)\r\n if (isNaN(rowCount) || isNaN(colCount) || rowCount === 0 || colCount === 0) return\r\n if (rowCount > MAX_TABLE_ROWS || colCount > MAX_TABLE_COLS) {\r\n warnings.push({ message: `테이블 크기 초과 (${rowCount}x${colCount}) — 스킵`, code: \"TRUNCATED_TABLE\" })\r\n return\r\n }\r\n\r\n // <ROW> → <CELL> 순회\r\n const children = el.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const rowEl = children[i] as Element\r\n if (rowEl.nodeType !== 1 || localName(rowEl) !== \"ROW\") continue\r\n\r\n const rowCells = rowEl.childNodes\r\n for (let j = 0; j < rowCells.length; j++) {\r\n const cellEl = rowCells[j] as Element\r\n if (cellEl.nodeType !== 1 || localName(cellEl) !== \"CELL\") continue\r\n\r\n const colAddr = parseInt(cellEl.getAttribute(\"ColAddr\") ?? \"0\", 10)\r\n const rowAddr = parseInt(cellEl.getAttribute(\"RowAddr\") ?? \"0\", 10)\r\n // colSpan/rowSpan 클램핑: NaN, 음수, 과대값 방어\r\n const colSpan = Math.min(Math.max(1, parseInt(cellEl.getAttribute(\"ColSpan\") ?? \"1\", 10) || 1), MAX_TABLE_COLS)\r\n const rowSpan = Math.min(Math.max(1, parseInt(cellEl.getAttribute(\"RowSpan\") ?? \"1\", 10) || 1), MAX_TABLE_ROWS)\r\n\r\n // 셀 텍스트: PARALIST > P 재귀 추출\r\n const cellText = extractCellText(cellEl)\r\n\r\n cells.push({ text: cellText, colSpan, rowSpan, colAddr, rowAddr })\r\n }\r\n }\r\n\r\n if (cells.length === 0) return\r\n\r\n // 그리드 배치 (HWP5와 동일한 방식 — colAddr/rowAddr 사용)\r\n const grid: (CellContext | null)[][] = Array.from({ length: rowCount }, () => Array(colCount).fill(null))\r\n for (const cell of cells) {\r\n const r = cell.rowAddr ?? 0\r\n const c = cell.colAddr ?? 0\r\n if (isNaN(r) || isNaN(c) || r >= rowCount || c >= colCount) continue\r\n grid[r][c] = cell\r\n for (let dr = 0; dr < cell.rowSpan; dr++) {\r\n for (let dc = 0; dc < cell.colSpan; dc++) {\r\n if (dr === 0 && dc === 0) continue\r\n if (r + dr < rowCount && c + dc < colCount) {\r\n grid[r + dr][c + dc] = { text: \"\", colSpan: 1, rowSpan: 1 }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const cellRows: CellContext[][] = grid.map(row =>\r\n row.map(cell => cell ?? { text: \"\", colSpan: 1, rowSpan: 1 })\r\n )\r\n\r\n const table = buildTable(cellRows)\r\n blocks.push({ type: \"table\", table, pageNumber: sectionNum })\r\n}\r\n\r\n/** 셀 내부 텍스트 추출 — PARALIST > P 재귀, 중첩 테이블은 평탄화 */\r\nfunction extractCellText(cellEl: Element): string {\r\n const textParts: string[] = []\r\n collectCellText(cellEl, textParts, 0)\r\n return textParts.filter(Boolean).join(\"\\n\").trim()\r\n}\r\n\r\nfunction collectCellText(node: Element, parts: string[], depth: number): void {\r\n if (depth > 20) return\r\n const children = node.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType !== 1) continue\r\n const tag = localName(el)\r\n\r\n if (tag === \"P\") {\r\n const t = extractParagraphText(el)\r\n if (t) parts.push(t)\r\n } else if (tag === \"TABLE\") {\r\n // 중첩 테이블 — 텍스트로 평탄화\r\n parts.push(\"[중첩 테이블]\")\r\n } else {\r\n collectCellText(el, parts, depth + 1)\r\n }\r\n }\r\n}\r\n\r\n// ─── XML 유틸 ────────────────────────────────────────────\r\n\r\nfunction localName(el: Element): string {\r\n return (el.tagName || el.localName || \"\").replace(/^[^:]+:/, \"\")\r\n}\r\n\r\nfunction findChild(parent: Element, tag: string): Element | null {\r\n const children = parent.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType === 1 && localName(el) === tag) return el\r\n }\r\n return null\r\n}\r\n\r\nfunction textContent(el: Element): string {\r\n const children = el.childNodes\r\n const parts: string[] = []\r\n for (let i = 0; i < children.length; i++) {\r\n const node = children[i]\r\n if (node.nodeType === 3) { // TEXT_NODE\r\n parts.push(node.nodeValue || \"\")\r\n } else if (node.nodeType === 1) {\r\n parts.push(textContent(node as Element))\r\n }\r\n }\r\n return parts.join(\"\")\r\n}\r\n\r\nfunction countSections(body: Element): number {\r\n let count = 0\r\n const children = body.childNodes\r\n for (let i = 0; i < children.length; i++) {\r\n const el = children[i] as Element\r\n if (el.nodeType === 1 && localName(el) === \"SECTION\") count++\r\n }\r\n return count\r\n}\r\n","/** 텍스트 유사도 및 diff 유틸리티 — 외부 의존성 없음 */\n\nexport interface TextChange {\n type: \"equal\" | \"insert\" | \"delete\"\n text: string\n}\n\n/** 두 문자열의 유사도 (0-1). 1 = 동일, 0 = 완전히 다름 */\nexport function similarity(a: string, b: string): number {\n if (a === b) return 1\n if (!a || !b) return 0\n const maxLen = Math.max(a.length, b.length)\n if (maxLen === 0) return 1\n return 1 - levenshtein(a, b) / maxLen\n}\n\n/** 공백 정규화 후 유사도 비교 (HWP/HWPX 포맷 차이 흡수) */\nexport function normalizedSimilarity(a: string, b: string): number {\n return similarity(normalize(a), normalize(b))\n}\n\nfunction normalize(s: string): string {\n return s.replace(/\\s+/g, \" \").trim()\n}\n\n/** 최대 입력 길이 합 — 초과 시 길이 차이 기반 빠른 추정 (O(m*n) CPU 폭발 방지) */\nconst MAX_LEVENSHTEIN_LEN = 10_000\n\n/** Levenshtein 편집 거리 — O(min(m,n)) 공간 최적화 */\nfunction levenshtein(a: string, b: string): number {\n if (a.length + b.length > MAX_LEVENSHTEIN_LEN) {\n // 길이 차이 + 앞 500자 샘플 비교로 근사 거리 추정\n const sampleLen = Math.min(500, a.length, b.length)\n let diffs = 0\n for (let i = 0; i < sampleLen; i++) if (a[i] !== b[i]) diffs++\n const sampleRate = sampleLen > 0 ? diffs / sampleLen : 1\n return Math.abs(a.length - b.length) + Math.round(Math.min(a.length, b.length) * sampleRate)\n }\n if (a.length > b.length) [a, b] = [b, a]\n const m = a.length\n const n = b.length\n let prev = Array.from({ length: m + 1 }, (_, i) => i)\n let curr = new Array(m + 1)\n\n for (let j = 1; j <= n; j++) {\n curr[0] = j\n for (let i = 1; i <= m; i++) {\n if (a[i - 1] === b[j - 1]) {\n curr[i] = prev[i - 1]\n } else {\n curr[i] = 1 + Math.min(prev[i - 1], prev[i], curr[i - 1])\n }\n }\n ;[prev, curr] = [curr, prev]\n }\n return prev[m]\n}\n\n/** 단어 단위 diff — LCS 기반 */\nexport function textDiff(a: string, b: string): TextChange[] {\n const wordsA = a.split(/(\\s+)/)\n const wordsB = b.split(/(\\s+)/)\n const lcs = lcsWords(wordsA, wordsB)\n\n const changes: TextChange[] = []\n let ia = 0, ib = 0, il = 0\n\n while (il < lcs.length) {\n // lcs 원소 이전에 있는 것들\n while (ia < wordsA.length && wordsA[ia] !== lcs[il]) {\n changes.push({ type: \"delete\", text: wordsA[ia++] })\n }\n while (ib < wordsB.length && wordsB[ib] !== lcs[il]) {\n changes.push({ type: \"insert\", text: wordsB[ib++] })\n }\n changes.push({ type: \"equal\", text: lcs[il] })\n ia++; ib++; il++\n }\n // 나머지\n while (ia < wordsA.length) changes.push({ type: \"delete\", text: wordsA[ia++] })\n while (ib < wordsB.length) changes.push({ type: \"insert\", text: wordsB[ib++] })\n\n return mergeChanges(changes)\n}\n\nfunction lcsWords(a: string[], b: string[]): string[] {\n const m = a.length, n = b.length\n // 대형 문서 보호: 5000 단어 초과 시 간이 비교\n if (m * n > 25_000_000) return simpleIntersect(a, b)\n\n const dp: number[][] = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0))\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n dp[i][j] = a[i - 1] === b[j - 1]\n ? dp[i - 1][j - 1] + 1\n : Math.max(dp[i - 1][j], dp[i][j - 1])\n }\n }\n\n const result: string[] = []\n let i = m, j = n\n while (i > 0 && j > 0) {\n if (a[i - 1] === b[j - 1]) { result.push(a[i - 1]); i--; j-- }\n else if (dp[i - 1][j] >= dp[i][j - 1]) i--\n else j--\n }\n return result.reverse()\n}\n\nfunction simpleIntersect(a: string[], b: string[]): string[] {\n const setB = new Set(b)\n return a.filter(w => setB.has(w))\n}\n\nfunction mergeChanges(changes: TextChange[]): TextChange[] {\n if (changes.length === 0) return changes\n const merged: TextChange[] = [changes[0]]\n for (let i = 1; i < changes.length; i++) {\n const last = merged[merged.length - 1]\n if (last.type === changes[i].type) {\n last.text += changes[i].text\n } else {\n merged.push({ ...changes[i] })\n }\n }\n return merged\n}\n","/** 문서 비교 엔진 — IR 레벨 블록 비교로 신구대조표 생성 */\n\nimport { parse } from \"../index.js\"\nimport { normalizedSimilarity } from \"./text-diff.js\"\nimport type { IRBlock, IRTable, DiffResult, BlockDiff, CellDiff, DiffChangeType, ParseOptions } from \"../types.js\"\n\n/** 유사도 임계값 — 이 이상이면 modified, 미만이면 removed+added */\nconst SIMILARITY_THRESHOLD = 0.4\n\n/**\n * 두 문서를 비교하여 블록 단위 diff 생성.\n * 크로스 포맷 지원 — HWP vs HWPX 비교 가능 (IR 레벨).\n */\nexport async function compare(\n bufferA: ArrayBuffer,\n bufferB: ArrayBuffer,\n options?: ParseOptions\n): Promise<DiffResult> {\n const [resultA, resultB] = await Promise.all([\n parse(bufferA, options),\n parse(bufferB, options),\n ])\n\n if (!resultA.success) throw new Error(`문서A 파싱 실패: ${resultA.error}`)\n if (!resultB.success) throw new Error(`문서B 파싱 실패: ${resultB.error}`)\n\n return diffBlocks(resultA.blocks, resultB.blocks)\n}\n\n/** IRBlock[] 간 diff — LCS 기반 정렬 */\nexport function diffBlocks(blocksA: IRBlock[], blocksB: IRBlock[]): DiffResult {\n const aligned = alignBlocks(blocksA, blocksB)\n const stats = { added: 0, removed: 0, modified: 0, unchanged: 0 }\n const diffs: BlockDiff[] = []\n\n for (const [a, b] of aligned) {\n if (a && b) {\n const sim = blockSimilarity(a, b)\n if (sim >= 0.99) {\n diffs.push({ type: \"unchanged\", before: a, after: b, similarity: 1 })\n stats.unchanged++\n } else {\n const diff: BlockDiff = { type: \"modified\", before: a, after: b, similarity: sim }\n if (a.type === \"table\" && b.type === \"table\" && a.table && b.table) {\n diff.cellDiffs = diffTableCells(a.table, b.table)\n }\n diffs.push(diff)\n stats.modified++\n }\n } else if (a) {\n diffs.push({ type: \"removed\", before: a })\n stats.removed++\n } else if (b) {\n diffs.push({ type: \"added\", after: b })\n stats.added++\n }\n }\n\n return { stats, diffs }\n}\n\n// ─── 블록 정렬 (LCS 기반) ───────────────────────────\n\nfunction alignBlocks(a: IRBlock[], b: IRBlock[]): [IRBlock | null, IRBlock | null][] {\n const m = a.length, n = b.length\n\n // 대형 문서 보호\n if (m * n > 10_000_000) return fallbackAlign(a, b)\n\n // 유사도 매트릭스 캐시\n const simCache = new Map<string, number>()\n const getSim = (i: number, j: number): number => {\n const key = `${i},${j}`\n let v = simCache.get(key)\n if (v === undefined) { v = blockSimilarity(a[i], b[j]); simCache.set(key, v) }\n return v\n }\n\n // LCS with similarity threshold\n const dp: number[][] = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0))\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (getSim(i - 1, j - 1) >= SIMILARITY_THRESHOLD) {\n dp[i][j] = dp[i - 1][j - 1] + 1\n } else {\n dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])\n }\n }\n }\n\n // 역추적\n const pairs: [number, number][] = []\n let i = m, j = n\n while (i > 0 && j > 0) {\n if (getSim(i - 1, j - 1) >= SIMILARITY_THRESHOLD && dp[i][j] === dp[i - 1][j - 1] + 1) {\n pairs.push([i - 1, j - 1]); i--; j--\n } else if (dp[i - 1][j] >= dp[i][j - 1]) {\n i--\n } else {\n j--\n }\n }\n pairs.reverse()\n\n // 정렬 결과 조립\n const result: [IRBlock | null, IRBlock | null][] = []\n let ai = 0, bi = 0\n for (const [pi, pj] of pairs) {\n while (ai < pi) result.push([a[ai++], null])\n while (bi < pj) result.push([null, b[bi++]])\n result.push([a[ai++], b[bi++]])\n }\n while (ai < m) result.push([a[ai++], null])\n while (bi < n) result.push([null, b[bi++]])\n\n return result\n}\n\nfunction fallbackAlign(a: IRBlock[], b: IRBlock[]): [IRBlock | null, IRBlock | null][] {\n const result: [IRBlock | null, IRBlock | null][] = []\n const len = Math.max(a.length, b.length)\n for (let i = 0; i < len; i++) {\n result.push([a[i] || null, b[i] || null])\n }\n return result\n}\n\n// ─── 블록 유사도 ────────────────────────────────────\n\nfunction blockSimilarity(a: IRBlock, b: IRBlock): number {\n if (a.type !== b.type) return 0\n\n // 텍스트 기반 블록: paragraph, heading, list, image(alt text)\n if (a.text !== undefined && b.text !== undefined) {\n return normalizedSimilarity(a.text || \"\", b.text || \"\")\n }\n\n if (a.type === \"table\" && a.table && b.table) {\n return tableSimilarity(a.table, b.table)\n }\n\n // separator 등 텍스트 없는 동일 타입 → 완전 일치\n if (a.type === b.type) return 1\n\n return 0\n}\n\nfunction tableSimilarity(a: IRTable, b: IRTable): number {\n // 구조 유사도 (차원)\n const dimSim = 1 - Math.abs(a.rows * a.cols - b.rows * b.cols) / Math.max(a.rows * a.cols, b.rows * b.cols, 1)\n\n // 내용 유사도 (셀 텍스트)\n const textsA = a.cells.flat().map(c => c.text).join(\" \")\n const textsB = b.cells.flat().map(c => c.text).join(\" \")\n const contentSim = normalizedSimilarity(textsA, textsB)\n\n return dimSim * 0.3 + contentSim * 0.7\n}\n\n// ─── 테이블 셀 diff ─────────────────────────────────\n\nfunction diffTableCells(a: IRTable, b: IRTable): CellDiff[][] {\n const maxRows = Math.max(a.rows, b.rows)\n const maxCols = Math.max(a.cols, b.cols)\n const result: CellDiff[][] = []\n\n for (let r = 0; r < maxRows; r++) {\n const row: CellDiff[] = []\n for (let c = 0; c < maxCols; c++) {\n const cellA = r < a.rows && c < a.cols ? a.cells[r][c].text : undefined\n const cellB = r < b.rows && c < b.cols ? b.cells[r][c].text : undefined\n\n let type: DiffChangeType\n if (cellA === undefined) type = \"added\"\n else if (cellB === undefined) type = \"removed\"\n else if (cellA === cellB) type = \"unchanged\"\n else type = \"modified\"\n\n row.push({ type, before: cellA, after: cellB })\n }\n result.push(row)\n }\n return result\n}\n"],"mappings":"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;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAC7E,CAAC;AAED,IAAM,YAAY,IAAI,WAAW;AAAA,EAC/B;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAC3E;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;AAAA,EAAK;A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","names":["warnings","inflateRawSync","MAX_DECOMPRESS_SIZE","inflateRawSync","require","cellRows","JSZip","DOMParser","JSZip","DOMParser","JSZip","JSZip","DOMParser","MAX_DECOMPRESS_SIZE","MAX_ROWS","MAX_COLS","DOMParser","JSZip","parsePageRange","JSZip","DOMParser","MAX_DECOMPRESS_SIZE","localName","parseXml","DOMParser","parseRels","runs","text","JSZip","DOMParser","MAX_XML_DEPTH","DOMParser","localName","parseSection","parseParagraph","parseTable","extractCellText","i","j"]}
|