kordoc 4.19.0 → 4.19.2

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.
Files changed (127) hide show
  1. package/README.md +3 -1
  2. package/dist/{-I3DEUQUC.js → -3JIHF2AL.js} +11 -11
  3. package/dist/{chunk-CR4RXHXO.js → chunk-4CS7XE2Q.js} +2 -2
  4. package/dist/{chunk-UCTD3BXH.js → chunk-56ZWAREM.js} +2 -2
  5. package/dist/{chunk-YV4PWWRW.cjs → chunk-5PGOZBEQ.cjs} +22 -22
  6. package/dist/{chunk-YV4PWWRW.cjs.map → chunk-5PGOZBEQ.cjs.map} +1 -1
  7. package/dist/{chunk-OMJWAJBD.js → chunk-6ZBORHFB.js} +2 -2
  8. package/dist/{chunk-DH75ZUYF.js → chunk-AKSCTGKG.js} +10 -4
  9. package/dist/chunk-AKSCTGKG.js.map +1 -0
  10. package/dist/{chunk-L5KNWOGR.js → chunk-AOVMOCTK.js} +10 -4
  11. package/dist/chunk-AOVMOCTK.js.map +1 -0
  12. package/dist/{chunk-XXUXQHUC.js → chunk-B4R443MV.js} +2 -2
  13. package/dist/{chunk-RYKNAYKC.js → chunk-EUCBMW6C.js} +3 -3
  14. package/dist/{chunk-XIWGPC3V.js → chunk-GNUGD4RO.js} +2 -2
  15. package/dist/{chunk-AMDDV4CI.js → chunk-I2TBI7PN.js} +2 -2
  16. package/dist/{chunk-5B55S4JU.js → chunk-J75TVRJB.js} +2 -2
  17. package/dist/{chunk-VZSOPECZ.js → chunk-JK7N4R4K.js} +2 -2
  18. package/dist/{chunk-DEYCTP2S.js → chunk-JXJDXDOU.js} +3 -3
  19. package/dist/{chunk-34RIECIF.cjs → chunk-MZ6XNFZL.cjs} +2 -2
  20. package/dist/{chunk-34RIECIF.cjs.map → chunk-MZ6XNFZL.cjs.map} +1 -1
  21. package/dist/{chunk-C4JHZNRL.js → chunk-NV46IWKF.js} +27 -21
  22. package/dist/chunk-NV46IWKF.js.map +1 -0
  23. package/dist/{chunk-UTVNSCEI.js → chunk-OKHKEYTL.js} +2 -2
  24. package/dist/{chunk-LNU54ZKV.cjs → chunk-ORGWGR4K.cjs} +15 -9
  25. package/dist/chunk-ORGWGR4K.cjs.map +1 -0
  26. package/dist/{chunk-AAE765MK.js → chunk-PWQ2BVAT.js} +3 -3
  27. package/dist/{chunk-FJE3TMZ7.js → chunk-QYJPU4FZ.js} +30 -2
  28. package/dist/chunk-QYJPU4FZ.js.map +1 -0
  29. package/dist/{chunk-DGXGVC4Z.js → chunk-QZ63HXEG.js} +51 -20
  30. package/dist/chunk-QZ63HXEG.js.map +1 -0
  31. package/dist/{chunk-GCYLNOTJ.js → chunk-R2JM2HOJ.js} +4 -4
  32. package/dist/{chunk-LLVF6X5W.cjs → chunk-RVBQT2PD.cjs} +3 -3
  33. package/dist/{chunk-LLVF6X5W.cjs.map → chunk-RVBQT2PD.cjs.map} +1 -1
  34. package/dist/{chunk-7TVRQJXI.cjs → chunk-T2DZTOGG.cjs} +12 -12
  35. package/dist/{chunk-7TVRQJXI.cjs.map → chunk-T2DZTOGG.cjs.map} +1 -1
  36. package/dist/{chunk-GYQVQF2J.js → chunk-TM4T4NYJ.js} +2 -2
  37. package/dist/{chunk-W5XX2GKO.js → chunk-U6AJU2VD.js} +2 -2
  38. package/dist/{chunk-QHN7N2QA.js → chunk-VO22VAR3.js} +2 -2
  39. package/dist/{chunk-5ZSQ2ZIH.cjs → chunk-WQAGDJIP.cjs} +3 -3
  40. package/dist/{chunk-5ZSQ2ZIH.cjs.map → chunk-WQAGDJIP.cjs.map} +1 -1
  41. package/dist/{chunk-HFMIAMEO.js → chunk-XPM7WC54.js} +4 -4
  42. package/dist/chunks-2EC2R7PY.js +10 -0
  43. package/dist/cli.js +26 -26
  44. package/dist/{formula-L2BKGA4J.cjs → formula-RNATQ3FQ.cjs} +9 -9
  45. package/dist/{formula-L2BKGA4J.cjs.map → formula-RNATQ3FQ.cjs.map} +1 -1
  46. package/dist/{formula-DUEAYGIG.js → formula-RSTTP626.js} +4 -4
  47. package/dist/{formula-I4MAHCTZ.js → formula-STIFIS57.js} +3 -3
  48. package/dist/{image-ocr-ZWRJZKYR.cjs → image-ocr-7L5TBBPA.cjs} +15 -15
  49. package/dist/{image-ocr-ZWRJZKYR.cjs.map → image-ocr-7L5TBBPA.cjs.map} +1 -1
  50. package/dist/{image-ocr-B32G24XW.js → image-ocr-R6ZDFBAN.js} +6 -6
  51. package/dist/{image-ocr-NUL3JKXM.js → image-ocr-XKSAEEXP.js} +9 -9
  52. package/dist/index.cjs +824 -761
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.js +94 -31
  55. package/dist/index.js.map +1 -1
  56. package/dist/mcp.js +22 -22
  57. package/dist/{model-bundle-OBIKG4K5.js → model-bundle-NGTKULP7.js} +5 -5
  58. package/dist/{models-2OZHOPPM.js → models-QOSU5SYL.js} +5 -5
  59. package/dist/{parser-FDE6GQST.js → parser-3G4G6SST.js} +176 -12
  60. package/dist/parser-3G4G6SST.js.map +1 -0
  61. package/dist/{parser-YW3NMSJE.js → parser-5GUEP6EK.js} +173 -9
  62. package/dist/parser-5GUEP6EK.js.map +1 -0
  63. package/dist/{parser-DMWCXX7A.cjs → parser-AIQBU7FS.cjs} +392 -228
  64. package/dist/parser-AIQBU7FS.cjs.map +1 -0
  65. package/dist/pdf-ocr-7OAFUDM4.cjs +17 -0
  66. package/dist/{pdf-ocr-K2OK7FNQ.cjs.map → pdf-ocr-7OAFUDM4.cjs.map} +1 -1
  67. package/dist/pdf-ocr-DAXS5RX4.js +16 -0
  68. package/dist/pdf-ocr-R3VGX6F2.js +20 -0
  69. package/dist/{profile-io-6WCU4KHY.js → profile-io-ZVNMDLLS.js} +3 -3
  70. package/dist/{rasterize-X3I7SWXF.js → rasterize-Y2RLMVCL.js} +3 -3
  71. package/dist/{redact-doc-6H7WPUQD.js → redact-doc-PNRDDBI3.js} +11 -11
  72. package/dist/{render-PBQJGZT3.js → render-S3ALARVO.js} +7 -7
  73. package/dist/seal-EJWPS5VK.js +10 -0
  74. package/dist/{setup-UORJSR3M.js → setup-JH66VSFZ.js} +3 -3
  75. package/dist/visual-GDXMKDVY.js +20 -0
  76. package/dist/{watch-S2ZMDDZU.js → watch-KMB2FTOD.js} +12 -12
  77. package/package.json +1 -1
  78. package/dist/chunk-C4JHZNRL.js.map +0 -1
  79. package/dist/chunk-DGXGVC4Z.js.map +0 -1
  80. package/dist/chunk-DH75ZUYF.js.map +0 -1
  81. package/dist/chunk-FJE3TMZ7.js.map +0 -1
  82. package/dist/chunk-L5KNWOGR.js.map +0 -1
  83. package/dist/chunk-LNU54ZKV.cjs.map +0 -1
  84. package/dist/chunks-JPNLQOVD.js +0 -10
  85. package/dist/parser-DMWCXX7A.cjs.map +0 -1
  86. package/dist/parser-FDE6GQST.js.map +0 -1
  87. package/dist/parser-YW3NMSJE.js.map +0 -1
  88. package/dist/pdf-ocr-7MR56U4P.js +0 -16
  89. package/dist/pdf-ocr-K2OK7FNQ.cjs +0 -17
  90. package/dist/pdf-ocr-YHC4X67W.js +0 -20
  91. package/dist/seal-E2E7CFZJ.js +0 -10
  92. package/dist/visual-4GDT5BRH.js +0 -20
  93. /package/dist/{-I3DEUQUC.js.map → -3JIHF2AL.js.map} +0 -0
  94. /package/dist/{chunk-CR4RXHXO.js.map → chunk-4CS7XE2Q.js.map} +0 -0
  95. /package/dist/{chunk-UCTD3BXH.js.map → chunk-56ZWAREM.js.map} +0 -0
  96. /package/dist/{chunk-OMJWAJBD.js.map → chunk-6ZBORHFB.js.map} +0 -0
  97. /package/dist/{chunk-XXUXQHUC.js.map → chunk-B4R443MV.js.map} +0 -0
  98. /package/dist/{chunk-RYKNAYKC.js.map → chunk-EUCBMW6C.js.map} +0 -0
  99. /package/dist/{chunk-XIWGPC3V.js.map → chunk-GNUGD4RO.js.map} +0 -0
  100. /package/dist/{chunk-AMDDV4CI.js.map → chunk-I2TBI7PN.js.map} +0 -0
  101. /package/dist/{chunk-5B55S4JU.js.map → chunk-J75TVRJB.js.map} +0 -0
  102. /package/dist/{chunk-VZSOPECZ.js.map → chunk-JK7N4R4K.js.map} +0 -0
  103. /package/dist/{chunk-DEYCTP2S.js.map → chunk-JXJDXDOU.js.map} +0 -0
  104. /package/dist/{chunk-UTVNSCEI.js.map → chunk-OKHKEYTL.js.map} +0 -0
  105. /package/dist/{chunk-AAE765MK.js.map → chunk-PWQ2BVAT.js.map} +0 -0
  106. /package/dist/{chunk-GCYLNOTJ.js.map → chunk-R2JM2HOJ.js.map} +0 -0
  107. /package/dist/{chunk-GYQVQF2J.js.map → chunk-TM4T4NYJ.js.map} +0 -0
  108. /package/dist/{chunk-W5XX2GKO.js.map → chunk-U6AJU2VD.js.map} +0 -0
  109. /package/dist/{chunk-QHN7N2QA.js.map → chunk-VO22VAR3.js.map} +0 -0
  110. /package/dist/{chunk-HFMIAMEO.js.map → chunk-XPM7WC54.js.map} +0 -0
  111. /package/dist/{chunks-JPNLQOVD.js.map → chunks-2EC2R7PY.js.map} +0 -0
  112. /package/dist/{formula-DUEAYGIG.js.map → formula-RSTTP626.js.map} +0 -0
  113. /package/dist/{formula-I4MAHCTZ.js.map → formula-STIFIS57.js.map} +0 -0
  114. /package/dist/{image-ocr-B32G24XW.js.map → image-ocr-R6ZDFBAN.js.map} +0 -0
  115. /package/dist/{image-ocr-NUL3JKXM.js.map → image-ocr-XKSAEEXP.js.map} +0 -0
  116. /package/dist/{model-bundle-OBIKG4K5.js.map → model-bundle-NGTKULP7.js.map} +0 -0
  117. /package/dist/{models-2OZHOPPM.js.map → models-QOSU5SYL.js.map} +0 -0
  118. /package/dist/{pdf-ocr-7MR56U4P.js.map → pdf-ocr-DAXS5RX4.js.map} +0 -0
  119. /package/dist/{pdf-ocr-YHC4X67W.js.map → pdf-ocr-R3VGX6F2.js.map} +0 -0
  120. /package/dist/{profile-io-6WCU4KHY.js.map → profile-io-ZVNMDLLS.js.map} +0 -0
  121. /package/dist/{rasterize-X3I7SWXF.js.map → rasterize-Y2RLMVCL.js.map} +0 -0
  122. /package/dist/{redact-doc-6H7WPUQD.js.map → redact-doc-PNRDDBI3.js.map} +0 -0
  123. /package/dist/{render-PBQJGZT3.js.map → render-S3ALARVO.js.map} +0 -0
  124. /package/dist/{seal-E2E7CFZJ.js.map → seal-EJWPS5VK.js.map} +0 -0
  125. /package/dist/{setup-UORJSR3M.js.map → setup-JH66VSFZ.js.map} +0 -0
  126. /package/dist/{visual-4GDT5BRH.js.map → visual-GDXMKDVY.js.map} +0 -0
  127. /package/dist/{watch-S2ZMDDZU.js.map → watch-KMB2FTOD.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/render/svg-render.ts","../src/render/layout.ts","../src/hwpx/font-metrics.ts","../src/hwpx/text-metrics.ts","../src/render/para-model.ts","../src/render/reflow.ts","../src/render/scene.ts","../src/render/page-stories.ts","../src/render/shape-geometry.ts","../src/hwp5/record.ts","../src/toc-entry.ts","../src/hwp5/images.ts","../src/hwp5/aes.ts","../src/hwp5/crypto.ts","../src/hwp5/cfb-lenient.ts","../src/hwp5/pw-crypto.ts","../src/hwp5/page-boundary.ts","../src/hwp5/table-ids.ts","../src/shared/numbering.ts","../src/hwp5/numbering.ts","../src/hwpx/equation.ts","../src/hwp5/equation.ts","../src/hwp5/ir-assemble.ts","../src/hwp5/body.ts","../src/hwp5/parser.ts","../src/render/hwp5-scene.ts","../src/render/document.ts","../src/detect.ts","../src/render/html.ts","../src/print/renderer.ts","../src/render/pdf.ts","../src/render/regions.ts"],"sourcesContent":["/**\n * 레이아웃 보존 렌더 — HWPX 조판 캐시(lineseg·cellAddr·hp:pos)를 SVG 절대배치로 그린다.\n *\n * 조판 엔진 없음: 한컴이 저장 시 기록한 좌표를 그대로 사용한다. 따라서\n * 한컴(또는 조판 캐시를 기록하는 편집기)에서 저장한 파일만 렌더 가능 —\n * markdownToHwpx 산출물엔 linesegarray가 없어 KordocError를 던진다.\n *\n * 좌표 산식(실측 검증 — .claude/plans/render-poc/findings.md):\n * - 단위 HWPUNIT(1/7200in), pt = /100. 최상위 문단 lineseg = 본문영역 로컬,\n * 셀 문단 = 셀 로컬. PARA 밀어내기 개체 anchor = 호스트vp − (omTop+h+omBottom) 역산.\n * - horzsize는 줄 \"영역\" 폭(텍스트 폭 아님) — 마지막 줄이 아닌 줄은 원본 줄바꿈에\n * 맞춰 textLength로 고정하고, 마지막 줄만 paraPr 정렬(LEFT 자연폭/CENTER/RIGHT/배분)을 적용.\n * - 좌표 속성엔 uint32로 저장된 음수가 섞여 있다(toInt32 필수).\n * - 표 열은 span 제약 경계 전파로, 행은 rs=1 max + 콘텐츠 초과 성장으로 푼다.\n *\n * 페이지: 최상위 lineseg vertpos는 페이지 로컬(페이지마다 0부터 리셋)이므로 역행\n * 지점을 페이지 경계로 감지, 전 페이지를 세로 스택으로 그린다(페이지별 흰 배경 +\n * 클립). 페이지에 걸친 표는 시작 페이지에서 잘린다(조판 캐시에 분할점이 없음).\n *\n * 범위: section0 한정(다중 구역은 첫 구역만), 수식·그리기개체 도형은 미지원(경고 수집).\n */\n\nimport JSZip from \"jszip\"\nimport { KordocError, precheckZipSize } from \"../utils.js\"\nimport { createXmlParser, findChildByLocalName, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES } from \"../hwpx/parser-shared.js\"\nimport { toInt32, solveBoundaries, solveRowHeights, type SpanConstraint } from \"./layout.js\"\nimport { measureTextWidth, faceClassOf, type WrapMode } from \"../hwpx/text-metrics.js\"\nimport { parseRenderStyles, DEFAULT_CHAR, type RenderStyles, type RenderBorderEdge, type RenderParaGeom } from \"./head-styles.js\"\nimport { reflowSection } from \"./reflow.js\"\nimport { RegionCollector, type PageBBox, type RenderRegion, type RenderScene } from \"./scene.js\"\nimport { ln, elements, num, findFirst, type Seg, type ParaChar, OBJ_TAGS, type ParaObj, type ParaModel, buildPara, prepareDeletedRanges, tabAdvance, type ExtentMemo, cellContentExtent, collectCells, measureTableHeight } from \"./para-model.js\"\nimport { pageStories } from \"./page-stories.js\"\nimport { mulAffine, objectGeometry, renderingMatrix, shapeGeometry, rotatedBounds, SHAPE_TAGS, type Affine, type RegionRotation } from \"./shape-geometry.js\"\nexport { buildPara, measureTableHeight, tabAdvance, type Seg, type ParaChar, type ParaModel, type ExtentMemo } from \"./para-model.js\"\n\nexport interface RenderSvgOptions {\n /** 이미지 1장당 허용 최대 바이트 (기본 40MB) */\n maxImageBytes?: number\n /** 검색어 형광펜 — 텍스트 조각 내 매치 구간에 배경 rect (대소문자 무시).\n * charPr(스타일) 경계에 걸친 매치는 칠하지 못한다. */\n highlights?: string[]\n /** Tier-2 reflow — 조판 캐시(linesegarray) 없는 파일도 순수 TS 조판으로 렌더.\n * 캐시가 있으면 무시(한컴본은 캐시 재생). 기본 false(캐시 없으면 KordocError). */\n reflow?: boolean\n /** reflow 줄바꿈 폴백 모드 — 'keep'(어절) / 'charAll'(글자). 문단 paraPr에\n * breakSetting 선언이 있으면 그 값이 우선하며(BREAK_WORD=어절, KEEP_WORD=글자 —\n * 이름 역전 주의), 이 옵션은 선언 없는 문단에만 적용된다. */\n reflowMode?: WrapMode\n}\n\nexport interface RenderSvgResult {\n svg: string\n /** 페이지 폭 (pt) */\n width: number\n /** 전체 캔버스 높이 (pt) — 페이지 세로 스택 + 간격 */\n height: number\n /** 렌더된 페이지 수 */\n pageCount: number\n warnings: string[]\n stats: { texts: number; images: number; tables: number; shapes: number }\n /** 개체 region(페이지 로컬 pt bbox·결정적 id) — #75 */\n regions: RenderRegion[]\n}\n\n// ─── XML 헬퍼 ─────────────────────────────────────\n\nexport function escapeXml(s: string): string {\n return s\n // XML 1.0 금지 C0 제어문자 제거(탭 0x09·개행 0x0A·CR 0x0D는 유지) — 남기면 산출 SVG가\n // 불법 XML이라 뷰어·브라우저가 그 페이지 렌더를 통째로 중단한다(\"PCDATA invalid Char value\").\n // gen-ids.escapeXml·source-map.escapeXmlText와 같은 계약 (rhwp #3382 동종)\n .replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, \"\")\n .replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\")\n}\n\n// ─── 내부 모델 ────────────────────────────────────\n\ninterface PageGeom { PW: number; PH: number; ML: number; MT: number; BODY_W: number; BODY_H: number }\n\ninterface Ctx {\n /** 페이지별 SVG 버퍼 — emit()이 현재 페이지(page)로 라우팅 */\n pages: string[][]\n /** 현재 그리는 페이지 인덱스 — 최상위 문단만 갱신, 셀/중첩 콘텐츠는 호스트 페이지 상속 */\n page: number\n geom: PageGeom\n styles: RenderStyles\n images: Map<string, { dataUri: string; symId?: string; orgW?: number; orgH?: number }>\n /** 이미지 심볼 defs — dataURI는 여기 1회만, 본문은 <use> 참조 (occurrence당 재emit 금지) */\n defs: string[]\n /** 검색어 형광펜 (소문자 정규화, 빈 문자열 제거) */\n highlights: string[]\n warnings: string[]\n warned: Set<string>\n stats: { texts: number; images: number; tables: number; shapes: number }\n /** 셀/표 측정 메모 — 중첩표 지수 재계산 캡 (렌더 1회 공유) */\n extentMemo: ExtentMemo\n /** 개체 region 수집 — 그리는 순서로 결정적 id (#75). 전 구역 공유 */\n regions: RegionCollector\n /** 현재 열린 부모 region id 스택 (표 셀 콘텐츠·문단 개체·도형 글상자) */\n parentStack: string[]\n /** 앞선 구역들의 페이지 수 누계 — 전역 1-based 페이지 = pageBase + page + 1 */\n pageBase: number\n /** 이 구역 페이지의 실높이(HWPUNIT) — 단일 페이지 캔버스 연장 포함. region 하단 클램프용 */\n pageH: number\n paintJobs: PaintJob[] | null\n /** Same ancestor rotations as the SVG groups containing this object's paint. */\n regionRotations: Array<RegionRotation | Affine>\n}\ninterface PaintJob { page: number; layer: number; z: number; draw: () => void }\n\nconst pt = (u: number): string => String(Math.round(u) / 100)\n/** HWPUNIT → pt (소수 둘째 자리) */\nconst ptNum = (u: number): number => Math.round(u) / 100\n\n/** 현재 그리는 페이지의 전역 1-based 번호 */\nfunction pageNo(ctx: Ctx): number { return ctx.pageBase + ctx.page + 1 }\n\n/** HWPUNIT 사각형 → 페이지 로컬 pt bbox. 페이지 하단을 넘는 높이는 잘라 기록(분할 지오메트리 없음) */\nfunction bboxOf(ctx: Ctx, x: number, y: number, w: number, h: number): PageBBox {\n const b = rotatedBounds({ x, y, w, h }, ctx.regionRotations)\n const clippedH = Math.max(0, Math.min(b.h, ctx.pageH - b.y))\n return { page: pageNo(ctx), x: ptNum(b.x), y: ptNum(b.y), width: ptNum(Math.max(0, b.w)), height: ptNum(clippedH) }\n}\n\nfunction regionOpenTag(id: string, type: string, page: number): string {\n return `<g data-kordoc-id=\"${id}\" data-kordoc-type=\"${type}\" data-kordoc-page=\"${page}\">`\n}\n\nfunction parentId(ctx: Ctx): string | undefined {\n return ctx.parentStack.length ? ctx.parentStack[ctx.parentStack.length - 1] : undefined\n}\n\nfunction emit(ctx: Ctx, s: string): void {\n ctx.pages[ctx.page].push(s)\n}\n\nfunction warnOnce(ctx: Ctx, key: string, msg: string): void {\n if (ctx.warned.has(key)) return\n ctx.warned.add(key)\n ctx.warnings.push(msg)\n}\n\n// ─── 문단 모델 구축 ────────────────────────────────\n\n// ─── 줄 렌더 (run별 charPr + 정렬) ──────────────────\n\ninterface LinePlan {\n seg: Seg\n /** 줄 시작 x 보정 (정렬) */\n xoff: number\n /** 텍스트 조각 폭 스케일 */\n scale: number\n start: number\n end: number\n /** 탭 슬롯 → 전진폭(HWPUNIT, 스케일 불변) */\n tabs: Map<number, number>\n /** 이 인덱스 앞(줄의 마지막 탭까지)은 스케일 없이 — 양쪽 정렬은 마지막 탭 뒤 글에만 걸린다 */\n fixedTo: number\n /** 줄 끝 공백을 뺀 끝 — 줄 끝 공백은 걸리기만 하고(hang) 정렬·그리기에서 빠진다 */\n textEnd: number\n}\n\nfunction charW(c: ParaChar, styles: RenderStyles): number {\n const st = (c.prId != null ? styles.charPr.get(c.prId) : undefined) ?? DEFAULT_CHAR\n return measureTextWidth(c.ch, st.height, st.ratio, { spacingPct: st.spacing, faceClass: faceClassOf(st.face) })\n}\n\n/** 줄 자연폭 = 텍스트 조각 + 인라인 개체 폭 (개체 폭은 스케일 불변, TAC 표는 outMargin 좌/우 포함) */\nfunction lineNaturalWidth(m: ParaModel, styles: RenderStyles, start: number, end: number): { text: number; obj: number } {\n let text = 0\n for (let i = start; i < end && i < m.chars.length; i++) text += charW(m.chars[i], styles)\n let obj = 0\n for (const o of m.objs) if (o.inline && o.index >= start && o.index < end) obj += o.omL + o.width + o.omR\n return { text, obj }\n}\n\nfunction planLines(m: ParaModel, styles: RenderStyles): LinePlan[] {\n const align = (m.paraPrId != null ? styles.paraAlign.get(m.paraPrId) : undefined) ?? \"JUSTIFY\"\n const geom = m.paraPrId != null ? styles.paraGeom.get(m.paraPrId) : undefined\n const hang = geom && geom.marginIntent < 0 ? -geom.marginIntent : 0\n const plans: LinePlan[] = []\n for (let i = 0; i < m.segs.length; i++) {\n const seg = m.segs[i]\n const start = seg.textpos\n const end = i + 1 < m.segs.length ? m.segs[i + 1].textpos : Math.max(m.chars.length, start)\n // 탭 해석 — 자연폭으로 정지점을 잡고, 마지막 탭까지는 스케일 없이 고정폭으로 둔다\n const tabs = new Map<number, number>()\n let x = i === 0 ? Math.max(geom?.marginIntent ?? 0, 0) : hang // 첫 줄은 들여쓰기(양수 intent)만큼 들어가서 시작\n let fixedTo = start\n let fixedW = 0\n for (let k = start; k < end && k < m.chars.length; k++) {\n const c = m.chars[k]\n if (!c.tab) { x += charW(c, styles); continue }\n const adv = tabAdvance(x, i === 0, geom, c.tabW)\n tabs.set(k, adv)\n x += adv\n fixedTo = k + 1\n }\n if (fixedTo > start) fixedW = lineNaturalWidth(m, styles, start, fixedTo).text + [...tabs.values()].reduce((a, b) => a + b, 0)\n // 줄 끝 공백 제외 — 어절 줄바꿈은 줄마다 끝에 공백이 걸린다. 넣고 늘이면 글이 오른쪽 끝에 못 닿는다\n let textEnd = Math.min(end, m.chars.length)\n while (textEnd > fixedTo && m.chars[textEnd - 1].ch === \" \") textEnd--\n const nat = lineNaturalWidth(m, styles, fixedTo, textEnd)\n for (const o of m.objs) if (o.inline && o.index >= textEnd && o.index < end) nat.obj += o.omL + o.width + o.omR\n nat.obj += fixedW\n for (const o of m.objs) if (o.inline && o.index >= start && o.index < fixedTo) nat.obj += o.omL + o.width + o.omR\n const isLast = i === m.segs.length - 1\n let xoff = 0\n let scale = 1\n const avail = seg.horzsize - nat.obj\n if (nat.text > 0 && (!isLast || align === \"DISTRIBUTE\" || align === \"DISTRIBUTE_SPACE\")) {\n // 줄바꿈이 고정된 중간 줄·배분정렬 — 줄 영역 폭에 맞춰 고정 (원본 조판 재현)\n scale = avail > 0 ? avail / nat.text : 1\n } else if (nat.text + nat.obj > 0 && isLast) {\n const w = nat.text + nat.obj\n if (align === \"CENTER\") xoff = Math.max(0, (seg.horzsize - w) / 2)\n else if (align === \"RIGHT\") xoff = Math.max(0, seg.horzsize - w)\n }\n if (!Number.isFinite(scale) || scale <= 0) scale = 1\n scale = Math.min(4, Math.max(0.25, scale))\n plans.push({ seg, xoff, scale, start, end, tabs, fixedTo, textEnd })\n }\n return plans\n}\n\n/** 줄 안 [start, upto) 구간의 전진폭 (텍스트×스케일 + 인라인 개체 — TAC 표는 outMargin 좌/우 포함) */\nfunction advanceTo(m: ParaModel, styles: RenderStyles, plan: LinePlan, upto: number): number {\n let x = 0\n for (let i = plan.start; i < upto && i < m.chars.length; i++) {\n x += m.chars[i].tab ? plan.tabs.get(i) ?? 0 : charW(m.chars[i], styles) * (i < plan.fixedTo ? 1 : plan.scale)\n }\n for (const o of m.objs) if (o.inline && o.index >= plan.start && o.index < upto) x += o.omL + o.width + o.omR\n return x\n}\n\n// ─── 문단 드로잉 ───────────────────────────────────\n\n/**\n * @param segPages 최상위 문단 전용 — seg 인덱스별 페이지 배정 (vertpos 리셋 감지 결과).\n * 셀/중첩 콘텐츠는 전달하지 않아 호스트의 ctx.page 를 상속한다.\n */\nfunction drawPara(p: Element, ox: number, oy: number, areaW: number, ctx: Ctx, depth: number, segPages?: number[]): void {\n if (depth > 16) { warnOnce(ctx, \"depth\", \"중첩 깊이 16 초과 — 이하 생략\"); return }\n const m = buildPara(p)\n if (m.segs.length === 0) {\n // 조판 캐시 없는 문단 — 개체만이라도 문단 원점에 배치. 텍스트는 무음 생략 금지\n if (m.chars.some(c => c.ch !== \"\")) {\n warnOnce(ctx, \"no-lineseg\", \"조판 캐시 없는 문단 텍스트 생략 — reflow 옵션으로 합성 가능\")\n }\n for (const o of m.objs) {\n const { x, y } = o.inline ? { x: ox, y: oy } : anchorObject(o, ox, oy, 0, areaW, ctx)\n drawObject(o, x, y, 0, areaW, ctx, depth)\n }\n return\n }\n const plans = planLines(m, ctx.styles)\n const baseV = m.segs[0].vertpos\n\n // 문단 region — 실문자가 있는 문단만. 줄(lineseg) 영역의 페이지별 합집합, 페이지가 바뀌면 조각을 나눈다.\n // SVG 는 페이지 버퍼별로 <g data-kordoc-*> 래퍼를 열고 닫는다(개체는 별도 region, parentId=문단).\n const hasText = m.chars.some(c => c.ch !== \"\" && c.ch.trim() !== \"\")\n let paraRegionId: string | null = null\n let gOpenOn = -1\n\n for (let li = 0; li < plans.length; li++) {\n const plan = plans[li]\n if (segPages && segPages[li] !== undefined) ctx.page = segPages[li]\n const { seg } = plan\n if (hasText) {\n const lineBox = bboxOf(ctx, ox + seg.horzpos, oy + seg.vertpos, seg.horzsize, seg.textheight)\n if (paraRegionId === null) paraRegionId = ctx.regions.add(\"paragraph\", lineBox, { parentId: parentId(ctx) })\n else ctx.regions.addFragment(paraRegionId, lineBox)\n if (gOpenOn !== ctx.page) {\n if (gOpenOn >= 0) ctx.pages[gOpenOn].push(\"</g>\")\n emit(ctx, regionOpenTag(paraRegionId, \"paragraph\", pageNo(ctx)))\n gOpenOn = ctx.page\n }\n }\n // charPr 단위 조각으로 분할\n let i = plan.start\n let cursor = ox + seg.horzpos + plan.xoff\n const y = oy + seg.vertpos + seg.baseline\n while (i < plan.end && i < m.chars.length) {\n if (i >= plan.textEnd && m.chars[i].ch === \" \") { i++; continue }\n // 필러 슬롯(컨트롤·서로게이트 자리)은 그리지 않고 건너뛴다 —\n // 인라인 개체의 폭 전진은 개체 첫 슬롯에서 1회 수행\n if (m.chars[i].ch === \"\") {\n if (m.chars[i].tab) cursor += plan.tabs.get(i) ?? 0\n for (const o of m.objs) if (o.inline && o.index === i) cursor += o.omL + o.width + o.omR\n i++\n continue\n }\n // 마지막 탭 앞(부호 등)은 양쪽 정렬 스케일 없이 — 탭 뒤 내용 시작이 정지점에 정확히 선다\n const scale = i < plan.fixedTo ? 1 : plan.scale\n const prId = m.chars[i].prId\n let j = i\n let piece = \"\"\n // 필러에서 멈추므로 piece 안은 실문자뿐 — 개체 경계 절단이 자연 발생\n while (j < plan.end && j < m.chars.length && m.chars[j].prId === prId && m.chars[j].ch !== \"\" && !(j >= plan.textEnd && m.chars[j].ch === \" \")) { piece += m.chars[j].ch; j++ }\n // 연속 공백(2+) 경계 절단 — 공백 폭 오차(한컴 0.5em 고정 vs 뷰어 폰트)를\n // 공백 구간에 가둔다 (공무원 스페이스 정렬 원문에서 글자 벌어짐 방지)\n // (문자열 인덱스를 슬롯 오프셋으로 쓴다 — 서로게이트 쌍이 섞이면 1슬롯 오차, 무시)\n {\n const cut = piece.search(/ {2,}/)\n if (cut > 0) { piece = piece.slice(0, cut); j = i + cut }\n else if (cut === 0) {\n const runEnd = piece.match(/^ +/)![0].length\n piece = piece.slice(0, runEnd); j = i + runEnd\n }\n }\n const st = (prId != null ? ctx.styles.charPr.get(prId) : undefined) ?? DEFAULT_CHAR\n\n // 텍스트 세그먼트 1개를 렌더하고 전진폭 반환. hit=true 면 형광펜 배경 rect 를\n // 텍스트 앞에 깐다. 세그먼트마다 자체 textLength 를 쓰므로 rect(hit)와 글자가\n // 완전히 같은 폭·위치로 계산돼 형광펜이 어긋나지 않는다.\n const renderSeg = (text: string, cx: number, hit: boolean): number => {\n const sw = measureTextWidth(text, st.height, st.ratio, { spacingPct: st.spacing, faceClass: faceClassOf(st.face) }) * scale\n if (hit) {\n emit(ctx, `<rect x=\"${pt(cx)}\" y=\"${pt(oy + seg.vertpos)}\" width=\"${pt(sw)}\" height=\"${pt(seg.textheight)}\" fill=\"#ffd54f\" fill-opacity=\"0.45\"/>`)\n }\n if (text.trim().length > 0) {\n const attrs: string[] = [`x=\"${pt(cx)}\"`, `y=\"${pt(y)}\"`, `font-size=\"${pt(st.height)}\"`]\n if (st.fontFamily) attrs.push(`font-family=\"${escapeXml(st.fontFamily)}\"`)\n if ([...text].length > 1 && sw > 50) {\n attrs.push(`textLength=\"${pt(sw)}\"`, `lengthAdjust=\"${scale < 1 ? \"spacingAndGlyphs\" : \"spacing\"}\"`)\n }\n if (st.bold) attrs.push(`font-weight=\"bold\"`)\n if (st.italic) attrs.push(`font-style=\"italic\"`)\n if (st.underline) attrs.push(`text-decoration=\"underline\"`)\n if (st.color) attrs.push(`fill=\"${escapeXml(st.color)}\"`)\n emit(ctx, `<text ${attrs.join(\" \")}>${escapeXml(text)}</text>`)\n ctx.stats.texts++\n }\n return sw\n }\n\n // 형광펜 매치 구간 수집 → 병합(겹침 제거) → [평문·매치·평문…] 분할 렌더\n const merged: Array<[number, number]> = []\n if (ctx.highlights.length > 0 && piece.trim().length > 0) {\n const found: Array<[number, number]> = []\n const lower = piece.toLowerCase()\n for (const term of ctx.highlights) {\n for (let f = lower.indexOf(term); f !== -1; f = lower.indexOf(term, f + term.length)) {\n found.push([f, f + term.length])\n }\n }\n found.sort((a, b) => a[0] - b[0])\n for (const [s, e] of found) {\n const tail = merged[merged.length - 1]\n if (tail && s <= tail[1]) tail[1] = Math.max(tail[1], e)\n else merged.push([s, e])\n }\n }\n\n if (merged.length === 0) {\n cursor += renderSeg(piece, cursor, false)\n } else {\n let segCur = cursor\n let last = 0\n for (const [s, e] of merged) {\n segCur += renderSeg(piece.slice(last, s), segCur, false)\n segCur += renderSeg(piece.slice(s, e), segCur, true)\n last = e\n }\n segCur += renderSeg(piece.slice(last), segCur, false)\n cursor = segCur\n }\n i = j\n }\n }\n if (gOpenOn >= 0) ctx.pages[gOpenOn].push(\"</g>\")\n\n // 개체 배치 — 인라인은 소속 줄 위치, 앵커는 hp:pos 해석. 개체 region 의 부모는 문단(없으면 상위)\n if (paraRegionId) ctx.parentStack.push(paraRegionId)\n for (const o of m.objs) {\n if (o.inline) {\n let planIdx = 0\n for (let k = 0; k < plans.length; k++) {\n const pl = plans[k]\n if (pl.start <= o.index && (o.index < pl.end || k === plans.length - 1)) planIdx = k\n }\n const plan = plans[planIdx]\n if (segPages && segPages[planIdx] !== undefined) ctx.page = segPages[planIdx]\n // TAC 표 괘선은 pen + outMargin좌 (advanceTo는 o.index 앞까지라 자신의 om 미포함)\n const x = ox + plan.seg.horzpos + plan.xoff + advanceTo(m, ctx.styles, plan, o.index) + o.omL\n // 개체가 줄보다 낮으면 baseline 위에 얹고, 줄을 채우는 개체(th==h)는 줄 상단\n const yTop = oy + plan.seg.vertpos + Math.max(0, plan.seg.baseline - o.height)\n drawObject(o, x, yTop, baseV, areaW, ctx, depth)\n } else {\n // 앵커 좌표는 첫 seg(baseV) 기준이므로 첫 seg 의 페이지에 귀속\n if (segPages && segPages[0] !== undefined) ctx.page = segPages[0]\n const { x, y } = anchorObject(o, ox, oy, baseV, areaW, ctx)\n drawObject(o, x, y, baseV, areaW, ctx, depth)\n }\n }\n if (paraRegionId) ctx.parentStack.pop()\n}\n\n/** hp:pos 기준계 해석 → 개체 좌상단 절대좌표 (tac=0) */\nfunction anchorObject(o: ParaObj, ox: number, oy: number, baseV: number, areaW: number, ctx: Ctx): { x: number; y: number } {\n const { PW, PH, ML, MT, BODY_W, BODY_H } = ctx.geom\n const pos = findChildByLocalName(o.el, \"pos\")\n const om = findChildByLocalName(o.el, \"outMargin\")\n const omT = num(om, \"top\"), omB = num(om, \"bottom\")\n // 표는 셀 콘텐츠 성장으로 선언 sz보다 커질 수 있다 — reflow가 실효높이로 밀어낸 만큼\n // 되돌려야 표가 뒤 문단 위에 겹치지 않는다 (한컴 저장본은 sz가 실높이라 차이 없음)\n const w = o.width, h = o.tag === \"tbl\" ? Math.max(o.height, measureTableHeight(o.el, ctx.extentMemo)) : o.height\n if (!pos) return { x: ox, y: oy + baseV }\n const vo = num(pos, \"vertOffset\")\n const ho = num(pos, \"horzOffset\")\n const vrel = pos.getAttribute(\"vertRelTo\") ?? \"PARA\"\n const hrel = pos.getAttribute(\"horzRelTo\") ?? \"PARA\"\n const va = pos.getAttribute(\"vertAlign\") ?? \"TOP\"\n const ha = pos.getAttribute(\"horzAlign\") ?? \"LEFT\"\n const wrap = o.el.getAttribute(\"textWrap\") ?? \"TOP_AND_BOTTOM\"\n\n let y: number\n if (vrel === \"PAPER\") {\n y = va === \"BOTTOM\" ? PH - h - vo : va === \"CENTER\" ? (PH - h) / 2 + vo : vo\n } else if (vrel === \"PAGE\") {\n y = va === \"BOTTOM\" ? MT + BODY_H - h - vo : va === \"CENTER\" ? MT + (BODY_H - h) / 2 + vo : MT + vo\n } else if (wrap === \"TOP_AND_BOTTOM\") {\n // PARA 밀어내기 역산 — 호스트 문단 vp가 개체(여백 포함)만큼 밀렸다는 모델 (실측 일치).\n // 음수면 밀지 않은 개체(빈 호스트 줄 등)로 보고 현재 흐름 y 사용.\n const pushed = baseV - (omT + h + omB)\n const anchor = pushed >= -100 ? pushed : baseV\n y = oy + anchor + omT + vo\n } else {\n // BEHIND_TEXT/IN_FRONT/SQUARE — 문단을 밀지 않는 개체는 문단 원점 흐름 배치\n y = oy + baseV + vo\n }\n\n let x: number\n if (hrel === \"PAGE\") {\n x = ha === \"RIGHT\" ? ML + BODY_W - w - ho : ha === \"CENTER\" ? ML + (BODY_W - w) / 2 + ho : ML + ho\n } else if (hrel === \"PAPER\") {\n x = ha === \"RIGHT\" ? PW - w - ho : ha === \"CENTER\" ? (PW - w) / 2 + ho : ho\n } else {\n // PARA/COLUMN — 현재 텍스트 영역 기준 (셀 안이면 셀 내부 폭. COLUMN을 페이지\n // 단으로 읽으면 셀 안 개체가 페이지 왼쪽으로 튄다 — 사진대지 우측 셀 실측)\n x = ha === \"RIGHT\" ? ox + areaW - w - ho : ha === \"CENTER\" ? ox + (areaW - w) / 2 + ho : ox + ho\n }\n return { x, y }\n}\n\nfunction drawObject(o: ParaObj, x: number, y: number, baseV: number, areaW: number, ctx: Ctx, depth: number): void {\n if (!o.inline && depth === 0 && ctx.paintJobs) {\n const wrap = o.el.getAttribute(\"textWrap\")\n const layer = wrap === \"BEHIND_TEXT\" ? -1 : wrap === \"IN_FRONT_OF_TEXT\" ? 1 : 0\n const page = ctx.page, parents = [...ctx.parentStack]\n ctx.paintJobs.push({ page, layer, z: num(o.el, \"zOrder\"), draw: () => {\n ctx.page = page\n const saved = ctx.parentStack\n ctx.parentStack = parents\n drawObject(o, x, y, baseV, areaW, ctx, depth)\n ctx.parentStack = saved\n } })\n return\n }\n if (o.tag === \"tbl\") drawTable(o.el, x, y, ctx, depth + 1)\n else if (o.tag === \"pic\") drawPic(o.el, x, y, ctx)\n else if (o.tag === \"container\") {\n if (drawGroupByMatrix(o, x, y, baseV, areaW, ctx, depth)) return\n // 그리기개체 묶음(행렬 없음 — HWP5 장면) — 자식 개체를 컨테이너 원점 기준으로 재귀 배치\n for (const ch of elements(o.el)) {\n const tag = ln(ch)\n if (!OBJ_TAGS.has(tag)) continue\n const sz = findChildByLocalName(ch, \"sz\")\n const off = findChildByLocalName(ch, \"offset\")\n const sub: ParaObj = { el: ch, tag, index: 0, inline: true, width: num(sz, \"width\"), height: num(sz, \"height\"), omL: 0, omR: 0 }\n drawObject(sub, x + num(off, \"x\"), y + num(off, \"y\"), baseV, areaW, ctx, depth + 1)\n }\n } else if (o.tag === \"equation\") {\n warnOnce(ctx, \"equation\", \"수식 개체는 렌더 미지원 — 생략\")\n } else if (SHAPE_TAGS.has(o.tag)) {\n drawShape(o, x, y, ctx, depth)\n } else {\n warnOnce(ctx, `shape:${o.tag}`, `개체(${o.tag}) 렌더 미지원 — 생략`)\n }\n}\n\n/**\n * 묶음(hp:container) — 안쪽 개체마다 renderingInfo 행렬로 원본 좌표를 최상위 묶음 상자에 놓는다(배율·회전·중첩 묶음 한 번에).\n * 최상위 묶음 자기 행렬이 그리는 상자를 묶음 자리(x, y)에 맞춘다. 종전엔 offset 만 더해 배율 묶음(지도·도식)의 그림이 작게,\n * 곡선·도형이 엉뚱한 자리에 크게 그려졌다(#116). 행렬 없는 묶음(HWP5 장면)은 false — 종전 offset 경로\n */\nfunction drawGroupByMatrix(o: ParaObj, x: number, y: number, baseV: number, areaW: number, ctx: Ctx, depth: number): boolean {\n const top = renderingMatrix(o.el)\n const leaves: Element[] = []\n const collect = (el: Element): boolean => {\n for (const ch of elements(el)) {\n const tag = ln(ch)\n if (!OBJ_TAGS.has(tag)) continue\n if (tag === \"container\") { if (!collect(ch)) return false }\n else if (renderingMatrix(ch)) leaves.push(ch)\n else return false\n }\n return true\n }\n if (!top || !collect(o.el)) return false\n const org = findChildByLocalName(o.el, \"orgSz\")\n const box = rotatedBounds({ x: 0, y: 0, w: num(org, \"width\"), h: num(org, \"height\") }, [top])\n const base: Affine = [1, 0, 0, 1, x - box.x, y - box.y]\n for (const leaf of leaves) {\n const m = mulAffine(base, renderingMatrix(leaf)!)\n const tag = ln(leaf), lorg = findChildByLocalName(leaf, \"orgSz\")\n const w = num(lorg, \"width\") || num(findChildByLocalName(leaf, \"curSz\"), \"width\")\n const h = num(lorg, \"height\") || num(findChildByLocalName(leaf, \"curSz\"), \"height\")\n ctx.regionRotations.push(m)\n emit(ctx, `<g transform=\"matrix(${m.slice(0, 4).map(v => Math.round(v * 1e6) / 1e6).join(\" \")} ${pt(m[4])} ${pt(m[5])})\">`)\n const sub: ParaObj = { el: leaf, tag, index: 0, inline: true, width: w, height: h, omL: 0, omR: 0 }\n if (tag === \"pic\") drawPicFrame(leaf, 0, 0, w, h, \"\", ctx)\n else if (SHAPE_TAGS.has(tag)) drawShape(sub, 0, 0, ctx, depth + 1, [Math.hypot(m[0], m[1]) || 1, Math.hypot(m[2], m[3]) || 1])\n else drawObject(sub, 0, 0, baseV, areaW, ctx, depth + 1)\n emit(ctx, \"</g>\")\n ctx.regionRotations.pop()\n }\n return true\n}\n\n// ─── 그리기 도형 ───────────────────────────────────\n/** groupScale — 묶음 행렬 안에서 원본 좌표로 그릴 때 그 행렬의 가로·세로 배율. 글상자 글은 배율을 되돌려 그린다(아래) */\nfunction drawShape(o: ParaObj, x: number, y: number, ctx: Ctx, depth: number, groupScale?: [number, number]): void {\n const geometry = shapeGeometry(o, x, y, ctx.defs, ctx.images, (key, msg) => warnOnce(ctx, key, msg), !!groupScale)\n // Transform original corners through every ancestor and this shape's rotation\n // before bounding once. An intermediate axis-aligned box inflates nested crops.\n const b = geometry\n if (geometry.rotation) ctx.regionRotations.push(geometry.rotation)\n ctx.stats.shapes++\n const shapeId = ctx.regions.add(\"shape\", bboxOf(ctx, b.x, b.y, b.w, b.h), { parentId: parentId(ctx) })\n emit(ctx, regionOpenTag(shapeId, \"shape\", pageNo(ctx)))\n ctx.parentStack.push(shapeId)\n if (geometry.transform) emit(ctx, `<g transform=\"${geometry.transform}\">`)\n emit(ctx, geometry.svg)\n const dt = findChildByLocalName(o.el, \"drawText\")\n const sub = dt ? findChildByLocalName(dt, \"subList\") : null\n // 글은 틀 자리(돌린 도형은 외접 상자 안으로 옮긴 틀, objectGeometry)에서. 묶음 안 도형은 글 줄 배치(lineseg)가 이미 배율 뒤\n // 단위라 행렬의 배율만 되돌린다 — 글자는 늘어나지 않고 회전만 따른다 (한컴: 묶음 크기를 바꿔도 글자 크기는 그대로)\n if (sub) {\n const [sx, sy] = groupScale ?? [1, 1]\n const unscale = sx !== 1 || sy !== 1\n if (unscale) { emit(ctx, `<g transform=\"scale(${1 / sx} ${1 / sy})\">`); ctx.regionRotations.push([1 / sx, 0, 0, 1 / sy, 0, 0]) }\n for (const p of elements(sub)) if (ln(p) === \"p\") drawPara(p, geometry.x * sx, geometry.y * sy, geometry.w * sx, ctx, depth + 1)\n if (unscale) { ctx.regionRotations.pop(); emit(ctx, \"</g>\") }\n }\n if (geometry.rotation) ctx.regionRotations.pop()\n if (geometry.transform) emit(ctx, \"</g>\")\n ctx.parentStack.pop()\n emit(ctx, \"</g>\")\n}\n\n// ─── 표 ───────────────────────────────────────────\n\nfunction edgeLine(x1: number, y1: number, x2: number, y2: number, e: RenderBorderEdge): string {\n const dash = /DASH|DOT/.test(e.type) ? ` stroke-dasharray=\"${e.type.includes(\"DOT\") ? \"1,1.5\" : \"3,1.5\"}\"` : \"\"\n return `<line x1=\"${pt(x1)}\" y1=\"${pt(y1)}\" x2=\"${pt(x2)}\" y2=\"${pt(y2)}\" stroke=\"${escapeXml(e.color)}\" stroke-width=\"${e.widthPt.toFixed(2)}\"${dash}/>`\n}\n\nfunction drawTable(tbl: Element, tx: number, ty: number, ctx: Ctx, depth: number): void {\n if (depth > 16) { warnOnce(ctx, \"depth\", \"중첩 깊이 16 초과 — 이하 생략\"); return }\n ctx.stats.tables++\n const tblSz = findChildByLocalName(tbl, \"sz\")\n const cells = collectCells(tbl)\n if (cells.length === 0 || cells.length > 4096) return\n\n const nCols = Math.max(...cells.map(c => c.ca + c.cs))\n const nRows = Math.max(...cells.map(c => c.ra + c.rs))\n const colCons: SpanConstraint[] = cells.map(c => ({ a: c.ca, b: c.ca + c.cs, size: c.w }))\n const colX = solveBoundaries(colCons, nCols, num(tblSz, \"width\") || undefined)\n const rowH = solveRowHeights(\n cells.map(c => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c, ctx.extentMemo) : undefined })),\n nRows,\n )\n const rowY: number[] = [0]\n for (let r = 0; r < nRows; r++) rowY.push(rowY[r] + rowH[r])\n\n // 표 region — 페이지에 걸친 표는 시작 페이지에서 잘려 그려지므로(조판 캐시에 분할점 없음) 시작 페이지\n // 조각만 기록하고 경고한다. sourceId = hp:tbl id (IR 표와의 조인 키, #76)\n const tblH = rowY[nRows]\n if (ty + tblH > ctx.pageH + 100) warnOnce(ctx, \"table-split\", \"페이지에 걸친 표 — 분할 지오메트리가 없어 시작 페이지 영역만 region 으로 기록\")\n const tblId = ctx.regions.add(\"table\", bboxOf(ctx, tx, ty, colX[nCols], tblH), { sourceId: tbl.getAttribute(\"id\") ?? undefined, parentId: parentId(ctx) })\n emit(ctx, regionOpenTag(tblId, \"table\", pageNo(ctx)))\n ctx.parentStack.push(tblId)\n\n // 1패스: 배경 → 2패스: 콘텐츠 → 3패스: 테두리 (테두리가 배경/콘텐츠 위)\n const geom = cells.map(c => ({\n c,\n x: tx + colX[c.ca], y: ty + rowY[c.ra],\n w: colX[Math.min(c.ca + c.cs, nCols)] - colX[c.ca],\n h: rowY[Math.min(c.ra + c.rs, nRows)] - rowY[c.ra],\n }))\n for (const g of geom) {\n const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : undefined\n if (bf?.fill) emit(ctx, `<rect x=\"${pt(g.x)}\" y=\"${pt(g.y)}\" width=\"${pt(g.w)}\" height=\"${pt(g.h)}\" fill=\"${escapeXml(bf.fill)}\"/>`)\n }\n for (const g of geom) {\n const { c } = g\n if (!c.sub) continue\n const innerH = g.h - c.marginT - c.marginB\n const extent = cellContentExtent(c, ctx.extentMemo)\n const va = c.sub.getAttribute(\"vertAlign\") ?? \"TOP\"\n let yoff = 0\n if (va === \"CENTER\") yoff = Math.max(0, (innerH - extent) / 2)\n else if (va === \"BOTTOM\") yoff = Math.max(0, innerH - extent)\n for (const p of elements(c.sub)) {\n if (ln(p) !== \"p\") continue\n drawPara(p, g.x + c.marginL, g.y + c.marginT + yoff, g.w - c.marginL - c.marginR, ctx, depth + 1)\n }\n }\n for (const g of geom) {\n const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : undefined\n if (!bf) continue\n if (bf.top) emit(ctx, edgeLine(g.x, g.y, g.x + g.w, g.y, bf.top))\n if (bf.bottom) emit(ctx, edgeLine(g.x, g.y + g.h, g.x + g.w, g.y + g.h, bf.bottom))\n if (bf.left) emit(ctx, edgeLine(g.x, g.y, g.x, g.y + g.h, bf.left))\n if (bf.right) emit(ctx, edgeLine(g.x + g.w, g.y, g.x + g.w, g.y + g.h, bf.right))\n }\n ctx.parentStack.pop()\n emit(ctx, \"</g>\")\n}\n\n// ─── 이미지 ───────────────────────────────────────\n\n/**\n * 이미지 심볼 등록(바이너리당 1회) — dataURI를 defs에 한 번만 넣고 <use>로 참조한다.\n * occurrence마다 dataURI를 재emit하면 반복 참조 문서에서 SVG 문자열이 기하급수로\n * 커져 RangeError/OOM (소형 입력 DoS). viewBox 100×100 + preserveAspectRatio none\n * 이라 <use width/height>가 기존 <image width/height> stretch와 동일하게 스케일된다.\n */\nfunction imageSymbol(loaded: { dataUri: string; symId?: string }, ctx: Ctx): string {\n if (!loaded.symId) {\n loaded.symId = `bin${ctx.defs.length}`\n ctx.defs.push(\n `<symbol id=\"${loaded.symId}\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\">` +\n `<image width=\"100\" height=\"100\" preserveAspectRatio=\"none\" href=\"${loaded.dataUri}\"/></symbol>`,\n )\n }\n return loaded.symId\n}\n\nfunction drawPic(pic: Element, x: number, y: number, ctx: Ctx): void {\n const sz = findChildByLocalName(pic, \"sz\")\n let w = num(sz, \"width\", 5669), h = num(sz, \"height\", 5669)\n // 돌린 사진 — sz 는 돌린 외접 상자, 그림은 curSz 틀로 그 상자 중심에 놓고 돌린다 (objectGeometry, #116)\n const cur = findChildByLocalName(pic, \"curSz\")\n const rotated = objectGeometry(pic, x, y, num(cur, \"width\") || w, num(cur, \"height\") || h, w, h)\n if (rotated.rotation) { x = rotated.x; y = rotated.y; w = rotated.w; h = rotated.h; ctx.regionRotations.push(rotated.rotation) }\n drawPicFrame(pic, x, y, w, h, rotated.transform, ctx)\n if (rotated.rotation) ctx.regionRotations.pop()\n}\n\nfunction drawPicFrame(pic: Element, x: number, y: number, w: number, h: number, transform: string, ctx: Ctx): void {\n const img = findFirst(pic, \"img\")\n const ref = img?.getAttribute(\"binaryItemIDRef\")\n const loaded = ref != null ? ctx.images.get(ref) : undefined\n const imgId = ctx.regions.add(\"image\", bboxOf(ctx, x, y, w, h), { sourceId: ref ?? undefined, parentId: parentId(ctx) })\n emit(ctx, regionOpenTag(imgId, \"image\", pageNo(ctx)))\n const close = transform ? \"</g></g>\" : \"</g>\"\n if (transform) emit(ctx, `<g transform=\"${transform}\">`)\n if (!loaded) {\n emit(ctx, `<rect x=\"${pt(x)}\" y=\"${pt(y)}\" width=\"${pt(w)}\" height=\"${pt(h)}\" fill=\"#eee\" stroke=\"#c00\" stroke-width=\"0.5\"/>`)\n warnOnce(ctx, `img:${ref}`, `이미지 바이너리 누락: ${ref ?? \"(ref 없음)\"}`)\n emit(ctx, close)\n return\n }\n ctx.stats.images++\n // imgClip 좌표계는 imgDim(이미지 내용 상자) 기준이다 — orgSz(최초 삽입 크기)가 아니다.\n // 실측(데모 코퍼스 pic 267개): clip==dim 254개(크롭 없음)·실제 크롭 8개 모두 dim 기준.\n // orgSz 로 비교하면 리사이즈된 로고(dim<org)가 좌상단 코너로 잘못 잘려 로고가 깨진다.\n const clip = findChildByLocalName(pic, \"imgClip\")\n const imgDim = findChildByLocalName(pic, \"imgDim\")\n const orgSz = findChildByLocalName(pic, \"orgSz\")\n const dimW = num(imgDim, \"dimwidth\"), dimH = num(imgDim, \"dimheight\")\n // 참조(전체 내용) 공간: imgDim 우선, 없으면 orgSz 폴백\n const refW = dimW > 0 ? dimW : num(orgSz, \"width\")\n const refH = dimH > 0 ? dimH : num(orgSz, \"height\")\n const cl = num(clip, \"left\"), ct = num(clip, \"top\")\n const cr = num(clip, \"right\", refW), cb = num(clip, \"bottom\", refH)\n const cropped =\n refW > 0 && refH > 0 && clip != null && (cl > 0 || ct > 0 || cr < refW || cb < refH) && cr > cl && cb > ct\n const symId = imageSymbol(loaded, ctx)\n if (cropped) {\n // 참조 좌표계 viewBox로 크롭 창을 내고, 이미지는 전체 내용 크기로 깐다\n emit(ctx,\n `<svg x=\"${pt(x)}\" y=\"${pt(y)}\" width=\"${pt(w)}\" height=\"${pt(h)}\" viewBox=\"${pt(cl)} ${pt(ct)} ${pt(cr - cl)} ${pt(cb - ct)}\" preserveAspectRatio=\"none\">` +\n `<use href=\"#${symId}\" x=\"0\" y=\"0\" width=\"${pt(refW)}\" height=\"${pt(refH)}\"/></svg>`,\n )\n } else {\n emit(ctx, `<use href=\"#${symId}\" x=\"${pt(x)}\" y=\"${pt(y)}\" width=\"${pt(w)}\" height=\"${pt(h)}\"/>`)\n }\n emit(ctx, close)\n}\n\nfunction sniffMime(name: string, bytes: Uint8Array): string {\n const lower = name.toLowerCase()\n if (lower.endsWith(\".png\") || (bytes.length > 4 && bytes[0] === 0x89 && bytes[1] === 0x50)) return \"image/png\"\n if (lower.endsWith(\".bmp\") || (bytes.length > 2 && bytes[0] === 0x42 && bytes[1] === 0x4d)) return \"image/bmp\"\n if (lower.endsWith(\".gif\") || (bytes.length > 3 && bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46)) return \"image/gif\"\n if (lower.endsWith(\".svg\")) return \"image/svg+xml\"\n if (bytes.length > 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) return \"image/jpeg\"\n return \"image/jpeg\"\n}\n\n// ─── 엔트리 ───────────────────────────────────────\n\n/** 구역 하나의 페이지 지오메트리 (HWPUNIT) */\nfunction readSectionGeom(root: Element): PageGeom {\n const pagePr = findFirst(root, \"pagePr\")\n const margin = pagePr ? findChildByLocalName(pagePr, \"margin\") : null\n let PW = num(pagePr, \"width\", 59528), PH = num(pagePr, \"height\", 84188)\n // 용지 방향 — width/height는 용지 원방향(세로) 치수로 저장되고 landscape=\"NARROWLY\"가\n // 가로(90° 회전)를 뜻한다(WIDELY=세로). 무시하면 가로 문서의 오른쪽이 잘린다\n // (실측: 가로 A4 문서 lineseg 줄폭 78520 = 84188 − 좌우마진, 세로폭 59528 초과).\n if (pagePr?.getAttribute(\"landscape\") === \"NARROWLY\" && PW < PH) {\n const t = PW; PW = PH; PH = t\n }\n const ML = num(margin, \"left\", 8504)\n const MT = num(margin, \"top\", 5668) + num(margin, \"header\", 0)\n const BODY_H = PH - MT - num(margin, \"bottom\", 4252) - num(margin, \"footer\", 0)\n const BODY_W = PW - ML - num(margin, \"right\", 8504)\n return { PW, PH, ML, MT, BODY_W, BODY_H }\n}\n\n/** 렌더된 구역 — 페이지 버퍼 + 페이지 크기(구역별 pagePr 상이 가능) */\ninterface RenderedSection { pages: string[][]; PW: number; pageH: number; clipId: string }\n\n/**\n * 구역 하나를 렌더 → 페이지 버퍼. 공유 자원(styles/images/defs/warnings/stats)은 ctx로 넘겨\n * 이미지 심볼·통계·경고가 전 구역에 누적되게 한다(dataURI 중복·페이지 카운트 정합).\n */\nfunction renderSectionToPages(\n root: Element,\n geom: PageGeom,\n ctxBase: Omit<Ctx, \"pages\" | \"page\" | \"geom\" | \"pageH\" | \"paintJobs\" | \"regionRotations\">,\n doReflow: boolean,\n reflowMode: WrapMode,\n masters: Element[] = [],\n hasDeletionMarkers = true,\n): { pages: string[][]; pageH: number } {\n const { PW, PH, ML, MT, BODY_W, BODY_H } = geom\n // Tier-2 reflow — 캐시 없는 문단에 linesegarray 합성 주입. 혼합 캐시 문서(한컴\n // 저장본을 프로그램 편집해 일부 문단만 캐시 없음)도 reflow 옵션이면 진입한다 —\n // 전량 캐시 문서는 전 문단 skip(Tier-1 무회귀)이라 no-op.\n if (hasDeletionMarkers) prepareDeletedRanges(root)\n if (doReflow) reflowSection(root, ctxBase.styles, { BODY_W, BODY_H }, reflowMode)\n\n // 페이지 분할 프리패스 — 최상위 lineseg vertpos는 페이지 로컬(페이지마다 0부터)이라\n // 역행 지점이 곧 페이지 경계다. 다단(colCount>1)은 단 이동도 vertpos가 리셋되지만\n // horzpos가 오른쪽으로 점프하므로, horzpos가 왼쪽으로 돌아올 때만 새 페이지로 본다.\n // vertpos \"동일\"도 문단 첫 seg + horzpos 비전진이면 경계다 — 페이지 전체가 표 하나인\n // 문단이 연속되면 vertpos가 매 페이지 0으로 같아서 strict 역행만으론 못 가른다\n // (의사일정표류: v0 문단 연속 → 뒤 페이지들이 전부 한 페이지에 겹침).\n // 문단 내부의 vertpos 동일 seg는 개체 좌우로 갈라진 같은 줄(h 우측 점프)이라 제외.\n const colPr = findFirst(root, \"colPr\")\n const multiCol = num(colPr, \"colCount\", 1) > 1\n const paraSegPages = new Map<Element, number[]>()\n let nPages = 1\n let maxTopV = 0\n {\n let prevV = Number.NEGATIVE_INFINITY\n let prevH = Number.NEGATIVE_INFINITY\n let cur = 0\n for (const p of elements(root)) {\n if (ln(p) !== \"p\") continue\n const lsa = findChildByLocalName(p, \"linesegarray\")\n const segEls = lsa ? elements(lsa).filter(s => ln(s) === \"lineseg\") : []\n const pagesOf: number[] = []\n let paraFirst = true\n for (const s of segEls) {\n const v = num(s, \"vertpos\")\n const h = num(s, \"horzpos\")\n const brk = v < prevV\n ? (!multiCol || h <= prevH)\n : (paraFirst && v === prevV && h <= prevH)\n if (brk) cur++\n paraFirst = false\n pagesOf.push(cur)\n maxTopV = Math.max(maxTopV, v + num(s, \"textheight\", 1000))\n prevV = v\n prevH = h\n }\n paraSegPages.set(p, pagesOf)\n nPages = Math.max(nPages, cur + 1)\n }\n }\n\n // 리셋이 없는데 본문이 페이지를 넘는 파일(누적 vertpos 기록본) 방어 —\n // 한 페이지로 두되 캔버스만 내용 끝까지 늘려 잘림을 막는다. (region 클램프에 쓰므로 그리기 전에 확정)\n const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2000) : PH\n const ctx: Ctx = {\n ...ctxBase,\n pages: Array.from({ length: nPages }, () => []),\n page: 0,\n geom,\n pageH,\n paintJobs: [],\n regionRotations: [],\n }\n for (const story of pageStories(root, masters, paraSegPages, nPages, ctx.pageBase, geom)) {\n prepareDeletedRanges(story.sub)\n if (doReflow) reflowSection(story.sub, ctx.styles, { BODY_W: story.width, BODY_H: 0 }, reflowMode)\n const draw = (): void => {\n ctx.page = story.page\n for (const p of elements(story.sub)) if (ln(p) === \"p\") drawPara(p, story.x, story.y, story.width, ctx, 0)\n }\n if (story.layer !== undefined) ctx.paintJobs!.push({ page: story.page, layer: story.layer, z: 0, draw })\n else draw()\n }\n ctx.page = 0\n for (const p of elements(root)) {\n if (ln(p) !== \"p\") continue\n drawPara(p, ML, MT, BODY_W, ctx, 0, paraSegPages.get(p))\n }\n const normal = ctx.pages\n const jobs = ctx.paintJobs!\n ctx.paintJobs = null\n const behind = Array.from({ length: nPages }, () => [] as string[])\n const front = Array.from({ length: nPages }, () => [] as string[])\n for (const job of jobs.sort((a, b) => a.layer - b.layer || a.z - b.z)) {\n ctx.pages = job.layer < 0 ? behind : front\n job.draw()\n }\n ctx.pages = normal.map((buf, page) => [...behind[page], ...buf, ...front[page]])\n return { pages: ctx.pages, pageH }\n}\n\n/** 내부 렌더 산출 — 구역별 페이지 버퍼 + 공유 defs·region. 세로 스택/페이지별 SVG 조립의 공통 입력 */\nexport interface InternalRender {\n sections: RenderedSection[]\n defs: string[]\n warnings: string[]\n stats: Ctx[\"stats\"]\n regions: RenderRegion[]\n}\n\n/** 이미지 참조(binaryItemIDRef) → dataURI (심볼 id 는 렌더 중 발급) */\nexport type RenderImages = Ctx[\"images\"]\n\n/** 렌더할 구역 DOM — HWPX section*.xml 또는 HWP5 어댑터가 합성한 동형 DOM (hwp5-scene) */\nexport interface SectionRoot {\n root: Element\n index: number\n masterPages?: Element[]\n /** False only when the source XML proved no deletion markers; custom DOMs default to scanning. */\n hasDeletionMarkers?: boolean\n}\n\nexport interface SectionRenderInput {\n styles: RenderStyles\n images: RenderImages\n /** 로딩 단계 경고를 이어받아 렌더 경고를 누적한다 */\n warnings: string[]\n reflow: boolean\n reflowMode: WrapMode\n highlights?: string[]\n}\n\n/**\n * 구역 DOM 들 → 페이지 버퍼 (포맷 무관 공용 단계). 공유 자원(styles/images/defs/warnings/stats/region)은\n * 전 구역에 누적된다 — 이미지 심볼 dataURI 중복 방지·전역 페이지 번호·결정적 region id.\n */\nexport function renderSectionRoots(sections: SectionRoot[], input: SectionRenderInput): InternalRender {\n const ctxBase: Omit<Ctx, \"pages\" | \"page\" | \"geom\" | \"pageH\" | \"paintJobs\" | \"regionRotations\"> = {\n styles: input.styles, images: input.images, defs: [],\n highlights: (input.highlights ?? []).map(s => s.trim().toLowerCase()).filter(s => s.length > 0),\n warnings: input.warnings, warned: new Set(), stats: { texts: 0, images: 0, tables: 0, shapes: 0 },\n extentMemo: { cell: new WeakMap(), table: new WeakMap() },\n regions: new RegionCollector(), parentStack: [], pageBase: 0,\n }\n const rendered: RenderedSection[] = []\n for (const { root, index, masterPages, hasDeletionMarkers } of sections) {\n const geom = readSectionGeom(root)\n const { pages, pageH } = renderSectionToPages(root, geom, ctxBase, input.reflow, input.reflowMode, masterPages, hasDeletionMarkers)\n rendered.push({ pages, PW: geom.PW, pageH, clipId: `pgclip${index}` })\n ctxBase.pageBase += pages.length\n }\n if (rendered.length === 0) throw new KordocError(\"렌더할 구역이 없습니다\")\n return { sections: rendered, defs: ctxBase.defs, warnings: input.warnings, stats: ctxBase.stats, regions: ctxBase.regions.regions }\n}\n\nconst SVG_FONT_FAMILY = `'HCR Batang','함초롬바탕','Hancom Batang',AppleMyungjo,'Noto Serif CJK KR',serif`\n\n/**\n * HWPX(한컴 저장본) → 구역별 페이지 버퍼. 조판 캐시(linesegarray)가 없는 구역은 reflow 옵션으로\n * 합성 조판(없으면 그 구역 생략); 렌더 가능한 구역이 하나도 없으면 KordocError.\n */\nasync function renderHwpxInternal(input: ArrayBuffer | Uint8Array, options?: RenderSvgOptions): Promise<InternalRender> {\n const maxImg = options?.maxImageBytes ?? 40 * 1024 * 1024\n // 압축폭탄 가드 — 파서 진입점과 동일하게 압축해제 전 central directory 선언 크기를 검사한다.\n // 렌더 이미지 캡은 f.async 로 엔트리를 전량 압축해제한 뒤에야 크기를 보므로, 단일 BinData\n // 압축폭탄(수 GB 선언)이 캡을 우회해 OOM 을 유발하던 것을 원천 차단한다 (reflow-1).\n const ab = input instanceof Uint8Array\n ? input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength) as ArrayBuffer\n : input\n precheckZipSize(ab, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES)\n let zip: JSZip\n try {\n zip = await JSZip.loadAsync(input)\n } catch {\n throw new KordocError(\"HWPX(ZIP) 형식이 아닙니다 — 렌더는 HWPX만 지원\")\n }\n // 모든 구역 파일 (section0, section1, … — 이름 정렬)\n const secFiles = zip.file(/Contents\\/section\\d+\\.xml$/i).sort((a, b) => a.name.localeCompare(b.name))\n if (secFiles.length === 0) throw new KordocError(\"Contents/section0.xml 없음 — HWPX가 아니거나 손상됨\")\n\n const warnings: string[] = []\n const headFile = zip.file(\"Contents/header.xml\") ?? zip.file(\"Contents/head.xml\")\n const styles: RenderStyles = headFile\n ? parseRenderStyles(await headFile.async(\"string\"))\n : { charPr: new Map(), paraAlign: new Map(), paraGeom: new Map(), borderFill: new Map() }\n if (!headFile) warnings.push(\"header.xml 없음 — 기본 스타일로 렌더\")\n\n // 구역 XML 선로딩 + 크기 가드\n const secXmls: string[] = []\n for (const f of secFiles) {\n const xml = await f.async(\"string\")\n if (xml.length > MAX_DECOMPRESS_SIZE) throw new KordocError(\"섹션 XML이 허용 크기를 초과\")\n secXmls.push(xml)\n }\n\n const masterPages = new Map<string, Element>()\n const masterXmls: string[] = []\n for (const file of zip.file(/Contents\\/masterpage[^/]*\\.xml$/i)) {\n const xml = await file.async(\"string\")\n if (xml.length > MAX_DECOMPRESS_SIZE) throw new KordocError(\"바탕쪽 XML이 허용 크기를 초과\")\n const root = createXmlParser().parseFromString(xml, \"text/xml\").documentElement as unknown as Element\n if (root && ln(root) === \"masterPage\") {\n masterPages.set(root.getAttribute(\"id\") || file.name.replace(/^.*\\/|\\.xml$/g, \"\"), root)\n masterXmls.push(xml)\n }\n }\n\n // BinData 매니페스트 (id → href) — content.hpf 우선, 파일명 휴리스틱 폴백\n const binmap = new Map<string, string>()\n const hpf = zip.file(/content\\.hpf$/i)[0]\n if (hpf) {\n const man = await hpf.async(\"string\")\n for (const m of man.matchAll(/<[^>]*\\bid=\"([^\"]+)\"[^>]*\\bhref=\"(BinData\\/[^\"]+)\"[^>]*>/g)) binmap.set(m[1], m[2])\n for (const m of man.matchAll(/<[^>]*\\bhref=\"(BinData\\/[^\"]+)\"[^>]*\\bid=\"([^\"]+)\"[^>]*>/g)) binmap.set(m[2], m[1])\n }\n // 참조된 이미지만 선로딩 — 전 구역 ref 합집합 (DOM 워크 전에 async 구간 종료).\n // 장당 캡(maxImg) 외에 개수·누적 바이트 캡 — distinct 다수/대형 반복 참조로\n // 렌더 경로에서만 OOM 가능하던 구멍(파서 경로의 ZIP bomb 가드에 상응)\n const MAX_IMAGE_REFS = 256\n const MAX_TOTAL_IMAGE_BYTES = 128 * 1024 * 1024\n const images: Ctx[\"images\"] = new Map()\n const refs = new Set<string>()\n for (const xml of [...secXmls, ...masterXmls]) for (const m of xml.matchAll(/binaryItemIDRef=\"([^\"]+)\"/g)) refs.add(m[1])\n let totalImgBytes = 0\n for (const ref of refs) {\n if (images.size >= MAX_IMAGE_REFS) {\n warnings.push(`이미지 ${refs.size}종 중 ${MAX_IMAGE_REFS}종만 로딩 — 개수 한도 초과분 생략`)\n break\n }\n let href = binmap.get(ref)\n if (!href) {\n const cand = zip.file(new RegExp(`BinData/.*${ref.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`, \"i\"))[0]\n href = cand?.name\n }\n if (!href) continue\n const f = zip.file(href) ?? zip.file(\"Contents/\" + href)\n if (!f) continue\n const bytes = await f.async(\"uint8array\")\n if (bytes.length > maxImg) {\n warnings.push(`이미지 ${href} ${(bytes.length / 1048576).toFixed(1)}MB — 한도 초과로 생략`)\n continue\n }\n if (totalImgBytes + bytes.length > MAX_TOTAL_IMAGE_BYTES) {\n warnings.push(`이미지 누적 ${Math.round(MAX_TOTAL_IMAGE_BYTES / 1048576)}MB 한도 초과 — 이후 생략`)\n break\n }\n totalImgBytes += bytes.length\n images.set(ref, { dataUri: `data:${sniffMime(href, bytes)};base64,${Buffer.from(bytes).toString(\"base64\")}` })\n }\n\n // 구역 XML → DOM. 조판 캐시 없는 구역은 reflow 옵션이 없으면 생략\n const roots: SectionRoot[] = []\n let noCacheSkipped = false\n for (let si = 0; si < secXmls.length; si++) {\n const secXml = secXmls[si]\n // 태그 마크업만 매치 — 본문 텍스트에 \"linesegarray\"가 있어도 캐시로 오판하지 않는다\n // (본문의 <는 XML에서 &lt;로 이스케이프되므로 리터럴 <…linesegarray는 태그뿐)\n const hasCache = /<(?:[A-Za-z][\\w.-]*:)?linesegarray[\\s/>]/.test(secXml)\n if (!hasCache && !options?.reflow) {\n noCacheSkipped = true\n warnings.push(`구역 ${si}: 조판 캐시 없음 — reflow 옵션 필요, 생략`)\n continue\n }\n const doc = createXmlParser().parseFromString(secXml, \"text/xml\")\n const root = doc.documentElement as unknown as Element\n if (!root) { warnings.push(`구역 ${si} XML 파싱 실패 — 생략`); continue }\n const secPr = findFirst(root, \"secPr\")\n const referenced = secPr ? elements(secPr).filter(el => ln(el) === \"masterPage\").map(el => masterPages.get(el.getAttribute(\"idRef\") || \"\")).filter((el): el is Element => !!el) : []\n roots.push({ root, index: si, masterPages: referenced, hasDeletionMarkers: secXml.includes(\"deleteBegin\") || secXml.includes(\"deleteEnd\") })\n }\n\n if (roots.length === 0) {\n if (noCacheSkipped) {\n throw new KordocError(\"조판 캐시(linesegarray) 없음 — 한컴에서 저장한 HWPX만 렌더 가능 (reflow 옵션으로 합성 렌더 가능)\")\n }\n throw new KordocError(\"렌더할 구역이 없습니다 — HWPX가 손상되었을 수 있습니다\")\n }\n return renderSectionRoots(roots, { styles, images, warnings, reflow: !!options?.reflow, reflowMode: options?.reflowMode ?? \"keep\", highlights: options?.highlights })\n}\n\n/**\n * HWPX(한컴 저장본) → 레이아웃 보존 SVG. **전 구역(section*)을 세로 스택으로** 렌더한다.\n * 조판 캐시(linesegarray)가 없는 구역은 reflow 옵션으로 합성 조판(없으면 그 구역 생략);\n * 렌더 가능한 구역이 하나도 없으면 KordocError.\n */\nexport async function renderHwpxToSvg(input: ArrayBuffer | Uint8Array, options?: RenderSvgOptions): Promise<RenderSvgResult> {\n const r = await renderHwpxInternal(input, options)\n // 전 구역 페이지를 세로 스택으로 조립 (구역마다 page 크기 상이 가능 → 구역별 clip)\n const GAP = 2400 // 페이지 사이 시각 간격 (24pt)\n const clipDefs: string[] = []\n const groups: string[] = []\n let y = 0\n let maxPW = 0\n let pageNo = 0\n for (const rs of r.sections) {\n maxPW = Math.max(maxPW, rs.PW)\n clipDefs.push(`<clipPath id=\"${rs.clipId}\"><rect x=\"0\" y=\"0\" width=\"${pt(rs.PW)}\" height=\"${pt(rs.pageH)}\"/></clipPath>`)\n for (const buf of rs.pages) {\n pageNo++\n groups.push(\n `<g data-page=\"${pageNo}\" transform=\"translate(0 ${pt(y)})\">` +\n `<rect width=\"${pt(rs.PW)}\" height=\"${pt(rs.pageH)}\" fill=\"white\" stroke=\"#c9c7c4\" stroke-width=\"0.75\"/>` +\n `<g clip-path=\"url(#${rs.clipId})\">\\n${buf.join(\"\\n\")}\\n</g></g>`,\n )\n y += rs.pageH + GAP\n }\n }\n const totalH = Math.max(0, y - GAP)\n\n // width/height는 pt 단위 명시 — 단위 없는 px로 두면 A4 실물(96dpi 기준)보다 25% 작게 보인다 (v3.10.1)\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${pt(maxPW)} ${pt(totalH)}\" width=\"${pt(maxPW)}pt\" height=\"${pt(totalH)}pt\" font-family=\"${SVG_FONT_FAMILY}\" xml:space=\"preserve\">\\n` +\n `<defs>${clipDefs.join(\"\")}${r.defs.join(\"\")}</defs>\\n` +\n `${groups.join(\"\\n\")}\\n</svg>`\n return { svg, width: Math.round(maxPW) / 100, height: Math.round(totalH) / 100, pageCount: pageNo, warnings: r.warnings, stats: r.stats, regions: r.regions }\n}\n\n/** 페이지별 독립 SVG 산출 — RenderScene 과 선택 페이지의 standalone SVG (#75 Task 3) */\nexport interface HwpxPagesResult {\n scene: RenderScene\n /** 페이지 번호(1-based) → standalone SVG. 선택된 페이지만 조립 */\n pageSvgs: Map<number, string>\n}\n\n/**\n * 페이지 버퍼 → 페이지별 독립 SVG + RenderScene (포맷 무관 조립 단계). `select` 가 있으면 그 페이지만 조립한다\n * (그리기 자체는 전 페이지 — 조립·래스터가 비싼 단계). 페이지 SVG 는 페이지 로컬 좌표(세로 스택 오프셋 없음),\n * 사용된 이미지 심볼만 defs 에 싣는다.\n */\nexport function assemblePageSvgs(\n r: InternalRender,\n format: RenderScene[\"format\"],\n select?: Set<number> | ((pageCount: number) => Set<number>),\n): HwpxPagesResult {\n const pages: RenderScene[\"pages\"] = []\n const pageSvgs = new Map<number, string>()\n const pageCount = r.sections.reduce((n, rs) => n + rs.pages.length, 0)\n const chosen = typeof select === \"function\" ? select(pageCount) : select\n let pageNo = 0\n for (const rs of r.sections) {\n for (const buf of rs.pages) {\n pageNo++\n pages.push({ page: pageNo, width: ptNum(rs.PW), height: ptNum(rs.pageH) })\n if (chosen && !chosen.has(pageNo)) continue\n const body = buf.join(\"\\n\")\n const used = new Set<string>()\n for (const m of body.matchAll(/href=\"#(bin\\d+)\"/g)) used.add(m[1])\n const defs = r.defs.filter(d => { const m = /<symbol id=\"(bin\\d+)\"/.exec(d); return m ? used.has(m[1]) : true })\n const clipId = `pgclip${pageNo}`\n pageSvgs.set(pageNo,\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${pt(rs.PW)} ${pt(rs.pageH)}\" width=\"${pt(rs.PW)}pt\" height=\"${pt(rs.pageH)}pt\" font-family=\"${SVG_FONT_FAMILY}\" xml:space=\"preserve\" data-page=\"${pageNo}\">\\n` +\n `<defs><clipPath id=\"${clipId}\"><rect x=\"0\" y=\"0\" width=\"${pt(rs.PW)}\" height=\"${pt(rs.pageH)}\"/></clipPath>${defs.join(\"\")}</defs>\\n` +\n `<rect width=\"${pt(rs.PW)}\" height=\"${pt(rs.pageH)}\" fill=\"white\"/>\\n` +\n `<g clip-path=\"url(#${clipId})\">\\n${body}\\n</g>\\n</svg>`)\n }\n }\n const scene: RenderScene = { format, pages, regions: r.regions, warnings: r.warnings, stats: { texts: r.stats.texts, tables: r.stats.tables, images: r.stats.images, shapes: r.stats.shapes } }\n return { scene, pageSvgs }\n}\n\n/** HWPX → 페이지별 독립 SVG (#75 Task 3). HWP5 는 hwp5-scene 의 renderHwp5Pages 가 같은 조립 단계를 쓴다 */\nexport async function renderHwpxPages(\n input: ArrayBuffer | Uint8Array,\n options?: RenderSvgOptions,\n select?: Set<number> | ((pageCount: number) => Set<number>),\n): Promise<HwpxPagesResult> {\n const r = await renderHwpxInternal(input, options)\n return assemblePageSvgs(r, \"hwpx\", select)\n}\n","/**\n * 레이아웃 보존 렌더 — 순수 계산 유틸 (XML 무접촉).\n * 좌표 산식은 .claude/plans/render-poc/findings.md에서 실측 검증된 모델.\n * 단위: HWPUNIT(1/7200in), pt 변환은 /100.\n */\n\n/** HWPX 좌표 속성은 uint32로 저장된 음수가 섞여 있다 (예: vertOffset=4294967103 = -193) */\nexport function toInt32(v: string | undefined, fallback = 0): number {\n if (v == null || v === \"\") return fallback\n const n = Number(v)\n if (!Number.isFinite(n)) return fallback\n return n > 0x7fffffff ? n - 0x100000000 : n\n}\n\n/** 셀 하나가 주는 경계 제약: boundary[a] + size = boundary[b] */\nexport interface SpanConstraint {\n a: number\n b: number\n size: number\n}\n\n/**\n * 표 열 경계 솔버 — 경계 전파.\n * 결재문서 트레일러처럼 41열 그리드에 span-1 셀이 거의 없어도,\n * x[ca+cs] = x[ca] + w 제약을 고정점까지 전파하면 전 경계가 풀린다 (실측 검증).\n * 미해결 경계는 인접 확정 경계 사이 균등 보간.\n */\nexport function solveBoundaries(constraints: SpanConstraint[], count: number, total?: number): number[] {\n const x = new Array<number | undefined>(count + 1).fill(undefined)\n x[0] = 0\n if (total != null && total > 0) x[count] = total\n let changed = true\n let guard = 0\n while (changed && guard++ < count + 8) {\n changed = false\n for (const c of constraints) {\n if (c.a < 0 || c.b > count || c.a >= c.b) continue\n const xa = x[c.a]\n const xb = x[c.b]\n if (xa != null && xb == null) {\n x[c.b] = xa + c.size\n changed = true\n } else if (xb != null && xa == null) {\n x[c.a] = xb - c.size\n changed = true\n }\n }\n }\n // 잔여 미해결 경계 — 좌우 확정 경계 사이 균등 보간\n let i = 0\n while (i <= count) {\n if (x[i] != null) { i++; continue }\n let lo = i - 1\n let hi = i\n while (hi <= count && x[hi] == null) hi++\n const loV = x[lo] as number\n const hiV = hi <= count ? (x[hi] as number) : loV + (hi - lo) * 1000\n const n = hi - lo\n for (let k = 1; k < n; k++) x[lo + k] = loV + ((hiV - loV) * k) / n\n if (hi > count) x[count] = hiV\n i = hi\n }\n // 단조 보정 (모순 제약 방어)\n const out = x as number[]\n for (let k = 1; k <= count; k++) if (out[k] < out[k - 1]) out[k] = out[k - 1]\n return out\n}\n\n/**\n * 표 행 높이 솔버 — rowSpan=1 셀의 max가 기본, rowSpan>1은 잔여 균등분배.\n * h=1 더미 셀(헤어라인 행 장식)이 섞이므로 열과 달리 max 방식이 안전.\n * grow: 셀 콘텐츠(사진 등)가 설계 높이를 넘으면 초과분만큼 행이 자란다 —\n * 사진대지 실측: 설계 16441 + 사진 22209 → 행 22209, 표 sz 합치 정확 일치.\n */\nexport function solveRowHeights(\n cells: { rowAddr: number; rowSpan: number; height: number; contentH?: number }[],\n rowCount: number,\n): number[] {\n const h = new Array<number>(rowCount).fill(0)\n for (const c of cells) {\n if (c.rowSpan === 1 && c.rowAddr >= 0 && c.rowAddr < rowCount) {\n h[c.rowAddr] = Math.max(h[c.rowAddr], c.height)\n }\n }\n // rowSpan>1 셀 — 포함 행 중 미확정(0) 행에 잔여 균등분배\n for (const c of cells) {\n if (c.rowSpan <= 1) continue\n const rows: number[] = []\n for (let r = c.rowAddr; r < Math.min(c.rowAddr + c.rowSpan, rowCount); r++) rows.push(r)\n const known = rows.reduce((s, r) => s + h[r], 0)\n const missing = rows.filter(r => h[r] === 0)\n if (missing.length > 0 && c.height > known) {\n const each = (c.height - known) / missing.length\n for (const r of missing) h[r] = each\n }\n }\n // 콘텐츠 초과 성장 (rowSpan=1 셀만 — 사진 셀 케이스)\n for (const c of cells) {\n if (c.rowSpan === 1 && c.contentH != null && c.rowAddr >= 0 && c.rowAddr < rowCount) {\n if (c.contentH > h[c.rowAddr]) h[c.rowAddr] = c.contentH\n }\n }\n return h\n}\n\n// 자연폭 계산은 hwpx/text-metrics.ts의 함초롬바탕 실측 advance 테이블(measureTextWidth)을 재사용한다.\n","/**\n * 생성용 실글꼴 advance 폭표 (em×1000) — 공문서 v5 엔진이 쓰는 글꼴의 hmtx 실측값.\n *\n * 한컴오피스 번들 TTF(한컴돋움·HY견고딕·휴먼명조·HY헤드라인M·굴림체·굴림·맑은 고딕·함초롬돋움)에서\n * 폭 계산에 필요한 수치만 뽑았다: ASCII 95자 + 전각(cjk)과 다른 기호 예외. 한글 음절·자모·\n * 원문자·도형 등 나머지는 전부 cjk 폭(글꼴 안에서 균일 — 전수 확인)이다. 글꼴에 없는 글리프는\n * 한컴이 대체 글꼴로 그리므로 cjk 폭으로 둔다(HY견고딕은 KS X 1001 2,350자만 있다).\n *\n * 검증: 이 표 + 공백 0.5em(useFontSpace=0) + ASCII 연속열 한 단어 + 금칙 규칙으로 한글 2024(윈도)가\n * 조판한 v5 보고서 PDF 본문 154줄의 줄바꿈 위치를 전부 재현했다. bold 는 폭이 같다(실결재 대조).\n * 공백(0x20) 칸은 한컴 고정 반각(500)이다 — measureTextWidth 가 spaceEm 으로 따로 처리한다.\n */\n\ninterface FontMetrics {\n /** 한글 음절(가) 폭 = 표에 없는 비ASCII 기본값 */\n cjk: number\n /** 0x20~0x7E advance */\n ascii: number[]\n /** cjk 폭과 다른 비ASCII 기호 */\n ex: Record<number, number>\n}\n\nconst FONTS: Record<string, FontMetrics> = {\n \"한컴돋움\": {\n cjk: 1000,\n ascii: [500,334,415,625,500,915,709,290,375,375,583,580,375,590,375,415,583,583,583,583,583,583,583,583,583,583,340,340,625,584,625,583,1000,665,680,720,725,629,600,750,724,259,484,655,540,810,695,750,\n 644,750,668,640,583,725,600,894,604,600,604,500,333,500,625,500,330,583,600,560,600,560,344,599,560,229,229,510,230,800,568,600,600,600,320,515,313,568,475,725,479,480,480,500,500,500,790],\n ex: {\n 0xa7:561, 0xb0:405, 0xb1:833, 0xb4:333, 0xb7:331, 0xd7:833, 0xf7:833, 0x2010:261, 0x2011:314, 0x2012:431, 0x2013:500, 0x2014:687,\n 0x2016:429, 0x2017:548, 0x2018:291, 0x2019:291, 0x201a:291, 0x201b:291, 0x201c:458, 0x201d:458, 0x201e:458, 0x201f:458, 0x2020:562, 0x2021:562,\n 0x2022:463, 0x2023:466, 0x2024:174, 0x2025:566, 0x2026:830, 0x2027:301, 0x202f:500, 0x2032:335, 0x2033:563, 0x2034:734, 0x2035:335, 0x2036:563,\n 0x2037:734, 0x2038:408, 0x2039:375, 0x203a:375, 0x203c:625, 0x203d:465, 0x203e:500, 0x203f:804, 0x2040:804, 0x2041:356, 0x2043:224, 0x2044:734,\n 0x2045:320, 0x2046:320, 0x2048:656, 0x2049:656, 0x204a:456, 0x204b:562, 0x204c:562, 0x204d:562, 0x2050:804, 0x2218:500, 0x2219:500,\n },\n },\n \"HY견고딕\": {\n cjk: 1000,\n ascii: [500,416,583,708,625,958,791,333,458,458,541,666,375,666,375,416,625,625,625,625,625,625,625,625,625,625,375,375,583,666,583,708,1000,791,791,791,791,708,666,833,791,375,583,833,666,875,791,833,\n 708,833,791,750,708,791,708,1000,750,750,708,458,416,458,541,500,250,625,666,625,666,625,375,666,666,375,333,666,375,958,666,666,666,666,458,625,416,666,625,833,625,625,583,500,375,500,666],\n ex: {},\n },\n \"휴먼명조\": {\n cjk: 1000,\n ascii: [500,203,500,656,504,750,770,289,313,313,500,500,270,500,270,313,500,500,500,500,500,500,500,500,500,500,270,270,500,500,500,430,777,660,602,641,680,625,582,703,680,258,395,645,578,781,691,715,\n 574,688,652,496,625,676,664,934,711,672,598,320,313,320,363,500,324,508,543,484,508,523,391,516,555,250,266,523,250,797,551,512,539,539,402,410,340,539,539,723,488,512,453,293,203,293,500],\n ex: {},\n },\n \"HY헤드라인M\": {\n cjk: 1000,\n ascii: [500,250,375,833,584,798,583,208,291,294,480,587,238,503,242,384,583,583,583,583,583,583,583,583,583,583,265,265,582,587,588,583,853,583,629,629,629,506,486,636,635,290,509,605,461,806,614,621,\n 566,621,596,592,520,625,625,917,583,552,485,323,384,322,542,500,324,583,583,579,583,617,423,592,601,283,338,542,281,847,589,625,594,594,405,542,418,599,542,792,500,579,447,417,292,417,708],\n ex: {},\n },\n \"굴림체\": {\n cjk: 1000,\n ascii: [500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,\n 500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500],\n ex: {\n 0x2013:500, 0x2014:500, 0x2017:500, 0x201a:500, 0x201b:500, 0x201e:500, 0x2022:500, 0x2039:500, 0x203a:500, 0x203c:500, 0x203e:500, 0x2044:500,\n 0x2105:500, 0x212e:500, 0x21a8:500, 0x2206:500, 0x2212:500, 0x2215:500, 0x2219:500, 0x221f:500, 0x2248:500, 0x2550:500, 0x2551:500, 0x2552:500,\n 0x2553:500, 0x2554:500, 0x2555:500, 0x2556:500, 0x2557:500, 0x2558:500, 0x2559:500, 0x255a:500, 0x255b:500, 0x255c:500, 0x255d:500, 0x255e:500,\n 0x255f:500, 0x2560:500, 0x2561:500, 0x2562:500, 0x2563:500, 0x2564:500, 0x2565:500, 0x2566:500, 0x2567:500, 0x2568:500, 0x2569:500, 0x256a:500,\n 0x256b:500, 0x256c:500, 0x2580:500, 0x2584:500, 0x2588:500, 0x258c:500, 0x2590:500, 0x2591:500, 0x2593:500, 0x25aa:500, 0x25ab:500, 0x25ac:500,\n 0x25ba:500, 0x25c4:500, 0x25ca:500, 0x25d8:500, 0x25d9:500, 0x25e6:500, 0x263a:500, 0x263b:500, 0x263c:500, 0x2666:500, 0x266b:500,\n },\n },\n \"굴림\": {\n cjk: 1000,\n ascii: [500,333,375,750,598,875,688,283,375,375,500,625,334,625,334,417,574,574,574,574,574,574,574,574,574,574,334,334,625,625,625,542,1000,646,677,718,722,625,596,770,730,270,500,635,543,813,699,770,\n 637,773,666,633,582,711,600,895,625,625,625,500,917,500,542,500,333,563,605,572,605,567,342,605,571,241,241,500,231,861,580,605,605,605,333,525,313,570,500,750,500,500,500,500,500,500,792],\n ex: {\n 0xa7:563, 0xb0:375, 0xb1:833, 0xb4:375, 0xb7:375, 0xd7:833, 0xf7:833, 0x2013:500, 0x2015:688, 0x2017:500, 0x2018:333, 0x2019:333,\n 0x201a:333, 0x201b:333, 0x201c:500, 0x201d:500, 0x201e:500, 0x2020:563, 0x2021:563, 0x2022:375, 0x2032:375, 0x2033:500, 0x2039:375, 0x203a:375,\n 0x203c:625, 0x203e:500, 0x2044:417, 0x2105:846, 0x2113:500, 0x2126:750, 0x212e:750, 0x215b:563, 0x215c:563, 0x215d:563, 0x215e:563, 0x2191:750,\n 0x2193:750, 0x2195:750, 0x21a8:750, 0x2202:563, 0x2206:613, 0x220f:688, 0x2211:813, 0x2212:833, 0x2215:417, 0x2219:250, 0x221a:549, 0x221e:913,\n 0x221f:982, 0x2229:719, 0x222b:375, 0x2248:833, 0x2260:833, 0x2261:833, 0x2264:833, 0x2265:833, 0x25a0:890, 0x25a1:890, 0x25aa:375, 0x25ab:375,\n 0x25b2:914, 0x25ba:750, 0x25bc:914, 0x25c4:750, 0x25ca:774, 0x25e6:375, 0x263c:916, 0x2640:625, 0x2642:625, 0x2660:750, 0x2663:844, 0x2665:793,\n 0x2666:774, 0x266a:688, 0x266b:763,\n },\n },\n \"맑은 고딕\": {\n cjk: 1000,\n ascii: [500,289,395,606,551,836,818,232,305,305,425,701,219,410,219,396,551,551,551,551,551,551,551,551,551,551,219,219,701,701,701,460,979,658,583,635,717,517,499,702,725,270,360,590,480,917,765,773,\n 571,773,610,543,534,703,634,954,601,563,583,305,764,305,701,426,272,520,601,473,602,535,316,602,579,246,246,506,246,880,578,599,601,602,354,433,345,578,487,736,465,493,462,305,239,305,701],\n ex: {\n 0xa7:458, 0xb0:387, 0xb1:701, 0xb4:288, 0xb7:219, 0xd7:701, 0xf7:701, 0x2013:513, 0x2014:1026, 0x2018:231, 0x2019:231, 0x201a:231,\n 0x201c:379, 0x201d:379, 0x201e:379, 0x2020:382, 0x2021:382, 0x2022:417, 0x2025:437, 0x2026:740, 0x2030:1238, 0x2032:231, 0x2033:379, 0x2039:322,\n 0x203a:322, 0x203b:800, 0x203c:578, 0x2047:920, 0x2048:749, 0x2049:749, 0x2103:950, 0x2109:950, 0x2113:332, 0x2116:1146, 0x2121:950, 0x2122:793,\n 0x2126:774, 0x212b:696, 0x2153:950, 0x2154:950, 0x215b:943, 0x215c:981, 0x215d:982, 0x215e:913, 0x2160:950, 0x2161:950, 0x2162:950, 0x2163:950,\n 0x2164:950, 0x2165:950, 0x2166:950, 0x2167:950, 0x2168:950, 0x2169:950, 0x2170:950, 0x2171:950, 0x2172:950, 0x2173:950, 0x2174:950, 0x2175:950,\n 0x2176:950, 0x2177:950, 0x2178:950, 0x2179:950, 0x2190:950, 0x2191:950, 0x2192:950, 0x2193:950, 0x2194:950, 0x2195:950, 0x2196:950, 0x2197:950,\n 0x2198:950, 0x2199:950, 0x21d2:950, 0x21d4:950, 0x2200:696, 0x2202:546, 0x2203:548, 0x2207:713, 0x2208:950, 0x220b:950, 0x220f:771, 0x2211:631,\n 0x2219:315, 0x221a:667, 0x221d:838, 0x221e:839, 0x2220:667, 0x2225:684, 0x2227:684, 0x2228:684, 0x2229:950, 0x222a:950, 0x222b:551, 0x222c:950,\n 0x222e:545, 0x2234:667, 0x2235:667, 0x223c:684, 0x223d:684, 0x2252:684, 0x2260:701, 0x2261:684, 0x2264:701, 0x2265:701, 0x226a:950, 0x226b:950,\n 0x2282:950, 0x2283:950, 0x2286:950, 0x2287:950, 0x2299:950, 0x22a5:950, 0x25cc:594, 0x2640:625, 0x2642:715, 0x2669:538, 0x266a:688, 0x266c:932,\n 0x266d:573, 0x3008:542, 0x3009:542, 0x300a:596, 0x300b:596, 0x300c:571, 0x300d:571, 0x300e:517, 0x300f:517, 0x3010:518, 0x3011:518, 0x3014:488,\n 0x3015:488, 0x302e:333, 0x302f:333,\n },\n },\n \"함초롬돋움\": {\n cjk: 970,\n ascii: [500,333,339,686,626,853,761,260,313,313,498,548,258,466,270,374,550,550,550,550,550,550,550,550,550,550,312,312,600,534,600,570,865,654,664,633,701,625,609,645,694,317,515,664,584,826,728,676,\n 660,681,683,617,573,702,594,897,607,581,584,338,373,329,562,532,373,563,581,513,578,567,336,563,594,271,268,559,258,919,595,582,552,552,427,513,349,595,469,760,483,466,479,372,342,372,546],\n ex: {\n 0xa7:486, 0xb0:306, 0xb1:782, 0xb4:240, 0xb7:294, 0xd7:587, 0xf7:670, 0x2010:237, 0x2011:300, 0x2012:398, 0x2013:348, 0x2014:522,\n 0x2015:672, 0x2016:368, 0x2017:419, 0x2018:288, 0x2019:288, 0x201a:288, 0x201b:288, 0x201c:451, 0x201d:453, 0x201e:451, 0x201f:488, 0x2020:518,\n 0x2021:448, 0x2022:586, 0x2023:590, 0x2024:288, 0x2025:544, 0x2026:702, 0x2027:288, 0x2030:892, 0x2031:956, 0x2032:304, 0x2033:488, 0x2034:550,\n 0x2035:304, 0x2036:488, 0x2037:544, 0x2038:494, 0x2039:448, 0x203a:448, 0x203b:690, 0x203c:502, 0x203d:556, 0x203e:348, 0x203f:941, 0x2040:941,\n 0x2041:517, 0x2042:764, 0x2043:681, 0x2044:359, 0x2045:343, 0x2046:343, 0x2047:930, 0x2048:688, 0x2049:712, 0x204a:549, 0x204b:543, 0x204c:573,\n 0x204d:573, 0x204e:551, 0x204f:297, 0x2050:941, 0x2051:436, 0x2052:610, 0x2053:523, 0x2054:941, 0x2055:468, 0x2056:595, 0x2057:625, 0x2058:690,\n 0x2059:690, 0x205a:494, 0x205b:630, 0x205c:678, 0x205d:494, 0x205e:494, 0x2135:520, 0x2136:526, 0x2137:320, 0x2138:471, 0x261c:1012, 0x261e:1012,\n 0x3008:500, 0x3009:500, 0x300a:500, 0x300b:500, 0x300c:500, 0x300d:500, 0x300e:500, 0x300f:500, 0x3010:500, 0x3011:500, 0x3014:500, 0x3015:500,\n 0x3016:500, 0x3017:500, 0x3018:500, 0x3019:500, 0x301a:500, 0x301b:500, 0x302c:246, 0x302d:246, 0x303f:485,\n },\n },\n}\n\n/** 실측 폭표가 있는 글꼴인지 */\nexport function hasFontMetrics(face: string | null | undefined): boolean {\n return !!face && Object.prototype.hasOwnProperty.call(FONTS, face.trim())\n}\n\n/** 코드포인트 advance(em×1000). 폭표 없는 글꼴은 null — 호출부가 근사 클래스로 폴백 */\nexport function fontAdvanceEm1000(face: string, cp: number): number | null {\n if (!hasFontMetrics(face)) return null\n const m = FONTS[face.trim()]\n if (cp >= 0x20 && cp <= 0x7e) return m.ascii[cp - 0x20]\n return m.ex[cp] ?? m.cjk\n}\n\n/** 문단 계산 전에 폭표를 한 번 선택한다 — 글자마다 글꼴명 trim·조회하지 않는다. */\nexport function fontWidthFn(face: string): ((cp: number) => number) | null {\n if (!hasFontMetrics(face)) return null\n const m = FONTS[face.trim()]\n return cp => cp >= 0x20 && cp <= 0x7e ? m.ascii[cp - 0x20] : m.ex[cp] ?? m.cjk\n}\n","/**\n * 한글 조판 텍스트 폭 계산 — 함초롬바탕(HCR Batang) 실측 advance 테이블\n *\n * 공문서 표준 본문 글꼴인 함초롬바탕 정품 TTF(한컴 공개 배포)의 hmtx advance를\n * upem=1000 기준으로 추출한 값이다. Bold도 advance가 Regular와 완전히 동일함을\n * 전수 확인했다(한컴이 굵기 간 폭을 통일해 제작). 한글 음절 11,172자 전수 = 970,\n * 한자 = 1000, 전각형·원문자·도형·화살표·단위기호 = 970으로 균일하다.\n *\n * 단위: em×1000. 실제 폭(HWPUNIT) = w/1000 × charPr.height × ratio(장평)/100.\n * (1em = 글자크기 = charPr.height HWPUNIT — 1pt = 100 HWPUNIT이므로)\n *\n * 공백: HWP는 charPr useFontSpace=0(기본)일 때 글꼴의 space advance(0.30em) 대신\n * 반각 고정폭(0.50em)을 쓴다 — kordoc 생성 문서는 모두 useFontSpace=0이므로\n * 기본 SPACE_EM=500. (글꼴값을 쓰려면 measure 옵션 fontSpace로 300 지정)\n *\n * 다른 글꼴(맑은 고딕 등)도 한글=전각 균일·숫자≈0.55em의 동일 부류 구조라\n * 이 테이블로 근사한다(오차 수 % 이내 — 공문서 본문은 어차피 함초롬바탕 관행).\n */\n\nimport { fontWidthFn, hasFontMetrics } from \"./font-metrics.js\"\n\n/** ASCII 0x20~0x7E advance (em×1000). 0x20은 useFontSpace=1일 때의 글꼴값(300) */\nconst ASCII_W = [\n 300, 320, 320, 610, 610, 830, 724, 320, 320, 320, 550, 550, 320, 550, 320, 550, // 0x20-0x2F\n 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 320, 320, 550, 550, 550, 550, // 0x30-0x3F\n 830, 706, 605, 685, 719, 627, 617, 683, 734, 305, 315, 660, 605, 839, 734, 732, // 0x40-0x4F\n 603, 705, 660, 627, 664, 731, 706, 910, 705, 705, 626, 320, 550, 320, 550, 550, // 0x50-0x5F\n 320, 569, 597, 552, 597, 536, 356, 562, 635, 287, 288, 582, 287, 907, 635, 588, // 0x60-0x6F\n 597, 579, 478, 496, 356, 635, 563, 720, 542, 543, 486, 320, 320, 320, 550, 0, // 0x70-0x7E(+DEL)\n]\n\n/** 개별 실측 예외 기호 (em×1000) */\nconst SYM_W: Record<number, number> = {\n 0xa0: 300, 0xa3: 568, 0xa5: 707, 0xa7: 498, 0xab: 440, 0xac: 564, 0xb0: 291,\n 0xb1: 798, 0xb6: 606, 0xb7: 320, 0xbb: 440, 0xd7: 617, 0xf7: 678,\n 0x2013: 625, 0x2014: 875, 0x2015: 875, 0x2018: 320, 0x2019: 320, 0x201c: 480, 0x201d: 480,\n 0x2020: 558, 0x2021: 438, 0x2025: 640, 0x2026: 960, 0x2030: 988, 0x2032: 335, 0x2033: 474,\n 0x203b: 770, 0x20ac: 656, 0x261c: 1012, 0x261e: 1012,\n}\n\n/** 코드포인트의 advance(em×1000). 미상 문자는 CJK권 970 / 라틴권 550 폴백 */\nexport function charWidthEm1000(cp: number): number {\n if (cp >= 0x20 && cp <= 0x7e) return ASCII_W[cp - 0x20]\n const sym = SYM_W[cp]\n if (sym !== undefined) return sym\n if (cp >= 0xac00 && cp <= 0xd7a3) return 970 // 한글 음절 (전수 균일 확인)\n if (cp >= 0x1100 && cp <= 0x11ff) return 970 // 옛한글 자모\n if (cp >= 0x3131 && cp <= 0x318e) return 970 // 호환 자모 (ㆍ 포함)\n if ((cp >= 0x4e00 && cp <= 0x9fff) || (cp >= 0xf900 && cp <= 0xfaff)) return 1000 // 한자\n if ((cp >= 0x3008 && cp <= 0x3011) || (cp >= 0x3014 && cp <= 0x301b)) return 500 // 「」『』〈〉《》〔〕【】\n if (cp === 0x3000) return 970 // 전각 공백\n if (cp >= 0x2160 && cp <= 0x2183) return 970 // 로마숫자 Ⅰ~Ⅻ (한글 글꼴 전각)\n if (cp >= 0x2190 && cp <= 0x22ff) return 970 // 화살표·수학 기호\n if (cp >= 0x2460 && cp <= 0x24ff) return 970 // 원문자 ①⑴\n if (cp >= 0x25a0 && cp <= 0x26ff) return 970 // 도형 □○◆★\n if (cp >= 0x3200 && cp <= 0x33ff) return 970 // 괄호한글 ㉮·단위 ㎡㎏\n if (cp >= 0xff01 && cp <= 0xff60) return 970 // 전각형 !~\n return cp >= 0x2e80 ? 970 : 550\n}\n\n/** HWP 공백 폭(em×1000) — useFontSpace=0(기본): 반각 고정 500 / =1: 글꼴값 300 */\nexport const SPACE_EM_FIXED = 500\nexport const SPACE_EM_FONT = 300\n\n/**\n * 폭 테이블 클래스 — 'hcr'(함초롬 실측, 기본) / 'fixedPitch'(고정폭 글꼴:\n * ASCII 0.5em·그 외 1.0em). 굴림체·돋움체·바탕체·궁서체('체' 접미 = 고정폭)가\n * 대상 — 전자결재 변환기 산출물의 지배 본문 글꼴. 한글이 HCR(0.97em)보다 3%\n * 넓어 함초롬 테이블로 재면 줄당 1~2자 과대적재로 wrap 지점이 어긋난다\n * (bench/verify-linebreak.mjs seoul 코퍼스 실측: fixedPitch 테이블로 74/75 일치).\n */\nexport type FaceClass = \"hcr\" | \"fixedPitch\" | \"gothic\" | `font:${string}`\n\n/**\n * HWP 글꼴명 → 폭 테이블 클래스. 미상/미지정은 hcr (기존 동작 불변).\n * 'gothic'(v5): 한컴돋움·맑은 고딕·HY견고딕·HY헤드라인M 등 굵은 고딕 — 한글 1.0em·ASCII 0.6em으로\n * 함초롬(0.97/0.55)보다 넉넉히 잡는다. 표 열폭·□ 한 줄 맞춤이 실렌더보다 좁게 재어 꺾이던 것 방지.\n */\nexport function faceClassOf(face: string | null | undefined): FaceClass {\n if (face && hasFontMetrics(face)) return `font:${face.trim()}`\n return face && /^(굴림체|돋움체|바탕체|궁서체)$/.test(face.trim()) ? \"fixedPitch\" : \"hcr\"\n}\n\n/**\n * 생성(v5) 전용 폭 클래스 — 렌더(reflow)는 faceClassOf 그대로(게이트 baseline 불변).\n * 실측 폭표(font-metrics.ts)가 있는 글꼴은 그 표(`font:이름`), 없으면 근사 클래스 — 굵은 고딕 계열은\n * 'gothic'으로 넉넉히 재어 표 열폭·□ 한 줄 맞춤이 실렌더에서 꺾이지 않게 한다.\n */\nexport function faceClassForGen(face: string | null | undefined): FaceClass {\n if (!face) return \"hcr\"\n const f = face.trim()\n if (hasFontMetrics(f)) return `font:${f}`\n if (/^(굴림체|돋움체|바탕체|궁서체)$/.test(f)) return \"fixedPitch\"\n if (/^(한컴돋움|맑은 고딕|HY견고딕|HY헤드라인M|HY중고딕|한양중고딕|나눔고딕|나눔스퀘어|돋움|굴림)$/.test(f)) return \"gothic\"\n return \"hcr\"\n}\n\n/**\n * 고딕 근사 advance(em×1000) — 한컴돋움·맑은 고딕 계열 실렌더 대비 보수적으로:\n * 대문자 0.72 · 소문자 0.56 · 숫자 0.58 · 괄호 0.35 · 마침표류 0.28 · 그 외 ASCII 0.5 · 전각 1.0\n * (실측: \"DT_1YL21161\" 한컴돋움 12pt 셀이 함초롬 테이블(0.55)로는 25% 좁게 재어져 글자 분해)\n */\nfunction gothicWidthEm1000(cp: number): number {\n if (cp >= 0x80) return 1000\n if (cp >= 0x41 && cp <= 0x5a) return 720\n if (cp >= 0x61 && cp <= 0x7a) return 560\n if (cp >= 0x30 && cp <= 0x39) return 580\n if (cp === 0x28 || cp === 0x29 || cp === 0x5b || cp === 0x5d) return 350\n if (cp === 0x2e || cp === 0x2c || cp === 0x3a || cp === 0x3b || cp === 0x27) return 280\n return 500\n}\n\n/** 고정폭 글꼴 advance(em×1000) — ASCII 반각 500, 그 외 전각 1000 */\nfunction fixedPitchWidthEm1000(cp: number): number {\n return cp < 0x80 ? 500 : 1000\n}\n\n/** 폭 클래스 → advance 함수. `font:이름` 은 실측 폭표(없는 글꼴명이면 gothic 근사) */\nfunction widthFnOf(faceClass: FaceClass | undefined): (cp: number) => number {\n if (faceClass === \"fixedPitch\") return fixedPitchWidthEm1000\n if (faceClass === \"gothic\") return gothicWidthEm1000\n if (faceClass?.startsWith(\"font:\")) {\n const face = faceClass.slice(5)\n return fontWidthFn(face) ?? gothicWidthEm1000\n }\n return charWidthEm1000\n}\n\n/** 공백류 — 일반 공백과 묶음 빈칸(U+00A0 → hp:nbSpace)은 한컴 고정 반각 공백 폭 */\nfunction isSpaceCp(cp: number): boolean {\n return cp === 0x20 || cp === 0xa0\n}\n\nexport interface MeasureOptions {\n /** 공백 폭(em×1000). 기본 SPACE_EM_FIXED(500) = useFontSpace 0 */\n spaceEm?: number\n /** 자간(charPr spacing %) — 글자폭의 %가 문자마다 추가 */\n spacingPct?: number\n /** 폭 테이블 클래스 — 기본 'hcr' (함초롬 실측 테이블) */\n faceClass?: FaceClass\n}\n\n/**\n * 텍스트 폭(HWPUNIT). height=charPr height(pt×100), ratioPct=장평 %.\n * 결과는 float — 호출부에서 비교 시 ±0.5 HWPUNIT 오차 허용 권장.\n */\nexport function measureTextWidth(\n text: string,\n height: number,\n ratioPct: number,\n opts?: MeasureOptions,\n): number {\n const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED\n const spacing = opts?.spacingPct ?? 0\n const widthEm = widthFnOf(opts?.faceClass)\n let em = 0\n for (const ch of text) {\n const cp = ch.codePointAt(0)!\n const w = isSpaceCp(cp) ? spaceEm : widthEm(cp)\n em += w * (1 + spacing / 100)\n }\n return (em / 1000) * height * (ratioPct / 100)\n}\n\n// ─── 줄바꿈 시뮬레이션 (한컴 조판 모델 — 실측 linesegarray 98% 일치 검증) ───\n//\n// 검증: bench/verify-linebreak.mjs — 서울 정보소통광장 실제 결재문서의 linesegarray\n// (한컴 계열 조판기가 계산한 줄 시작 오프셋)와 대조. 고정폭 글꼴 버킷에서 98%\n// (56/57 줄바꿈점, 미스는 코퍼스 좌표계 노이즈) 일치. 이 과정에서 확정된 규칙:\n// - 공백 폭 = 0.5em 고정(useFontSpace=0), 장평·자간 모두 공백에도 적용\n// - 자간 = 글자폭 × (1+spacing/100)\n// - 줄 끝 공백은 hang (줄바꿈을 유발하지 않음)\n// - 금칙: 시작금칙(닫는 부호 줄머리 금지)은 직전 글자 1개를 동반해 다음 줄로\n// (밀어내기), 끝금칙(여는 괄호 줄끝 금지)은 그 글자를 다음 줄로 내림\n\n/** 줄머리 금지(시작금칙) 문자 — 한컴 기본 */\nconst FORBID_START = new Set([...\"!%),.:;?]}¢°′″℃〉》」』】〕!%),.:;?]}₩~…·、。〃\"])\n/** 줄끝 금지(끝금칙) 문자 — 한컴 기본 */\nconst FORBID_END = new Set([...\"$([{£¥〈《「『【〔$([{₩\"])\n\nexport type WrapMode = \"keep\" | \"charAll\"\n\ninterface PreparedWrap { units: string[]; advances: number[]; steps: number[] }\n\n/** 한 문단의 장평·자간 후보 사이에서 문자 해독과 글꼴 폭표 조회만 재사용한다. */\nexport function prepareWrap(text: string, mode: WrapMode, opts?: MeasureOptions): PreparedWrap {\n const widthEm = widthFnOf(opts?.faceClass), spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED\n const advances: number[] = [], steps: number[] = []\n let i = 0\n for (const ch of text) {\n const cp = ch.codePointAt(0)!\n advances[i] = isSpaceCp(cp) ? spaceEm : widthEm(cp)\n steps[i] = ch.length\n i += ch.length\n }\n return { units: splitWrapUnits(text, mode), advances, steps }\n}\n\nfunction splitWrapUnits(text: string, mode: WrapMode): string[] {\n return text.match(mode === \"keep\" ? / +|[^ ]+/gu : / +|(?:[\\x21-\\x7e]+|[^ ])(?:\\u00a0(?:[\\x21-\\x7e]+|[^ \\u00a0])?)*/gu) ?? []\n}\n\nexport interface WrapOptions extends MeasureOptions {\n /** UTF-16 단위별 실폭(HWPUNIT) — 주면 height·장평·폭 클래스 대신 쓴다(여러 run 문단, 탭 전진폭) */\n widths?: number[]\n /** 같은 text·mode·글꼴·공백 폭으로 준비한 원 폭 — 한 문단 압축 후보에만 재사용 */\n prepared?: PreparedWrap\n}\n\nexport interface WrapResult {\n /** 줄 수 */\n lines: number\n /** 각 줄의 시작 오프셋(UTF-16) — [0, …] */\n starts: number[]\n /** 마지막 줄 텍스트 폭(HWPUNIT) */\n lastLineWidth: number\n}\n\n/**\n * 문단 줄바꿈 시뮬레이션.\n * mode 'keep' = 어절 단위(공문서 모드 — 저장 속성 breakNonLatinWord=\"BREAK_WORD\", 이름 역전 주의),\n * mode 'charAll' = 글자 단위(breakNonLatinWord=\"KEEP_WORD\" — 한글 기본값·전자결재 변환기).\n * 한 어절이 줄보다 길면 keep에서도 글자 단위로 강제 분해 — 한컴처럼 다음 줄로 넘긴 뒤 쪼갠다(앞 줄은 벌어진다).\n *\n * @param text 문단 전체 텍스트(항목 부호 포함)\n * @param firstWidth 첫 줄 가용 폭(HWPUNIT)\n * @param contWidth 둘째 줄부터 가용 폭(HWPUNIT) — 내어쓰기 반영\n * @param height charPr height (pt×100)\n * @param ratioPct 장평 %\n */\nexport function simulateWrap(\n text: string,\n firstWidth: number,\n contWidth: number,\n height: number,\n ratioPct: number,\n mode: WrapMode = \"keep\",\n opts?: WrapOptions,\n): WrapResult {\n const EPS = 0.5\n const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED\n const spacing = opts?.spacingPct ?? 0\n const prepared = opts?.prepared\n const widthEm = prepared ? null : widthFnOf(opts?.faceClass)\n const k = (height * ratioPct) / 100 / 1000\n const unitW = opts?.widths\n /** i 위치(UTF-16) 글자 ch 의 폭 — widths 가 있으면 그 값(서로게이트 쌍은 두 칸 합) */\n const charWAt = (i: number, ch: string): number => {\n if (unitW) return (unitW[i] ?? 0) + (ch.length === 2 ? unitW[i + 1] ?? 0 : 0)\n if (prepared) return prepared.advances[i] * (1 + spacing / 100) * k\n const cp = ch.codePointAt(0)!\n return (isSpaceCp(cp) ? spaceEm : widthEm!(cp)) * (1 + spacing / 100) * k\n }\n const rangeW = (from: number, to: number): number => {\n let w = 0\n if (prepared && !unitW) {\n for (let i = from; i < to; i += prepared.steps[i]) w += prepared.advances[i] * (1 + spacing / 100) * k\n return w\n }\n for (let i = from; i < to;) {\n const ch = String.fromCodePoint(text.codePointAt(i)!)\n w += charWAt(i, ch)\n i += ch.length\n }\n return w\n }\n\n // u 플래그 필수 — 없으면 astral 문자(𝐀·이모지 등)가 서로게이트 반쪽 2개로 쪼개져 폭이 2배로 계산된다.\n // 글자 단위에서도 ASCII 연속열(\"1.):\", \"LLM),\")은 한 단어 — breakLatinWord=KEEP_WORD(한글 2024 실렌더 PDF 대조).\n // 묶음 빈칸(U+00A0)은 어느 모드에서도 끊지 않는다 — 글자 단위에선 앞뒤 단위를 하나로 묶는다\n const units = prepared?.units ?? splitWrapUnits(text, mode)\n\n const starts = [0]\n let lineW = 0\n let avail = firstWidth\n let pos = 0\n const lineStart = (): number => starts[starts.length - 1]\n\n /** 유닛(시작 unitPos, 폭 w)이 안 들어갈 때 줄바꿈 + 금칙 보정 */\n const breakBefore = (unitPos: number, w: number): void => {\n let bp = unitPos\n const u = text[unitPos]\n const prevStart = (end: number): number => end - (end >= 2 && /[\\uDC00-\\uDFFF]/.test(text[end - 1]) && /[\\uD800-\\uDBFF]/.test(text[end - 2]) ? 2 : 1)\n // 시작금칙: 줄머리 금지 문자면 직전 글자 1개를 함께 내린다 (밀어내기)\n if (u !== undefined && FORBID_START.has(u) && prevStart(bp) > lineStart() && text[bp - 1] !== \" \") bp = prevStart(bp)\n // 끝금칙: 남는 줄 끝이 여는 괄호류면 그 글자(들)도 함께 내린다\n while (bp - 1 > lineStart() && FORBID_END.has(text[bp - 1])) bp--\n if (bp <= lineStart()) bp = unitPos\n starts.push(bp)\n avail = contWidth\n lineW = rangeW(bp, unitPos) + w\n }\n\n for (const u of units) {\n if (u[0] === \" \") {\n lineW += rangeW(pos, pos + u.length) // 줄 끝 공백은 hang\n pos += u.length\n continue\n }\n const w = rangeW(pos, pos + u.length)\n if (lineW + w <= avail + EPS) {\n lineW += w\n pos += u.length\n continue\n }\n if (lineW === 0 || w > contWidth + EPS) {\n // 빈 줄이거나 다음 줄에도 안 들어가는 초장 유닛 — 글자 단위 강제 분해. 어절 단위에서는 한컴이 먼저 다음 줄로\n // 넘긴 뒤 쪼갠다(코퍼스 어절 단위 문단 400개: 넘긴 뒤 347·제자리 340 일치, 갈리는 13건은 넘긴 뒤가 맞음)\n if (mode === \"keep\" && lineW > 0) breakBefore(pos, 0)\n let sub = 0\n for (const ch of u) {\n const c = charWAt(pos + sub, ch)\n if (lineW + c > avail + EPS && lineW > 0) breakBefore(pos + sub, 0)\n lineW += c\n sub += ch.length\n }\n pos += u.length\n continue\n }\n breakBefore(pos, w)\n pos += u.length\n }\n return { lines: starts.length, starts, lastLineWidth: lineW }\n}\n\n/** @deprecated simulateWrap(text, …, 'keep') 별칭 — 하위 호환 */\nexport function simulateWrapKeepWord(\n text: string,\n firstWidth: number,\n contWidth: number,\n height: number,\n ratioPct: number,\n opts?: MeasureOptions,\n): WrapResult {\n return simulateWrap(text, firstWidth, contWidth, height, ratioPct, \"keep\", opts)\n}\n\n/**\n * 한두 글자(짧은 꼬리)가 다음 줄로 넘어간 문단을 장평 축소로 한 줄 줄일 수 있는지\n * 탐색. baseRatio에서 줄 수 N≥2일 때 r=baseRatio-1…minRatio를 내려가며 처음으로\n * 줄 수가 줄어드는(가장 큰) r을 반환. 불가능하면 null.\n * (실무 관행: 공무원이 문단 장평을 95→92 등으로 줄여 orphan을 위로 당기는 조작의 자동화.\n * keep 모드로 판정 — 글자 단위 조판은 항상 keep 이하의 줄 수라 함께 만족된다.)\n */\nexport function fitRatioForFewerLines(\n text: string,\n firstWidth: number,\n contWidth: number,\n height: number,\n baseRatio: number,\n minRatio: number,\n opts?: MeasureOptions,\n): number | null {\n const base = simulateWrap(text, firstWidth, contWidth, height, baseRatio, \"keep\", opts)\n if (base.lines < 2) return null\n for (let r = baseRatio - 1; r >= minRatio; r--) {\n const sim = simulateWrap(text, firstWidth, contWidth, height, r, \"keep\", opts)\n if (sim.lines < base.lines) return r\n }\n return null\n}\n","/**\n * 렌더 문단 모델·표 높이 측정 — HWPX 문단을 슬롯 스트림(글자·필러·탭)과 개체 목록으로 만들고, 표 실효 높이를 잰다.\n * 그리기(svg-render)와 조판 합성(reflow)이 공유하는 순수 계층 — 그리기 코드에 의존하지 않는다.\n */\n\nimport { findChildByLocalName } from \"../hwpx/parser-shared.js\"\nimport { toInt32, solveRowHeights } from \"./layout.js\"\nimport { type RenderParaGeom } from \"./head-styles.js\"\n\nexport function ln(el: Element): string {\n return (el.tagName || \"\").replace(/^[^:]+:/, \"\")\n}\n\nexport function elements(el: Element): Element[] {\n const out: Element[] = []\n const children = el.childNodes\n if (!children) return out\n for (let i = 0; i < children.length; i++) {\n if (children[i].nodeType === 1) out.push(children[i] as Element)\n }\n return out\n}\n\nexport function num(el: Element | null, attr: string, fallback = 0): number {\n return el ? toInt32(el.getAttribute(attr) ?? undefined, fallback) : fallback\n}\n\nexport function findFirst(el: Element, name: string, depth = 0): Element | null {\n if (depth > 64) return null\n for (const ch of elements(el)) {\n if (ln(ch) === name) return ch\n const found = findFirst(ch, name, depth + 1)\n if (found) return found\n }\n return null\n}\n\nexport interface Seg { textpos: number; vertpos: number; horzpos: number; horzsize: number; textheight: number; baseline: number }\n\n/** tab: 탭 컨트롤 첫 슬롯(나머지 7슬롯은 필러) — 폭은 줄 조판 때 탭 정지점으로 정한다. tabW: 저장된 hp:tab width.\n * nb: 묶음 빈칸(hp:nbSpace) — 그리기는 공백, 줄바꿈은 끊지 않는 빈칸(U+00A0) */\nexport interface ParaChar { ch: string; prId: string | null; tab?: boolean; tabW?: number; nb?: boolean }\n\n/** 렌더 대상 개체 태그 — 이 외(도형류)는 경고 후 생략 */\nexport const OBJ_TAGS = new Set([\"tbl\", \"pic\", \"container\", \"equation\", \"rect\", \"ellipse\", \"polygon\", \"curv\", \"curve\", \"line\", \"arc\", \"ole\", \"textart\"])\n\n/** omL/omR: TAC 인라인 표의 outMargin 좌/우(HWPUNIT) — 한글은 TAC 표를 \"outMargin 포함\n * 폭의 문자\"로 배치한다: 가로 전진폭 = om좌 + 표폭 + om우, 괘선(표 자체)은 pen + om좌\n * (rhwp #3396 동종 — 오라클 실측이 표뿐이라 표 외 개체는 0). */\nexport interface ParaObj { el: Element; tag: string; index: number; inline: boolean; width: number; height: number; omL: number; omR: number }\n\nexport interface ParaModel { chars: ParaChar[]; segs: Seg[]; objs: ParaObj[]; paraPrId: string | null }\n\n/** hp:t 안에서 1슬롯을 차지하는 문자형 컨트롤 (HWP5 문자 스트림 모델) —\n * 탭(0x09)은 char가 아니라 inline 컨트롤 = 8슬롯(16바이트)이라 여기 넣으면 안 된다\n * (record.ts 0x09 처리의 i+=14와 같은 모델. 1슬롯로 세면 탭당 7슬롯씩 줄 경계가 밀린다) */\nexport const CHAR_CTRL_1SLOT = new Set([\"lineBreak\", \"hyphen\", \"nbSpace\", \"fwSpace\"])\n\n/** lineseg textpos 정합용 0폭 필러 슬롯 — 컨트롤이 차지하는 문자 위치를 채운다 */\nexport function pushFillers(chars: ParaChar[], n: number, prId: string | null): void {\n for (let i = 0; i < n; i++) chars.push({ ch: \"\", prId })\n}\n\n/**\n * hp:t 내용을 슬롯 스트림으로 변환 — lineseg textpos 는 HWP5 문자 스트림 기준이라\n * 텍스트 1문자=1슬롯(서로게이트 쌍은 2), tab 등 문자형 컨트롤도 1슬롯을 차지한다.\n * markpen 등 래퍼 요소는 슬롯 없이 내용만 재귀한다.\n * (탭 폭은 planLines 가 탭 정지점 — 내어쓰기용 자동 탭·기본 40pt 간격 — 으로 정한다)\n * 한컴 저장본은 변경 추적 삭제 표지(deleteBegin/End)를 hp:t 안에 둔다 — 슬롯 없이 del.deleted 만 바꾸고, 삭제 구간 글자는 빈 슬롯\n * (파서 section-walker 와 같은 최종본). 종전엔 hp:t 안 표지를 못 읽어 지운 글을 그렸다\n */\nexport function pushTextSlots(t: Element, chars: ParaChar[], prId: string | null, depth: number, del: { deleted: number } = { deleted: 0 }): void {\n if (depth > 32) return\n const kids = t.childNodes\n if (!kids) return\n for (let i = 0; i < kids.length; i++) {\n const c = kids[i]\n if (c.nodeType === 3 || c.nodeType === 4) { // CDATA(4) 포함 — 누락 시 해당 런이 렌더에서 사라진다\n for (const cp of c.textContent ?? \"\") {\n chars.push({ ch: del.deleted ? \"\" : cp, prId })\n if (cp.length === 2) chars.push({ ch: \"\", prId }) // UTF-16 두 번째 유닛 슬롯\n }\n } else if (c.nodeType === 1) {\n const el = c as Element\n const tag = ln(el)\n if (tag === \"deleteBegin\" || tag === \"deleteEnd\") {\n del.deleted = deletionDelta(el, del.deleted)\n } else if (tag === \"tab\") {\n // inline 컨트롤 8슬롯 — 첫 슬롯에 탭 표지(폭은 planLines 가 탭 정지점으로), 나머지는 필러\n if (del.deleted) pushFillers(chars, 8, prId)\n else { chars.push({ ch: \"\", prId, tab: true, tabW: num(el, \"width\") }); pushFillers(chars, 7, prId) }\n } else if (CHAR_CTRL_1SLOT.has(tag)) {\n chars.push(del.deleted ? { ch: \"\", prId } : tag === \"nbSpace\" ? { ch: \" \", prId, nb: true } : { ch: tag === \"fwSpace\" ? \" \" : \"\", prId })\n } else {\n pushTextSlots(el, chars, prId, depth + 1, del)\n }\n }\n }\n}\n\n// Each section/subList is a separate text story. Keep deletion ranges across its\n// paragraphs without removing DOM nodes: cached lineseg.textpos still counts them.\nconst deletedStarts = new WeakMap<Element, number>()\nconst deletedControls = new WeakMap<Element, boolean>()\nexport function isDeletedControl(el: Element): boolean { return deletedControls.get(el) === true }\nfunction deletionDelta(ctrl: Element, depth: number, xmlDepth = 0): number {\n if (xmlDepth > 64) return depth\n if (ln(ctrl) === \"deleteBegin\") return depth + 1\n if (ln(ctrl) === \"deleteEnd\") return Math.max(0, depth - 1)\n if (ln(ctrl) === \"ctrl\") for (const ch of elements(ctrl)) depth = deletionDelta(ch, depth, xmlDepth + 1)\n return depth\n}\nexport function prepareDeletedRanges(root: Element, depth = 0): void {\n if (depth > 64) return\n let deleted = 0\n for (const p of elements(root)) {\n if (ln(p) !== \"p\") continue\n deletedStarts.set(p, deleted)\n const scan = (el: Element, xmlDepth: number): void => {\n if (xmlDepth > 64) return\n if (ln(el) === \"ctrl\" || ln(el) === \"t\") for (const ch of elements(el)) scan(ch, xmlDepth + 1)\n else {\n if (ln(el) === \"deleteBegin\" || ln(el) === \"deleteEnd\") deleted = deletionDelta(el, deleted)\n deletedControls.set(el, deleted > 0)\n }\n }\n for (const ch of elements(p)) {\n if (ln(ch) === \"run\") for (const el of elements(ch)) scan(el, depth + 2)\n else if (ln(ch) === \"deleteBegin\" || ln(ch) === \"deleteEnd\") scan(ch, depth + 1)\n }\n }\n // Nested table cells, headers, and shape text have independent deletion state.\n function nested(el: Element, depth: number): void {\n if (depth > 64) return\n for (const ch of elements(el)) {\n if (ln(ch) === \"subList\") prepareDeletedRanges(ch, depth + 1)\n else nested(ch, depth + 1)\n }\n }\n nested(root, depth)\n}\n\nexport function buildPara(p: Element): ParaModel {\n const chars: ParaChar[] = []\n const objs: ParaObj[] = []\n let segs: Seg[] = []\n let deleted = deletedStarts.get(p) ?? 0\n for (const runEl of elements(p)) {\n const tag = ln(runEl)\n if (tag === \"run\") {\n const prId = runEl.getAttribute(\"charPrIDRef\")\n for (const ch of elements(runEl)) {\n const cn = ln(ch)\n if (cn === \"t\") {\n const del = { deleted }\n pushTextSlots(ch, chars, prId, 0, del)\n deleted = del.deleted\n } else if (OBJ_TAGS.has(cn)) {\n const sz = findChildByLocalName(ch, \"sz\")\n const pos = findChildByLocalName(ch, \"pos\")\n // 그리기 도형은 hp:sz가 없다 — curSz(>0) → orgSz 폴백\n const w = num(sz, \"width\") || num(findChildByLocalName(ch, \"curSz\"), \"width\") || num(findChildByLocalName(ch, \"orgSz\"), \"width\")\n const h = num(sz, \"height\") || num(findChildByLocalName(ch, \"curSz\"), \"height\") || num(findChildByLocalName(ch, \"orgSz\"), \"height\")\n const inline = pos?.getAttribute(\"treatAsChar\") === \"1\"\n // TAC 표만 outMargin 좌/우를 가로 배선 (ParaObj.omL 주석 — rhwp #3396 동종)\n const om = inline && cn === \"tbl\" ? findChildByLocalName(ch, \"outMargin\") : null\n if (!deleted) objs.push({\n el: ch, tag: cn, index: chars.length,\n inline,\n width: w, height: h,\n omL: num(om, \"left\"), omR: num(om, \"right\"),\n })\n // 확장 컨트롤(GSO 등)은 문자 스트림에서 8슬롯 — 실측: 데모 코퍼스 1,132개\n // 멀티라인 문단에서 textpos 가 8슬롯 블록 중간에 걸린 경계 0건\n pushFillers(chars, 8, prId)\n } else {\n if (cn === \"ctrl\" || cn === \"deleteBegin\" || cn === \"deleteEnd\") deleted = deletionDelta(ch, deleted)\n // secPr·ctrl(구역/단 정의)·필드 등 나머지 run 자식도 확장/인라인 컨트롤 8슬롯\n pushFillers(chars, 8, prId)\n }\n }\n } else if (tag === \"deleteBegin\" || tag === \"deleteEnd\") {\n // These compatibility markers live outside the run's character stream.\n deleted = deletionDelta(runEl, deleted)\n } else if (tag === \"linesegarray\") {\n segs = elements(runEl).filter(s => ln(s) === \"lineseg\").map(s => ({\n textpos: num(s, \"textpos\"), vertpos: num(s, \"vertpos\"), horzpos: num(s, \"horzpos\"),\n horzsize: num(s, \"horzsize\"), textheight: num(s, \"textheight\", 1000), baseline: num(s, \"baseline\", 850),\n }))\n }\n }\n return { chars, segs, objs, paraPrId: p.getAttribute(\"paraPrIDRef\") }\n}\n\n/** 한컴 기본 탭 간격 40pt (실결재 secPr tabStopVal 4000) */\nexport const DEFAULT_TAB_HU = 4000\n\n/**\n * 탭 전진폭 — x 는 문단 왼쪽 여백 기준. 첫 줄은 내어쓰기용 자동 탭(autoTabLeft)이면 내어쓰기 위치가\n * 정지점(법제처 서식 \"(a)⇥\" 실측: 부호폭 + 탭폭 = 내어쓰기). 그 밖엔 저장된 폭(한컴이 조판 때 잰 값 —\n * 문서 고유 탭 정의를 대신한다), 없으면 기본 간격 배수.\n */\nexport function tabAdvance(x: number, firstLine: boolean, geom: RenderParaGeom | undefined, saved = 0): number {\n const hang = geom && geom.marginIntent < 0 ? -geom.marginIntent : 0\n if (firstLine && geom?.autoTabLeft && hang > 0 && x < hang - 0.5) return hang - x\n if (saved > 0) return saved\n return (Math.floor(x / DEFAULT_TAB_HU) + 1) * DEFAULT_TAB_HU - x\n}\n\nexport interface CellModel {\n el: Element\n ca: number; ra: number; cs: number; rs: number\n w: number; h: number\n bfId: string | null\n sub: Element | null\n marginL: number; marginR: number; marginT: number; marginB: number\n}\n\n/**\n * 셀/표 측정 메모 — drawTable(rowH·yoff)과 cellContentExtent↔measureTableHeight 상호재귀가\n * 중첩 단계마다 하위 표를 재측정해 지수적으로 불어나는 것을 캡. 렌더 1회 안에서만 공유\n * (reflow의 DOM 변형은 드로잉 시작 전에 끝나므로 캐시가 stale해지지 않는다).\n */\nexport interface ExtentMemo { cell: WeakMap<Element, number>; table: WeakMap<Element, number> }\n\n/** 셀 콘텐츠 세로 범위 — 줄(vp+th) + 인라인 개체(중첩표·treatAsChar) + PARA 앵커 개체(anchor+h) 최대값 */\nexport function cellContentExtent(cell: CellModel, memo?: ExtentMemo): number {\n if (!cell.sub) return 0\n const hit = memo?.cell.get(cell.el)\n if (hit !== undefined) return hit\n let ext = 0\n for (const p of elements(cell.sub)) {\n if (ln(p) !== \"p\") continue\n const m = buildPara(p)\n for (const s of m.segs) ext = Math.max(ext, s.vertpos + s.textheight)\n const baseV = m.segs[0]?.vertpos ?? 0\n for (const o of m.objs) {\n if (o.inline) {\n // 인라인 개체(중첩 표·treatAsChar 이미지)는 줄 위치에서 개체 높이만큼 아래로 뻗는다.\n // 이를 빼먹으면 중첩 표를 담은 셀 높이가 과소측정돼 표지 중첩표가 겹친다(리뷰: 셀 성장 누락).\n const h = o.tag === \"tbl\" ? Math.max(o.height, measureTableHeight(o.el, memo)) : o.height\n ext = Math.max(ext, baseV + h)\n continue\n }\n const pos = findChildByLocalName(o.el, \"pos\")\n if ((pos?.getAttribute(\"vertRelTo\") ?? \"PARA\") !== \"PARA\") continue\n const om = findChildByLocalName(o.el, \"outMargin\")\n const pushed = baseV - (num(om, \"top\") + o.height + num(om, \"bottom\"))\n const anchor = pushed >= -100 ? pushed : baseV\n ext = Math.max(ext, anchor + num(om, \"top\") + num(pos, \"vertOffset\") + o.height)\n }\n }\n memo?.cell.set(cell.el, ext)\n return ext\n}\n\n/** tbl의 셀 모델 수집 — drawTable과 measureTableHeight가 같은 셀 해석을 공유 */\nexport function collectCells(tbl: Element): CellModel[] {\n const inMargin = findChildByLocalName(tbl, \"inMargin\")\n const defL = num(inMargin, \"left\", 141), defR = num(inMargin, \"right\", 141)\n const defT = num(inMargin, \"top\", 141), defB = num(inMargin, \"bottom\", 141)\n\n const cells: CellModel[] = []\n for (const tr of elements(tbl)) {\n if (ln(tr) !== \"tr\") continue\n for (const tc of elements(tr)) {\n if (ln(tc) !== \"tc\") continue\n const addr = findChildByLocalName(tc, \"cellAddr\")\n const span = findChildByLocalName(tc, \"cellSpan\")\n const csz = findChildByLocalName(tc, \"cellSz\")\n const cm = findChildByLocalName(tc, \"cellMargin\")\n if (!addr || !csz) continue\n cells.push({\n el: tc,\n // 음수 주소(uint32 역변환·손상 입력) 방어 — colX/rowY 인덱스 이탈로 NaN 좌표 방지\n ca: Math.max(0, num(addr, \"colAddr\")), ra: Math.max(0, num(addr, \"rowAddr\")),\n cs: Math.max(1, num(span, \"colSpan\", 1)), rs: Math.max(1, num(span, \"rowSpan\", 1)),\n w: num(csz, \"width\"), h: num(csz, \"height\"),\n bfId: tc.getAttribute(\"borderFillIDRef\"),\n sub: findChildByLocalName(tc, \"subList\"),\n marginL: cm ? num(cm, \"left\", defL) : defL, marginR: cm ? num(cm, \"right\", defR) : defR,\n marginT: cm ? num(cm, \"top\", defT) : defT, marginB: cm ? num(cm, \"bottom\", defB) : defB,\n })\n }\n }\n return cells\n}\n\n/**\n * 표 실효 높이(HWPUNIT) — drawTable의 rowH 모델(solveRowHeights + 셀 콘텐츠 성장) 그대로.\n * 선언 hp:sz는 셀 콘텐츠로 자란 높이를 모르므로, reflow가 표 뒤 문단을 실제 그려질\n * 표 바닥 아래로 배치할 때 이 값을 쓴다. 셀 lineseg가 있어야 성장분이 측정된다.\n */\nexport function measureTableHeight(tbl: Element, memo?: ExtentMemo): number {\n const hit = memo?.table.get(tbl)\n if (hit !== undefined) return hit\n const cells = collectCells(tbl)\n if (cells.length === 0 || cells.length > 4096) return 0\n const nRows = Math.max(...cells.map(c => c.ra + c.rs))\n const rowH = solveRowHeights(\n cells.map(c => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c, memo) : undefined })),\n nRows,\n )\n let sum = 0\n for (const h of rowH) sum += h\n memo?.table.set(tbl, sum)\n return sum\n}\n","/**\n * Tier-2 reflow — 조판 캐시(linesegarray)가 없는 문단에 좌표를 합성 주입한다.\n *\n * 한컴은 저장 시 각 줄의 좌표(vertpos/horzpos/…)를 linesegarray로 캐시한다.\n * markdownToHwpx 산출물·에이전트 생성본·편집본엔 이 캐시가 없어 svg-render가\n * KordocError로 거부한다. reflow는 `simulateWrap`(수평, 실측 98% 일치)과 세로 모델\n * (실측 역설계 — `.claude/plans/render-poc/findings.md`)로 lineseg를 계산해 DOM\n * `<hp:p>`에 `<hp:linesegarray>`를 append → 그 뒤는 기존 렌더 파이프가 그대로 소비한다.\n *\n * 세로 모델(HWPUNIT): textheight = 줄 지배 charPr.height, baseline = round(0.85×th),\n * 줄 pitch = round(th × lineSpacing%/100), spacing(leading) = pitch − th.\n * 문단 세로 흐름 = 본문영역 로컬 누적(다음문단 = 이전끝 + pitch + next + prev).\n *\n * 원칙: 한컴본 캐시는 절대 건드리지 않는다(Tier-1 무회귀). segs가 있는 문단은 건너뛴다.\n * Phase 3(v4.0.4): 개체 세로 흐름 — float(TOP_AND_BOTTOM)는 텍스트를 outMargin 포함\n * 개체 아래로 밀고, PAGE/PAPER 앵커·BEHIND/IN_FRONT는 흐름 불참, inline 표는 실효높이\n * +leading 전진. 혼합 캐시 문서는 캐시 문단의 한컴 좌표로 커서를 이어 받는다\n * (seoul 코퍼스 자기일관성 59/59 — 36264961 전 문단 d=0 실측).\n */\n\nimport { buildPara, measureTableHeight, tabAdvance } from \"./para-model.js\"\nimport { simulateWrap, measureTextWidth, faceClassOf, type WrapMode } from \"../hwpx/text-metrics.js\"\nimport { DEFAULT_CHAR, DEFAULT_PARA_GEOM, type RenderStyles, type RenderParaGeom } from \"./head-styles.js\"\nimport { findChildByLocalName } from \"../hwpx/parser-shared.js\"\nimport { toInt32 } from \"./layout.js\"\n\n/** lineseg flags 고정값 (한컴 저장본 실측 — 0x60000) */\nconst LINESEG_FLAGS = \"393216\"\n/** baseline / textheight 비율 (실측 94/94 일치) */\nconst BASELINE_RATIO = 0.85\n\nfunction ln(el: Element): string {\n return (el.tagName || \"\").replace(/^[^:]+:/, \"\")\n}\n\nfunction elements(el: Element): Element[] {\n const out: Element[] = []\n const kids = el.childNodes\n if (!kids) return out\n for (let i = 0; i < kids.length; i++) if (kids[i].nodeType === 1) out.push(kids[i] as Element)\n return out\n}\n\nfunction num(el: Element | null, attr: string, fallback = 0): number {\n return el ? toInt32(el.getAttribute(attr) ?? undefined, fallback) : fallback\n}\n\n/** 문단의 합성 linesegarray 줄들의 vertpos를 delta만큼 이동 (페이지 로컬 리셋용) */\nfunction shiftParaVert(p: Element, delta: number): void {\n for (const lsa of elements(p)) {\n if (ln(lsa) !== \"linesegarray\") continue\n for (const seg of elements(lsa)) {\n if (ln(seg) !== \"lineseg\") continue\n seg.setAttribute(\"vertpos\", String(num(seg, \"vertpos\") + delta))\n }\n }\n}\n\nexport interface ReflowGeom {\n BODY_W: number\n BODY_H: number\n}\n\n/** 줄 pitch(다음 줄 vertpos 증분, HWPUNIT) — lineSpacing type별 */\nfunction pitchFor(height: number, geom: RenderParaGeom): number {\n const v = geom.lineSpacingValue\n switch (geom.lineSpacingType) {\n case \"PERCENT\": return Math.round((height * v) / 100)\n case \"FIXED\": return v > 0 ? v : Math.round(height * 1.6) // 고정 줄높이(HWPUNIT)\n case \"AT_LEAST\": return Math.max(v, height)\n default: return Math.round(height * 1.6) // BETWEEN_LINES 등 — 기본 160% 근사\n }\n}\n\n/**\n * 문단 하나의 linesegarray를 합성해 p에 삽입.\n * @returns 세로 흐름 갱신값 (캐시 있어 건너뛴 경우 null)\n */\nfunction reflowPara(\n p: Element,\n doc: Document,\n styles: RenderStyles,\n areaW: number,\n startV: number,\n mode: WrapMode,\n): { paraBottom: number; spaceAfter: number } | null {\n const m = buildPara(p)\n if (m.segs.length > 0) return null // 이미 캐시 있음 — Tier-1 무회귀\n\n const geom = styles.paraGeom.get(m.paraPrId ?? \"\") ?? DEFAULT_PARA_GEOM\n // 실텍스트 + UTF-16 유닛 → chars 슬롯 인덱스 매핑 (서로게이트 쌍은 슬롯 1개, 유닛 2개).\n // 폭은 글자마다 제 charPr(글꼴·크기·장평·자간)로 — 부호 run 과 압축된 내용 run 이 섞인 문단도 한컴과 같게.\n // 탭은 \"\\t\" 한 글자로 넣고 폭 = 탭 전진폭(첫 줄 기준 — 항목부호 뒤 내어쓰기용 자동 탭).\n // 묶음 빈칸은 U+00A0 — 공백 폭이지만 줄을 끊지 않는다(\"2026. 9. 23.\"·\"3억 원\")\n const realIdx: number[] = []\n const widths: number[] = []\n let text = \"\"\n let x = Math.max(geom.marginIntent, 0) // 탭 정지점은 왼쪽 여백 기준 — 첫 줄 들여쓰기만큼 들어가서 시작\n for (let i = 0; i < m.chars.length; i++) {\n const c = m.chars[i]\n if (c.tab) {\n const adv = tabAdvance(x, true, geom, c.tabW)\n realIdx.push(i); widths.push(adv); text += \"\\t\"; x += adv\n continue\n }\n if (c.ch === \"\") continue\n const st = (c.prId != null ? styles.charPr.get(c.prId) : undefined) ?? DEFAULT_CHAR\n // 폭 테이블: 고정폭 글꼴(굴림체류)은 전용 클래스 — 함초롬 테이블(한글 0.97em)로 재면 줄당 1~2자\n // 과대적재로 wrap 이 어긋난다 (seoul 코퍼스 3건 실측)\n const w = measureTextWidth(c.ch, st.height, st.ratio, { spacingPct: st.spacing, faceClass: faceClassOf(st.face) })\n for (let u = 0; u < c.ch.length; u++) { realIdx.push(i); widths.push(u === 0 ? w : 0) }\n text += c.nb ? \"\\u00a0\" : c.ch\n x += w\n }\n\n // 문단 지배 charPr — 첫 실문자 우선(줄 높이·기준선. 가로 폭은 위 widths 가 글자별로 잰다)\n let domChar = DEFAULT_CHAR\n for (const c of m.chars) {\n if (c.ch !== \"\" && c.prId != null) {\n const st = styles.charPr.get(c.prId)\n if (st) { domChar = st; break }\n }\n }\n // 실문자가 없는 문단(빈 줄·개체 전용)도 run의 charPrIDRef가 줄높이를 지배한다 —\n // DEFAULT_CHAR(1000)로 재면 빈 문단 pitch가 본문(예: 1300→2080)보다 작아 이후\n // 문단 전체가 위로 밀린다 (seoul 코퍼스 36264961 실측 −480×빈줄 누적)\n if (domChar === DEFAULT_CHAR) {\n for (const run of elements(p)) {\n if (ln(run) !== \"run\") continue\n const st = styles.charPr.get(run.getAttribute(\"charPrIDRef\") ?? \"\")\n if (st) { domChar = st; break }\n }\n }\n const height = domChar.height || 1000\n\n const marginL = geom.marginLeft\n const avail = Math.max(1000, areaW - marginL - geom.marginRight)\n // hanging(intent<0): 둘째 줄부터 |intent| 더 들어감 → contWidth 축소·contHorz 우측 이동\n const firstWidth = avail\n const contWidth = Math.max(500, avail + Math.min(0, geom.marginIntent))\n const contHorz = marginL - Math.min(0, geom.marginIntent)\n\n // 문단 paraPr의 breakSetting이 있으면 그 선언(어절/글자)을 따르고, 없으면 호출자 모드\n const paraMode = geom.wrapMode ?? mode\n const wrap = text.length === 0\n ? { lines: 1, starts: [0], lastLineWidth: 0 }\n : simulateWrap(text, firstWidth, contWidth, height, 100, paraMode, { widths })\n\n const pitch = pitchFor(height, geom)\n const baseline = Math.round(height * BASELINE_RATIO)\n const spacing = Math.max(0, pitch - height)\n\n // ── 개체(표·이미지) 세로 흐름 분류 (Phase 3 — 한컴 실측 모델, 36264961) ──\n // · float(treatAsChar=0) + TOP_AND_BOTTOM + PARA 앵커: 개체가 문단 상단을 차지하고\n // 텍스트는 outTop+개체높이+outBottom 아래부터 흐른다 (두문 결재표 실측:\n // 140+16653+852=17645 정확 일치)\n // · BEHIND_TEXT/IN_FRONT_OF_TEXT 또는 PAGE/PAPER 앵커: 본문 흐름에 불참\n // (페이지 하단 직인 스탬프가 커서를 밀던 것 제거)\n // · inline(treatAsChar=1): 줄 하나로 취급 — outMargin 포함 높이 + 줄 leading(spacing)\n // 만큼 전진 (실측: 표끝→다음 문단 간격 = 성장 포함 표높이 + spacing)\n let floatBelow = 0 // 텍스트 시작을 아래로 미는 float 개체 바닥 (문단 로컬)\n let objBottom = startV\n for (const o of m.objs) {\n const effH = o.tag === \"tbl\" ? Math.max(o.height, measureTableHeight(o.el)) : o.height\n const pos = findChildByLocalName(o.el, \"pos\")\n const om = findChildByLocalName(o.el, \"outMargin\")\n const outT = num(om, \"top\"), outB = num(om, \"bottom\")\n if (o.inline) {\n objBottom = Math.max(objBottom, startV + outT + effH + outB + spacing)\n continue\n }\n const wrapAttr = o.el.getAttribute(\"textWrap\") ?? \"\"\n if (wrapAttr === \"BEHIND_TEXT\" || wrapAttr === \"IN_FRONT_OF_TEXT\") continue\n const vertRel = pos?.getAttribute(\"vertRelTo\") ?? \"PARA\"\n if (vertRel === \"PAGE\" || vertRel === \"PAPER\") continue\n const vo = Math.max(0, num(pos, \"vertOffset\"))\n if (wrapAttr === \"TOP_AND_BOTTOM\") {\n floatBelow = Math.max(floatBelow, vo + outT + effH + outB)\n } else {\n objBottom = Math.max(objBottom, startV + vo + outT + effH + outB)\n }\n }\n const textStartV = startV + floatBelow\n\n const lsa = doc.createElement(\"hp:linesegarray\")\n for (let li = 0; li < wrap.starts.length; li++) {\n const startReal = wrap.starts[li]\n // 실텍스트 없는 문단(인라인 표·개체만)의 textpos는 0 — chars.length로 폴백하면\n // planLines의 plan.start가 개체 index보다 커져 advanceTo 가로 전진에서 개체가\n // 전부 빠지고 같은 x에 겹쳐 그려진다 (공문 결재란 라벨표∩스탬프표 겹침의 원인)\n const textpos = startReal < realIdx.length ? realIdx[startReal] : 0\n const vertpos = textStartV + li * pitch\n const isFirst = li === 0\n const seg = doc.createElement(\"hp:lineseg\")\n seg.setAttribute(\"textpos\", String(textpos))\n seg.setAttribute(\"vertpos\", String(vertpos))\n seg.setAttribute(\"vertsize\", String(height))\n seg.setAttribute(\"textheight\", String(height))\n seg.setAttribute(\"baseline\", String(baseline))\n seg.setAttribute(\"spacing\", String(spacing))\n seg.setAttribute(\"horzpos\", String(isFirst ? marginL : contHorz))\n seg.setAttribute(\"horzsize\", String(isFirst ? firstWidth : contWidth))\n seg.setAttribute(\"flags\", LINESEG_FLAGS)\n lsa.appendChild(seg)\n }\n p.appendChild(lsa)\n\n // 문단 바닥 = 텍스트 줄 흐름(float 개체 아래부터)과 개체 바닥 중 큰 쪽.\n // 표는 셀 콘텐츠 성장으로 선언 sz보다 커질 수 있어 drawTable과 같은 실효 높이를 쓴다\n // (셀 lineseg 필요 — reflowBlockFlow가 reflowTablesIn을 먼저 돌린다).\n const textBottom = textStartV + wrap.starts.length * pitch\n return { paraBottom: Math.max(textBottom, objBottom), spaceAfter: geom.spaceAfter }\n}\n\n/** 문단 run 안의 표를 찾아 각 셀 subList를 셀 로컬로 reflow (중첩 재귀) */\nfunction reflowTablesIn(p: Element, doc: Document, styles: RenderStyles, mode: WrapMode, counter: { n: number }): void {\n for (const run of elements(p)) {\n if (ln(run) !== \"run\") continue\n for (const obj of elements(run)) {\n if (ln(obj) !== \"tbl\") continue\n for (const tr of elements(obj)) {\n if (ln(tr) !== \"tr\") continue\n for (const tc of elements(tr)) {\n if (ln(tc) !== \"tc\") continue\n const csz = findChildByLocalName(tc, \"cellSz\")\n const cm = findChildByLocalName(tc, \"cellMargin\")\n const cellW = num(csz, \"width\")\n const mL = cm ? num(cm, \"left\", 141) : 141\n const mR = cm ? num(cm, \"right\", 141) : 141\n const areaW = Math.max(500, cellW - mL - mR)\n const sub = findChildByLocalName(tc, \"subList\")\n if (sub) reflowBlockFlow(sub, doc, styles, areaW, mode, counter, 0)\n }\n }\n }\n }\n}\n\n/**\n * 한 블록 컨테이너(본문 root 또는 셀 subList) 안 문단들을 세로 흐름으로 reflow.\n * @param bodyH 최상위(본문)일 때만 >0 — 문단 단위 자동 페이지 나눔(vertpos 페이지 로컬 리셋).\n * 셀 subList는 0(페이지 나눔 없음).\n */\nfunction reflowBlockFlow(\n container: Element,\n doc: Document,\n styles: RenderStyles,\n areaW: number,\n mode: WrapMode,\n counter: { n: number },\n bodyH: number,\n): void {\n let cursorV = 0\n let prevSpaceAfter = 0\n for (const p of elements(container)) {\n if (ln(p) !== \"p\") continue\n // 문단 안 표 셀을 먼저 셀 로컬 좌표로 reflow (본문 세로 흐름과 무관) —\n // reflowPara의 표 실효높이 측정이 셀 lineseg를 읽으므로 호스트 문단보다 앞서야 한다\n reflowTablesIn(p, doc, styles, mode, counter)\n const g = styles.paraGeom.get(p.getAttribute(\"paraPrIDRef\") ?? \"\")\n const startV = cursorV + prevSpaceAfter + (g?.spaceBefore ?? 0)\n const res = reflowPara(p, doc, styles, areaW, startV, mode)\n if (!res) {\n // 캐시 보유 문단(Tier-1) — 한컴 실좌표로 커서를 전진시킨다. 혼합 캐시 문서\n // (한컴 저장본을 프로그램 편집해 일부 문단만 캐시가 없는 경우)에서 캐시 없는\n // 문단이 흐름 위치 대신 0에 겹쳐 그려지던 결함 수정 (36264961 실측: 캐시 문단\n // p18 57165+1300+780 → 캐시 없는 p19가 정확히 59245에 붙는다).\n const lsa = findChildByLocalName(p, \"linesegarray\")\n if (lsa) {\n let bottom = Number.NEGATIVE_INFINITY\n for (const seg of elements(lsa)) {\n if (ln(seg) !== \"lineseg\") continue\n const th = Math.max(num(seg, \"vertsize\", 1000), num(seg, \"textheight\", 1000))\n bottom = Math.max(bottom, num(seg, \"vertpos\") + th + num(seg, \"spacing\"))\n }\n if (Number.isFinite(bottom)) {\n cursorV = bottom\n prevSpaceAfter = g?.spaceAfter ?? 0\n }\n }\n continue\n }\n const paraH = res.paraBottom - startV\n // 페이지 넘김: 문단이 현재 페이지를 넘치고, 문단 자체는 한 페이지에 들어가면 다음 페이지로.\n // 명시적 쪽 나눔(hp:p pageBreak=\"1\" — 한컴 '쪽 나누기' 저장 속성)도 같은 경로로 존중한다.\n const explicitBreak = bodyH > 0 && startV > 0 && p.getAttribute(\"pageBreak\") === \"1\"\n if (explicitBreak || (bodyH > 0 && startV > 0 && res.paraBottom > bodyH && paraH <= bodyH)) {\n shiftParaVert(p, -startV) // 새 페이지 상단(로컬 0)으로 이동 → 프리패스가 vertpos 역행 감지\n cursorV = paraH\n } else {\n cursorV = res.paraBottom\n }\n prevSpaceAfter = res.spaceAfter\n counter.n++\n }\n}\n\n/**\n * section root의 조판 캐시 없는 문단에 linesegarray를 합성 주입한다(표 셀 재귀 포함).\n * 반환: 합성한 문단 수.\n * (Phase 2~3: 최상위 텍스트 + 표 셀 내부. 개체 밀어내기·자동 페이지 분할은 후속.)\n */\nexport function reflowSection(\n root: Element,\n styles: RenderStyles,\n geom: ReflowGeom,\n mode: WrapMode = \"keep\",\n): number {\n const doc = root.ownerDocument as unknown as Document\n const counter = { n: 0 }\n reflowBlockFlow(root, doc, styles, geom.BODY_W, mode, counter, geom.BODY_H)\n return counter.n\n}\n","/**\n * 공유 렌더 장면(RenderScene) 계약 — HWPX/HWP5 어댑터가 채우고 SVG·HTML·래스터·PDF·crop 이 소비한다 (#75).\n *\n * 좌표 규약: 공개 bbox 는 **pt**, **페이지 로컬**(물리 페이지 좌상단 원점), 페이지 번호 **1-based**.\n * 한 논리 개체가 여러 페이지에 걸치면 `regions` 에 페이지별 조각을 여럿 둔다(세로 스택 전역 좌표 금지).\n * region id 는 같은 문서·같은 옵션이면 결정적 — 그리기(문서) 순서로 유형별 일련번호를 매긴다.\n */\n\nexport type RenderSourceFormat = \"hwpx\" | \"hwp\"\n\nexport type RenderObjectType = \"paragraph\" | \"table\" | \"image\" | \"shape\" | \"equation\" | \"unknown\"\n\nexport interface PageBBox {\n /** 1-based 페이지 */\n page: number\n /** pt, 페이지 로컬, 좌상단 원점 */\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface RenderRegion {\n /** 결정적 id — `table-000017` 꼴 */\n id: string\n type: RenderObjectType\n /** 첫 조각의 페이지 (편의 필드) */\n page: number\n /** 페이지별 조각 — 한 논리 개체가 여러 물리 페이지에 걸칠 수 있다 */\n regions: PageBBox[]\n /** 원본 식별자(HWPX `hp:tbl id` 등) — 있을 때만 */\n sourceId?: string\n /** 중첩 표·셀 안 이미지·문단 안 개체의 부모 region */\n parentId?: string\n}\n\nexport interface ScenePage {\n page: number\n /** pt */\n width: number\n height: number\n}\n\nexport interface RenderScene {\n format: RenderSourceFormat\n pages: ScenePage[]\n regions: RenderRegion[]\n warnings: string[]\n stats: { texts: number; tables: number; images: number; shapes: number }\n}\n\n/** 유형별 일련번호 → 결정적 id (`table-000017`). seq 는 1-based */\nexport function regionId(type: RenderObjectType, seq: number): string {\n return `${type}-${String(seq).padStart(6, \"0\")}`\n}\n\n/** 두 bbox 의 합집합 (같은 페이지 가정) */\nexport function unionBBox(a: PageBBox, b: PageBBox): PageBBox {\n const x = Math.min(a.x, b.x), y = Math.min(a.y, b.y)\n return { page: a.page, x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y }\n}\n\n/**\n * region 수집기 — 어댑터가 그리는 순서대로 `add()` 하면 유형별 카운터로 id 를 발급한다.\n * 같은 논리 개체의 추가 페이지 조각은 `addFragment()`.\n */\nexport class RegionCollector {\n readonly regions: RenderRegion[] = []\n private readonly counters = new Map<RenderObjectType, number>()\n\n add(type: RenderObjectType, bbox: PageBBox, extra: { sourceId?: string; parentId?: string } = {}): string {\n const seq = (this.counters.get(type) ?? 0) + 1\n this.counters.set(type, seq)\n const id = regionId(type, seq)\n const region: RenderRegion = { id, type, page: bbox.page, regions: [bbox] }\n if (extra.sourceId) region.sourceId = extra.sourceId\n if (extra.parentId) region.parentId = extra.parentId\n this.regions.push(region)\n return id\n }\n\n addFragment(id: string, bbox: PageBBox): void {\n const r = this.regions.find((x) => x.id === id)\n if (!r) return\n // 같은 페이지 조각은 합집합, 다른 페이지면 새 조각\n const same = r.regions.find((f) => f.page === bbox.page)\n if (same) Object.assign(same, unionBBox(same, bbox))\n else r.regions.push(bbox)\n }\n}\n","/** Page decorations are separate stories: their line positions never split body pages. */\nimport { findChildByLocalName } from \"../hwpx/parser-shared.js\"\nimport { elements, findFirst, isDeletedControl, ln, num } from \"./para-model.js\"\n\nexport interface PageStory { sub: Element; page: number; x: number; y: number; width: number; layer?: -2 | 2 }\nexport function pageStories(root: Element, masters: Element[], paraPages: Map<Element, number[]>, pageCount: number, pageBase: number,\n geom: { ML: number; MT: number; PH: number; BODY_W: number }): PageStory[] {\n const secPr = findFirst(root, \"secPr\"), visibility = secPr && findChildByLocalName(secPr, \"visibility\")\n const pagePr = secPr && findChildByLocalName(secPr, \"pagePr\"), margin = pagePr && findChildByLocalName(pagePr, \"margin\")\n const declarations: Array<{ el: Element; page: number; kind: string }> = []\n const hidden = new Map<number, Set<string>>()\n for (const p of elements(root)) if (ln(p) === \"p\") {\n const page = paraPages.get(p)?.[0] ?? 0\n for (const run of elements(p)) if (ln(run) === \"run\") for (const ctrl of elements(run)) if (ln(ctrl) === \"ctrl\") {\n for (const el of elements(ctrl)) {\n if (isDeletedControl(el)) continue\n const kind = ln(el)\n if (kind === \"header\" || kind === \"footer\") declarations.push({ el, page, kind })\n else if (kind === \"pageHiding\") {\n const kinds = hidden.get(page) ?? new Set<string>()\n for (const name of [\"Header\", \"Footer\", \"MasterPage\"]) if (num(el, `hide${name}`)) kinds.add(name)\n hidden.set(page, kinds)\n }\n }\n }\n }\n const parity = (type: string, n: number): boolean => type === \"BOTH\" || (type === \"ODD\" && n % 2 === 1) || (type === \"EVEN\" && n % 2 === 0)\n const out: PageStory[] = []\n for (let page = 0; page < pageCount; page++) {\n const n = pageBase + page + 1\n const hides = (name: string): boolean => hidden.get(page)?.has(name) === true || (page === 0 && num(visibility, `hideFirst${name}`) !== 0)\n if (!hides(\"MasterPage\")) {\n // ODD/EVEN replace BOTH on their pages; they are not an overlay.\n const matching = masters.filter(m => m.getAttribute(\"type\") === (n % 2 ? \"ODD\" : \"EVEN\"))\n const both = masters.filter(m => (m.getAttribute(\"type\") || \"BOTH\") === \"BOTH\")\n const normal = (matching.length ? matching : both).slice(-1)\n const specific = masters.filter(m => m.getAttribute(\"type\") === \"LAST_PAGE\" ? page === pageCount - 1 : m.getAttribute(\"type\") === \"OPTIONAL_PAGE\" && num(m, \"pageNumber\") === page + 1)\n const special = specific.slice(-1)\n const selected = special.length && !num(special[0], \"pageDuplicate\") ? special : [...normal, ...special]\n for (const master of selected) {\n const sub = findChildByLocalName(master, \"subList\")\n if (sub) out.push({ sub, page, x: geom.ML, y: geom.MT, width: geom.BODY_W, layer: num(master, \"pageFront\") ? 2 : -2 })\n }\n }\n for (const kind of [\"header\", \"footer\"]) {\n if (hides(kind === \"header\" ? \"Header\" : \"Footer\")) continue\n const eligible = declarations.filter(d => d.kind === kind && d.page <= page && parity(d.el.getAttribute(\"applyPageType\") || \"BOTH\", n))\n const decl = eligible[eligible.length - 1]\n const sub = decl && findChildByLocalName(decl.el, \"subList\")\n if (sub) out.push({ sub, page, x: geom.ML, y: kind === \"header\" ? num(margin, \"top\", 5668) : geom.PH - num(margin, \"bottom\", 4252) - num(margin, \"footer\"), width: geom.BODY_W })\n }\n }\n return out\n}\n","/** HWPX drawing geometry and paints, shared by the page renderer's shape regions. */\nimport { findChildByLocalName } from \"../hwpx/parser-shared.js\"\nimport { elements, ln, num, type ParaObj } from \"./para-model.js\"\n\nconst pt = (u: number): string => String(Math.round(u) / 100)\nconst esc = (s: string): string => s.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, \"\").replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\")\nexport const SHAPE_TAGS = new Set([\"rect\", \"ellipse\", \"line\", \"polygon\", \"curv\", \"curve\", \"arc\"])\nexport interface RegionRotation { angle: number; cx: number; cy: number }\n/** SVG matrix(a b c d e f) — x' = a·x + c·y + e, y' = b·x + d·y + f (HWPUNIT) */\nexport type Affine = [number, number, number, number, number, number]\ninterface Box { x: number; y: number; w: number; h: number }\nexport function rotatedBounds(box: Box, rotations: Array<RegionRotation | Affine>): Box {\n if (!rotations.length) return box\n let points = [[box.x, box.y], [box.x + box.w, box.y], [box.x + box.w, box.y + box.h], [box.x, box.y + box.h]]\n // SVG nested groups apply the innermost transform first.\n for (let i = rotations.length - 1; i >= 0; i--) {\n const r = rotations[i]\n if (Array.isArray(r)) { points = points.map(([x, y]) => [r[0] * x + r[2] * y + r[4], r[1] * x + r[3] * y + r[5]]); continue }\n const { angle, cx, cy } = r\n const a = angle * Math.PI / 180, c = Math.cos(a), s = Math.sin(a)\n points = points.map(([x, y]) => [cx + c * (x - cx) - s * (y - cy), cy + s * (x - cx) + c * (y - cy)])\n }\n const x = Math.min(...points.map(p => p[0])), y = Math.min(...points.map(p => p[1]))\n return { x, y, w: Math.max(...points.map(p => p[0])) - x, h: Math.max(...points.map(p => p[1])) - y }\n}\nexport function mulAffine(m: Affine, n: Affine): Affine {\n return [m[0] * n[0] + m[2] * n[1], m[1] * n[0] + m[3] * n[1], m[0] * n[2] + m[2] * n[3], m[1] * n[2] + m[3] * n[3],\n m[0] * n[4] + m[2] * n[5] + m[4], m[1] * n[4] + m[3] * n[5] + m[5]]\n}\n\n/**\n * 묶음 안 개체의 renderingInfo — transMatrix·scaMatrix·rotMatrix 를 적힌 순서로 곱하면 개체 원본(orgSz) 좌표가 최상위 묶음의\n * 상자 좌표로 간다(e1 e2 e3 / e4 e5 e6 = x'·y' 행). 위례선 트램 보도자료 지도 묶음 실측: 지도 사진 48443×63972 → 0~42907 × 0~44754\n * (최상위 묶음 curSz 42907×44780), 노선 곡선 첫 점 → 지도 폭 0.77 지점(한컴 PDF 와 같다). 행렬이 없으면 null\n */\nexport function renderingMatrix(el: Element): Affine | null {\n const info = findChildByLocalName(el, \"renderingInfo\")\n if (!info) return null\n let m: Affine | null = null\n for (const c of elements(info)) {\n const tag = ln(c)\n if (tag !== \"transMatrix\" && tag !== \"scaMatrix\" && tag !== \"rotMatrix\") continue\n const n: Affine = [num(c, \"e1\", 1), num(c, \"e4\"), num(c, \"e2\"), num(c, \"e5\", 1), num(c, \"e3\"), num(c, \"e6\")]\n m = m ? mulAffine(m, n) : n\n }\n return m\n}\n\nexport interface ObjectGeometry {\n x: number; y: number; w: number; h: number\n bbox: { x: number; y: number; w: number; h: number }\n transform: string\n rotation?: RegionRotation\n}\n\n/**\n * 회전 개체 — 한컴 저장본은 sz 가 틀(curSz)을 돌린 외접 상자이고 pos·offset 이 그 상자 자리다(회전 사진 ta-pic-001-r: 13668×12686 을\n * 34° 돌린 외접 18425×18160, rotationInfo center 가 그 상자 중심 · 270° 다각형 2536×4798 → sz 4798×2536). 틀 중심을 외접 상자(x, y, boxW, boxH)\n * 중심에 두고 그 점을 축으로 돌린다 — 다른 회전 중심도 같은 외접 상자에 놓이면 평행 이동만 다르다. 종전엔 틀을 상자 왼쪽 위에 두고\n * orgSz 비율로 중심을 잡아 실문서의 회전 도형이 어긋났고, 사진·묶음 개체는 회전을 읽지 않았다(#116)\n */\nexport function objectGeometry(el: Element, x: number, y: number, w: number, h: number, boxW = w, boxH = h): ObjectGeometry {\n const rot = findChildByLocalName(el, \"rotationInfo\")\n const angle = num(rot, \"angle\") % 360\n if (!angle) return { x, y, w, h, bbox: { x, y, w, h }, transform: \"\" }\n const cx = x + boxW / 2, cy = y + boxH / 2\n const fx = cx - w / 2, fy = cy - h / 2\n const rotation = { angle, cx: Math.round(cx), cy: Math.round(cy) }\n return { x: fx, y: fy, w, h, bbox: rotatedBounds({ x: fx, y: fy, w, h }, [rotation]), rotation, transform: `rotate(${angle} ${pt(cx)} ${pt(cy)})` }\n}\n\ninterface PaintImage { dataUri: string; paintId?: string }\nfunction shapeFill(el: Element, defs: string[], images: Map<string, PaintImage>, warn: (key: string, text: string) => void): string {\n const brush = findChildByLocalName(el, \"fillBrush\")\n const win = brush && findChildByLocalName(brush, \"winBrush\")\n const face = win?.getAttribute(\"faceColor\")\n if (face && face.toLowerCase() !== \"none\") return esc(face)\n const grad = brush && findChildByLocalName(brush, \"gradation\")\n if (grad) {\n const colors = elements(grad).filter(c => ln(c) === \"color\").map(c => c.getAttribute(\"value\") || \"#000000\")\n if (!colors.length) return \"none\"\n const type = grad.getAttribute(\"type\") ?? \"LINEAR\"\n if (type !== \"LINEAR\" && type !== \"RADIAL\") {\n warn(`gradient:${type}`, `그라데이션(${type}) 미지원 — 첫 색으로 채움`)\n return esc(colors[0])\n }\n const id = `paint${defs.length}`\n const opacity = Math.max(0, Math.min(1, 1 - num(grad, \"alpha\") / 255))\n const stops = colors.map((color, i) => `<stop offset=\"${colors.length === 1 ? 0 : i / (colors.length - 1)}\" stop-color=\"${esc(color)}\" stop-opacity=\"${opacity}\"/>`).join(\"\")\n if (type === \"RADIAL\") defs.push(`<radialGradient id=\"${id}\" cx=\"${num(grad, \"centerX\", 50)}%\" cy=\"${num(grad, \"centerY\", 50)}%\">${stops}</radialGradient>`)\n else {\n const a = num(grad, \"angle\") * Math.PI / 180, dx = Math.cos(a) / 2, dy = -Math.sin(a) / 2\n defs.push(`<linearGradient id=\"${id}\" x1=\"${0.5 - dx}\" y1=\"${0.5 - dy}\" x2=\"${0.5 + dx}\" y2=\"${0.5 + dy}\">${stops}</linearGradient>`)\n }\n return `url(#${id})`\n }\n const imgBrush = brush && findChildByLocalName(brush, \"imgBrush\")\n const img = imgBrush && findChildByLocalName(imgBrush, \"img\")\n if (imgBrush && img) {\n const ref = img.getAttribute(\"binaryItemIDRef\") ?? \"\"\n const loaded = images.get(ref)\n if (!loaded) { warn(`brush-image:${ref}`, `그림 채움 이미지(${ref}) 없음 — 채움 생략`); return \"none\" }\n const mode = imgBrush.getAttribute(\"mode\") ?? \"TOTAL\"\n if (mode !== \"TOTAL\") { warn(`brush-mode:${mode}`, `그림 채움(${mode}) 미지원 — 채움 생략`); return \"none\" }\n if (loaded.paintId) return `url(#${loaded.paintId})`\n const id = `paint${defs.length}`\n // Embed in the paint definition; standalone page assembly can discard unused\n // image symbols without accidentally removing a pattern's referenced image.\n defs.push(`<pattern id=\"${id}\" width=\"1\" height=\"1\" patternContentUnits=\"objectBoundingBox\"><image href=\"${esc(loaded.dataUri)}\" width=\"1\" height=\"1\" preserveAspectRatio=\"none\"/></pattern>`)\n loaded.paintId = id\n return `url(#${id})`\n }\n return \"none\"\n}\n\n/** local — 묶음 안 개체를 원본(orgSz) 좌표로 그린다(배율·회전은 바깥 renderingInfo 행렬 그룹이 맡는다) */\nexport function shapeGeometry(o: ParaObj, boxX: number, boxY: number, defs: string[], images: Map<string, PaintImage>, warn: (key: string, text: string) => void, local = false): ObjectGeometry & { svg: string } {\n const el = o.el, org = findChildByLocalName(el, \"orgSz\"), cur = findChildByLocalName(el, \"curSz\")\n const ow = num(org, \"width\"), oh = num(org, \"height\")\n const w = (local ? ow : num(cur, \"width\")) || ow || num(cur, \"width\") || o.width\n const h = (local ? oh : num(cur, \"height\")) || oh || num(cur, \"height\") || o.height\n const geometry: ObjectGeometry = local ? { x: boxX, y: boxY, w, h, bbox: { x: boxX, y: boxY, w, h }, transform: \"\" }\n : objectGeometry(el, boxX, boxY, w, h, o.width || w, o.height || h)\n const { x, y } = geometry\n const sx = ow > 0 ? w / ow : 1, sy = oh > 0 ? h / oh : 1\n const line = findChildByLocalName(el, \"lineShape\"), style = line?.getAttribute(\"style\") ?? \"SOLID\"\n const color = esc(line?.getAttribute(\"color\") || \"#000000\")\n const strokeW = style === \"NONE\" ? 0 : Math.max(0.2, (num(line, \"width\", 33) / 100) * 2.834645)\n const dash = /DASH|DOT/.test(style) ? ` stroke-dasharray=\"${style.includes(\"DOT\") ? \"1,1.5\" : \"3,1.5\"}\"` : \"\"\n // 묶음 행렬 안(local)에서는 선 굵기를 배율로 늘리지 않는다 — 한컴은 묶음 크기를 바꿔도 선 굵기가 그대로\n const keepWidth = local ? ` vector-effect=\"non-scaling-stroke\"` : \"\"\n const stroke = strokeW ? ` stroke=\"${color}\" stroke-width=\"${strokeW.toFixed(2)}\"${dash}${keepWidth}` : \"\"\n const fill = ` fill=\"${shapeFill(el, defs, images, warn)}\"`\n let svg = \"\"\n if (o.tag === \"rect\") svg = `<rect x=\"${pt(x)}\" y=\"${pt(y)}\" width=\"${pt(w)}\" height=\"${pt(h)}\"${fill}${stroke}/>`\n else if (o.tag === \"ellipse\") svg = `<ellipse cx=\"${pt(x + w / 2)}\" cy=\"${pt(y + h / 2)}\" rx=\"${pt(w / 2)}\" ry=\"${pt(h / 2)}\"${fill}${stroke}/>`\n else if (o.tag === \"line\") {\n const s = findChildByLocalName(el, \"startPt\"), e = findChildByLocalName(el, \"endPt\")\n svg = `<line x1=\"${pt(x + num(s, \"x\") * sx)}\" y1=\"${pt(y + num(s, \"y\") * sy)}\" x2=\"${pt(x + num(e, \"x\") * sx)}\" y2=\"${pt(y + num(e, \"y\") * sy)}\" stroke=\"${color}\" stroke-width=\"${(strokeW || 0.3).toFixed(2)}\"${dash}${keepWidth}/>`\n } else if (o.tag === \"polygon\" || o.tag === \"curv\") {\n const pts = elements(el).filter(c => ln(c) === \"pt\").map(c => `${pt(x + num(c, \"x\") * sx)},${pt(y + num(c, \"y\") * sy)}`)\n if (pts.length >= 2) svg = `<polygon points=\"${pts.join(\" \")}\"${fill}${stroke}/>`\n } else if (o.tag === \"curve\") {\n // CURVE 조각도 점을 잇는다 — 한컴은 곡선을 촘촘한 점(약 200 HWPUNIT 간격)으로 펴서 조각마다 담는다(위례선 트램 보도자료 실측).\n // 종전엔 CURVE 조각이 하나라도 있으면 통째 생략해, 곡선 덮개가 가리던 자리가 드러났다(#116)\n const segs = elements(el).filter(c => ln(c) === \"seg\")\n if (segs.length) {\n let path = \"\", lastX: number | undefined, lastY: number | undefined\n for (const s of segs) {\n const x1 = num(s, \"x1\"), y1 = num(s, \"y1\"), x2 = num(s, \"x2\"), y2 = num(s, \"y2\")\n if (x1 !== lastX || y1 !== lastY) path += `M${pt(x + x1 * sx)} ${pt(y + y1 * sy)}`\n path += `L${pt(x + x2 * sx)} ${pt(y + y2 * sy)}`\n lastX = x2; lastY = y2\n }\n svg = `<path d=\"${path}\"${fill}${stroke}/>`\n }\n } else if (o.tag === \"arc\") svg = `<ellipse cx=\"${pt(x + w / 2)}\" cy=\"${pt(y + h / 2)}\" rx=\"${pt(w / 2)}\" ry=\"${pt(h / 2)}\" fill=\"none\"${stroke || ` stroke=\"${color}\" stroke-width=\"0.3\"`}/>`\n return { ...geometry, svg }\n}\n","/** HWP 5.x 레코드 리더, UTF-16LE 텍스트 추출, 스트림 압축해제 */\n\nimport type { ScriptKind } from \"../script-tags.js\"\nimport { inflateRawSync, inflateSync } from \"zlib\"\nimport { KordocError } from \"../utils.js\"\nimport { TOC_LEADER } from \"../toc-entry.js\"\nimport type { Edges } from \"../table/layout-frames.js\"\n\n// ─── 레코드 태그 상수 ────────────────────────────────\n\nexport const TAG_PARA_HEADER = 0x0042\nexport const TAG_PARA_TEXT = 0x0043\nexport const TAG_CHAR_SHAPE = 0x0044\nexport const TAG_PARA_SHAPE = 0x0045\n/** 0x45의 스펙상 정체 — 본문 문단의 줄 조판 정보(HWPTAG_BEGIN+53). TAG_PARA_SHAPE는 역사적 오명 */\nexport const TAG_PARA_LINE_SEG = 0x0045\nexport const TAG_CTRL_HEADER = 0x0047\nexport const TAG_LIST_HEADER = 0x0048\nexport const TAG_TABLE = 0x004d\nexport const TAG_SHAPE_COMPONENT = 0x004c // HWPTAG_BEGIN + 60\nexport const TAG_SHAPE_COMPONENT_PICTURE = 0x0055 // HWPTAG_BEGIN + 69\nexport const TAG_SHAPE_COMPONENT_CONTAINER = 0x0056 // HWPTAG_BEGIN + 70\nexport const TAG_CTRL_DATA = 0x0057 // HWPTAG_BEGIN + 71\nexport const TAG_EQEDIT = 0x0058\n\n// DocInfo 태그 (스타일 정보 해석용) — HWPTAG_BEGIN(0x0010) 기준\nexport const TAG_ID_MAPPINGS = 0x0011 // HWPTAG_BEGIN + 1\nexport const TAG_BORDER_FILL = 0x0014 // HWPTAG_BEGIN + 4\nexport const TAG_BIN_DATA = 0x0012 // HWPTAG_BEGIN + 2\nexport const TAG_FACE_NAME = 0x0013 // HWPTAG_BEGIN + 3\nexport const TAG_DOC_CHAR_SHAPE = 0x0015 // HWPTAG_BEGIN + 5\nexport const TAG_NUMBERING = 0x0017 // HWPTAG_BEGIN + 7\nexport const TAG_BULLET = 0x0018 // HWPTAG_BEGIN + 8\nexport const TAG_DOC_PARA_SHAPE = 0x0019 // HWPTAG_BEGIN + 9\nexport const TAG_DOC_STYLE = 0x001a // HWPTAG_BEGIN + 10\n\n// 특수 문자 코드 (UTF-16LE) — HWP 5.0 바이너리 스펙 + rhwp 검증\n// 3가지 카테고리: char(2바이트), inline(16바이트), extended(16바이트)\n// char: 0, 13, 24-31 — 제어문자만, 확장 데이터 없음\n// inline: 4-9, 19-20 — 제어문자(2) + 확장(14) = 16바이트\n// extended: 1-3, 10-12, 14-18, 21-23 — 제어문자(2) + 확장(14) = 16바이트\nconst CHAR_LINE = 0x0000 // char: 줄바꿈\nconst CHAR_SECTION_BREAK = 0x000a // char: 강제 줄 나눔(line break, 2바이트). 구역/단 정의는 코드2(extended)라 혼동 주의\nconst CHAR_PARA = 0x000d // char: 문단 끝\nconst CHAR_TAB = 0x0009 // inline: 탭\nconst CHAR_HYPHEN = 0x0018 // char: 하이픈 (스펙 코드 24 — pyhwp HYPHEN, hwp3 SIMPLE_CTRL 24/25와 동일)\nconst CHAR_NBSP = 0x001e // char: 묶음 빈칸(비분리 공백, 스펙 코드 30 — pyhwp NONBREAK_SPACE)\nconst CHAR_FIXED_WIDTH = 0x001f // char: 고정폭 빈칸 (스펙 코드 31 — pyhwp FIXWIDTH_SPACE). 25~29(0x19~0x1d)는 예약\n\n// FileHeader 플래그\nexport const FLAG_COMPRESSED = 1 << 0\nexport const FLAG_ENCRYPTED = 1 << 1\nexport const FLAG_DISTRIBUTION = 1 << 2\nexport const FLAG_DRM = 1 << 4\n\n// ─── 레코드 구조 ─────────────────────────────────────\n\nexport interface HwpRecord {\n tagId: number\n level: number\n size: number\n data: Buffer\n}\n\nexport interface HwpFileHeader {\n signature: string\n versionMajor: number\n flags: number\n}\n\n// ─── 레코드 리더 ─────────────────────────────────────\n\n/** 최대 레코드 수 — 비정상 파일에 의한 메모리 폭주 방지 */\nconst MAX_RECORDS = 500_000\n\nexport function readRecords(data: Buffer): HwpRecord[] {\n const records: HwpRecord[] = []\n let offset = 0\n\n while (offset + 4 <= data.length && records.length < MAX_RECORDS) {\n const header = data.readUInt32LE(offset)\n offset += 4\n\n const tagId = header & 0x3ff\n const level = (header >> 10) & 0x3ff\n let size = (header >> 20) & 0xfff\n\n // 확장 크기\n if (size === 0xfff) {\n if (offset + 4 > data.length) break\n size = data.readUInt32LE(offset)\n offset += 4\n }\n\n if (offset + size > data.length) break\n records.push({ tagId, level, size, data: data.subarray(offset, offset + size) })\n offset += size\n }\n\n return records\n}\n\n// ─── 스트림 압축 해제 ────────────────────────────────\n\n/** 압축 해제 최대 크기 (100MB) — decompression bomb 방지 */\nconst MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024\n\nexport function decompressStream(data: Buffer): Buffer {\n const opts = { maxOutputLength: MAX_DECOMPRESS_SIZE }\n if (data.length >= 2 && data[0] === 0x78) {\n try { return inflateSync(data, opts) } catch { /* fallback to raw */ }\n }\n return inflateRawSync(data, opts)\n}\n\n// ─── FileHeader 파싱 ─────────────────────────────────\n\nexport function parseFileHeader(data: Buffer): HwpFileHeader {\n if (data.length < 40) throw new KordocError(\"FileHeader가 너무 짧습니다 (최소 40바이트)\")\n const sig = data.subarray(0, 32).toString(\"utf8\").replace(/\\0+$/, \"\")\n return {\n signature: sig,\n versionMajor: data[35],\n flags: data.readUInt32LE(36),\n }\n}\n\n// ─── 스타일 정보 구조 ────────────────────────────────\n\n/** DocInfo에서 추출한 문단 모양 (PARA_SHAPE) */\nexport interface HwpParaShape {\n /** 문단 머리 종류 (attr1 bits 23-24): 0=없음, 1=개요, 2=번호, 3=글머리표 */\n headType: number\n /** 문단 수준 (attr1 bits 25-27): 0-6 → 1-7수준 */\n paraLevel: number\n /** 번호/글머리표 ID (1-based) — headType=2면 numbering, 3이면 bullet 인덱스 */\n numberingId: number\n}\n\n/** DocInfo BIN_DATA 항목 — binDataId(1-based 레코드 순서) → BinData 스토리지 매핑 */\nexport interface HwpBinDataItem {\n /** 0=link(외부 파일), 1=embedding, 2=storage */\n kind: \"link\" | \"embed\" | \"storage\"\n /** BinData 스토리지 엔트리 번호 — 스트림명 \"BIN%04X.ext\" (16진) */\n storageId: number\n /** 확장자 (예: \"jpg\") */\n extension: string\n}\n\n/** DocInfo NUMBERING — 문단 번호 정의 (7수준) */\nexport interface HwpNumbering {\n /** 수준별(0-6) 번호 형식 문자열 (예: \"^1.\") — ^N은 N수준 번호로 치환 */\n levelFormats: string[]\n /** 수준별 번호 모양 코드 (HWP 표 43: 0=숫자, 1=원숫자, 8=가나다, 12=일이삼, 13=一二三 등) */\n numberFormats: number[]\n /** 수준별 시작 번호 (5.0.2.5+, 기본 1) */\n startNumbers: number[]\n}\n\n/** DocInfo BULLET — 글머리표 문자 */\nexport interface HwpBullet {\n /** 글머리표 문자 (PUA 가능 — 출력 시 mapPuaText로 정규화됨), U+FFFF=이미지 글머리표 */\n char: string\n}\n\n/** DocInfo에서 추출한 글자 모양 (CHAR_SHAPE) */\nexport interface HwpCharShape {\n /** 글꼴 크기 (단위: 0.1pt, 예: 100 = 10pt) */\n fontSize: number\n /**\n * 속성 플래그 (HWP5 바이너리 스펙 1.1 기준):\n * bit 0 = italic, bit 1 = bold, bit 2 = underline, bit 3 = outline\n * 검증 완료: 공식 스펙 + pyhwp/hwp.js 등 오픈소스 파서와 일치 (v1.7)\n */\n attrFlags: number\n}\n\n/** DocInfo에서 추출한 스타일 */\nexport interface HwpStyle {\n name: string\n /** 한글 이름 (UTF-16LE) */\n nameKo: string\n /** 연결된 charShape 인덱스 */\n charShapeId: number\n /** 연결된 paraShape 인덱스 */\n paraShapeId: number\n /** 스타일 타입: 0=paragraph, 1=character */\n type: number\n}\n\n/** DocInfo 파싱 결과 */\nexport interface HwpDocInfo {\n charShapes: HwpCharShape[]\n paraShapes: HwpParaShape[]\n styles: HwpStyle[]\n /** BIN_DATA 항목 (1-based binDataId → binData[id-1]) */\n binData: HwpBinDataItem[]\n /** NUMBERING 정의 (1-based numberingId → numberings[id-1]) */\n numberings: HwpNumbering[]\n /** BULLET 정의 (1-based bulletId → bullets[id-1]) */\n bullets: HwpBullet[]\n /** BORDER_FILL 의 보이는 변 (1-based borderFillId → borderEdges[id-1]) — 보이지 않는 틀 표 풀기 (v4.17.0) */\n borderEdges: Edges[]\n}\n\n/** length-prefixed UTF-16LE 문자열 읽기 (HWP WCHAR 배열) */\nfunction readHwpString(data: Buffer, offset: number): { value: string; next: number } {\n if (offset + 2 > data.length) return { value: \"\", next: data.length }\n const len = data.readUInt16LE(offset)\n const start = offset + 2\n const end = start + len * 2\n if (len === 0 || end > data.length) return { value: \"\", next: start }\n return { value: data.subarray(start, end).toString(\"utf16le\"), next: end }\n}\n\n/** DocInfo 레코드들에서 스타일 정보 추출 */\nexport function parseDocInfo(records: HwpRecord[]): HwpDocInfo {\n const charShapes: HwpCharShape[] = []\n const paraShapes: HwpParaShape[] = []\n const styles: HwpStyle[] = []\n const binData: HwpBinDataItem[] = []\n const numberings: HwpNumbering[] = []\n const bullets: HwpBullet[] = []\n const borderEdges: Edges[] = []\n\n for (const rec of records) {\n // BORDER_FILL — 속성 u16@0 · 변 4개(왼/오/위/아래) @2+6k = 종류 u8(0 없음) · 굵기 u8 · COLORREF u32 (render/hwp5-scene 과 같은 해독).\n // 흰 선(0xFFFFFF)은 흰 바탕에 안 보인다\n if (rec.tagId === TAG_BORDER_FILL) {\n const seen = (k: number): boolean => {\n const o = 2 + 6 * k\n return rec.data.length >= o + 6 && rec.data[o] !== 0 && (rec.data.readUInt32LE(o + 2) & 0xffffff) !== 0xffffff\n }\n borderEdges.push({ l: seen(0), r: seen(1), t: seen(2), b: seen(3) })\n }\n\n // PARA_SHAPE — 문단 모양 (rhwp doc_info.rs parse_para_shape)\n // attr1(u32@0) 비트 팩: bits 23-24 = 머리 종류, bits 25-27 = 문단 수준\n // numberingId: u16@30 (attr1 4 + 여백/간격 i32*6 = 24 + tabDefId 2 → offset 30)\n if (rec.tagId === TAG_DOC_PARA_SHAPE && rec.data.length >= 4) {\n const attr1 = rec.data.readUInt32LE(0)\n const headType = (attr1 >>> 23) & 0x03\n const paraLevel = (attr1 >>> 25) & 0x07\n const numberingId = rec.data.length >= 32 ? rec.data.readUInt16LE(30) : 0\n paraShapes.push({ headType, paraLevel, numberingId })\n }\n\n // BIN_DATA — binDataId(레코드 순서 1-based) → 스토리지 ID(16진)/확장자 매핑\n // (rhwp doc_info.rs parse_bin_data: attr u16 → type/compression/status)\n if (rec.tagId === TAG_BIN_DATA && rec.data.length >= 2) {\n const attr = rec.data.readUInt16LE(0)\n const typeBits = attr & 0x000f\n if (typeBits === 0) {\n // link: absPath + relPath (외부 파일 — 스토리지 없음)\n binData.push({ kind: \"link\", storageId: 0, extension: \"\" })\n } else {\n const storageId = rec.data.length >= 4 ? rec.data.readUInt16LE(2) : 0\n const { value: extension } = readHwpString(rec.data, 4)\n binData.push({ kind: typeBits === 2 ? \"storage\" : \"embed\", storageId, extension })\n }\n }\n\n // NUMBERING — 문단 번호 정의 (rhwp doc_info.rs parse_numbering, HWP 표 40/41)\n if (rec.tagId === TAG_NUMBERING && rec.data.length >= 14) {\n const levelFormats: string[] = []\n const numberFormats: number[] = []\n const startNumbers: number[] = [1, 1, 1, 1, 1, 1, 1]\n let offset = 0\n for (let level = 0; level < 7; level++) {\n if (offset + 12 > rec.data.length) {\n levelFormats.push(\"\")\n numberFormats.push(0)\n continue\n }\n // 문단 머리 정보(12B): attr(u32) + widthAdjust(i16) + textDistance(i16) + charShapeId(u32)\n const attr = rec.data.readUInt32LE(offset)\n numberFormats.push((attr >>> 5) & 0x0f)\n offset += 12\n // 번호 형식 문자열 (가변)\n const { value, next } = readHwpString(rec.data, offset)\n levelFormats.push(value)\n offset = next\n }\n // 시작 번호 (u16) + 수준별 시작 번호 (5.0.2.5+, u32×7)\n let baseStart = 1\n if (offset + 2 <= rec.data.length) {\n baseStart = rec.data.readUInt16LE(offset) || 1\n offset += 2\n }\n for (let level = 0; level < 7; level++) {\n if (offset + 4 <= rec.data.length) {\n startNumbers[level] = rec.data.readUInt32LE(offset) || 1\n offset += 4\n } else {\n startNumbers[level] = baseStart\n }\n }\n numberings.push({ levelFormats, numberFormats, startNumbers })\n }\n\n // BULLET — 글머리표 (rhwp doc_info.rs parse_bullet, HWP 표 44)\n // 머리 정보(12B) 다음 WCHAR가 글머리표 문자\n if (rec.tagId === TAG_BULLET && rec.data.length >= 14) {\n const code = rec.data.readUInt16LE(12)\n bullets.push({ char: code > 0 ? String.fromCharCode(code) : \"•\" })\n }\n\n if (rec.tagId === TAG_DOC_CHAR_SHAPE && rec.data.length >= 18) {\n // HWP5 CHAR_SHAPE 구조 (바이너리 스펙 1.1 기준):\n // faceId: 7개 언어 * u16 = 14바이트 (offset 0-13)\n // ratio: 7개 언어 * u8 = 7바이트 (offset 14-20)\n // spacing: 7개 언어 * s8 = 7바이트 (offset 21-27)\n // relSize: 7개 언어 * u8 = 7바이트 (offset 28-34)\n // charOffset: 7개 언어 * s8 = 7바이트 (offset 35-41)\n // baseSize: u32 at offset 42 (단위: 0.1pt)\n // attrFlags: u32 at offset 46 (bit0=italic, bit1=bold) — 공식 스펙 검증 완료\n if (rec.data.length >= 50) {\n const fontSize = rec.data.readUInt32LE(42) // 단위: 0.1pt\n const attrFlags = rec.data.readUInt32LE(46)\n charShapes.push({ fontSize, attrFlags })\n } else {\n // 짧은 레코드 — 스타일 정보 없음\n charShapes.push({ fontSize: 0, attrFlags: 0 })\n }\n }\n\n if (rec.tagId === TAG_DOC_STYLE && rec.data.length >= 8) {\n try {\n // STYLE 구조: nameLen(u16) + name(UTF-16LE) + nameKoLen(u16) + nameKo(UTF-16LE)\n // + type(u8) + nextStyleId(u16) + langId(s16) + paraShapeId(u16) + charShapeId(u16)\n let offset = 0\n const nameLen = rec.data.readUInt16LE(offset); offset += 2\n const nameBytes = nameLen * 2\n const name = nameBytes > 0 && offset + nameBytes <= rec.data.length\n ? rec.data.subarray(offset, offset + nameBytes).toString(\"utf16le\")\n : \"\"\n offset += nameBytes\n\n let nameKo = \"\"\n if (offset + 2 <= rec.data.length) {\n const nameKoLen = rec.data.readUInt16LE(offset); offset += 2\n const nameKoBytes = nameKoLen * 2\n if (nameKoBytes > 0 && offset + nameKoBytes <= rec.data.length) {\n nameKo = rec.data.subarray(offset, offset + nameKoBytes).toString(\"utf16le\")\n }\n offset += nameKoBytes\n }\n\n // type(u8) + nextStyleId(u8) + langId(s16) + paraShapeId(u16) + charShapeId(u16)\n // 주의: nextStyleId는 스펙상 BYTE — 2바이트 스킵하면 paraShapeId가 256배수로 깨짐 (off-by-one 버그 수정)\n const type = offset < rec.data.length ? rec.data.readUInt8(offset) : 0; offset += 1\n offset += 1 // nextStyleId (BYTE)\n offset += 2 // langId\n const paraShapeId = offset + 2 <= rec.data.length ? rec.data.readUInt16LE(offset) : 0; offset += 2\n const charShapeId = offset + 2 <= rec.data.length ? rec.data.readUInt16LE(offset) : 0\n\n styles.push({ name, nameKo, charShapeId, paraShapeId, type })\n } catch {\n // 파싱 실패 — 스킵\n }\n }\n }\n\n return { charShapes, paraShapes, styles, binData, numberings, bullets, borderEdges }\n}\n\n// ─── UTF-16LE 텍스트 추출 (21가지 제어문자 처리) ─────\n\nexport type InlineControlResolver = (ctrlId: string) => string | null | undefined\n\n/**\n * 확장 컨트롤(CTRL_HEADER 보유) 기반 인라인 치환 콜백.\n * - ctrlIdx: 문단 내 확장 컨트롤 등장 순서 (0-based, CTRL_HEADER 자식 레코드 순서와 1:1)\n * - ctrlId: u32 정규화 ID — 파일의 LE DWORD를 그대로 읽은 값 (\"tbl \" → 0x74626c20)\n * 인라인 컨트롤(4-9, 19-20 — CTRL_HEADER 없음)은 ctrlIdx = -1로 호출된다.\n */\nexport type IndexedControlResolver = (ctrlIdx: number, ctrlId: number) => string | null | undefined\n\n/** FIELD_BEGIN(0x03)/FIELD_END(0x04) 스택 페어링으로 복원한 필드 anchor 범위 */\nexport interface HwpFieldRange {\n /** state.text 기준 시작 인덱스 */\n start: number\n /** state.text 기준 끝 인덱스 (exclusive) */\n end: number\n /** FIELD_BEGIN 컨트롤의 확장 컨트롤 인덱스 (CTRL_HEADER 순서) */\n ctrlIdx: number\n}\n\n/** 문단 단위 PARA_TEXT 누적 상태 — 여러 PARA_TEXT 레코드에 걸친 컨트롤 인덱스/필드 스택 유지 */\nexport interface ParaTextState {\n text: string\n ctrlIdx: number\n fieldStack: Array<{ start: number; ctrlIdx: number }>\n fieldRanges: HwpFieldRange[]\n /** 레코드 끝 상위 서로게이트 반쪽과 □ 로 낸 자리 — 다음 레코드 첫 하위 반쪽과 한 글자로 되돌린다 */\n loneHigh?: number\n loneHighAt?: number\n /** 리터럴 \"$\"·\"<\" 를 LITERAL_DOLLAR_MARK·LITERAL_LT_MARK 한 글자로 — 본문 파서만 켠다. 필드 범위가 글자 위치라\n * 두 글자 \"\\$\" 를 바로 넣지 않고, 필드 처리 뒤 본문 파서가 \"\\$\" 로 바꾼다(escapeLiteralDollar 규약) */\n dollarMark?: boolean\n /** 채움 탭을 TOC_LEADER 표지로 — 본문 파서만 켜고 문단 글을 다 모은 뒤 takeTocLeaders 로 보통 탭이 된다 */\n tocLeaders?: boolean\n /** 글자 위치(문단 WCHAR 순번)의 첨자 종류 — 문단 글자 모양(PARA_CHAR_SHAPE) 위치표로 본문 파서가 채운다.\n * 있으면 첨자 글자를 <sup>·<sub> 로 감싼다(제어 문자·개체 자리에서는 닫는다) */\n scriptAt?: (pos: number) => ScriptKind | null\n /** 지금 열린 첨자 태그 */\n script?: ScriptKind | null\n /** 앞 PARA_TEXT 레코드까지의 WCHAR 수 — 글자 모양 위치는 문단 전체 기준 */\n wpos?: number\n}\n\n/** 리터럴 \"$\" 표지 (dollarMark) — 유니코드 비문자라 문서 글에 나오지 않는다 */\nexport const LITERAL_DOLLAR_MARK = \"\\uFDD0\"\n/** 리터럴 \"<\" 표지 (dollarMark 와 함께) — 첨자 태그(scriptAt)와 원문 글자 \"<sub>\" 를 가른다 (#122, escapeLiteralTags) */\nexport const LITERAL_LT_MARK = \"\\uFDD1\"\nconst LITERAL_LT_TAG = /\\uFDD1(?=\\/?(?:u|sup|sub)>)/g\n\n/** 필드 처리 뒤 리터럴 \"<\" 표지를 글로 — 태그 모양(\"<sub>\"·\"</sup>\"·\"<u>\")이면 \\<, 아니면 < (escapeLiteralTags 규약) */\nexport function resolveLiteralLt(text: string): string {\n return text.includes(LITERAL_LT_MARK) ? text.replace(LITERAL_LT_TAG, \"\\\\<\").replaceAll(LITERAL_LT_MARK, \"<\") : text\n}\n\nexport function createParaTextState(): ParaTextState {\n return { text: \"\", ctrlIdx: 0, fieldStack: [], fieldRanges: [] }\n}\n\nexport function extractText(data: Buffer): string {\n return extractTextWithControls(data)\n}\n\n/** 레거시 인터페이스 — ctrlId를 on-disk 바이트 순서 ASCII 문자열로 받는 resolver */\nexport function extractTextWithControls(data: Buffer, resolveControl?: InlineControlResolver): string {\n const state = createParaTextState()\n appendParaText(state, data, resolveControl\n ? (_idx, id) => resolveControl(ctrlIdToDiskAscii(id))\n : undefined)\n return state.text\n}\n\n/** u32 컨트롤 ID → on-disk 바이트 순서 ASCII (LE 저장이므로 역순 문자열, 예: 0x74626c20 → \" lbt\") */\nfunction ctrlIdToDiskAscii(id: number): string {\n return String.fromCharCode(id & 0xff, (id >>> 8) & 0xff, (id >>> 16) & 0xff, (id >>> 24) & 0xff)\n}\n\n/** 한컴 PUA-A 접힘 영역 — HWP5 WCHAR 의 U+A000~U+A48C(이 글자 음절) ↔ U+F0000~U+F048C. 관찰 범위만 (v4.12.2) */\nconst HANCOM_FOLDED_PUA_START = 0xa000\nconst HANCOM_FOLDED_PUA_END = 0xa48c\nconst HANCOM_FOLDED_PUA_SHIFT = 0xf0000 - 0xa000\n\n/** 확장 전용 컨트롤 문자 (CTRL_HEADER 자식 레코드 1개와 대응) — rhwp is_extended_only_ctrl_char */\nexport function isExtendedOnlyCtrlChar(ch: number): boolean {\n return (ch >= 1 && ch <= 3) || (ch >= 11 && ch <= 12) || (ch >= 14 && ch <= 18) || (ch >= 21 && ch <= 23)\n}\n\n/**\n * PARA_TEXT 레코드 1개를 상태에 누적.\n * 텍스트 추출 + 확장 컨트롤 인라인 치환 + FIELD_BEGIN/END 범위 추적을 동시에 수행한다.\n */\nexport function appendParaText(state: ParaTextState, data: Buffer, resolveControl?: IndexedControlResolver): void {\n let result = \"\"\n let i = 0\n const wbase = state.wpos ?? 0\n // 첨자 태그 전환 — 보이는 글자 앞에서 그 자리 글자 모양대로 열고, 제어 문자 앞에서는 닫는다\n const setScript = (want: ScriptKind | null): void => {\n const cur = state.script ?? null\n if (cur === want) return\n if (cur) result += `</${cur}>`\n if (want) result += `<${want}>`\n state.script = want\n }\n // 앞 레코드 끝의 상위 반쪽(□ 로 냈다)과 이 레코드 첫 하위 반쪽은 한 글자 — 문단 글이 여러 레코드로 나뉜 자리\n if (state.loneHighAt !== undefined) {\n const lo = data.length >= 2 ? data.readUInt16LE(0) : 0\n if (lo >= 0xdc00 && lo <= 0xdfff && state.loneHighAt === state.text.length - 1) {\n state.text = state.text.slice(0, -1) + String.fromCharCode(state.loneHigh!, lo)\n i = 2\n }\n state.loneHighAt = undefined\n }\n // 필드 범위는 state.text 기준 인덱스로 기록\n const base = state.text.length\n\n const resolveAt = (byteOffset: number, extended: boolean): void => {\n const ctrlId = data.readUInt32LE(byteOffset)\n const idx = extended ? state.ctrlIdx : -1\n const replacement = resolveControl?.(idx, ctrlId)\n if (replacement) result += replacement\n if (extended) state.ctrlIdx++\n }\n\n while (i + 1 < data.length) {\n const ch = data.readUInt16LE(i)\n if (state.scriptAt) setScript(ch >= 0x0020 ? state.scriptAt(wbase + i / 2) : null)\n i += 2\n\n switch (ch) {\n // ── char 타입 (2바이트만, 확장 데이터 없음) ──\n case CHAR_LINE: result += \"\\n\"; break\n case CHAR_SECTION_BREAK: { // 강제 줄 나눔(char 2바이트). 단, 뒤에 0x000b 수식 래퍼가 오는 경우만 확장 처리\n // 일부 HWP5 문서는 수식 placeholder를 0x000a + 0x000b + ctrlId + payload + 0x000b로 저장한다.\n if (i + 16 <= data.length && data.readUInt16LE(i) === 0x000b) {\n resolveAt(i + 2, true)\n i += 16\n break\n }\n // bare 0x000a = char 타입 줄바꿈(2바이트, 확장 데이터 없음) — 뒤 14바이트 소비 금지(줄마다 7글자 증발 버그).\n result += \"\\n\"\n break\n }\n case CHAR_PARA: break // 문단 끝\n // 하이픈(24)은 한컴이 그리지 않는다 — 한컴 PDF·rhwp export-hwpx 모두 \"60g/㎡\"(licbyl2 17855137,\n // 전 코퍼스 유일 출현). 실렌더가 진실이므로 방출하지 않는다 (v4.12.3, 종전 \"-\")\n case CHAR_HYPHEN: break\n case CHAR_NBSP: result += \"\\u00a0\"; break // 진짜 NBSP\n case CHAR_FIXED_WIDTH: result += \" \"; break // 고정폭 공백\n\n // ── inline 타입 (2바이트 + 14바이트 확장) ──\n // 확장 u16[7] 중 [2] 의 하위 바이트 = 채움 모양(0 없음·3 점선 …), 상위 = 탭 종류+1 (rhwp tab_extended 실측).\n // 채움 탭도 보통 탭 — 채움선은 글이 아니고 뒤 글(목차 쪽 번호 등)은 남긴다 (HWPX section-walker \"tab\" 과 같은 정책)\n // 본문 파서(tocLeaders)는 목차 항목 판정까지 채움 탭을 TOC_LEADER 표지로 받는다 (#121)\n case CHAR_TAB:\n result += state.tocLeaders && i + 6 <= data.length && (data.readUInt16LE(i + 4) & 0xff) !== 0 ? TOC_LEADER : \"\\t\"\n if (i + 14 <= data.length) i += 14\n break\n\n default:\n if (ch >= 0x0001 && ch <= 0x001f) {\n // rhwp 기준 3-카테고리 분류:\n // extended(1-3, 11-12, 14-18, 21-23) + inline(4-9, 19-20) → 14바이트 스킵\n // char(24-31) → 스킵 없음 (24,30,31은 switch에서 처리, 25-29는 예약 — 출력 없이 통과)\n const isExtended = isExtendedOnlyCtrlChar(ch)\n const isInline = (ch >= 4 && ch <= 9) || (ch >= 19 && ch <= 20)\n if ((isExtended || isInline) && i + 14 <= data.length) {\n if (ch === 0x0003) {\n // FIELD_BEGIN: anchor 시작 위치 + 컨트롤 인덱스 push\n state.fieldStack.push({ start: base + result.length, ctrlIdx: state.ctrlIdx })\n } else if (ch === 0x0004) {\n // FIELD_END: 스택 페어링 → anchor 범위 확정\n const open = state.fieldStack.pop()\n if (open) {\n state.fieldRanges.push({ start: open.start, end: base + result.length, ctrlIdx: open.ctrlIdx })\n }\n }\n resolveAt(i, isExtended)\n i += 14\n }\n } else if (ch >= 0x0020) {\n // HWP5 문자는 2바이트 WCHAR 라 한컴 Supplementary PUA-A(U+F0000대) 기호를 담을 수 없다 —\n // 한컴은 U+A000 + (code − 0xF0000) 으로 접어 저장한다(이 글자 음절 블록, 한국어 문서에는\n // 안 나온다). 결재란 \"(인)\"(F012B)이 A12B 로 들어 있는 법령 별지서식 실측 — rhwp export-hwpx\n // 도 같은 자리를 F012B→\"(인)\" 으로 복원한다. 펴서 mapPuaText(pua.ts) 표에 넘기고, 매핑 없는\n // 코드는 종전 PUA-A 정책(sanitizeText 제거)을 따른다 (v4.12.2)\n if (ch >= HANCOM_FOLDED_PUA_START && ch <= HANCOM_FOLDED_PUA_END) {\n result += String.fromCodePoint(ch + HANCOM_FOLDED_PUA_SHIFT)\n break\n }\n // UTF-16 surrogate pair 처리 (BMP 외 문자: 이모지, CJK 확장 등)\n if (ch >= 0xd800 && ch <= 0xdbff && i + 1 < data.length) {\n const lo = data.readUInt16LE(i)\n if (lo >= 0xdc00 && lo <= 0xdfff) {\n i += 2\n const codePoint = ((ch - 0xd800) << 10) + (lo - 0xdc00) + 0x10000\n result += String.fromCodePoint(codePoint)\n break\n }\n }\n // 짝 없는 서로게이트 반쪽 — 한글은 같은 자리를 □ 로 보여 주고 HWPX 로 저장할 때도 □ 를 쓴다. 반쪽을 그대로 내면\n // JSON 에 \\udfda 로 남고 UTF-8 로 쓰면 U+FFFD 가 된다 (rhwp cff474c8d #6873, 한글 HWPX 정본 대조). 레코드 끝의 상위 반쪽은\n // 자리를 남겨 다음 PARA_TEXT 레코드가 하위 반쪽으로 시작하면 한 글자로 되돌린다\n if (ch >= 0xd800 && ch <= 0xdfff) {\n result += \"\\u25a1\"\n if (ch <= 0xdbff && i >= data.length) { state.loneHigh = ch; state.loneHighAt = base + result.length - 1 }\n break\n }\n result += !state.dollarMark ? String.fromCharCode(ch) : ch === 0x24 ? LITERAL_DOLLAR_MARK : ch === 0x3c ? LITERAL_LT_MARK : String.fromCharCode(ch)\n }\n break\n }\n }\n\n if (state.scriptAt) setScript(null)\n state.wpos = wbase + Math.floor(data.length / 2)\n state.text += result\n}\n\n/** HWP5 EQEDIT(0x58) 레코드에서 한글 수식 스크립트 원문 추출 */\nexport function extractEquationText(data: Buffer): string | null {\n if (data.length < 6) return null\n\n const scriptLength = data.readUInt16LE(4)\n const scriptStart = 6\n const scriptEnd = scriptStart + scriptLength * 2\n if (scriptLength <= 0 || scriptEnd > data.length) return null\n\n const equation = data.subarray(scriptStart, scriptEnd).toString(\"utf16le\").replace(/\\0+/g, \"\").trim()\n return equation || null\n}\n","/**\n * HWPX·HWP5 목차 항목 — 줄마다 \"글 + 채움 탭 + 쪽 번호\"뿐인 문단. 채움 탭 뒤 글을 남기면서(4.17.0) 쪽 번호가 제목 글에 붙어\n * 본문 제목과 다른 제목으로 outline·breadcrumb 에 들어갔다(#121). 글은 그대로 두고 제목으로 올리지 않는다\n * (PDF table-roles TOC_BLOCKS 와 같은 규칙). 일정 줄 \"의견 조사 ····· 10~11월\" 은 쪽 번호가 아니라 목차가 아니다.\n */\n\nimport type { IRBlock } from \"./types.js\"\n\n/** 채움 탭 표지 — 문단 글을 다 모은 뒤 목차 판정을 하고 보통 탭으로 바꾼다 */\nexport const TOC_LEADER = \"\\x1F\"\nconst LEADERS = /\\x1F/g\n/** 목차 쪽 번호 — 아라비아·로마 숫자, 장-쪽(\"3-12\"), 가운데 쪽(\"- 5 -\") */\nconst PAGE_LABEL = /^(?:\\d{1,4}|[ivxlc]{1,7}|\\d{1,3}\\s*[-–]\\s*\\d{1,4}|[-–]\\s*\\d{1,4}\\s*[-–])$/i\n\n/** 제목 추정(개요 번호·글꼴 크기·\"제N장\")에서 뺄 목차 항목 블록 */\nexport const TOC_ENTRY_BLOCKS = new WeakSet<IRBlock>()\n\n/** 채움 탭 표지를 보통 탭으로 바꾸고, 문단이 목차 항목인지 돌려준다 (\\x1E 는 글자취급 표 경계) */\nexport function takeTocLeaders(text: string): { text: string; tocEntry: boolean } {\n if (!text.includes(TOC_LEADER)) return { text, tocEntry: false }\n const lines = text.replace(/\\x1E/g, \"\").split(\"\\n\").filter(l => l.trim())\n const tocEntry = lines.length > 0 && lines.every(l => {\n const at = l.lastIndexOf(TOC_LEADER)\n return at > 0 && /\\S/.test(l.slice(0, at).replace(LEADERS, \"\")) && PAGE_LABEL.test(l.slice(at + 1).trim())\n })\n return { text: text.replace(LEADERS, \"\\t\"), tocEntry }\n}\n","/**\n * HWP5 BinData 이미지 추출.\n *\n * - DocInfo BIN_DATA의 binDataId(1-based) → storage_id 매핑은 parser.ts(pictureToImageBlock)에서 수행\n * - 이 모듈은 BinData 스토리지 엔트리(\"BIN%04X.ext\" — storage_id는 16진!)를 storageId 키로 수집하고\n * 블록 트리(셀 내부 blocks 포함)의 image 블록과 매칭해 ExtractedImage로 변환한다.\n */\n\nimport { decompressStream } from \"./record.js\"\nimport type { LenientCfbContainer } from \"./cfb-lenient.js\"\nimport type { ExtractedImage, IRBlock, ParseWarning } from \"../types.js\"\n\n/** CFB FileIndex 엔트리 (cfb 모듈 호환 최소 형태) */\nexport interface BinCfbEntry { name?: string; content?: Buffer | Uint8Array }\n\n/** MIME 타입 매직바이트 판별 */\nexport function detectImageMime(data: Buffer | Uint8Array): string | null {\n if (data.length < 4) return null\n if (data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47) return \"image/png\"\n if (data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) return \"image/jpeg\"\n if (data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46) return \"image/gif\"\n if (data[0] === 0x42 && data[1] === 0x4d) return \"image/bmp\"\n if (data[0] === 0xd7 && data[1] === 0xcd && data[2] === 0xc6 && data[3] === 0x9a) return \"image/wmf\"\n // 배치 헤더 없는 표준 WMF — mtType 1(메모리)·2(디스크) + mtHeaderSize 9워드. 한컴 BinData 의 .wmf 는 대개 이 꼴\n // (법령 서식 BIN0001.wmf 실측 01 00 09 00 00 03). 종전엔 \"알 수 없는 형식\"이라 셀에 [이미지: …] 글이 박혔다\n if ((data[0] === 0x01 || data[0] === 0x02) && data[1] === 0x00 && data[2] === 0x09 && data[3] === 0x00) return \"image/wmf\"\n if (data[0] === 0x01 && data[1] === 0x00 && data[2] === 0x00 && data[3] === 0x00) return \"image/emf\"\n // TIFF — 리틀(II*\\0)·빅(MM\\0*) 엔디언 (행정업무편람 BIN016C.TIF 실측)\n if ((data[0] === 0x49 && data[1] === 0x49 && data[2] === 0x2a && data[3] === 0x00) || (data[0] === 0x4d && data[1] === 0x4d && data[2] === 0x00 && data[3] === 0x2a)) return \"image/tiff\"\n return null\n}\n\n/** MIME → 추출 파일 확장자 (HWPX images.ts mimeToExt 와 같은 표) */\nfunction mimeExt(mime: string): string {\n if (mime.includes(\"jpeg\")) return \"jpg\"\n if (mime.includes(\"png\")) return \"png\"\n if (mime.includes(\"gif\")) return \"gif\"\n if (mime.includes(\"bmp\")) return \"bmp\"\n if (mime.includes(\"tiff\")) return \"tif\"\n if (mime.includes(\"wmf\")) return \"wmf\"\n if (mime.includes(\"emf\")) return \"emf\"\n return \"bin\"\n}\n\n/**\n * BinData 페이로드 정규화 — 항목별 압축 플래그가 문서 압축 플래그와 다를 수 있으므로\n * 매직바이트가 안 보이면 압축 해제를 시도하고, 실패하면 원본 유지.\n */\nfunction normalizeBinPayload(data: Buffer): Buffer {\n if (detectImageMime(data)) return data\n try {\n const inflated = decompressStream(data)\n if (inflated.length > 0) return inflated\n } catch { /* 비압축 데이터 */ }\n return data\n}\n\n/** BinData 스토리지 엔트리명 — \"BIN%04X.ext\" (storage_id는 16진!) */\nconst BIN_ENTRY_RE = /(?:^|\\/)BIN([0-9A-Fa-f]{4,8})(?:\\.[^./\\\\]*)?$/\n\n/** 블록 트리 재귀 깊이 상한 — hwpx collectImageBlocks(MAX_XML_DEPTH)와 동일 값.\n * 손상/악성 문서의 자기참조·초심층 트리 스택 오버플로 방지 */\nconst MAX_BLOCK_DEPTH = 200\n\n/** 블록 트리(셀 내부 blocks 포함)에서 image 블록 수집.\n * hwpx 쪽과 안전장치 합집합: depth 가드 + children 순회 (둘 다 수행) */\nfunction collectImageBlocks(blocks: IRBlock[], out: IRBlock[], depth = 0): void {\n if (depth > MAX_BLOCK_DEPTH) return\n for (const b of blocks) {\n if (b.type === \"image\") out.push(b)\n if (b.table) {\n for (const row of b.table.cells) {\n for (const cell of row) {\n if (cell.blocks) collectImageBlocks(cell.blocks, out, depth + 1)\n }\n }\n }\n if (b.children) collectImageBlocks(b.children, out, depth + 1)\n }\n}\n\n/**\n * 이미지 sentinel(\"![image](hwp5bin:ID)\")을 추출된 파일명으로 치환 — 표 셀 텍스트, 그리고 머리말·각주·캡션처럼\n * 표를 평문으로 편 글(ir-assemble tableFlatText)에도 남으므로 블록 트리 전체(문단 글·각주·캡션·셀)를 훑는다.\n * 파일명이 없으면(BinData 없음·페이지 필터로 스윕 안 함) \"[이미지]\"\n */\nconst IMAGE_SENTINEL_RE = /!\\[image\\]\\(hwp5bin:(\\d+)\\)/g\nfunction resolveImageSentinels(blocks: IRBlock[], renamed: Map<number, string>, depth = 0): void {\n if (depth > MAX_BLOCK_DEPTH) return\n const fix = (s: string): string => s.includes(\"hwp5bin:\")\n ? s.replace(IMAGE_SENTINEL_RE, (_m, idStr: string) => {\n const filename = renamed.get(Number(idStr))\n return filename ? `![image](${filename})` : \"[이미지]\"\n })\n : s\n for (const b of blocks) {\n if (b.text) b.text = fix(b.text)\n if (b.footnoteText) b.footnoteText = fix(b.footnoteText)\n if (b.table) {\n if (b.table.caption) b.table.caption = fix(b.table.caption)\n for (const row of b.table.cells) {\n for (const cell of row) {\n cell.text = fix(cell.text)\n if (cell.blocks) resolveImageSentinels(cell.blocks, renamed, depth + 1)\n }\n }\n }\n if (b.children) resolveImageSentinels(b.children, renamed, depth + 1)\n }\n}\n\n/** binDataMap 기반 이미지 블록 해결 — strict/lenient 공용 */\nfunction resolveImageBlocks(\n binDataMap: Map<number, { data: Buffer; name: string }>,\n blocks: IRBlock[],\n warnings: ParseWarning[],\n sweepUnreferenced?: boolean,\n): ExtractedImage[] {\n const imageBlocks: IRBlock[] = []\n collectImageBlocks(blocks, imageBlocks)\n\n const images: ExtractedImage[] = []\n const renamed = new Map<number, string>()\n // 같은 BinData를 참조하는 개체가 수천 개일 수 있다(도형 반복 등) — storageId당\n // 1회만 변환·추출하고 데이터 버퍼를 공유한다 (블록마다 복사하면 메모리 폭발)\n const resolved = new Map<number, { filename: string; data: Uint8Array; mime: string } | null>()\n let imageIndex = 0\n\n for (const block of imageBlocks) {\n if (!block.text) continue\n const storageId = parseInt(block.text, 10)\n if (isNaN(storageId)) continue\n\n let img = resolved.get(storageId)\n if (img === undefined) {\n const bin = binDataMap.get(storageId)\n if (!bin) {\n warnings.push({ page: block.pageNumber, message: `BinData ${storageId} 없음`, code: \"SKIPPED_IMAGE\" })\n resolved.set(storageId, null)\n } else {\n const mime = detectImageMime(bin.data)\n if (!mime) {\n warnings.push({ page: block.pageNumber, message: `BinData ${storageId}: 알 수 없는 이미지 형식`, code: \"SKIPPED_IMAGE\" })\n resolved.set(storageId, null)\n } else {\n imageIndex++\n const ext = mimeExt(mime)\n img = { filename: `image_${String(imageIndex).padStart(3, \"0\")}.${ext}`, data: new Uint8Array(bin.data), mime }\n resolved.set(storageId, img)\n images.push({ filename: img.filename, data: img.data, mimeType: img.mime, source: bin.name })\n renamed.set(storageId, img.filename)\n }\n }\n img = resolved.get(storageId)\n }\n\n if (!img) {\n const bin = binDataMap.get(storageId)\n block.type = \"paragraph\"\n block.text = bin ? `[이미지: ${bin.name}]` : `[이미지: BinData ${storageId}]`\n continue\n }\n block.text = img.filename\n block.imageData = { data: img.data, mimeType: img.mime, filename: binDataMap.get(storageId)!.name }\n }\n\n // 본문 미참조 BinData 이미지 스윕 — pic 컨트롤이 닿지 않는 이미지(셀 배경 등)를\n // 문서 끝에 image 블록으로 보강한다. 이미지가 아닌 스트림(OLE 등)은 건너뛴다.\n if (sweepUnreferenced) {\n for (const [storageId, bin] of [...binDataMap.entries()].sort((a, b) => a[0] - b[0])) {\n if (resolved.has(storageId)) continue\n const mime = detectImageMime(bin.data)\n if (!mime) continue\n imageIndex++\n const ext = mimeExt(mime)\n const filename = `image_${String(imageIndex).padStart(3, \"0\")}.${ext}`\n const data = new Uint8Array(bin.data)\n images.push({ filename, data, mimeType: mime, source: bin.name })\n blocks.push({ type: \"image\", text: filename, imageData: { data, mimeType: mime, filename: bin.name } })\n // 머리말·각주 안 표를 편 글의 sentinel 도 이 파일을 가리킨다 (본문 image 블록이 없던 그림)\n renamed.set(storageId, filename)\n }\n }\n\n resolveImageSentinels(blocks, renamed)\n return images\n}\n\n/** BinData 스토리지의 모든 파일을 FileIndex 순회로 수집 — 엔트리명은 \"BIN%04X.ext\" 16진.\n * 파서(이미지 추출)와 렌더 어댑터(#75 HWP5)가 공유한다 */\nexport function collectHwp5BinData(fileIndex: BinCfbEntry[] | undefined, readStream?: (path: string) => Buffer | null): Map<number, { data: Buffer; name: string }> {\n const binDataMap = new Map<number, { data: Buffer; name: string }>()\n if (fileIndex) {\n for (const entry of fileIndex) {\n if (!entry?.name || !entry.content) continue\n const match = entry.name.match(BIN_ENTRY_RE)\n if (!match) continue\n const idx = parseInt(match[1], 16)\n const raw = readStream ? readStream(`/BinData/${entry.name}`) : Buffer.from(entry.content)\n if (!raw) continue\n const data = normalizeBinPayload(raw)\n binDataMap.set(idx, { data, name: entry.name })\n }\n }\n return binDataMap\n}\n\n/** Lenient CFB: BinData 엔트리 수집 — 엔트리명 \"BIN%04X.ext\" 16진 */\nexport function collectHwp5BinDataLenient(lcfb: LenientCfbContainer, readStream = lcfb.findStream): Map<number, { data: Buffer; name: string }> {\n const binDataMap = new Map<number, { data: Buffer; name: string }>()\n const binRe = /^BIN([0-9A-Fa-f]{4,8})(?:\\.|$)/\n for (const e of lcfb.entries()) {\n const match = e.name.match(binRe)\n if (!match) continue\n const idx = parseInt(match[1], 16)\n // 경로로 찾는다 — 맨 이름은 같은 이름이 둘이면 고르지 않는다 (cfb-lenient findEntryByPath)\n const raw = readStream(`/BinData/${e.name}`) ?? readStream(e.name)\n if (!raw) continue\n binDataMap.set(idx, { data: normalizeBinPayload(raw), name: e.name })\n }\n return binDataMap\n}\n\n/** OLE2 BinData 스토리지(FileIndex)에서 이미지 추출, blocks의 image 블록과 매핑 */\nexport function extractHwp5Images(\n fileIndex: BinCfbEntry[] | undefined,\n blocks: IRBlock[],\n warnings: ParseWarning[],\n sweepUnreferenced?: boolean,\n readStream?: (path: string) => Buffer | null,\n): ExtractedImage[] {\n const binDataMap = collectHwp5BinData(fileIndex, readStream)\n\n if (binDataMap.size === 0) {\n // 이미지 블록이 있는데 BinData가 없으면 sentinel 정리만 수행\n resolveImageSentinels(blocks, new Map())\n return []\n }\n return resolveImageBlocks(binDataMap, blocks, warnings, sweepUnreferenced)\n}\n\n/** Lenient CFB: BinData 이미지 추출 */\nexport function extractHwp5ImagesLenient(\n lcfb: LenientCfbContainer,\n blocks: IRBlock[],\n warnings: ParseWarning[],\n sweepUnreferenced?: boolean,\n readStream?: (path: string) => Buffer | null,\n): ExtractedImage[] {\n const binDataMap = collectHwp5BinDataLenient(lcfb, readStream)\n if (binDataMap.size === 0) {\n resolveImageSentinels(blocks, new Map())\n return []\n }\n return resolveImageBlocks(binDataMap, blocks, warnings, sweepUnreferenced)\n}\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 left: number\n right: number\n child: 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 headerFatSectorCount = data.readUInt32LE(44)\n if (headerFatSectorCount > 10000) throw new Error(\"FAT 섹터 수가 너무 많습니다: \" + headerFatSectorCount)\n // 실 파일 크기 sanity — FAT 엔트리는 파일 내 실존 섹터 수를 넘을 수 없다\n // (헤더 카운트 뻥튀기로 인한 fatTable 할당 증폭 방지: ~130KB 파일이 655MB 할당하던 버그)\n const maxRealSectors = Math.ceil(Math.max(0, data.length - 512) / sectorSize)\n const maxFatSectors = Math.max(1, Math.ceil(maxRealSectors / (sectorSize / 4)))\n const fatSectorCount = Math.min(headerFatSectorCount, maxFatSectors)\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 if (buf.length < sectorSize) break // 파일 범위 밖 DIFAT 섹터(빈 버퍼) — readUInt32LE RangeError 방지, lenient 중단\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, left: FREE_SECT, right: FREE_SECT, child: FREE_SECT })\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, left: dirData.readUInt32LE(offset + 68), right: dirData.readUInt32LE(offset + 72), child: dirData.readUInt32LE(offset + 76) })\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 /** 스토리지의 자식 가운데 이름이 같은 엔트리 — 형제 트리를 따라간다 (순환·범위 밖 번호는 건너뛴다) */\n function findChild(parent: DirEntry, name: string): DirEntry | null {\n const pending = [parent.child]\n const visited = new Set<number>()\n while (pending.length) {\n const id = pending.pop()!\n if (id === END_OF_CHAIN || id === FREE_SECT || id >= dirEntries.length || visited.has(id)) continue\n visited.add(id)\n const e = dirEntries[id]\n if (e.name === name) return e\n pending.push(e.left, e.right)\n }\n return null\n }\n\n // 루트에서 경로 조각마다 스토리지를 따라 내려간다. 이름만 비교하면 BodyText/Section0 과 ViewText/Section0 처럼 이름이 같은\n // 스트림 가운데 디렉터리에 먼저 나오는 쪽이 잡혀, 비배포 문서에서 암호화된 ViewText 를 본문으로 읽다 글이 통째로 빠졌다\n // (rhwp ce6bca037·4b1f1179b). 트리가 깨진 입력은 이름으로 찾되, 같은 이름이 둘 이상이면 어느 쪽인지 모르니 고르지 않는다\n function findEntryByPath(path: string): DirEntry | null {\n const parts = path.replace(/^\\//, \"\").split(\"/\").filter(Boolean)\n const last = parts.pop()\n if (last === undefined) return null\n const root = dirEntries.find(e => e.type === 5)\n if (root) {\n let current: DirEntry | null = root\n for (const part of parts) {\n const next: DirEntry | null = current ? findChild(current, part) : null\n current = next && next.type === 1 ? next : null\n }\n const hit = current ? findChild(current, last) : null\n if (hit && (hit.type === 1 || hit.type === 2 || hit.type === 5)) return hit\n }\n const named = dirEntries.filter(e => e.name === last && (e.type === 1 || e.type === 2 || e.type === 5))\n return named.length === 1 ? named[0] : 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","/**\n * 비밀번호로 보호된 HWP5 문서 복호화 (EncryptVersion 4).\n *\n * 배포용(ViewText) 문서와는 완전히 다른 방식이다 — 이쪽은 사용자가 지정한 열기 암호로\n * `DocInfo`·`BodyText/Section*`·`BinData/*`·`Scripts/*` 스트림을 각각 복호한다.\n *\n * 키 = SHA-1(인터리브(비밀번호))[0..16]\n * 본문 = AES-128 **비트 단위 CFB**(CFB-1) 복호 — IV 0, 레지스터는 스트림 전역\n *\n * CFB-1 이라 16바이트마다 AES 블록 연산을 128회 수행한다(바이트당 8회). 표준 AES 대비\n * 128배 연산량이라, 블록 암호기 객체를 스트림당 하나만 만들어 재사용한다.\n *\n * 알고리즘 참조: rhwp (MIT) src/parser/crypto.rs — 원 출처는 volexity/hwp-extract\n * (BSD-3-Clause) 의 encrypt.py.\n */\n\nimport { createCipheriv, createHash } from \"crypto\"\nimport { KordocError } from \"../utils.js\"\n\n/** FileHeader 의 EncryptVersion(u32 LE) 위치 */\nconst ENCRYPT_VERSION_OFFSET = 44\n/** 우리가 아는 유일한 비밀번호 암호화 버전 */\nconst SUPPORTED_ENCRYPT_VERSION = 4\n/** 키 유도 인터리브의 첫 \"이전 바이트\" 시드 */\nconst KEY_SEED = 236\n\n/** FileHeader 에서 EncryptVersion 을 읽는다. 짧으면 0. */\nexport function readEncryptVersion(fileHeader: Uint8Array): number {\n if (fileHeader.length < ENCRYPT_VERSION_OFFSET + 4) return 0\n return (\n (fileHeader[ENCRYPT_VERSION_OFFSET] |\n (fileHeader[ENCRYPT_VERSION_OFFSET + 1] << 8) |\n (fileHeader[ENCRYPT_VERSION_OFFSET + 2] << 16) |\n (fileHeader[ENCRYPT_VERSION_OFFSET + 3] << 24)) >>>\n 0\n )\n}\n\n/**\n * 비밀번호 → AES-128 키 16바이트.\n *\n * 각 비밀번호 바이트 앞에 \"이전 바이트를 1비트 왼쪽 회전한 값\"을 끼워 넣어 길이 2배의\n * 버퍼를 만들고, 그 SHA-1 다이제스트 앞 16바이트를 키로 쓴다. 첫 바이트의 \"이전 값\"은\n * 시드 상수 236.\n */\nexport function derivePasswordKey(password: Uint8Array): Buffer {\n const buf = Buffer.alloc(password.length * 2)\n for (let i = 0; i < password.length; i++) {\n const prev = i === 0 ? KEY_SEED : password[i - 1]\n buf[i * 2] = ((prev << 1) | (prev >> 7)) & 0xff // ROL 1\n buf[i * 2 + 1] = password[i]\n }\n return createHash(\"sha1\").update(buf).digest().subarray(0, 16)\n}\n\n/** 16바이트 배수로 패드. 이미 배수면 그대로(참조 구현과 동일). */\nfunction padToBlock(data: Uint8Array): Buffer {\n const rem = data.length % 16\n if (rem === 0) return Buffer.from(data)\n const amount = 16 - rem\n return Buffer.concat([Buffer.from(data), Buffer.alloc(amount, amount)])\n}\n\n/** 128비트 레지스터를 1비트 왼쪽 시프트하고 최하위 비트에 feedback 을 넣는다 */\nfunction shiftRegister(reg: Uint8Array, feedbackBit: number): void {\n for (let i = 0; i < 15; i++) {\n reg[i] = ((reg[i] << 1) | (reg[i + 1] >> 7)) & 0xff\n }\n reg[15] = ((reg[15] << 1) | (feedbackBit & 1)) & 0xff\n}\n\n/**\n * AES-128 비트 단위 CFB 복호.\n *\n * 복호에도 AES 는 **암호화 방향**을 쓴다(CFB 의 정의). 레지스터는 블록마다 리셋하지 않고\n * 스트림 전체에서 이어진다. IV 는 0.\n */\nfunction decryptAesCfb1(data: Buffer, key: Buffer): Buffer {\n // ECB 는 블록 간 체이닝이 없어 Cipher 객체 하나를 계속 재사용해도 안전하다.\n // 매 블록 새로 만들면 128배 호출이라 감당이 안 된다.\n const cipher = createCipheriv(\"aes-128-ecb\", key, null)\n cipher.setAutoPadding(false)\n\n const register = new Uint8Array(16)\n const registerBuf = Buffer.alloc(16)\n const out = Buffer.alloc(data.length)\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const len = Math.min(16, data.length - offset)\n const chunk = Buffer.alloc(16)\n data.copy(chunk, 0, offset, offset + len)\n\n for (let i = 0; i < 128; i++) {\n registerBuf.set(register)\n const keystreamMsb = cipher.update(registerBuf)[0]\n\n const byteIdx = i >> 3\n const bitInByte = i & 7\n // 되먹임은 **암호문** 비트 — XOR 로 평문이 되기 전 값을 넣어야 한다\n const cipherBit = (chunk[byteIdx] >> (7 - bitInByte)) & 1\n shiftRegister(register, cipherBit)\n\n chunk[byteIdx] ^= (keystreamMsb & 0x80) >> bitInByte\n }\n\n chunk.copy(out, offset, 0, len)\n }\n\n return out\n}\n\n/**\n * 암호 스트림 복호 (압축 해제는 하지 않음).\n * CFB 는 길이를 보존하므로 정렬용으로 덧붙인 패딩은 결과에서 잘라낸다.\n */\nexport function decryptPasswordStream(raw: Uint8Array, password: string): Buffer {\n const key = derivePasswordKey(Buffer.from(password, \"utf8\"))\n return decryptAesCfb1(padToBlock(raw), key).subarray(0, raw.length)\n}\n\n/** DocInfo 선두에 반드시 오는 레코드 (HWPTAG_BEGIN + 0 / + 1) */\nconst TAG_DOCUMENT_PROPERTIES = 0x0010\nconst TAG_ID_MAPPINGS = 0x0011\n\n/**\n * 복호한 DocInfo 가 실제 HWP 문서인지 확인한다.\n *\n * 비밀번호가 틀리면 복호 결과가 난수인데, 파서의 DocInfo·섹션 경로는 실패를 경고로\n * 흡수하도록 되어 있어 **\"성공했는데 내용이 쓰레기\"** 인 결과가 나온다. 압축 문서는\n * inflate 가 곧 무결성 검사지만 비압축 문서는 그것도 없으므로, DocInfo 가 반드시\n * 갖는 선두 레코드 두 종으로 확인한다.\n *\n * @param readRecords 레코드 리더 (순환 import 회피용 주입)\n */\nexport function assertDecryptedDocInfo(\n data: Buffer,\n readRecords: (buf: Buffer) => Array<{ tagId: number }>,\n): void {\n const wrong = () =>\n new KordocError(\"비밀번호가 일치하지 않거나 암호화 데이터가 손상되었습니다.\")\n let records: Array<{ tagId: number }>\n try {\n records = readRecords(data)\n } catch {\n throw wrong()\n }\n if (records[0]?.tagId !== TAG_DOCUMENT_PROPERTIES) throw wrong()\n if (!records.some(r => r.tagId === TAG_ID_MAPPINGS)) throw wrong()\n}\n\n/** EncryptVersion 을 검사하고 미지원이면 던진다 */\nexport function assertSupportedEncryptVersion(version: number): void {\n if (version !== SUPPORTED_ENCRYPT_VERSION) {\n // 구버전(1~3)을 같은 알고리즘으로 풀면 결과가 쓰레기인데도 \"비밀번호 오답\"으로\n // 보고돼 사용자가 암호를 계속 다시 입력하게 된다 — 방식이 다르다고 분명히 알린다.\n throw new KordocError(\n `지원하지 않는 HWP 암호화 버전입니다 (EncryptVersion=${version}, 지원: ${SUPPORTED_ENCRYPT_VERSION})`,\n )\n }\n}\n","/**\n * HWP 5.x 실제 페이지 경계 복원 (#66) — PARA_LINE_SEG(0x45) 조판 캐시로\n * 섹션 내 top-level 문단별 실제 페이지 배정을 계산한다.\n *\n * 알고리즘은 hwpx/page-boundary.ts와 동일한 신호 4종 (그쪽 헤더 주석 참조).\n * HWP5 매핑 (bench/corpus/pairs hwp↔hwpx 10쌍 전수 일치로 검증):\n * - lineseg → PARA_LINE_SEG 36바이트 엔트리 (v=+4 i32, h=+24 i32; 태그 +32의\n * bit0 \"페이지 첫 줄\"은 저장본에서 항상 0 — 못 씀)\n * - pageBreak → PARA_HEADER 데이터 offset 11(문단 나눔 종류)의 bit2(쪽 나누기)\n * - top 문단 → PARA_HEADER level 0, 그 lineseg는 level 1\n * - 표 셀 → CTRL_HEADER('tbl ') level 1 → LIST_HEADER level 2(셀 주소\n * col u16@8·row u16@10) → 셀 직접 문단은 같은 level 2, lineseg level 3\n * (중첩 표는 level 4/5라 자연 배제)\n */\n\nimport type { HwpRecord } from \"./record.js\"\nimport { TAG_PARA_HEADER, TAG_PARA_LINE_SEG, TAG_CTRL_HEADER, TAG_LIST_HEADER } from \"./record.js\"\n\n/** 이 값 이상에서 시작하는 역행은 페이지 상단 리셋이 아니다 (신호 4 판별, HWPUNIT) */\nconst MIDPAGE_V = 2000\n\n/** \"tbl \" 컨트롤 ID — LE 저장(\" lbt\")을 readUInt32LE로 읽은 BE 상수 (parser.ts CTRL_TBL과 동일) */\nconst CTRL_ID_TBL = 0x74626c20\n\n/** 섹션 페이지 감지 결과 */\nexport interface Hwp5SectionPageDetect {\n /** top-level 문단 순번(등장 순서) → 섹션 내 0-based 페이지 (문단 시작 위치 기준) */\n pageAtPara: number[]\n /** 섹션이 차지하는 페이지 수 (≥1) */\n pages: number\n /** 조판 캐시 신뢰 가능 여부 — top-level 문단 전부가 LINE_SEG를 가질 때만 true */\n usable: boolean\n}\n\n/** 표 컨트롤 범위의 내부 페이지 나눔 수 — 행별 max(셀 직접 문단 흐름 리셋) 합산 */\nfunction tableIntraBreaks(records: HwpRecord[], start: number, end: number): number {\n const rowMax = new Map<number, number>()\n let curRow = -1\n let prevV = Number.NEGATIVE_INFINITY\n let resets = 0\n const flush = () => {\n if (curRow >= 0) rowMax.set(curRow, Math.max(rowMax.get(curRow) ?? 0, resets))\n resets = 0\n prevV = Number.NEGATIVE_INFINITY\n }\n for (let i = start; i < end; i++) {\n const rec = records[i]\n if (rec.tagId === TAG_LIST_HEADER && rec.level === 2 && rec.data.length >= 12) {\n flush()\n curRow = rec.data.readUInt16LE(10)\n } else if (rec.tagId === TAG_PARA_LINE_SEG && rec.level === 3 && curRow >= 0) {\n for (let off = 0; off + 36 <= rec.data.length; off += 36) {\n const v = rec.data.readInt32LE(off + 4)\n if (v < prevV) resets++\n prevV = v\n }\n }\n }\n flush()\n let sum = 0\n for (const v of rowMax.values()) sum += v\n return sum\n}\n\n/**\n * 섹션 레코드에서 top-level 문단별 페이지 배정을 계산한다.\n * 반환 pageAtPara의 인덱스는 parseParagraphList가 만나는 top-level 문단 순서와 같다.\n */\nexport function detectHwp5SectionPages(records: HwpRecord[]): Hwp5SectionPageDetect {\n const pageAtPara: number[] = []\n let cur = 0\n let prevV = Number.NEGATIVE_INFINITY\n let prevH = Number.NEGATIVE_INFINITY\n let first = true\n let suppressMidReset = false\n let parasWithSegs = 0\n\n let i = 0\n while (i < records.length) {\n const rec = records[i]\n if (rec.tagId !== TAG_PARA_HEADER || rec.level !== 0) {\n i++\n continue\n }\n let j = i + 1\n while (j < records.length && records[j].level > 0) j++\n\n // 문단 나눔 종류(offset 11): bit0 구역, bit1 다단, bit2 쪽, bit3 단\n const divideSort = rec.data.length >= 12 ? rec.data.readUInt8(11) : 0\n const explicit = (divideSort & 0x04) !== 0\n let brokeByExplicit = false\n if (explicit && !first) {\n cur++\n brokeByExplicit = true\n suppressMidReset = false\n }\n\n let paraFirst = true\n let startPage = cur\n let hadSegs = false\n let intraAdd = 0\n for (let k = i + 1; k < j; k++) {\n const r = records[k]\n if (r.tagId === TAG_PARA_LINE_SEG && r.level === 1) {\n hadSegs = true\n for (let off = 0; off + 36 <= r.data.length; off += 36) {\n const v = r.data.readInt32LE(off + 4)\n const h = r.data.readInt32LE(off + 24)\n // HWPX 쪽 다단 예외(colCount>1일 때 h 우측 점프 배제)는 여기선 미적용 —\n // pairs 10쌍에서 HWPX와 결과 동일 검증. 다단 HWP5에서 어긋나면 그쪽 규칙 이식\n const brk = v < prevV || (paraFirst && v === prevV && h <= prevH)\n if (brk && !(paraFirst && brokeByExplicit)) {\n if (paraFirst && suppressMidReset && v >= MIDPAGE_V) {\n // 신호 4: 분할 표 꼬리 아래 재개 — 같은 경계\n } else {\n cur++\n }\n }\n if (paraFirst) {\n suppressMidReset = false\n startPage = cur\n }\n paraFirst = false\n prevV = v\n prevH = h\n }\n }\n // 신호 3: top-level 표(CTRL_HEADER 'tbl ', level 1)의 내부 분할\n if (r.tagId === TAG_CTRL_HEADER && r.level === 1 && r.data.length >= 4\n && r.data.readUInt32LE(0) === CTRL_ID_TBL) {\n let ce = k + 1\n while (ce < j && records[ce].level > 1) ce++\n intraAdd += tableIntraBreaks(records, k + 1, ce)\n k = ce - 1\n }\n }\n if (hadSegs) parasWithSegs++\n pageAtPara.push(startPage)\n first = false\n if (intraAdd > 0) {\n cur += intraAdd\n suppressMidReset = true\n }\n i = j\n }\n\n return {\n pageAtPara,\n pages: cur + 1,\n usable: pageAtPara.length > 0 && parasWithSegs === pageAtPara.length,\n }\n}\n","/**\n * HWP5 표 순번 (#75 Task 7 · #76 조인 키) — 섹션 레코드에서 표 컨트롤(CTRL_HEADER 'tbl ')을 문서(레코드) 순서로\n * 번호 매긴다. 파서(IRTable.sourceId)와 렌더 어댑터(RenderRegion.sourceId)가 **같은 프리패스**로 키를 얻어야\n * 중첩 표·머리말 안 표·깊이 상한으로 한쪽만 건너뛰는 개체가 있어도 번호가 어긋나지 않는다.\n * HWPX 의 `hp:tbl id` 에 해당하며, 형식은 `t{전역 1-based 순번}` (여러 구역은 base 누적).\n */\n\nimport { TAG_CTRL_HEADER, type HwpRecord } from \"./record.js\"\n\n/** \"tbl \" — LE 저장(\" lbt\")을 readUInt32LE 로 읽은 BE 상수 (parser.ts CTRL_TBL 과 동일) */\nconst CTRL_ID_TBL = 0x74626c20\n\nfunction swap32(id: number): number {\n return (((id & 0xff) << 24) | (((id >>> 8) & 0xff) << 16) | (((id >>> 16) & 0xff) << 8) | ((id >>> 24) & 0xff)) >>> 0\n}\n\nexport function isHwp5TableCtrl(rec: HwpRecord): boolean {\n if (rec.tagId !== TAG_CTRL_HEADER || rec.data.length < 4) return false\n const id = rec.data.readUInt32LE(0)\n return id === CTRL_ID_TBL || swap32(id) === CTRL_ID_TBL\n}\n\n/** 레코드 인덱스(CTRL_HEADER 위치) → sourceId. count 는 이 섹션의 표 수(다음 섹션 base 로 누적) */\nexport function indexHwp5Tables(records: HwpRecord[], base = 0): { ids: Map<number, string>; count: number } {\n const ids = new Map<number, string>()\n let n = 0\n for (let i = 0; i < records.length; i++) {\n if (isHwp5TableCtrl(records[i])) { n++; ids.set(i, `t${base + n}`) }\n }\n return { ids, count: n }\n}\n","/**\n * 번호 시퀀스 포맷터 공용 엔진 — hwpx(gongmun/para-heading)와 hwp5(numbering)가 공유.\n *\n * 정본 규칙은 공문서 생성기(구 gongmun.ts) 기준:\n * - 원숫자는 50까지(① ~ ㊿), 초과는 순환 대신 \"(51)\" 괄호수\n * - 원문자 가나다는 14자(㉮~㉻), 초과는 가나다 서수로 폴백\n * 파서 자동번호 폴백과 같은 규칙이라 왕복 시 마커가 어긋나지 않는다 (v4.0.4).\n */\n\n// 가나다 초성 14자(쌍자음 제외) — 0xAC00 음절 조합용 초성 인덱스\nconst HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18]\n// 단모음 순 중성 인덱스: ㅏ ㅓ ㅗ ㅜ ㅡ ㅣ (편람: 가→…→하→거→…→허→고→…)\nconst HANGUL_MEDIALS = [0, 4, 8, 13, 18, 20]\n\n/** 0-based n → 가, 나, 다, … 하, 거, 너, … (단모음 연속) */\nexport function hangulOrdinal(n: number): string {\n const cols = HANGUL_INITIALS.length // 14\n const vowel = HANGUL_MEDIALS[Math.min(Math.floor(n / cols), HANGUL_MEDIALS.length - 1)]\n const init = HANGUL_INITIALS[n % cols]\n return String.fromCodePoint(0xac00 + init * 588 + vowel * 28)\n}\n\n/**\n * 0-based n → ① ② … ⑳ ㉑ … ㊿ (U+2460~ / U+3251~ / U+32B1~, 50까지).\n * 초과(실무 도달 불가)는 순환 대신 '(51)' 괄호수 — 파서 자동번호 폴백\n * (para-heading CIRCLED_DIGIT)과 같은 규칙이라 왕복 시 마커가 어긋나지 않는다 (v4.0.4)\n */\nexport function circledNumber(n: number): string {\n if (n < 20) return String.fromCodePoint(0x2460 + n) // ①~⑳\n if (n < 35) return String.fromCodePoint(0x3251 + (n - 20)) // ㉑~㉟\n if (n < 50) return String.fromCodePoint(0x32b1 + (n - 35)) // ㊱~㊿\n return `(${n + 1})`\n}\n\n/**\n * 0-based n → ㉮ ㉯ ㉰ … ㉻ (U+326E~, 14자). 15번째+는 순환 대신 가나다 서수 —\n * 파서 자동번호 폴백(para-heading CIRCLED_HANGUL_SYLLABLE)과 동일 규칙 (v4.0.4).\n * 순환(mod 14)이면 15번째가 ㉮로 되돌아가 형제 순번 재유도가 모호해진다\n */\nexport function circledHangul(n: number): string {\n return n < 14 ? String.fromCodePoint(0x326e + n) : hangulOrdinal(n)\n}\n\nconst AMOUNT_DIGITS = \"영일이삼사오육칠팔구\"\nconst AMOUNT_SMALL = [\"\", \"십\", \"백\", \"천\"]\nconst AMOUNT_BIG = [\"\", \"만\", \"억\", \"조\", \"경\", \"해\"]\n\n/**\n * 금액 숫자 → 한글 병기값 (규정 시행규칙 제2조: 아라비아 숫자 다음 괄호 안에 한글).\n * 4자리 그룹 × 만·억·조·경·해, 그룹 안 천·백·십, 숫자 영일이삼사오육칠팔구.\n * 공문 관행대로 `일십`·`일백`·`일천`·`일만`의 \"일\"을 생략하지 않는다 — 금113,560원(금일십일만삼천오백육십원).\n * 값이 0인 그룹은 단위까지 생략, 0은 \"영\". 쉼표·공백 등 숫자 아닌 문자는 무시.\n * 반환은 순수 한글 — 접두·접미(금·원·원정)는 호출자가 붙인다.\n */\nexport function hangulAmount(n: number | string): string {\n const digits = String(n).replace(/\\D/g, \"\").replace(/^0+(?=\\d)/, \"\")\n if (!digits || digits === \"0\") return \"영\"\n const groups: string[] = []\n for (let end = digits.length; end > 0; end -= 4) groups.unshift(digits.slice(Math.max(0, end - 4), end))\n let out = \"\"\n groups.forEach((g, i) => {\n let part = \"\"\n const padded = g.padStart(4, \"0\")\n for (let k = 0; k < 4; k++) {\n const d = Number(padded[k])\n if (d) part += AMOUNT_DIGITS[d] + AMOUNT_SMALL[3 - k]\n }\n if (part) out += part + (AMOUNT_BIG[groups.length - 1 - i] ?? \"\")\n })\n return out\n}\n\n/** 1-based n → 로마 숫자 (범위 밖은 아라비아 숫자 폴백) */\nexport function romanNumeral(n: number, upper: boolean): string {\n if (n <= 0 || n > 3999) return String(n)\n const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]\n const symbols = upper\n ? [\"M\", \"CM\", \"D\", \"CD\", \"C\", \"XC\", \"L\", \"XL\", \"X\", \"IX\", \"V\", \"IV\", \"I\"]\n : [\"m\", \"cm\", \"d\", \"cd\", \"c\", \"xc\", \"l\", \"xl\", \"x\", \"ix\", \"v\", \"iv\", \"i\"]\n let result = \"\"\n let num = n\n for (let i = 0; i < values.length; i++) {\n while (num >= values[i]) { result += symbols[i]; num -= values[i] }\n }\n return result\n}\n","/**\n * HWP5 문단번호/글머리표 — 7수준 카운터 상태기계 + ^N 형식 치환 + 번호 포맷터.\n *\n * 출처: rhwp (MIT) renderer/layout.rs NumberingState::advance,\n * renderer/layout/utils.rs expand_numbering_format, renderer/mod.rs format_number.\n */\n\nimport type { HwpNumbering } from \"./record.js\"\nimport { hangulOrdinal, circledNumber, circledHangul, romanNumeral } from \"../shared/numbering.js\"\n\n// ─── 7수준 카운터 상태기계 ───────────────────────────\n\n/**\n * 문단 번호 카운터 (수준별 1-7).\n * HWP 동작:\n * - 같은 numberingId 연속 = \"앞 번호 이어\" (카운터 유지)\n * - 다른 id (히스토리 있음) = \"이전 번호 이어\" (히스토리 복원)\n * - 다른 id (히스토리 없음) = 상위 수준 상속 + 현재 수준 이하 리셋\n */\nexport class NumberingState {\n private currentId = 0\n private counters: number[] = [0, 0, 0, 0, 0, 0, 0]\n private history = new Map<number, number[]>()\n\n /** 번호 문단 처리: 카운터 갱신 후 수준별 카운터 스냅샷 반환 */\n advance(numberingId: number, level: number): number[] {\n const lv = Math.min(Math.max(level, 0), 6)\n\n if (this.currentId !== numberingId) {\n if (this.currentId !== 0) this.history.set(this.currentId, [...this.counters])\n const saved = this.history.get(numberingId)\n if (saved) {\n this.counters = [...saved]\n } else {\n const prev = this.counters\n this.counters = [0, 0, 0, 0, 0, 0, 0]\n for (let i = 0; i < lv; i++) this.counters[i] = prev[i]\n }\n this.currentId = numberingId\n }\n\n this.counters[lv]++\n for (let i = lv + 1; i < 7; i++) this.counters[i] = 0\n return [...this.counters]\n }\n}\n\n// ─── 번호 포맷터 ─────────────────────────────────────\n\nexport type NumFmt =\n | \"digit\" | \"circled\" | \"romanUpper\" | \"romanLower\" | \"latinUpper\" | \"latinLower\"\n | \"ganada\" | \"circledGanada\" | \"jamo\" | \"circledJamo\" | \"hangulNum\" | \"hanjaNum\"\n\n/** HWP 표 43 (문단 머리 번호 형식 코드) → NumFmt */\nfunction headFormatToNumFmt(code: number): NumFmt {\n switch (code) {\n case 1: return \"circled\"\n case 2: return \"romanUpper\"\n case 3: return \"romanLower\"\n case 4: return \"latinUpper\"\n case 5: return \"latinLower\"\n case 8: return \"ganada\"\n case 9: return \"circledGanada\"\n case 10: return \"jamo\"\n case 11: return \"circledJamo\"\n case 12: return \"hangulNum\"\n case 13: return \"hanjaNum\"\n default: return \"digit\"\n }\n}\n\n/** HWP 표 134 (자동번호/쪽번호 모양 코드) → NumFmt */\nexport function shapeFormatToNumFmt(code: number): NumFmt {\n switch (code) {\n case 1: return \"circled\"\n case 2: return \"romanUpper\"\n case 3: return \"romanLower\"\n case 4: return \"latinUpper\"\n case 5: return \"latinLower\"\n case 6: return \"ganada\"\n case 7: return \"hangulNum\"\n case 8: return \"hanjaNum\"\n default: return \"digit\"\n }\n}\n\nconst JAMO = \"ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ\"\nconst CIRCLED_JAMO = \"㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭\"\n\nfunction fromTable(n: number, table: string): string {\n return n >= 1 && n <= table.length ? table[n - 1] : String(n)\n}\n\nfunction formatLatin(n: number, upper: boolean): string {\n if (n <= 0) return \"\"\n let result = \"\"\n let num = n\n while (num > 0) {\n num--\n result = String.fromCharCode((upper ? 65 : 97) + (num % 26)) + result\n num = Math.floor(num / 26)\n }\n return result\n}\n\n/** 한글/한자 숫자 (일이삼 / 一二三) — 만 단위까지 */\nfunction formatEastAsianNumber(n: number, digits: string[], units: string[], zero: string): string {\n if (n === 0) return zero\n if (n < 0 || n > 99999) return String(n)\n let result = \"\"\n let num = n\n let unit = 0\n while (num > 0) {\n const d = num % 10\n if (d > 0) {\n const digitStr = d === 1 && unit > 0 ? \"\" : digits[d]\n result = digitStr + units[unit] + result\n }\n num = Math.floor(num / 10)\n unit++\n }\n return result\n}\n\nconst HANGUL_DIGITS = [\"\", \"일\", \"이\", \"삼\", \"사\", \"오\", \"육\", \"칠\", \"팔\", \"구\"]\nconst HANGUL_UNITS = [\"\", \"십\", \"백\", \"천\", \"만\"]\nconst HANJA_DIGITS = [\"\", \"一\", \"二\", \"三\", \"四\", \"五\", \"六\", \"七\", \"八\", \"九\"]\nconst HANJA_UNITS = [\"\", \"十\", \"百\", \"千\", \"萬\"]\n\n/** 번호 → 문자열 (rhwp format_number 포팅 — 시퀀스 폴백은 shared/numbering 정본 규칙) */\nexport function formatNumber(n: number, fmt: NumFmt): string {\n switch (fmt) {\n case \"circled\": return n >= 1 ? circledNumber(n - 1) : String(n)\n case \"romanUpper\": return romanNumeral(n, true)\n case \"romanLower\": return romanNumeral(n, false)\n case \"latinUpper\": return formatLatin(n, true) || String(n)\n case \"latinLower\": return formatLatin(n, false) || String(n)\n case \"ganada\": return n >= 1 ? hangulOrdinal(n - 1) : String(n)\n case \"circledGanada\": return n >= 1 ? circledHangul(n - 1) : String(n)\n case \"jamo\": return fromTable(n, JAMO)\n case \"circledJamo\": return fromTable(n, CIRCLED_JAMO)\n case \"hangulNum\": return formatEastAsianNumber(n, HANGUL_DIGITS, HANGUL_UNITS, \"영\")\n case \"hanjaNum\": return formatEastAsianNumber(n, HANJA_DIGITS, HANJA_UNITS, \"零\")\n default: return String(n)\n }\n}\n\n// ─── ^N 형식 치환 ────────────────────────────────────\n\n/**\n * 번호 형식 문자열의 `^1`~`^7` 제어코드를 실제 번호로 치환.\n * (rhwp expand_numbering_format 포팅 — 시작번호 보정: counter>0이면 (start-1)+counter)\n */\nexport function expandNumberingFormat(formatStr: string, counters: number[], numbering: HwpNumbering): string {\n let result = \"\"\n let i = 0\n while (i < formatStr.length) {\n const ch = formatStr[i]\n if (ch === \"^\" && i + 1 < formatStr.length && formatStr[i + 1] >= \"1\" && formatStr[i + 1] <= \"7\") {\n const levelRef = formatStr.charCodeAt(i + 1) - 48 // '1'..'7' → 1..7\n const idx = levelRef - 1\n const counterVal = counters[idx] ?? 0\n const start = numbering.startNumbers[idx] ?? 1\n const num = counterVal > 0 ? start - 1 + counterVal : start\n result += formatNumber(num, headFormatToNumFmt(numbering.numberFormats[idx] ?? 0))\n i += 2\n continue\n }\n // 레벨 숫자가 아닌 글자(공백·기호) 앞의 ^ 는 한컴이 그리지 않는다 — HWPX para-heading 과 같은 규칙\n // (\"^ □\" → \"□\", 정책브리핑 156775690 한컴 PDF). 문자·숫자 앞은 근거가 없어 그대로 둔다\n if (ch === \"^\" && !/[\\p{L}\\p{N}]/u.test(formatStr[i + 1] ?? \"\")) {\n i++\n continue\n }\n result += ch\n i++\n }\n return result\n}\n","/**\n * HWPX/HWP equation script → LaTeX\n *\n * Ported from hml-equation-parser (Python, Apache 2.0)\n * https://github.com/OpenBapul/hml-equation-parser\n * Copyright 2018 Open Bapul\n * See THIRD_PARTY/hml-equation-parser.txt for the full license.\n *\n * Entry point: `hmlToLatex(script)`.\n *\n * Notes:\n * - The input is the raw text of <hp:script> inside <hp:equation>, i.e.\n * Hancom's mini equation language (\"HULK-style\"). Example:\n * x = { -b +- SQRT { b^2 -4ac } } over {2a}\n * is converted to:\n * x = \\frac { -b +- \\sqrt { b^2 -4ac } }{2a}\n * - The algorithm is a direct port of hulkEqParser.py / hulkReplaceMethod.py\n * with the same 5-pass rewrite (frac → rootOf → matrix → bar → brace).\n */\n\ninterface MatrixMapping {\n begin: string\n end: string\n removeOutterBrackets: boolean\n}\n\n// ─── convertMap (from convertMap.json) ────────────────────────────────────\n// Single-token replacements (applied to each whitespace-delimited token).\n// Exported for equation-generate.ts: 예약어 도출 + 전 토큰 왕복 테스트 (PR #39).\nexport const CONVERT_MAP: Record<string, string> = {\n TIMES: \"\\\\times\", times: \"\\\\times\",\n LEFT: \"\\\\left\", RIGHT: \"\\\\right\",\n under: \"\\\\underline\",\n SMALLSUM: \"\\\\sum\", sum: \"\\\\sum\",\n SMALLPROD: \"\\\\prod\", prod: \"\\\\prod\",\n SMALLINTER: \"\\\\cap\",\n CUP: \"\\\\cup\",\n OPLUS: \"\\\\oplus\", OMINUS: \"\\\\ominus\", OTIMES: \"\\\\otimes\", ODIV: \"\\\\oslash\", ODOT: \"\\\\odot\",\n LOR: \"\\\\lor\", LAND: \"\\\\land\",\n SUBSET: \"\\\\subset\", SUPERSET: \"\\\\supset\", SUBSETEQ: \"\\\\subseteq\", SUPSETEQ: \"\\\\supseteq\",\n IN: \"\\\\in\", OWNS: \"\\\\owns\", NOTIN: \"\\\\notin\",\n LEQ: \"\\\\leq\", GEQ: \"\\\\geq\",\n \"<<\": \"\\\\ll\", \">>\": \"\\\\gg\", \"<<<\": \"\\\\lll\", \">>>\": \"\\\\ggg\",\n PREC: \"\\\\prec\", SUCC: \"\\\\succ\",\n UPLUS: \"\\\\uplus\",\n \"±\": \"\\\\pm\", \"+-\": \"\\\\pm\", \"-+\": \"\\\\mp\", \"÷\": \"\\\\div\",\n cdot: \"\\\\cdot\",\n CIRC: \"\\\\circ\", BULLET: \"\\\\bullet\", DEG: \" ^\\\\circ\",\n AST: \"\\\\ast\", STAR: \"\\\\bigstar\", BIGCIRC: \"\\\\bigcirc\",\n EMPTYSET: \"\\\\emptyset\",\n THEREFORE: \"\\\\therefore\", BECAUSE: \"\\\\because\", EXIST: \"\\\\exists\",\n \"!=\": \"\\\\neq\",\n SMCOPROD: \"\\\\coprod\", coprod: \"\\\\coprod\",\n SQCAP: \"\\\\sqcap\", SQCUP: \"\\\\sqcup\",\n SQSUBSET: \"\\\\sqsubset\", SQSUBSETEQ: \"\\\\sqsubseteq\",\n BIGSQCUP: \"\\\\bigsqcup\",\n BIGOPLUS: \"\\\\bigoplus\", BIGOTIMES: \"\\\\bigotimes\", BIGODOT: \"\\\\bigodot\", BIGUPLUS: \"\\\\biguplus\",\n inter: \"\\\\bigcap\", union: \"\\\\bigcup\",\n BIGOMINUS: \"{\\\\Large\\\\ominus}\", BIGODIV: \"{\\\\Large\\\\oslash}\",\n UNDEROVER: \"\",\n SIM: \"\\\\sim\", APPROX: \"\\\\approx\", SIMEQ: \"\\\\simeq\", CONG: \"\\\\cong\",\n \"==\": \"\\\\equiv\",\n DIAMOND: \"\\\\diamond\", FORALL: \"\\\\forall\",\n prime: \"'\", Partial: \"\\\\partial\", INF: \"\\\\infty\", PROPTO: \"\\\\propto\",\n lim: \"\\\\lim\", Lim: \"\\\\lim\",\n larrow: \"\\\\leftarrow\", \"->\": \"\\\\rightarrow\",\n uparrow: \"\\\\uparrow\", downarrow: \"\\\\downarrow\",\n LARROW: \"\\\\Leftarrow\", RARROW: \"\\\\Rightarrow\",\n UPARROW: \"\\\\Uparrow\", DOWNARROW: \"\\\\Downarrow\",\n udarrow: \"\\\\updownarrow\",\n \"<->\": \"\\\\leftrightarrow\",\n UDARROW: \"\\\\Updownarrow\", LRARROW: \"\\\\Leftrightarrow\",\n NWARROW: \"\\\\nwarrow\", SEARROW: \"\\\\searrow\", NEARROW: \"\\\\nearrow\", SWARROW: \"\\\\swarrow\",\n HOOKLEFT: \"\\\\hookleftarrow\", HOOKRIGHT: \"\\\\hookrightarrow\",\n PVER: \"\\\\|\", MAPSTO: \"\\\\mapsto\",\n CDOTS: \"\\\\cdots\", LDOTS: \"\\\\ldots\", VDOTS: \"\\\\vdots\", DDOTS: \"\\\\ddots\",\n DAGGER: \"\\\\dagger\", DDAGGER: \"\\\\ddagger\", DOTEQ: \"\\\\doteq\",\n image: \"\\\\fallingdotseq\", REIMAGE: \"\\\\risingdotseq\",\n ASYMP: \"\\\\asymp\", ISO: \"\\\\Bumpeq\",\n DSUM: \"\\\\dotplus\", XOR: \"\\\\veebar\",\n TRIANGLE: \"\\\\triangle\", NABLA: \"\\\\nabla\",\n ANGLE: \"\\\\angle\", MSANGLE: \"\\\\measuredangle\", SANGLE: \"\\\\sphericalangle\",\n VDASH: \"\\\\vdash\", DASHV: \"\\\\dashv\",\n BOT: \"\\\\bot\", TOP: \"\\\\top\", MODELS: \"\\\\models\",\n LAPLACE: \"\\\\mathcal{L}\",\n CENTIGRADE: \"^{\\\\circ}C\", FAHRENHEIT: \"^{\\\\circ}F\",\n LSLANT: \"\\\\diagup\", RSLANT: \"\\\\diagdown\",\n\n sqrt: \"\\\\sqrt\",\n int: \"\\\\int\", dint: \"\\\\iint\", tint: \"\\\\iiint\", oint: \"\\\\oint\",\n\n alpha: \"\\\\alpha\", beta: \"\\\\beta\", gamma: \"\\\\gamma\", delta: \"\\\\delta\",\n epsilon: \"\\\\epsilon\", zeta: \"\\\\zeta\", eta: \"\\\\eta\", theta: \"\\\\theta\",\n iota: \"\\\\iota\", kappa: \"\\\\kappa\", lambda: \"\\\\lambda\", mu: \"\\\\mu\",\n nu: \"\\\\nu\", xi: \"\\\\xi\", omicron: \"\\\\omicron\", pi: \"\\\\pi\",\n rho: \"\\\\rho\", sigma: \"\\\\sigma\", tau: \"\\\\tau\", upsilon: \"\\\\upsilon\",\n phi: \"\\\\phi\", chi: \"\\\\chi\", psi: \"\\\\psi\", omega: \"\\\\omega\",\n ALPHA: \"A\", BETA: \"B\", GAMMA: \"\\\\Gamma\", DELTA: \"\\\\Delta\",\n EPSILON: \"E\", ZETA: \"Z\", ETA: \"H\", THETA: \"\\\\Theta\",\n IOTA: \"I\", KAPPA: \"K\", LAMBDA: \"\\\\Lambda\", MU: \"M\",\n NU: \"N\", XI: \"\\\\Xi\", OMICRON: \"O\", PI: \"\\\\Pi\",\n RHO: \"P\", SIGMA: \"\\\\Sigma\", TAU: \"T\", UPSILON: \"\\\\Upsilon\",\n PHI: \"\\\\Phi\", CHI: \"X\", PSI: \"\\\\Psi\", OMEGA: \"\\\\Omega\",\n\n \"⌈\": \"\\\\lceil\", \"⌉\": \"\\\\rceil\",\n \"⌊\": \"\\\\lfloor\", \"⌋\": \"\\\\rfloor\",\n \"∥\": \"\\\\|\",\n \"⊐\": \"\\\\sqsupset\", \"⊒\": \"\\\\sqsupseteq\",\n\n odint: \"\\\\mathop ∯\",\n otint: \"\\\\mathop ∰\",\n BIGSQCAP: \"\\\\mathop ⨅\",\n ATT: \"\\\\mathop ※\",\n HUND: \"\\\\mathop ‰\",\n THOU: \"\\\\mathop ‱\",\n IDENTICAL: \"\\\\mathop ∷\",\n RTANGLE: \"\\\\mathop ⊾\",\n BASE: \"\\\\mathop ⌂\",\n BENZENE: \"\\\\mathop ⌬\",\n}\n\n// Tokens rewritten to a HULK-prefixed marker, then expanded in second passes.\nexport const MIDDLE_CONVERT_MAP: Record<string, string> = {\n matrix: \"HULKMATRIX\",\n pmatrix: \"HULKPMATRIX\",\n bmatrix: \"HULKBMATRIX\",\n dmatrix: \"HULKDMATRIX\",\n eqalign: \"HULKEQALIGN\",\n cases: \"HULKCASE\",\n vec: \"HULKVEC\",\n dyad: \"HULKDYAD\",\n acute: \"HULKACUTE\",\n grave: \"HULKGRAVE\",\n dot: \"HULKDOT\",\n ddot: \"HULKDDOT\",\n bar: \"HULKBAR\",\n hat: \"HULKHAT\",\n check: \"HULKCHECK\",\n arch: \"HULKARCH\",\n tilde: \"HULKTILDE\",\n BOX: \"HULKBOX\",\n OVERBRACE: \"HULKOVERBRACE\",\n UNDERBRACE: \"HULKUNDERBRACE\",\n}\n\nconst BAR_CONVERT_MAP: Record<string, string> = {\n HULKVEC: \"\\\\overrightarrow\",\n HULKDYAD: \"\\\\overleftrightarrow\",\n HULKACUTE: \"\\\\acute\",\n HULKGRAVE: \"\\\\grave\",\n HULKDOT: \"\\\\dot\",\n HULKDDOT: \"\\\\ddot\",\n HULKBAR: \"\\\\overline\",\n HULKHAT: \"\\\\widehat\",\n HULKCHECK: \"\\\\check\",\n HULKARCH: \"\\\\overset{\\\\frown}\",\n HULKTILDE: \"\\\\widetilde\",\n HULKBOX: \"\\\\boxed\",\n}\n\nconst MATRIX_CONVERT_MAP: Record<string, MatrixMapping> = {\n HULKMATRIX: { begin: \"\\\\begin{matrix}\", end: \"\\\\end{matrix}\", removeOutterBrackets: true },\n HULKPMATRIX: { begin: \"\\\\begin{pmatrix}\", end: \"\\\\end{pmatrix}\", removeOutterBrackets: true },\n HULKBMATRIX: { begin: \"\\\\begin{bmatrix}\", end: \"\\\\end{bmatrix}\", removeOutterBrackets: true },\n HULKDMATRIX: { begin: \"\\\\begin{vmatrix}\", end: \"\\\\end{vmatrix}\", removeOutterBrackets: true },\n HULKCASE: { begin: \"\\\\begin{cases}\", end: \"\\\\end{cases}\", removeOutterBrackets: true },\n HULKEQALIGN: { begin: \"\\\\eqalign{\", end: \"}\", removeOutterBrackets: false },\n}\n\nconst BRACE_CONVERT_MAP: Record<string, string> = {\n HULKOVERBRACE: \"\\\\overbrace\",\n HULKUNDERBRACE: \"\\\\underbrace\",\n}\n\n// ─── Bracket scanning helpers ─────────────────────────────────────────────\n\n/**\n * Find a matching `{...}` pair at/after `startIdx`. Returns [start, end) such that eqString.slice(start, end)\n * is the full bracketed substring including the `{` and `}`.\n * Throws when no matching bracket exists.\n * (역방향 탐색은 replaceFrac 이 출력 배열 꼬리에서 직접 한다 — 문자열 전체를 뒤집던 방식이 over 마다 O(n))\n */\nfunction findBrackets(eqString: string, startIdx: number): [number, number] {\n const startCur = eqString.indexOf(\"{\", startIdx)\n if (startCur === -1) throw new Error(\"cannot find bracket\")\n let bracketCount = 1\n for (let i = startCur + 1; i < eqString.length; i++) {\n const ch = eqString[i]\n if (ch === \"{\") bracketCount += 1\n else if (ch === \"}\") bracketCount -= 1\n if (bracketCount === 0) return [startCur, i + 1]\n }\n throw new Error(\"cannot find bracket\")\n}\n\n/**\n * From a cursor inside a bracketed group, walk backward to the enclosing\n * `{` and return the outer `{...}` span.\n */\nfunction findOutterBrackets(eqString: string, startIdx: number): [number, number] {\n const outer = findEnclosingBrackets(eqString, startIdx)\n if (!outer) throw new Error(\"cannot find bracket\")\n return outer\n}\n\n/**\n * Find the nearest `{...}` group that actually encloses `startIdx`.\n * A previous closed group such as `_ { x } HULKBAR { y }` must not be\n * treated as the outer wrapper for `HULKBAR`.\n */\nfunction findEnclosingBrackets(eqString: string, startIdx: number): [number, number] | null {\n let depth = 0\n for (let idx = startIdx - 1; idx >= 0; idx--) {\n const ch = eqString[idx]\n if (ch === \"}\") {\n depth += 1\n } else if (ch === \"{\") {\n if (depth > 0) {\n depth -= 1\n continue\n }\n try {\n const [start, end] = findBrackets(eqString, idx)\n if (start === idx && end > startIdx) return [start, end]\n } catch {\n return null\n }\n return null\n }\n }\n return null\n}\n\n// ─── Rewrite passes ───────────────────────────────────────────────────────\n\n/**\n * \"...\" 리터럴과 \\text{...} 스팬을 동일 길이 필러로 가린 사본 — 예약어(over/root/of)\n * 검색이 리터럴 내부 부분문자열을 연산자로 오인하지 않게 한다 (인덱스 정렬 보존).\n * 따옴표 홀수(비페어)면 해당 구간은 못 가리고 기존 동작으로 남는다.\n */\nfunction maskLiteralSpans(eqString: string): string {\n return eqString\n .replace(/\"[^\"]*\"/g, (m) => \"￿\".repeat(m.length))\n .replace(/\\\\text\\{[^}]*\\}/g, (m) => \"￿\".repeat(m.length))\n}\n\n/** 공백 경계의 독립 토큰으로만 예약어 검색 (리터럴 스팬 마스킹). 없으면 -1 */\nfunction findKeywordToken(eqString: string, word: string, from = 0): number {\n const masked = maskLiteralSpans(eqString)\n for (let i = masked.indexOf(word, from); i !== -1; i = masked.indexOf(word, i + 1)) {\n const okL = i === 0 || /\\s/.test(masked[i - 1])\n const okR = i + word.length === masked.length || /\\s/.test(masked[i + word.length])\n if (okL && okR) return i\n }\n return -1\n}\n\n/**\n * `{1} over {2}` → `\\frac{1}{2}`. over 토큰을 왼쪽부터 한 번 훑으며 출력 배열 꼬리에서 분자를 바꾼다.\n * 종전엔 over 하나마다 식 전체를 다시 가리고(maskLiteralSpans)·뒤집고(findBrackets 역방향)·이어 붙여\n * over n 개에 O(n²)였다(×2000 1.25초, ×4000 5.3초). 가림은 입력에서 한 번만 한다 — hmlToLatex 토큰화 뒤라\n * 치환이 따옴표를 더하거나 빼지 않고, \\text{…} 안은 공백이 없어 over 토큰이 생기지 않으므로 매번 가린 것과 같다.\n * 원소는 한 글자(치환이 넣는 \"\\\\frac\" 만 한 원소 — 중괄호·공백이 없어 역방향 훑기에 글자열과 같다).\n */\nfunction replaceFrac(eqString: string): string {\n const hmlFrac = \"over\"\n const masked = maskLiteralSpans(eqString)\n const out: string[] = []\n let pos = 0 // eqString 에서 out 으로 옮긴 끝\n for (let cursor = masked.indexOf(hmlFrac); cursor !== -1; cursor = masked.indexOf(hmlFrac, cursor + 1)) {\n const okL = cursor === 0 || /\\s/.test(masked[cursor - 1])\n const okR = cursor + hmlFrac.length === masked.length || /\\s/.test(masked[cursor + hmlFrac.length])\n if (!okL || !okR) continue\n for (; pos < cursor; pos++) out.push(eqString[pos])\n // 분자는 over 바로 앞(공백 스킵)의 인접 토큰 — 왼쪽 가장 가까운 } 그룹을 잡아\n // 그 사이 콘텐츠를 무음 삭제하던 것 방지 (sqrt {x} + 1 over 2 에서 \" + 1 \" 증발)\n let end = out.length\n // 공백류 클래스를 findKeywordToken 경계(/\\s/)와 일치시켜 다중줄 스크립트의 분자 무음\n // 유실 방지 — over 앞뒤가 \\n·\\t·nbsp 여도 분자 경계를 옳게 잡는다 (eqrt-3/eqrt-2 정합)\n while (end > 0 && /\\s/.test(out[end - 1])) end--\n let numStart = end\n if (end > 0 && out[end - 1] === \"}\") {\n // 짝 { 까지 거꾸로 (없으면 종전 findBrackets 예외와 같이 여기서 멈춘다)\n let depth = 0\n for (numStart = end - 1; numStart >= 0; numStart--) {\n if (out[numStart] === \"}\") depth++\n else if (out[numStart] === \"{\" && --depth === 0) break\n }\n if (numStart < 0) break\n out.length = end\n out.splice(numStart, 0, \"\\\\frac\")\n } else {\n while (numStart > 0 && !/\\s/.test(out[numStart - 1])) numStart--\n if (numStart === end) break // 빈 분자\n out.length = end\n out.splice(numStart, 0, \"\\\\frac\", \"{\")\n out.push(\"}\")\n }\n pos = cursor + hmlFrac.length\n }\n return out.join(\"\") + eqString.slice(pos)\n}\n\n/** `root {1} of {2}` → `\\sqrt[1]{2}` */\nfunction replaceRootOf(eqString: string): string {\n while (true) {\n const rootCursor = findKeywordToken(eqString, \"root\")\n if (rootCursor === -1) break\n try {\n const elem1 = findBrackets(eqString, rootCursor)\n // of는 root의 지수 그룹 뒤에서만 유효 — 전역 첫 매치는 리터럴/선행 텍스트를 오인한다\n const ofCursor = findKeywordToken(eqString, \"of\", elem1[1])\n if (ofCursor === -1) return eqString\n const elem2 = findBrackets(eqString, ofCursor)\n const e1 = eqString.slice(elem1[0] + 1, elem1[1] - 1)\n const e2 = eqString.slice(elem2[0] + 1, elem2[1] - 1)\n eqString =\n eqString.slice(0, rootCursor) +\n \"\\\\sqrt\" +\n \"[\" + e1 + \"]\" +\n \"{\" + e2 + \"}\" +\n eqString.slice(elem2[1] + 1)\n } catch {\n return eqString\n }\n }\n return eqString\n}\n\n/** matrix/pmatrix/bmatrix/dmatrix/cases/eqalign expansion. */\nfunction replaceAllMatrix(eqString: string): string {\n const replaceElements = (bracketStr: string): string => {\n let inner = bracketStr.slice(1, -1) // strip outer `{` `}`\n inner = inner.replace(/#/g, \" \\\\\\\\ \")\n inner = inner.replace(/&amp;/g, \"&\")\n return inner\n }\n\n const replaceMatrix = (input: string, matStr: string, matElem: MatrixMapping): string => {\n while (true) {\n const cursor = input.indexOf(matStr)\n if (cursor === -1) break\n try {\n const [eStart, eEnd] = findBrackets(input, cursor)\n const elem = replaceElements(input.slice(eStart, eEnd))\n let beforeMat: string\n let afterMat: string\n const outer = matElem.removeOutterBrackets ? findEnclosingBrackets(input, cursor) : null\n if (outer && outer[1] >= eEnd) {\n const [bStart, bEnd] = outer\n beforeMat = input.slice(0, bStart)\n afterMat = input.slice(bEnd)\n } else {\n beforeMat = input.slice(0, cursor)\n afterMat = input.slice(eEnd)\n }\n input = beforeMat + matElem.begin + elem + matElem.end + afterMat\n } catch {\n return input\n }\n }\n return input\n }\n\n for (const [matKey, matElem] of Object.entries(MATRIX_CONVERT_MAP)) {\n eqString = replaceMatrix(eqString, matKey, matElem)\n }\n return eqString\n}\n\n/** vec/hat/bar/dot/ddot/tilde/… (HULK-prefixed) → LaTeX accent. */\nfunction replaceAllBar(eqString: string): string {\n const replaceBar = (input: string, barStr: string, barElem: string): string => {\n while (true) {\n const cursor = input.indexOf(barStr)\n if (cursor === -1) break\n try {\n const [eStart, eEnd] = findBrackets(input, cursor)\n const elem = input.slice(eStart, eEnd)\n const outer = findEnclosingBrackets(input, cursor)\n const [replaceStart, replaceEnd] = outer && outer[1] >= eEnd ? outer : [cursor, eEnd]\n const beforeBar = input.slice(0, replaceStart)\n const afterBar = input.slice(replaceEnd)\n input = beforeBar + barElem + elem + afterBar\n } catch {\n return input\n }\n }\n return input\n }\n\n for (const [barKey, barElem] of Object.entries(BAR_CONVERT_MAP)) {\n eqString = replaceBar(eqString, barKey, barElem)\n }\n return eqString\n}\n\n/** overbrace/underbrace: `BRACE {body} {label}` → `\\overbrace{body}^{label}` */\nfunction replaceAllBrace(eqString: string): string {\n const replaceBrace = (input: string, braceStr: string, braceElem: string): string => {\n while (true) {\n const cursor = input.indexOf(braceStr)\n if (cursor === -1) break\n try {\n const [eStart1, eEnd1] = findBrackets(input, cursor)\n const [eStart2, eEnd2] = findBrackets(input, eEnd1)\n const elem1 = input.slice(eStart1, eEnd1)\n const elem2 = input.slice(eStart2, eEnd2)\n const beforeBrace = input.slice(0, cursor)\n const afterBrace = input.slice(eEnd2)\n input = beforeBrace + braceElem + elem1 + \"^\" + elem2 + afterBrace\n } catch {\n return input\n }\n }\n return input\n }\n\n for (const [braceKey, braceElem] of Object.entries(BRACE_CONVERT_MAP)) {\n eqString = replaceBrace(eqString, braceKey, braceElem)\n }\n return eqString\n}\n\n/** After single-token pass, fix `\\left {` → `\\left \\{` and `\\right }` → `\\right \\}`. */\nfunction replaceBracket(strList: string[]): string[] {\n for (let i = 0; i < strList.length; i++) {\n if (strList[i] === \"{\" && i > 0 && strList[i - 1] === \"\\\\left\") strList[i] = \"\\\\{\"\n if (strList[i] === \"}\" && i > 0 && strList[i - 1] === \"\\\\right\") strList[i] = \"\\\\}\"\n }\n return strList\n}\n\n/**\n * Convert an HWPX equation script (mini Hancom equation language) to LaTeX.\n * Returns the converted LaTeX body (without `$` delimiters).\n */\nexport function hmlToLatex(hmlEqStr: string): string {\n if (!hmlEqStr) return \"\"\n\n // 스크립트 안 줄바꿈·탭은 공백 — HULK 행 구분은 `#` 이고 개행은 서식일 뿐이다. 남기면 토큰에\n // 붙어 LaTeX 로 새고(`5x+a \\\\ ⏎ }`), 인라인 수식 `$…$` 이 줄을 넘어 수식으로 인식되지 않는다 (math-001 cases)\n let s = hmlEqStr.replace(/[\\r\\n\\t]+/g, \" \").replace(/`/g, \" \")\n s = s.replace(/\\{/g, \" { \").replace(/\\}/g, \" } \").replace(/&/g, \" & \")\n\n let tokens = s.split(\" \")\n for (let i = 0; i < tokens.length; i++) {\n const t = tokens[i]\n // hasOwn 필수 — `in`은 Object.prototype 키(constructor·toString 등)에도 걸려\n // 수식 토큰이 그 이름이면 함수가 치환값이 된다 (출력에 native code 주입/무음 삭제)\n if (Object.hasOwn(CONVERT_MAP, t)) tokens[i] = CONVERT_MAP[t]\n else if (Object.hasOwn(MIDDLE_CONVERT_MAP, t)) tokens[i] = MIDDLE_CONVERT_MAP[t]\n else {\n // EqEdit 리터럴 따옴표(\"int\" 등 — 명령 해석 차단용) → \\text{...}로 복원.\n // 생성기(equation-generate.ts)의 \\text→\"...\" 출력과 고정점을 이룬다.\n // 공백 포함 다중 토큰 인용은 기존대로 통과 (토큰 단위라 식별 불가).\n const quoted = /^\"(.+)\"$/.exec(t)\n if (quoted) tokens[i] = `\\\\text{${quoted[1]}}`\n }\n }\n tokens = tokens.filter(tok => tok.length !== 0)\n tokens = replaceBracket(tokens)\n\n let out = tokens.join(\" \")\n out = replaceFrac(out)\n out = replaceRootOf(out)\n out = replaceAllMatrix(out)\n out = replaceAllBar(out)\n out = replaceAllBrace(out)\n\n return out\n}\n","/**\n * HWP5 수식 레코드(EQEDIT) 스크립트 → LaTeX.\n *\n * HWP5의 EQEDIT 레코드도 HWPX <hp:script>와 같은 한컴 EqEdit 언어라\n * 구조 변환(분수·근호·행렬·액센트·토큰 매핑)은 hwpx/equation.ts(hmlToLatex)를\n * 정본으로 재사용한다. 이 모듈은 HWP5 실문서에서 관측되는 표기 편차\n * (소문자 예약어·붙여쓴 첨자·유니코드 연산자·글꼴 지시자)를 정본 토크나이저가\n * 인식할 수 있는 형태로 정규화하는 호환 프리패스만 담당한다.\n */\n\nimport { CONVERT_MAP, MIDDLE_CONVERT_MAP, hmlToLatex } from \"../hwpx/equation.js\"\n\n/**\n * 정본 CONVERT_MAP에 (해당 표기로는) 없는 hwp5 지원 어휘 → LaTeX 직접 치환.\n * 키는 소문자 — 대소문자 무관 매칭(구 hwp5 동작 보존). 정본에 정확히 있는\n * 토큰(IN, GEQ, ALPHA 등)이 우선이라 여기까지 오지 않는다.\n */\nconst WORD_ALIASES: Record<string, string> = {\n // 삼각/로그 함수 — 정본 맵 부재\n sin: \"\\\\sin\", cos: \"\\\\cos\", tan: \"\\\\tan\", sec: \"\\\\sec\", csc: \"\\\\csc\", cot: \"\\\\cot\",\n log: \"\\\\log\", ln: \"\\\\ln\",\n // 연산자·관계 기호 (정본은 대문자 키만 보유)\n divide: \"\\\\div\", div: \"\\\\div\",\n le: \"\\\\leq\", ge: \"\\\\geq\", geq: \"\\\\geq\", deg: \"^\\\\circ\",\n rarrow: \"\\\\rightarrow\", lrarrow: \"\\\\leftrightarrow\",\n rightarrow: \"\\\\rightarrow\", leftarrow: \"\\\\leftarrow\",\n in: \"\\\\in\", notin: \"\\\\notin\", emptyset: \"\\\\emptyset\",\n subset: \"\\\\subset\", nsubset: \"\\\\nsubseteq\",\n cup: \"\\\\cup\", cap: \"\\\\cap\", smallinter: \"\\\\cap\", smallsum: \"\\\\sum\",\n sim: \"\\\\sim\", circ: \"\\\\circ\", bot: \"\\\\perp\",\n partial: \"\\\\partial\", nabla: \"\\\\nabla\",\n angle: \"\\\\angle\", triangle: \"\\\\triangle\",\n inf: \"\\\\infty\",\n left: \"\\\\left\", right: \"\\\\right\",\n // 글꼴 지시자 — 제거\n rm: \"\", it: \"\",\n}\n\n/** 구조 예약어 — hmlToLatex가 소문자만 인식하므로 대소문자 무관 입력을 접는다 */\nconst STRUCTURAL_FOLD = new Set([\"over\", \"root\", \"of\"])\n\n/** 어절 단위 사전 정규화 — 정본 키 우선, 없으면 소문자 별칭/정본 키로 폴드 */\nfunction normalizeWords(input: string): string {\n return input.replace(/(?<![\\\\A-Za-z0-9])([A-Za-z][A-Za-z0-9]*)(?![A-Za-z0-9])/g, word => {\n // hasOwn 필수 — `in`은 Object.prototype 키에도 걸린다 (hwpx/equation.ts 동일 주석 참조)\n if (Object.hasOwn(CONVERT_MAP, word) || Object.hasOwn(MIDDLE_CONVERT_MAP, word)) return word\n const lower = word.toLowerCase()\n if (STRUCTURAL_FOLD.has(lower)) return lower\n if (Object.hasOwn(WORD_ALIASES, lower)) return WORD_ALIASES[lower]\n if (lower !== word && (Object.hasOwn(CONVERT_MAP, lower) || Object.hasOwn(MIDDLE_CONVERT_MAP, lower))) return lower\n return word\n })\n}\n\n/** 첨자 정규화 — 공백 제거 후 미괄호 인자를 중괄호로 감싸고, 정본 토큰화용으로 분리 */\nfunction normalizeScripts(input: string): string {\n return input\n .replace(/\\s*\\^\\s*/g, \"^\")\n .replace(/\\s*_\\s*/g, \"_\")\n .replace(/\\^(?!\\{)([^\\s{}_^]+)/g, \"^{$1}\")\n .replace(/_(?!\\{)([^\\s{}_^]+)/g, \"_{$1}\")\n // \"sum_{k}\"가 통짜 토큰으로 남지 않게 ^/_를 독립 토큰으로 분리 (LaTeX 의미 동일)\n .replace(/([_^])/g, \" $1 \")\n}\n\n/** 붙여쓴/유니코드 연산자 → LaTeX (정본 토큰 맵은 공백 분리 토큰만 인식) */\nfunction normalizeOperators(input: string): string {\n return input\n .replace(/\\+-/g, \" \\\\pm \")\n .replace(/-\\+/g, \" \\\\mp \")\n .replace(/\\/\\//g, \" \\\\parallel \")\n .replace(/!=/g, \" \\\\neq \")\n .replace(/<=/g, \" \\\\leq \")\n .replace(/>=/g, \" \\\\geq \")\n .replace(/==/g, \" \\\\equiv \")\n .replace(/△/g, \" \\\\triangle \")\n .replace(/□/g, \" \\\\square \")\n .replace(/‧/g, \" \\\\cdot \")\n}\n\nexport function hwpEquationToLatex(equation: string): string {\n let s = equation.replace(/\\0/g, \"\").replace(/\\s+/g, \" \").trim()\n if (!s) return \"\"\n\n s = normalizeScripts(s)\n s = normalizeOperators(s)\n s = normalizeWords(s)\n // 미괄호 근호 지수(\"root n of {x}\") — 정본 replaceRootOf는 중괄호 그룹만 인식\n s = s.replace(/\\broot\\s+(?!\\{)(\\S+)\\s+of(?![A-Za-z0-9])/gi, \"root { $1 } of\")\n\n return hmlToLatex(s).replace(/\\s+/g, \" \").trim()\n}\n","/**\n * 문단·셀 IR 조립 — HWP5(body.ts)·HWP3(hwp3/parser.ts) 공용.\n *\n * 두 바이너리 파서가 레코드/문자 스트림에서 뽑은 문단 글·개체 블록·좌표 셀을 HWPX 파서와 같은 IR 모양으로\n * 조립한다: 글자처럼 취급 표 앞뒤 글 분할(#49/#50), 셀 평탄화 줄 모델(#52), 머리말·각주 안 표 평탄화,\n * 좌표 셀 → builder 직접 배치(후행 빈 열 트림 계약)와 손상 표(겹친 앵커) 정리.\n */\n\nimport { buildTable, convertTableToText, MAX_COLS, MAX_ROWS } from \"../table/builder.js\"\nimport type { CellContext, IRBlock, IRTable, InlineStyle } from \"../types.js\"\nimport { CELL_EDGES, type Edges } from \"../table/layout-frames.js\"\nimport { TOC_ENTRY_BLOCKS } from \"../toc-entry.js\"\n\n/** 글자처럼 취급 표 자리 표지 — 문단 글을 표 앞뒤로 나눈다 (HWPX section-walker 와 같은 문자) */\nexport const INLINE_TABLE_MARK = \"\\x1E\"\n\n/**\n * 셀 평탄화에서 앞 줄에 공백으로 잇는 블록 — 글자처럼 취급 표와 그 뒤 조각은 한 줄이다\n * (HWPX completeTable/flush 의 lineOpen 규칙 #52). emitParagraphBlocks 가 표시하고 cellTextFromBlocks 가 읽는다\n */\nconst INLINE_JOIN = new WeakSet<IRBlock>()\n\n/** 문단에 딸린 개체 (컨트롤 순서) */\nexport interface ParaObject {\n /** 개체 파생 블록 (표·그림·글상자) */\n blocks: IRBlock[]\n /** 표 개체 — 앞 글 조각을 먼저 내보낸다 */\n table: boolean\n /** 글자처럼 취급 표 — 문단 글에 INLINE_TABLE_MARK 자리가 있다 */\n inline: boolean\n}\n\nexport interface ParaEmitInput {\n /** 필드 편집·리더 절단을 마친 문단 글 (INLINE_TABLE_MARK 포함 가능) */\n text: string\n headMarker: string | null\n /** 개요 제목 수준 (0 = 본문) */\n headingLevel: number\n /** 목차 항목(채움 탭 + 쪽 번호 줄) — 글자 크기·\"제N장\" 제목 추정에서 뺀다 (#121) */\n tocEntry?: boolean\n style?: InlineStyle\n /** 각주/미주 (\"1) 내용\") */\n footnotes: string[]\n objects: ParaObject[]\n pageNumber?: number\n}\n\n/** 문단 1개 → [문단 블록(들), 개체 블록] */\nexport function emitParagraphBlocks(p: ParaEmitInput): IRBlock[] {\n const { headMarker, style, footnotes, objects, pageNumber } = p\n const blocks: IRBlock[] = []\n // 취소선·밑줄 문단(법령 개정문 삭제·개정 표시 등)은 ~~…~~ / <u>…</u> 로 방출 —\n // 대표(최빈) 스타일 기준이라 문단 전체가 그어진 경우만 잡는다 (부분 서식은 미지원)\n const decorate = (t: string): string => {\n let deco = style?.strike ? `~~${t}~~` : t\n if (style?.underline) deco = `<u>${deco}</u>`\n return deco\n }\n\n if (p.text.includes(INLINE_TABLE_MARK)) {\n // 글자처럼 취급 표가 있는 문단 — 표 앞 조각을 표 직전에 방출해 원문 순서를 지킨다(HWPX walkParagraphChildren\n // onTbl 대칭: 떠 있는 표도 앞 조각을 먼저 내고, 그리기 개체는 조각을 당기지 않는다). 조각은 본문 문단\n // (개요 제목 승격 없음 — HWPX 와 같다), 문단 머리표는 첫 글 조각에, 스타일·각주는 첫 블록에\n const segments = p.text.split(INLINE_TABLE_MARK)\n let segIdx = 0\n let first = true\n let lineOpen = false\n let markerPending = !!headMarker\n const flush = (): void => {\n if (segIdx >= segments.length) return\n const s = segments[segIdx++].replace(/\\$\\$/g, \"$ $\").trim()\n if (!s) return\n let t = decorate(s)\n if (markerPending) { t = `${headMarker} ${t}`; markerPending = false }\n const block: IRBlock = { type: \"paragraph\", text: t, pageNumber }\n if (first) {\n if (style) block.style = style\n if (footnotes.length > 0) block.footnoteText = footnotes.join(\"; \")\n first = false\n }\n if (lineOpen) INLINE_JOIN.add(block)\n lineOpen = true\n blocks.push(block)\n }\n for (const obj of objects) {\n if (obj.table) {\n flush()\n if (obj.inline && lineOpen) for (const b of obj.blocks) INLINE_JOIN.add(b)\n lineOpen = obj.inline\n }\n blocks.push(...obj.blocks)\n }\n while (segIdx < segments.length) flush()\n // 글 조각이 하나도 없는 각주 anchor — 각주 내용 자체를 문단으로 보존 (아래 단일 문단 경로와 같다)\n if (first && footnotes.length > 0) blocks.unshift({ type: \"paragraph\", text: `(주: ${footnotes.join(\"; \")})`, pageNumber })\n return blocks\n }\n\n const trimmed = p.text.replace(/\\$\\$/g, \"$ $\").trim()\n if (trimmed) {\n const block: IRBlock = {\n type: p.headingLevel > 0 ? \"heading\" : \"paragraph\",\n text: headMarker ? `${headMarker} ${trimmed}` : trimmed,\n pageNumber,\n }\n if (p.headingLevel > 0) block.level = p.headingLevel\n if (p.tocEntry) TOC_ENTRY_BLOCKS.add(block)\n if (style) {\n block.style = style\n if (style.strike || style.underline) block.text = headMarker ? `${headMarker} ${decorate(trimmed)}` : decorate(trimmed)\n }\n if (footnotes.length > 0) block.footnoteText = footnotes.join(\"; \")\n blocks.push(block)\n } else if (footnotes.length > 0) {\n // 본문 없는 각주 anchor — 각주 내용 자체를 문단으로 보존\n blocks.push({ type: \"paragraph\", text: `(주: ${footnotes.join(\"; \")})`, pageNumber })\n }\n for (const obj of objects) blocks.push(...obj.blocks)\n return blocks\n}\n\n// ─── 평문 평탄화 ─────────────────────────────────────\n\n/**\n * 표 → 평문 — 셀은 \" / \", 행은 줄바꿈(convertTableToText), 캡션 먼저. HWPX completeTable·buildSubListTable 과 같은 모양.\n * 셀 이미지 sentinel(\"![image](hwp5bin:N)\")은 남긴다 — images.ts 가 블록 트리 전체에서 추출 파일명으로 편다\n * (HWPX 도 머리말 표 이미지 셀을 \"![image](…)\" 로 남긴다). 종전엔 머리말·각주의 표를 건너뛰어 표 글이 통째로 사라졌다\n */\nexport function tableFlatText(table: IRTable): string {\n const flat = convertTableToText(table.cells)\n return table.caption ? table.caption + (flat ? \"\\n\" + flat : \"\") : flat\n}\n\n/** 블록 리스트 → 평문 (각주 인라인 포함, 표는 평탄화, 이미지는 제외) — 머리말·각주·캡션용 */\nexport function blocksPlainText(blocks: IRBlock[], sep: string): string {\n const parts: string[] = []\n for (const b of blocks) {\n if (b.type === \"image\") continue\n if (b.type === \"table\") {\n const flat = b.table ? tableFlatText(b.table) : \"\"\n if (flat) parts.push(flat)\n continue\n }\n if (b.text) {\n let t = b.text\n if (b.footnoteText) t += ` (주: ${b.footnoteText})`\n parts.push(t)\n }\n }\n return parts.join(sep).trim()\n}\n\n/**\n * 셀 문단 블록 → 하위 호환 셀 텍스트 (문단 평탄화 + 이미지 표지 + 중첩표 평문).\n * 글자처럼 취급 표와 그 뒤 조각은 앞 줄에 공백으로 잇는다(INLINE_JOIN). 셀 문단의 각주는 문단 글에 접어 넣는다 —\n * HWPX 셀 블록과 같은 IR(블록 text 에 \"(주: …)\"); footnoteText 로 따로 두면 병합 셀 HTML 경로(blocks 직렬화)가\n * 각주를 버렸다. hasStructure = 셀에 blocks 를 달 구조(이미지·중첩표)가 있는가\n */\nexport function cellTextFromBlocks(blocks: IRBlock[]): { text: string; hasStructure: boolean } {\n let text = \"\"\n const add = (part: string, b: IRBlock): void => { text += (text ? (INLINE_JOIN.has(b) ? \" \" : \"\\n\") : \"\") + part }\n let hasStructure = false\n for (const b of blocks) {\n if (b.type === \"image\" && b.text) {\n add(`![image](hwp5bin:${b.text})`, b)\n hasStructure = true\n } else if (b.type === \"table\" && b.table) {\n // flattenLayoutTables 경유 시를 위한 평문 — 구조는 blocks가 보존\n const flat = tableFlatText(b.table)\n if (flat) add(flat, b)\n hasStructure = true\n } else if (b.text) {\n if (b.footnoteText) {\n b.text += ` (주: ${b.footnoteText})`\n delete b.footnoteText\n }\n add(b.text, b)\n }\n }\n return { text, hasStructure }\n}\n\n// ─── 좌표 셀 → IRTable ──────────────────────────────\n\n/** 좌표를 가진 셀 (HWP5 LIST_HEADER colAddr/rowAddr, HWP3 셀 기하에서 복원한 좌표) */\nexport interface AddressedCell extends CellContext {\n blocks?: IRBlock[]\n isHeader?: boolean\n /** 칸의 보이는 변 (셀 borderFillId → DocInfo BORDER_FILL) — IR 칸의 CELL_EDGES 로 */\n edges?: Edges\n}\n\n/**\n * 좌표 셀 겹침 정리 (손상 파일) — 먼저 나온 셀이 칸을 차지한다. 앵커가 앞 셀 영역에 떨어진 셀은 글·구조를\n * 앞 셀에 이어 붙이고(한컴은 겹쳐 그릴 뿐 글을 버리지 않는다), 앞 셀 영역을 덮는 병합은 겹치지 않을 때까지\n * 줄인다. builder 직접 배치는 뒤 셀이 앞 앵커를 덮어써 글이 사라진다. 정상 표는 입력 그대로(같은 객체) 통과\n */\nfunction resolveCellOverlaps<T extends AddressedCell>(cells: T[], rows: number, cols: number): T[] {\n const owner = new Map<number, T>()\n const out: T[] = []\n for (const cell of cells) {\n const r0 = cell.rowAddr ?? 0, c0 = cell.colAddr ?? 0\n const hit = owner.get(r0 * cols + c0)\n if (hit) {\n if (cell.blocks?.length) {\n hit.blocks = [...(hit.blocks ?? (hit.text ? [{ type: \"paragraph\" as const, text: hit.text }] : [])), ...cell.blocks]\n }\n if (cell.text.trim()) hit.text = hit.text.trim() ? `${hit.text}\\n${cell.text}` : cell.text\n continue\n }\n // 첫 행에서 먼저 차지된 열 앞까지, 그 폭으로 먼저 차지된 행 앞까지 줄인다(표 안 칸만 한 번씩 훑는다).\n // 겹침이 없으면 span 을 건드리지 않는다 — 표 밖으로 뻗은 span 도 종전대로 builder 에 맡긴다\n const csIn = Math.max(1, Math.min(cell.colSpan, cols - c0))\n let cs = cell.colSpan\n for (let c = c0 + 1; c < c0 + csIn; c++) if (owner.has(r0 * cols + c)) { cs = c - c0; break }\n const rsIn = Math.max(1, Math.min(cell.rowSpan, rows - r0))\n let rs = cell.rowSpan\n scan: for (let r = r0 + 1; r < r0 + rsIn; r++) {\n for (let c = c0; c < c0 + Math.min(cs, csIn); c++) if (owner.has(r * cols + c)) { rs = r - r0; break scan }\n }\n const placed = cs === cell.colSpan && rs === cell.rowSpan ? cell : { ...cell, colSpan: cs, rowSpan: rs }\n for (let r = r0; r < Math.min(r0 + rs, rows); r++) for (let c = c0; c < Math.min(c0 + cs, cols); c++) owner.set(r * cols + c, placed)\n out.push(placed)\n }\n return out\n}\n\n/**\n * 좌표 셀 → IRTable — HWPX(table-build.ts)와 같은 builder 직접 배치(buildTableDirect)를 태워 표 계약\n * (후행 빈 열 트림 + 잘린 열에 걸친 span 절단, keepAnchoredEmptyCols 면 앵커 열 보존)을 맞춘다. HWP5 종전 경로는\n * 자체 그리드를 그대로 IRTable 로 내 빈 입력란 열이 남았다 — 같은 문서 HWPX 보다 1~9열 넓음(서식 표 300+ 실측).\n * 행 묶음은 rowAddr 기준(HWPX <tr> 과 같은 경계), 셀 blocks·제목 셀은 좌표로 재부착.\n * 손상 레코드(행·열 0, 표 밖 앵커)는 표를 버리지 않고 셀 좌표까지 넓힌다 — 글 손실 금지\n */\nexport function buildAddressedTable(cells: AddressedCell[], rows: number, cols: number, keepAnchoredEmptyCols?: boolean): IRTable | null {\n for (const c of cells) {\n rows = Math.max(rows, Math.min((c.rowAddr ?? 0) + 1, MAX_ROWS))\n cols = Math.max(cols, Math.min((c.colAddr ?? 0) + 1, MAX_COLS))\n }\n const placed = resolveCellOverlaps(cells, rows, cols)\n const byRow: AddressedCell[][] = Array.from({ length: rows }, () => [])\n for (const c of placed) {\n const r = c.rowAddr ?? 0\n if (r < rows && (c.colAddr ?? 0) < cols) byRow[r].push(c)\n }\n const table = buildTable(byRow, { keepAnchoredEmptyCols })\n if (table.rows === 0) return null\n for (const c of placed) {\n // 보이는 변은 글 대조 없이 좌표 자리 칸에 — 빈 칸도 테두리를 가진다\n if (c.edges) {\n const at = table.cells[c.rowAddr ?? 0]?.[c.colAddr ?? 0]\n if (at) CELL_EDGES.set(at, c.edges)\n }\n if (!c.blocks?.length && !c.isHeader) continue\n const target = table.cells[c.rowAddr ?? 0]?.[c.colAddr ?? 0]\n // 겹친 앵커로 합쳐진 셀·잘린 열의 앵커는 텍스트가 달라 붙지 않는다\n if (!target || target.text !== c.text.trim()) continue\n if (c.blocks?.length) target.blocks = c.blocks\n if (c.isHeader) target.isHeader = true\n }\n return table\n}\n","/** HWP 5.x 본문 파서 — 섹션 레코드 → 문단 리스트·컨트롤 디스패치(표·그리기 개체·수식·각주·머리말·필드) → IRBlock */\n\nimport { tidyScriptTags, type ScriptKind } from \"../script-tags.js\"\nimport {\n extractEquationText, createParaTextState, appendParaText, LITERAL_DOLLAR_MARK, LITERAL_LT_MARK, resolveLiteralLt, TAG_PARA_HEADER, TAG_PARA_TEXT, TAG_CHAR_SHAPE,\n TAG_CTRL_HEADER, TAG_LIST_HEADER, TAG_TABLE, TAG_EQEDIT, TAG_SHAPE_COMPONENT, TAG_SHAPE_COMPONENT_CONTAINER,\n TAG_SHAPE_COMPONENT_PICTURE, type HwpRecord, type HwpDocInfo, type IndexedControlResolver,\n} from \"./record.js\"\nimport { NumberingState, expandNumberingFormat, formatNumber, shapeFormatToNumFmt } from \"./numbering.js\"\nimport { hwpEquationToLatex } from \"./equation.js\"\nimport { buildTable, flattenLayoutTables, markNonLayoutTable, MAX_COLS, MAX_ROWS } from \"../table/builder.js\"\nimport {\n INLINE_TABLE_MARK, blocksPlainText, buildAddressedTable, cellTextFromBlocks, emitParagraphBlocks,\n} from \"./ir-assemble.js\"\nimport type { CellContext, IRBlock, IRTable, ParseOptions, ParseWarning, InlineStyle } from \"../types.js\"\nimport type { Edges } from \"../table/layout-frames.js\"\nimport { sanitizeHref } from \"../utils.js\"\nimport { takeTocLeaders } from \"../toc-entry.js\"\n\n/** 중첩표/글상자 재귀 깊이 상한 — 표 \"중첩 단계\" 기준.\n * 실무 문서 중첩은 2~3단이라 8이면 충분하며, 바이너리 파싱 비용상\n * filler/소스맵(16)보다 보수적으로 둔다. hwpx MAX_XML_DEPTH(200)는\n * XML 요소 깊이라 좌표계가 다름 — 상수 통일 대상 아님 */\nconst MAX_NEST_DEPTH = 8\n\n// ─── 컨트롤 ID (u32 LE 정규화) ───────────────────────\n// HWP는 ctrl_id를 DWORD(LE)로 저장한다. \"tbl \"은 파일에 [0x20,0x6c,0x62,0x74](\" lbt\")로\n// 기록되므로, readUInt32LE로 읽으면 BE 문자열 상수 0x74626c20과 일치한다 (rhwp tags.rs 방식).\n\n/** 4바이트 ASCII 문자열 → u32 컨트롤 ID 상수 (\"tbl \" → 0x74626c20) */\nfunction cid(s: string): number {\n return ((s.charCodeAt(0) << 24) | (s.charCodeAt(1) << 16) | (s.charCodeAt(2) << 8) | s.charCodeAt(3)) >>> 0\n}\n\nconst CTRL_TBL = cid(\"tbl \") // 표\nconst CTRL_GSO = cid(\"gso \") // 그리기 개체 (그림/글상자)\nconst CTRL_EQED = cid(\"eqed\") // 수식\nconst CTRL_HEAD = cid(\"head\") // 머리말\nconst CTRL_FOOT = cid(\"foot\") // 꼬리말\nconst CTRL_FN = cid(\"fn \") // 각주\nconst CTRL_EN = cid(\"en \") // 미주\nconst CTRL_ATNO = cid(\"atno\") // 자동 번호\nconst CTRL_NWNO = cid(\"nwno\") // 새 번호\nconst CTRL_PGNP = cid(\"pgnp\") // 쪽 번호 위치\nconst CTRL_PGHD = cid(\"pghd\") // 감추기\nconst CTRL_IDXM = cid(\"idxm\") // 찾아보기 표식\nconst CTRL_BOKM = cid(\"bokm\") // 책갈피\nconst CTRL_TCPS = cid(\"tcps\") // 글자 겹침\nconst CTRL_TDUT = cid(\"tdut\") // 덧말\nconst CTRL_TCMT = cid(\"tcmt\") // 숨은 설명\nconst CTRL_SECD = cid(\"secd\") // 구역 정의\nconst CTRL_COLD = cid(\"cold\") // 단 정의\nconst CTRL_FORM = cid(\"form\") // 양식 개체\nconst CTRL_OLE = cid(\"ole \") // OLE 개체\nconst FIELD_HLK = cid(\"%hlk\") // 필드: 하이퍼링크\nconst FIELD_CLK = cid(\"%clk\") // 필드: 누름틀\n\nconst KNOWN_CTRL_IDS = new Set([\n CTRL_TBL, CTRL_GSO, CTRL_EQED, CTRL_HEAD, CTRL_FOOT, CTRL_FN, CTRL_EN,\n CTRL_ATNO, CTRL_NWNO, CTRL_PGNP, CTRL_PGHD, CTRL_IDXM, CTRL_BOKM,\n CTRL_TCPS, CTRL_TDUT, CTRL_TCMT, CTRL_SECD, CTRL_COLD, CTRL_FORM, CTRL_OLE,\n])\n\n/** 필드 컨트롤 여부 (첫 바이트 '%') */\nfunction isFieldCtrlId(id: number): boolean {\n return (id >>> 24) === 0x25\n}\n\n/** 바이트 순서 뒤집기 — 비표준 작성기의 BE 저장 ctrl_id 방어 */\nfunction swap32(id: number): number {\n return (((id & 0xff) << 24) | (((id >>> 8) & 0xff) << 16) | (((id >>> 16) & 0xff) << 8) | ((id >>> 24) & 0xff)) >>> 0\n}\n\n/** LE로 읽은 ctrl_id를 정규화 — 알려진 ID/필드가 아니고 스왑하면 일치할 때만 스왑 */\nfunction normalizeCtrlId(raw: number): number {\n if (KNOWN_CTRL_IDS.has(raw) || isFieldCtrlId(raw)) return raw\n const sw = swap32(raw)\n if (KNOWN_CTRL_IDS.has(sw) || isFieldCtrlId(sw)) return sw\n return raw\n}\n\n// ─── 수식 ────────────────────────────────────────────\n\nfunction formatEquationForMarkdown(equation: string): string {\n const normalized = hwpEquationToLatex(equation)\n if (!normalized) return \"\"\n return `$${normalized.replace(/\\$/g, \"\\\\$\")}$`\n}\n\n/** 컨트롤 자식 레코드 범위에서 EQEDIT 수식 추출 */\nfunction extractEquationFromSlice(records: HwpRecord[], start: number, end: number): string | null {\n for (let i = start; i < end; i++) {\n if (records[i].tagId !== TAG_EQEDIT) continue\n const equation = extractEquationText(records[i].data)\n return equation ? formatEquationForMarkdown(equation) : null\n }\n return null\n}\n\n// ─── 본문 파싱 (문단 리스트 + 컨트롤 디스패치) ───────\n\n/** 문서 전역 파싱 상태 — 섹션 간 유지 (번호 카운터, 머리말/꼬리말 등) */\nexport interface Hwp5DocState {\n numbering: NumberingState\n /** 구역 정의(secd)의 개요 번호 ID */\n outlineNumberingId: number\n /** 자동 번호 종류(0=쪽,1=각주,2=미주,3=그림,4=표,5=수식)별 다음 번호 */\n autoCounters: Map<number, number>\n headerTexts: Set<string>\n /** 목차 항목 글 — 표 칸에서 풀려 나온 문단도 제목 추정에서 뺀다 (#121, HWPX SectionShared.tocTexts 와 같은 구실) */\n tocTexts: Set<string>\n headerBlocks: IRBlock[]\n footerBlocks: IRBlock[]\n /** 표 후행 빈 열(앵커 있는 입력란) 보존 — ParseOptions.keepTrailingEmptyCols (#47) */\n keepTrailingEmptyCols?: boolean\n /** 미기입 누름틀 안내문도 본문에 — ParseOptions.includeFieldPlaceholders (#92) */\n includeFieldPlaceholders?: boolean\n}\n\nexport function createHwp5DocState(): Hwp5DocState {\n return {\n numbering: new NumberingState(),\n outlineNumberingId: 0,\n autoCounters: new Map(),\n headerTexts: new Set(),\n tocTexts: new Set(),\n headerBlocks: [],\n footerBlocks: [],\n }\n}\n\ninterface Hwp5Ctx {\n docInfo: HwpDocInfo | null\n warnings: ParseWarning[]\n sectionNum: number\n doc: Hwp5DocState\n depth: number\n /** 현재 페이지 (#66) — layout 모드에서 top-level 문단마다 갱신, 셀/중첩은 호스트 상속.\n * 섹션 근사 모드에선 sectionNum 고정 */\n page: number\n /** 프리패스 페이지 맵 — base: 이전 섹션 누적 페이지, pageAtPara: top-level 문단 순번별 페이지 */\n pageMap?: { base: number; pageAtPara: number[] }\n /** 지금까지 만난 top-level 문단 수 (pageAtPara 인덱스) */\n topOrdinal: number\n /** 표 CTRL_HEADER 레코드 인덱스 → sourceId (table-ids.ts 프리패스, 렌더 region 조인 키 #76) */\n tableIds?: Map<number, string>\n}\n\n/** 섹션 레코드 → IRBlock[] (테스트에서 직접 사용 가능하도록 export) */\nexport function parseSection(\n records: HwpRecord[],\n docInfo: HwpDocInfo | null,\n warnings: ParseWarning[],\n sectionNum: number,\n doc?: Hwp5DocState,\n pageMap?: { base: number; pageAtPara: number[] },\n tableIds?: Map<number, string>,\n): IRBlock[] {\n const ctx: Hwp5Ctx = {\n docInfo, warnings, sectionNum, doc: doc ?? createHwp5DocState(), depth: 0,\n page: pageMap ? pageMap.base + 1 : sectionNum, pageMap, topOrdinal: 0, tableIds,\n }\n return parseParagraphList(records, 0, records.length, ctx)\n}\n\n/**\n * 레코드 범위에서 문단 리스트 파싱 (rhwp body_text.rs parse_paragraph_list 패턴).\n * 표 셀/글상자/머리말/각주 내부에서도 동일하게 재귀 사용된다.\n */\nfunction parseParagraphList(records: HwpRecord[], start: number, end: number, ctx: Hwp5Ctx): IRBlock[] {\n const blocks: IRBlock[] = []\n let i = start\n while (i < end) {\n if (records[i].tagId === TAG_PARA_HEADER) {\n const baseLevel = records[i].level\n // 실제 페이지 갱신 (#66) — 프리패스의 top-level 문단 열거와 같은 순서(level 0)\n if (baseLevel === 0 && ctx.depth === 0 && ctx.pageMap) {\n const pg = ctx.pageMap.pageAtPara[ctx.topOrdinal]\n if (pg !== undefined) ctx.page = ctx.pageMap.base + pg + 1\n ctx.topOrdinal++\n }\n let j = i + 1\n while (j < end && records[j].level > baseLevel) j++\n blocks.push(...parseParagraph(records, i, j, ctx))\n i = j\n } else {\n i++\n }\n }\n return blocks\n}\n\n/** 문단 내 CTRL_HEADER 1개의 파싱 상태 */\ninterface ParsedCtrl {\n /** 정규화된 컨트롤 ID (u32, BE 문자열 상수와 비교 가능) */\n id: number\n /** LE로 읽은 원본 ID — PARA_TEXT 인라인 컨트롤과의 대조용 */\n idRaw: number\n /** CTRL_HEADER 레코드 데이터 (ctrl_id 4바이트 포함) */\n data: Buffer\n childStart: number\n childEnd: number\n /** 인라인 치환 텍스트 (수식/자동번호/각주 마커) */\n inlineText?: string\n /** 문단 뒤에 붙는 블록 (표/이미지/글상자) */\n afterBlocks?: IRBlock[]\n /** 각주/미주 내용 (\"1) 내용\" 형식) */\n footnote?: string\n /** 하이퍼링크 URL (%hlk) */\n href?: string\n /** 미기입 누름틀(%clk, 속성 bit 15 = 0)의 안내문 — 본문 run 에 같은 글이 있으면 값이 아니다 */\n guide?: string\n /** resolver 중복 매칭 방지 */\n resolved?: boolean\n}\n\n/** 문단 1개 파싱 → [문단 블록?, ...컨트롤 파생 블록] */\nfunction parseParagraph(records: HwpRecord[], start: number, end: number, ctx: Hwp5Ctx): IRBlock[] {\n const header = records[start]\n const baseLevel = header.level\n const paraShapeId = header.data.length >= 10 ? header.data.readUInt16LE(8) : -1\n\n const textRecords: Buffer[] = []\n const charShapeIds: number[] = []\n /** 글자 모양 위치표 [WCHAR 위치, charShapeId] — 첨자 판정 */\n const charShapeRuns: Array<[number, number]> = []\n const ctrls: ParsedCtrl[] = []\n\n let i = start + 1\n while (i < end) {\n const rec = records[i]\n\n if (rec.tagId === TAG_CTRL_HEADER && rec.level === baseLevel + 1 && rec.data.length >= 4) {\n // 컨트롤 자식 레코드 범위 수집 (rhwp parse_paragraph 패턴)\n const childStart = i + 1\n let j = childStart\n while (j < end && records[j].level > baseLevel + 1) j++\n const idRaw = rec.data.readUInt32LE(0)\n ctrls.push({ id: normalizeCtrlId(idRaw), idRaw, data: rec.data, childStart, childEnd: j })\n i = j\n continue\n }\n\n if (rec.tagId === TAG_PARA_TEXT && rec.level === baseLevel + 1) {\n textRecords.push(rec.data)\n } else if (rec.tagId === TAG_CHAR_SHAPE && rec.level === baseLevel + 1 && rec.data.length >= 8) {\n // 구조: [position(u32) + charShapeId(u32)] * N\n for (let offset = 0; offset + 7 < rec.data.length; offset += 8) {\n charShapeIds.push(rec.data.readUInt32LE(offset + 4))\n charShapeRuns.push([rec.data.readUInt32LE(offset), rec.data.readUInt32LE(offset + 4)])\n }\n }\n i++\n }\n\n // 컨트롤별 효과 계산 (인라인 치환/파생 블록/각주/링크)\n for (const ctrl of ctrls) {\n applyCtrlEffect(ctrl, records, ctx)\n // 글자처럼 취급 표는 문단 글 흐름 안의 자리 — 표지를 심어 앞뒤 글을 나눈다 (#49/#50 HWPX 대칭)\n if (ctrl.id === CTRL_TBL && ctrl.afterBlocks && ctrl.data.length >= 8 && (ctrl.data.readUInt32LE(4) & 1)) {\n ctrl.inlineText = INLINE_TABLE_MARK\n }\n }\n\n // 텍스트 렌더링 — 확장 컨트롤 인덱스 ↔ CTRL_HEADER 순서 매핑\n const state = createParaTextState()\n state.dollarMark = true\n state.tocLeaders = true\n state.scriptAt = scriptLookup(charShapeRuns, ctx.docInfo)\n const resolver: IndexedControlResolver = (idx, id) => {\n let ctrl = idx >= 0 && idx < ctrls.length ? ctrls[idx] : undefined\n if (!ctrl || (ctrl.idRaw !== id && ctrl.id !== id)) {\n ctrl = ctrls.find(c => !c.resolved && (c.idRaw === id || c.id === id))\n }\n if (!ctrl) return null\n ctrl.resolved = true\n return ctrl.inlineText ?? null\n }\n for (const data of textRecords) {\n appendParaText(state, data, resolver)\n }\n\n // FIELD_BEGIN/END 범위 — 시작 위치 내림차순으로 안전하게 치환. 하이퍼링크는 [anchor](url),\n // 미기입 누름틀의 안내문 run 은 지운다(한컴은 화면에만 흐리게 보이고 인쇄하지 않는다 — PDF 실측)\n let text = state.text\n if (state.fieldRanges.length > 0) {\n const ranges = [...state.fieldRanges].sort((a, b) => b.start - a.start)\n const applied: Array<[number, number]> = []\n for (const r of ranges) {\n const ctrl = ctrls[r.ctrlIdx]\n if (!ctrl || r.end <= r.start) continue\n if (applied.some(([s, e]) => r.start < e && r.end > s)) continue\n const anchor = text.slice(r.start, r.end)\n if (ctrl.guide !== undefined) {\n if (ctx.doc.includeFieldPlaceholders) continue\n const plain = anchor.replaceAll(LITERAL_DOLLAR_MARK, \"$\").replaceAll(LITERAL_LT_MARK, \"<\") // 안내문 원문과 맞댄다\n if (plain === ctrl.guide || plain.trimEnd() === ctrl.guide) {\n text = text.slice(0, r.start) + text.slice(r.end)\n applied.push([r.start, r.end])\n }\n continue\n }\n if (!ctrl.href) continue\n const href = sanitizeHref(ctrl.href)\n if (!href) continue\n if (!anchor.trim() || anchor.includes(INLINE_TABLE_MARK)) continue\n text = text.slice(0, r.start) + `[${anchor}](${href})` + text.slice(r.end)\n applied.push([r.start, r.end])\n }\n }\n // 리터럴 $ → \\$ (필드 위치를 다 쓴 뒤라 이제 두 글자로 늘려도 된다, escapeLiteralDollar 규약)\n if (text.includes(LITERAL_DOLLAR_MARK)) text = text.replaceAll(LITERAL_DOLLAR_MARK, \"\\\\$\")\n text = resolveLiteralLt(text)\n // 글자마다 여닫은 첨자 태그 정리 — HWPX section-walker 와 같은 꼴로\n text = tidyScriptTags(text)\n // 목차 항목(채움 탭 + 쪽 번호 줄) 판정 — 채움 탭 표지는 여기서 보통 탭이 된다 (#121, HWPX 와 같은 규칙)\n const toc = takeTocLeaders(text)\n text = toc.text\n\n // 문단번호/글머리표/개요 처리 (DocInfo PARA_SHAPE headType)\n let headingLevel = 0\n let headMarker: string | null = null\n const ps = ctx.docInfo && paraShapeId >= 0 && paraShapeId < ctx.docInfo.paraShapes.length\n ? ctx.docInfo.paraShapes[paraShapeId]\n : null\n if (ps && ps.headType > 0) {\n if (ps.headType === 1) {\n // 개요 — paraLevel 0-6 → heading 1-6 (개요 7수준은 H6로 클램프). 목차 항목은 개요 문단 모양을 빌려도 제목이 아니다\n if (!toc.tocEntry) headingLevel = Math.min(ps.paraLevel + 1, 6)\n }\n if (ps.headType === 1 || ps.headType === 2) {\n // 개요/번호 → NUMBERING 카운터 전진 + ^N 치환\n const nid = ps.numberingId || (ps.headType === 1 ? ctx.doc.outlineNumberingId : 0)\n const numbering = nid >= 1 ? ctx.docInfo?.numberings[nid - 1] : undefined\n if (numbering) {\n const counters = ctx.doc.numbering.advance(nid, ps.paraLevel)\n const fmt = numbering.levelFormats[Math.min(ps.paraLevel, 6)]\n if (fmt) {\n const headText = expandNumberingFormat(fmt, counters, numbering)\n if (headText) headMarker = headText\n }\n }\n } else if (ps.headType === 3) {\n // 글머리표 — U+FFFF는 이미지 글머리표 (문자 렌더링 불가)\n const bullet = ps.numberingId >= 1 ? ctx.docInfo?.bullets[ps.numberingId - 1] : undefined\n if (bullet && bullet.char !== \"￿\") headMarker = bullet.char\n }\n }\n\n if (toc.tocEntry) for (const line of text.split(\"\\n\")) if (line.trim()) {\n ctx.doc.tocTexts.add(line.trim())\n if (headMarker) ctx.doc.tocTexts.add(`${headMarker} ${line.trim()}`)\n }\n return emitParagraphBlocks({\n text,\n headMarker,\n headingLevel,\n tocEntry: toc.tocEntry,\n style: ctx.docInfo && charShapeIds.length > 0 ? resolveCharStyle(charShapeIds, ctx.docInfo) : undefined,\n footnotes: ctrls.filter(c => c.footnote).map(c => c.footnote!),\n // 컨트롤 파생 블록 (표/이미지/글상자) — 컨트롤 순서대로\n objects: ctrls.filter(c => c.afterBlocks).map(c => ({ blocks: c.afterBlocks!, table: c.id === CTRL_TBL, inline: c.inlineText === INLINE_TABLE_MARK })),\n pageNumber: ctx.page,\n })\n}\n\n/** 컨트롤 종류별 디스패치 (rhwp control.rs parse_control 대응) */\nfunction applyCtrlEffect(ctrl: ParsedCtrl, records: HwpRecord[], ctx: Hwp5Ctx): void {\n switch (ctrl.id) {\n case CTRL_TBL: {\n const table = parseTableControl(ctrl, records, ctx)\n if (table) ctrl.afterBlocks = [{ type: \"table\", table, pageNumber: ctx.page }]\n return\n }\n case CTRL_GSO: {\n const blocks = parseGsoControl(ctrl, records, ctx)\n if (blocks.length > 0) ctrl.afterBlocks = blocks\n return\n }\n case CTRL_EQED: {\n const eq = extractEquationFromSlice(records, ctrl.childStart, ctrl.childEnd)\n if (eq) ctrl.inlineText = eq\n return\n }\n case CTRL_FN:\n case CTRL_EN: {\n applyNoteEffect(ctrl, records, ctx, ctrl.id === CTRL_FN ? 1 : 2)\n return\n }\n case CTRL_HEAD:\n case CTRL_FOOT: {\n applyHeaderFooterEffect(ctrl, records, ctx, ctrl.id === CTRL_HEAD)\n return\n }\n case CTRL_ATNO: {\n // 자동 번호 (표 144): attr(u32) + number(u16) + 사용자기호 + 앞장식 + 뒤장식 (WCHAR)\n if (ctrl.data.length >= 8) {\n const attr = ctrl.data.readUInt32LE(4)\n const type = attr & 0x0f\n const format = (attr >>> 4) & 0xff\n const num = ctx.doc.autoCounters.get(type) ?? 1\n ctx.doc.autoCounters.set(type, num + 1)\n const prefix = ctrl.data.length >= 14 ? wcharAt(ctrl.data, 12) : \"\"\n const suffix = ctrl.data.length >= 16 ? wcharAt(ctrl.data, 14) : \"\"\n ctrl.inlineText = `${prefix}${formatNumber(num, shapeFormatToNumFmt(format))}${suffix}`\n }\n return\n }\n case CTRL_NWNO: {\n // 새 번호 지정 — 해당 종류의 카운터를 재설정 (표시 없음)\n if (ctrl.data.length >= 10) {\n const attr = ctrl.data.readUInt32LE(4)\n const type = attr & 0x0f\n const num = ctrl.data.readUInt16LE(8)\n if (num > 0) ctx.doc.autoCounters.set(type, num)\n }\n return\n }\n case CTRL_SECD: {\n // 구역 정의 — 개요 번호 ID (ctrl_id 4 + flags 4 + 간격 2*3 + tab 4 = offset 18)\n if (ctrl.data.length >= 20) {\n ctx.doc.outlineNumberingId = ctrl.data.readUInt16LE(18)\n }\n return\n }\n case CTRL_OLE: {\n ctx.warnings.push({ page: ctx.page, message: \"스킵된 OLE 개체\", code: \"SKIPPED_OLE\" })\n return\n }\n // 숨은 설명/단 정의/쪽번호 위치/감추기/찾아보기/책갈피/글자겹침/덧말 — 본문 텍스트 없음 또는 의도적 스킵\n case CTRL_TCMT:\n case CTRL_COLD:\n case CTRL_PGNP:\n case CTRL_PGHD:\n case CTRL_IDXM:\n case CTRL_BOKM:\n case CTRL_TCPS:\n case CTRL_TDUT:\n case CTRL_FORM:\n return\n default: {\n if (isFieldCtrlId(ctrl.id)) {\n applyFieldEffect(ctrl)\n return\n }\n // 알 수 없는 컨트롤 — LIST_HEADER 문단 리스트가 있으면 텍스트 보존 (정보손실 방지)\n const blocks = parseListHeaderParagraphs(ctrl, records, ctx)\n if (blocks.length > 0) ctrl.afterBlocks = blocks\n }\n }\n}\n\n/** WCHAR 1글자 읽기 (0이면 빈 문자열) */\nfunction wcharAt(data: Buffer, offset: number): string {\n const code = data.readUInt16LE(offset)\n return code > 0 ? String.fromCharCode(code) : \"\"\n}\n\n/** 컨트롤 자식에서 첫 LIST_HEADER 이후의 문단 리스트 파싱 (rhwp find_list_header_paragraphs) */\nfunction parseListHeaderParagraphs(ctrl: ParsedCtrl, records: HwpRecord[], ctx: Hwp5Ctx): IRBlock[] {\n if (ctx.depth >= MAX_NEST_DEPTH) return []\n for (let i = ctrl.childStart; i < ctrl.childEnd; i++) {\n if (records[i].tagId === TAG_LIST_HEADER) {\n return parseParagraphList(records, i + 1, ctrl.childEnd, { ...ctx, depth: ctx.depth + 1 })\n }\n }\n return []\n}\n\n/** 각주('fn ')/미주('en ') — 번호 + 장식문자 + 내용 (rhwp parse_footnote_control) */\nfunction applyNoteEffect(ctrl: ParsedCtrl, records: HwpRecord[], ctx: Hwp5Ctx, autoType: number): void {\n // ctrl 데이터: ctrl_id(4) + number(u32) + before(WCHAR) + after(WCHAR) + numberShape(u32)\n // 번호는 각주 내용 안의 atno(자동번호)가 같은 카운터를 소비하므로 peek만 하고,\n // 내용 파싱 후 카운터가 안 움직였으면(atno 없음) 직접 전진시킨다 (rhwp assign_auto_numbers 정합)\n const num = ctx.doc.autoCounters.get(autoType) ?? 1\n\n let before = \"\"\n let after = \"\"\n let shape = 0\n if (ctrl.data.length >= 12) {\n before = wcharAt(ctrl.data, 8)\n after = wcharAt(ctrl.data, 10)\n }\n if (ctrl.data.length >= 16) {\n shape = ctrl.data.readUInt32LE(12) & 0xff\n }\n const formatted = formatNumber(num, shapeFormatToNumFmt(shape))\n const marker = before || after ? `${before}${formatted}${after}` : `${formatted})`\n\n const content = blocksPlainText(parseListHeaderParagraphs(ctrl, records, ctx), \" \")\n if ((ctx.doc.autoCounters.get(autoType) ?? 1) <= num) {\n ctx.doc.autoCounters.set(autoType, num + 1)\n }\n ctrl.inlineText = marker\n // 각주 내용 첫머리의 atno가 이미 같은 마커를 생성했으면 중복 방지\n if (content) ctrl.footnote = content.startsWith(marker) ? content : `${marker} ${content}`\n}\n\n/** 머리말('head')/꼬리말('foot') — 문서당 1회, 동일 텍스트 중복 제거 */\nfunction applyHeaderFooterEffect(ctrl: ParsedCtrl, records: HwpRecord[], ctx: Hwp5Ctx, isHeader: boolean): void {\n const text = blocksPlainText(parseListHeaderParagraphs(ctrl, records, ctx), \"\\n\")\n if (!text) return\n const key = (isHeader ? \"h:\" : \"f:\") + text\n if (ctx.doc.headerTexts.has(key)) return\n ctx.doc.headerTexts.add(key)\n const block: IRBlock = { type: \"paragraph\", text, pageNumber: ctx.page }\n if (isHeader) ctx.doc.headerBlocks.push(block)\n else ctx.doc.footerBlocks.push(block)\n}\n\n/** 필드 컨트롤(%hlk/%clk 등) — command 파싱 (rhwp parse_field_control, 표 154) */\nfunction applyFieldEffect(ctrl: ParsedCtrl): void {\n if (ctrl.id === FIELD_HLK) {\n const command = parseFieldCommand(ctrl.data)\n if (command) {\n const url = hyperlinkUrlFromCommand(command)\n if (url) ctrl.href = url\n }\n } else if (ctrl.id === FIELD_CLK && ctrl.data.length >= 8 && !(ctrl.data.readUInt32LE(4) & (1 << 15))) {\n // 누름틀 — 속성 bit 15(내용 수정됨)가 꺼진 미기입 필드는 한컴이 안내문(command Direction)을 본문 run 으로\n // 저장해 두고 화면에만 흐리게 그린다(인쇄·PDF 에 없음). 같은 글이면 값이 아니므로 parseParagraph 가 지운다\n // (rhwp clear_initial_field_texts 와 같은 이중 조건 — 수정됨 비트 + 안내문 일치)\n const command = parseFieldCommand(ctrl.data)\n const guide = command ? clickHereGuide(command) : undefined\n if (guide) ctrl.guide = guide\n }\n // 그 밖의 필드: anchor 텍스트는 PARA_TEXT에 있으므로 그대로 보존됨\n}\n\n/** 누름틀 command 의 안내문 — `Direction:wstring:<N>:` 뒤 N자 (UTF-16) */\nfunction clickHereGuide(command: string): string | undefined {\n const m = /Direction:wstring:(\\d+):/.exec(command)\n if (!m) return undefined\n const start = m.index + m[0].length\n return command.slice(start, start + Number(m[1])) || undefined\n}\n\n/** 필드 CTRL_HEADER 데이터에서 command 추출 — ctrl_id(4) + 속성(4) + 기타(1) + len(u16) + UTF-16LE */\nfunction parseFieldCommand(data: Buffer): string | null {\n if (data.length < 11) return null\n const cmdLen = data.readUInt16LE(9)\n if (cmdLen === 0) return null\n const start = 11\n const end = start + cmdLen * 2\n if (end > data.length) return null\n return data.subarray(start, end).toString(\"utf16le\").replace(/\\0+$/, \"\")\n}\n\n/** %hlk command에서 URL 추출 — 첫 ';' 구분 토큰 ('\\;' 이스케이프 존중). mailto/책갈피(#) 포함 */\nfunction hyperlinkUrlFromCommand(command: string): string | null {\n let url = \"\"\n for (let i = 0; i < command.length; i++) {\n const c = command[i]\n if (c === \"\\\\\" && i + 1 < command.length) {\n url += command[i + 1]\n i++\n continue\n }\n if (c === \";\") break\n url += c\n }\n url = url.trim()\n return url.length > 0 && url.length < 2000 ? url : null\n}\n\n// ─── 표 파싱 ─────────────────────────────────────────\n\n/** HWP5 셀 — CellContext + 중첩 구조 */\ninterface Hwp5Cell extends CellContext {\n blocks?: IRBlock[]\n isHeader?: boolean\n /** 칸의 보이는 변 (LIST_HEADER borderFillId u16@32 → DocInfo BORDER_FILL) */\n edges?: Edges\n /** 칸 높이가 A4 용지보다 크다 — 여러 쪽에 걸친 칸 */\n pageSpanning?: boolean\n}\n\n/** A4 용지 높이 (HWPUNIT, 297mm) — 이보다 높은 칸은 한 쪽에 들어갈 수 없다 */\nconst A4_HEIGHT = 84188\n\n/** 레이아웃 표 해체(flattenLayoutTables)는 여러 쪽에 걸친 칸이 있는 본문 상자만 — 나머지 표는 같은 문서의 HWPX 처럼 표로 둔다.\n * 보도자료 본문 전체를 9쪽짜리 3×1 상자에 담은 문서(rhwp issue3637)는 표로 두면 문서가 거대한 HTML 칸 하나가 된다 */\nfunction keepUnlessPageSpanning(table: IRTable, cells: Hwp5Cell[]): void {\n if (!cells.some(c => c.pageSpanning)) markNonLayoutTable(table)\n}\n\n/**\n * 표 컨트롤 파싱 (rhwp parse_table_control).\n * HWPTAG_TABLE 레코드 '이전'의 LIST_HEADER는 캡션, '이후'는 셀.\n */\nfunction parseTableControl(ctrl: ParsedCtrl, records: HwpRecord[], ctx: Hwp5Ctx): IRTable | null {\n if (ctx.depth >= MAX_NEST_DEPTH) return null\n const { childStart, childEnd } = ctrl\n // sourceId — CTRL_HEADER 레코드 인덱스(childStart − 1)로 프리패스 순번을 찾는다 (렌더 어댑터와 같은 키)\n const sourceId = ctx.tableIds?.get(childStart - 1)\n\n // HWPTAG_TABLE 레코드에서 행/열 수 — 직계 자식 레벨(CTRL_HEADER + 1)의 것만. 캡션 문단 안 표는 자기 TABLE\n // 레코드를 바깥 TABLE 보다 먼저 방출하므로 첫 TABLE 을 집으면 행/열이 그 표 것이 되고 캡션이 잘린다\n // (rhwp #3528 — 저장 순서 CTRL_HEADER → 캡션 LIST_HEADER·문단 → TABLE → 셀). hwp5-patch 스캔과 같은 규칙\n const directLevel = records[childStart - 1].level + 1\n let rows = 0\n let cols = 0\n let tableIdx = -1\n for (let i = childStart; i < childEnd; i++) {\n if (records[i].tagId === TAG_TABLE && records[i].level === directLevel && records[i].data.length >= 8) {\n rows = Math.min(records[i].data.readUInt16LE(4), MAX_ROWS)\n cols = Math.min(records[i].data.readUInt16LE(6), MAX_COLS)\n tableIdx = i\n break\n }\n }\n if (tableIdx < 0) return null\n\n // 캡션: TABLE 레코드 이전의 LIST_HEADER\n let caption: string | undefined\n for (let i = childStart; i < tableIdx; i++) {\n if (records[i].tagId === TAG_LIST_HEADER) {\n const capBlocks = parseParagraphList(records, i + 1, tableIdx, { ...ctx, depth: ctx.depth + 1 })\n const capText = blocksPlainText(capBlocks, \" \")\n if (capText) caption = capText\n break\n }\n }\n\n // 셀: TABLE 레코드 이후의 LIST_HEADER\n const cells: Hwp5Cell[] = []\n let i = tableIdx + 1\n while (i < childEnd) {\n const rec = records[i]\n if (rec.tagId === TAG_LIST_HEADER) {\n const cellLevel = rec.level\n let j = i + 1\n while (j < childEnd) {\n const r = records[j]\n if (r.level < cellLevel) break\n if (r.level === cellLevel && (r.tagId === TAG_LIST_HEADER || r.tagId === TAG_TABLE)) break\n j++\n }\n cells.push(parseCell(records, i, j, ctx))\n i = j\n continue\n }\n i++\n }\n\n if (cells.length === 0) return null\n\n // colAddr/rowAddr 절대 좌표 — builder 직접 배치로 HWPX 와 같은 표 계약(후행 빈 열 트림 등, ir-assemble.ts)\n if (cells.some(c => c.colAddr !== undefined && c.rowAddr !== undefined)) {\n const table = buildAddressedTable(cells, rows, cols, ctx.doc.keepTrailingEmptyCols)\n if (!table) return null\n if (caption) table.caption = caption\n if (sourceId) table.sourceId = sourceId\n keepUnlessPageSpanning(table, cells)\n return table\n }\n\n // 좌표 없는 셀 + 행·열 0 손상 레코드 — 셀마다 한 행으로 (글 손실 금지)\n if (rows === 0 || cols === 0) { rows = Math.min(cells.length, MAX_ROWS); cols = 1 }\n const cellRows = arrangeCells(rows, cols, cells)\n const table = buildTable(cellRows, { keepAnchoredEmptyCols: ctx.doc.keepTrailingEmptyCols })\n if (caption && table.rows > 0) table.caption = caption\n if (sourceId && table.rows > 0) table.sourceId = sourceId\n keepUnlessPageSpanning(table, cells)\n return table.rows > 0 ? table : null\n}\n\n/**\n * 표 셀 파싱 (rhwp parse_cell) — LIST_HEADER 구조:\n * paraCount(u16) + listAttr(u32) + widthRef(u16) + colAddr(u16) + rowAddr(u16) + colSpan(u16) + rowSpan(u16)\n * offset: 0 2 6 8 10 12 14\n * widthRef bit 2 = 제목 셀(is_header).\n * 셀 내부 문단 리스트는 재귀 파싱 — 중첩표는 IRCell.blocks에 IRBlock(type:'table')로 보존.\n */\nfunction parseCell(records: HwpRecord[], lhIdx: number, end: number, ctx: Hwp5Ctx): Hwp5Cell {\n const rec = records[lhIdx]\n let colSpan = 1\n let rowSpan = 1\n let colAddr: number | undefined\n let rowAddr: number | undefined\n let isHeader = false\n if (rec.data.length >= 16) {\n isHeader = (rec.data.readUInt16LE(6) & 0x04) !== 0\n colAddr = rec.data.readUInt16LE(8)\n rowAddr = rec.data.readUInt16LE(10)\n const cs = rec.data.readUInt16LE(12)\n const rs = rec.data.readUInt16LE(14)\n if (cs > 0) colSpan = Math.min(cs, MAX_COLS)\n if (rs > 0) rowSpan = Math.min(rs, MAX_ROWS)\n }\n\n const blocks = ctx.depth < MAX_NEST_DEPTH\n ? parseParagraphList(records, lhIdx + 1, end, { ...ctx, depth: ctx.depth + 1 })\n : []\n\n // 하위 호환 텍스트: 문단 평탄화 + 이미지 sentinel + 중첩표 평문 (각주는 문단 글에 접힌다)\n const { text, hasStructure } = cellTextFromBlocks(blocks)\n const cell: Hwp5Cell = { text, colSpan, rowSpan, colAddr, rowAddr }\n if (hasStructure && blocks.length > 0) cell.blocks = blocks\n if (isHeader) cell.isHeader = true\n if (rec.data.length >= 24 && rec.data.readUInt32LE(20) > A4_HEIGHT) cell.pageSpanning = true\n if (rec.data.length >= 34) {\n const borderFillId = rec.data.readUInt16LE(32)\n const definitions = ctx.docInfo?.borderEdges\n // 1-based 목록이 확인되면 0은 선 없음이다. 정의가 없는 파일은 미상으로 유지한다.\n const edges = borderFillId === 0 && definitions?.length\n ? { t: false, b: false, l: false, r: false }\n : definitions?.[borderFillId - 1]\n if (edges) cell.edges = edges\n }\n return cell\n}\n\n/** colAddr 없는 셀(짧은 LIST_HEADER)의 순차 배치 폴백 — 좌표가 있으면 parseTableControl 이 builder 직접 배치 */\nfunction arrangeCells(rows: number, cols: number, cells: Hwp5Cell[]): Hwp5Cell[][] {\n const grid: (Hwp5Cell | null)[][] = Array.from({ length: rows }, () => Array(cols).fill(null))\n let cellIdx = 0\n for (let r = 0; r < rows && cellIdx < cells.length; r++) {\n for (let c = 0; c < cols && cellIdx < cells.length; c++) {\n if (grid[r][c] !== null) continue\n const cell = cells[cellIdx++]\n grid[r][c] = cell\n\n for (let dr = 0; dr < cell.rowSpan; dr++) {\n for (let dc = 0; dc < cell.colSpan; dc++) {\n if (dr === 0 && dc === 0) continue\n if (r + dr < rows && c + dc < cols)\n grid[r + dr][c + dc] = { text: \"\", colSpan: 1, rowSpan: 1 }\n }\n }\n }\n }\n return grid.map(row => row.map(c => c || { text: \"\", colSpan: 1, rowSpan: 1 }))\n}\n\n// ─── 그리기 개체(GSO) 파싱 ───────────────────────────\n\n/**\n * GSO 컨트롤 파싱 (rhwp parse_gso_control).\n * - SHAPE_COMPONENT '이전' LIST_HEADER = 캡션\n * - SHAPE_COMPONENT '이후' 첫 LIST_HEADER = 글상자 문단 리스트\n * - SHAPE_COMPONENT_PICTURE 레코드 → binDataId(고정 오프셋 71) → 이미지 블록\n */\nfunction parseGsoControl(ctrl: ParsedCtrl, records: HwpRecord[], ctx: Hwp5Ctx): IRBlock[] {\n if (ctx.depth >= MAX_NEST_DEPTH) return []\n const { childStart, childEnd } = ctrl\n const blocks: IRBlock[] = []\n\n // 첫 SHAPE_COMPONENT(_CONTAINER) 위치\n let scIdx = -1\n for (let i = childStart; i < childEnd; i++) {\n const t = records[i].tagId\n if (t === TAG_SHAPE_COMPONENT || t === TAG_SHAPE_COMPONENT_CONTAINER) {\n scIdx = i\n break\n }\n }\n\n // 캡션: SHAPE_COMPONENT 이전의 LIST_HEADER\n if (scIdx > childStart) {\n for (let i = childStart; i < scIdx; i++) {\n if (records[i].tagId === TAG_LIST_HEADER) {\n blocks.push(...parseParagraphList(records, i + 1, scIdx, { ...ctx, depth: ctx.depth + 1 }))\n break\n }\n }\n }\n\n // 글상자: SHAPE_COMPONENT 이후 첫 LIST_HEADER부터 끝까지\n const scanStart = scIdx >= 0 ? scIdx + 1 : childStart\n let textListIdx = -1\n for (let i = scanStart; i < childEnd; i++) {\n if (records[i].tagId === TAG_LIST_HEADER) {\n textListIdx = i\n break\n }\n }\n\n // 그림: 글상자 리스트 이전 구간에서 SHAPE_COMPONENT_PICTURE 스캔\n // (글상자 내부의 중첩 gso 그림은 문단 리스트 재귀에서 처리되므로 이중 집계 없음)\n const picEnd = textListIdx >= 0 ? textListIdx : childEnd\n for (let i = scanStart; i < picEnd; i++) {\n if (records[i].tagId === TAG_SHAPE_COMPONENT_PICTURE) {\n const img = pictureToImageBlock(records[i].data, ctx)\n if (img) blocks.push(img)\n }\n }\n\n if (textListIdx >= 0) {\n blocks.push(...parseParagraphList(records, textListIdx + 1, childEnd, { ...ctx, depth: ctx.depth + 1 }))\n }\n\n return blocks\n}\n\n/**\n * SHAPE_COMPONENT_PICTURE → 이미지 블록.\n * rhwp parse_picture(shape.rs)의 고정 레이아웃:\n * borderColor(4) + borderWidth(4) + borderAttr(4) + 꼭짓점 x4/y4(32) + crop(16)\n * + padding(8) + 밝기(1)/대비(1)/효과(1) = 71 → binDataId(u16 @71)\n * binDataId는 DocInfo BIN_DATA 1-based 인덱스 → storage_id로 변환 (스트림명 BIN%04X 16진).\n */\nfunction pictureToImageBlock(data: Buffer, ctx: Hwp5Ctx): IRBlock | null {\n if (data.length < 73) return null\n const binDataId = data.readUInt16LE(71)\n if (binDataId === 0) return null\n\n const item = ctx.docInfo?.binData[binDataId - 1]\n if (item?.kind === \"link\") {\n ctx.warnings.push({ page: ctx.page, message: `외부 연결 이미지 (binDataId ${binDataId})`, code: \"SKIPPED_IMAGE\" })\n return null\n }\n // DocInfo BIN_DATA가 없으면 binDataId == storageId 관례에 폴백\n const storageId = item && item.storageId > 0 ? item.storageId : binDataId\n return { type: \"image\", text: String(storageId), pageNumber: ctx.page }\n}\n\n// ─── 스타일 ──────────────────────────────────────────\n\n/**\n * 글자 위치 → 첨자 종류. CharShape attr bit 15 = 위 첨자, bit 16 = 아래 첨자(HWP5 스펙 표 35 — 계량법 별표\n * \"10⁴ m²\" 의 위첨자 글자 모양 id 가 HWPX supscript charPr id 와 같음을 실측). 첨자 글자 모양이 없으면 undefined\n */\nfunction scriptLookup(runs: Array<[number, number]>, docInfo: HwpDocInfo | null): ((pos: number) => ScriptKind | null) | undefined {\n if (!docInfo || !runs.length) return undefined\n const kindOf = (id: number): ScriptKind | null => {\n const f = docInfo.charShapes[id]?.attrFlags ?? 0\n return f & (1 << 15) ? \"sup\" : f & (1 << 16) ? \"sub\" : null\n }\n if (!runs.some(([, id]) => kindOf(id))) return undefined\n let k = -1 // 글자 위치는 늘어나는 순서로 묻는다 — 커서를 앞으로만(되돌아가면 처음부터)\n return (pos) => {\n if (k >= 0 && runs[k][0] > pos) k = -1\n while (k + 1 < runs.length && runs[k + 1][0] <= pos) k++\n return k >= 0 ? kindOf(runs[k][1]) : null\n }\n}\n\n/** CHAR_SHAPE ID 배열에서 대표 스타일 결정 (최빈값) */\nfunction resolveCharStyle(charShapeIds: number[], docInfo: HwpDocInfo): InlineStyle | undefined {\n if (charShapeIds.length === 0 || docInfo.charShapes.length === 0) return undefined\n\n // 가장 많이 나타나는 charShapeId 사용\n const freq = new Map<number, number>()\n let maxCount = 0, dominantId = charShapeIds[0]\n for (const id of charShapeIds) {\n const count = (freq.get(id) || 0) + 1\n freq.set(id, count)\n if (count > maxCount) { maxCount = count; dominantId = id }\n }\n\n const cs = docInfo.charShapes[dominantId]\n if (!cs) return undefined\n\n const style: InlineStyle = {}\n if (cs.fontSize > 0) style.fontSize = cs.fontSize / 10 // 0.1pt → pt\n if (cs.attrFlags & 0x01) style.italic = true\n if (cs.attrFlags & 0x02) style.bold = true\n if (hasRealStrike(cs.attrFlags)) style.strike = true\n if (hasRealUnderline(cs.attrFlags)) style.underline = true\n\n return (style.fontSize || style.bold || style.italic || style.strike || style.underline) ? style : undefined\n}\n\n/**\n * CharShape attr 의 취소선 판정 — 비트(18-20)만 믿으면 안 된다: 한컴은 취소선 없는\n * 문자에도 1을 기본값으로 넣는다. 진짜 판별자는 취소선 모양(bit 26-29)이며, 취소선이\n * 없으면 표 27 선 종류(0~12)가 아닌 placeholder(15 등)가 들어온다. 알 수 없는 값은\n * fail-closed 로 no-strike (rhwp 0a967e0d, HWPX isRealStrikeShape 와 같은 원리).\n */\nexport function hasRealStrike(attrFlags: number): boolean {\n const strikeBits = (attrFlags >> 18) & 0x07\n const shapeId = (attrFlags >> 26) & 0x0f\n return strikeBits !== 0 && shapeId <= 12\n}\n\n/**\n * CharShape attr 의 밑줄 판정 — bit 2-3 밑줄 종류 (0=없음, 1=글자 아래, 3=글자 위;\n * rhwp doc_info 직렬화와 동일 레이아웃). 취소선과 달리 종류 비트 자체가 판별자다\n * (HWPX 실측: 밑줄 없는 charPr 는 type=\"NONE\"=비트 0). BOTTOM(1)만 인정 —\n * 윗줄(3)·미지 값은 fail-closed.\n */\nexport function hasRealUnderline(attrFlags: number): boolean {\n return ((attrFlags >> 2) & 0x03) === 1\n}\n","/** HWP 5.x 바이너리 파서 — OLE2 컨테이너 → 섹션 → Markdown */\n\nimport {\n readRecords, decompressStream, parseFileHeader, parseDocInfo, FLAG_COMPRESSED, FLAG_ENCRYPTED,\n FLAG_DISTRIBUTION, FLAG_DRM, type HwpRecord, type HwpDocInfo, type HwpFileHeader,\n} from \"./record.js\"\nimport { extractHwp5Images, extractHwp5ImagesLenient, collectHwp5BinData, collectHwp5BinDataLenient } from \"./images.js\"\nimport { inlineImagesIntoMarkdown } from \"../image/transcode.js\"\nimport { decryptViewText } from \"./crypto.js\"\nimport { parseLenientCfb, type LenientCfbContainer } from \"./cfb-lenient.js\"\nimport { blocksToMarkdown, flattenLayoutTables, dedupeRunningHeaders } from \"../table/builder.js\"\nimport { unframeLayoutTables } from \"../table/layout-frames.js\"\nimport type { IRBlock, DocumentMetadata, InternalParseResult, ParseOptions, ParseWarning, OutlineItem } from \"../types.js\"\nimport { HEADING_RATIO_H1, HEADING_RATIO_H2, HEADING_RATIO_H3 } from \"../types.js\"\nimport { assertDecryptedDocInfo, assertSupportedEncryptVersion, decryptPasswordStream, readEncryptVersion } from \"./pw-crypto.js\"\nimport { KordocError } from \"../utils.js\"\nimport { TOC_ENTRY_BLOCKS } from \"../toc-entry.js\"\nimport { parsePageRange } from \"../page-range.js\"\nimport { detectHwp5SectionPages, type Hwp5SectionPageDetect } from \"./page-boundary.js\"\nimport { indexHwp5Tables } from \"./table-ids.js\"\nimport { parseSection, createHwp5DocState } from \"./body.js\"\n\nexport { parseSection, createHwp5DocState, hasRealStrike, hasRealUnderline, type Hwp5DocState } from \"./body.js\"\n\nimport { createRequire } from \"module\"\nconst require = createRequire(import.meta.url)\nconst CFB: CfbModule = require(\"cfb\")\n\ninterface CfbEntry { name?: string; content?: Buffer | Uint8Array }\ninterface CfbContainer { FileIndex?: CfbEntry[] }\ninterface CfbModule {\n parse(data: Buffer): CfbContainer\n find(cfb: CfbContainer, path: string): CfbEntry | null\n}\n\n/** 최대 섹션 수 — 비정상 파일에 의한 무한 루프 방지 */\nconst MAX_SECTIONS = 100\n/** 누적 압축 해제 최대 크기 (100MB) */\nconst MAX_TOTAL_DECOMPRESS = 100 * 1024 * 1024\n\n/**\n * 열린 HWP5 컨테이너 — 파서와 렌더 어댑터(#75 Task 7)가 공유하는 스트림 접근 계층.\n * strict CFB → lenient 폴백, DRM 거부, 암호 문서 복호(findStream 에 내장), 압축·배포용 플래그.\n */\nexport interface Hwp5Container {\n cfb: CfbContainer | null\n lenientCfb: LenientCfbContainer | null\n header: HwpFileHeader\n compressed: boolean\n distribution: boolean\n encrypted: boolean\n /** 스트림 읽기 — 암호 문서는 복호를 거친다 */\n findStream: (path: string) => Buffer | null\n warnings: ParseWarning[]\n}\n\nexport function openHwp5Container(buffer: Buffer, options?: Pick<ParseOptions, \"password\">): Hwp5Container {\n // CFB 파싱: strict 먼저, 실패 시 lenient 폴백\n let cfb: CfbContainer | null = null\n let lenientCfb: LenientCfbContainer | null = null\n const warnings: ParseWarning[] = []\n\n try {\n cfb = CFB.parse(buffer)\n } catch {\n try {\n lenientCfb = parseLenientCfb(buffer)\n warnings.push({ message: \"손상된 CFB 컨테이너 — lenient 모드로 복구\", code: \"LENIENT_CFB_RECOVERY\" })\n } catch {\n throw new KordocError(\"CFB 컨테이너 파싱 실패 (strict 및 lenient 모두)\")\n }\n }\n\n // CFB 래퍼: strict/lenient 통합 인터페이스\n const readRawStream = (path: string): Buffer | null => {\n if (cfb) {\n const entry = CFB.find(cfb, path)\n return entry?.content ? Buffer.from(entry.content) : null\n }\n return lenientCfb!.findStream(path)\n }\n\n const headerData = readRawStream(\"/FileHeader\")\n if (!headerData) throw new KordocError(\"FileHeader 스트림 없음\")\n const header = parseFileHeader(headerData)\n if (header.signature !== \"HWP Document File\") throw new KordocError(\"HWP 시그니처 불일치\")\n if (header.flags & FLAG_DRM) throw new KordocError(\"DRM 보호된 HWP는 지원하지 않습니다\")\n\n // 비밀번호 암호 문서 — FileHeader 외 모든 스트림이 AES-CFB 로 암호화되어 있다.\n // 복호를 findStream 래퍼에 끼워 넣어 이후 경로가 평문 문서와 같아지게 한다.\n const encrypted = (header.flags & FLAG_ENCRYPTED) !== 0\n if (encrypted) {\n if (!options?.password) {\n throw new KordocError(\"암호로 보호된 HWP 문서입니다. password 옵션에 열기 암호를 지정하세요.\")\n }\n assertSupportedEncryptVersion(readEncryptVersion(headerData))\n }\n const password = encrypted ? options!.password! : undefined\n const findStream = (path: string): Buffer | null => {\n const raw = readRawStream(path)\n if (!raw || !password) return raw\n return decryptPasswordStream(raw, password)\n }\n\n const compressed = (header.flags & FLAG_COMPRESSED) !== 0\n const distribution = (header.flags & FLAG_DISTRIBUTION) !== 0\n\n // 암호 문서는 여기서 비밀번호를 검증한다 — DocInfo·섹션 파싱은 실패를 경고로 흡수해서,\n // 오답으로 나온 난수 데이터가 \"성공했는데 내용이 쓰레기\"인 결과로 흘러가기 때문이다.\n if (encrypted) {\n const rawDocInfo = findStream(\"/DocInfo\")\n if (!rawDocInfo) throw new KordocError(\"DocInfo 스트림 없음\")\n let docInfoData: Buffer\n try {\n docInfoData = compressed ? decompressStream(rawDocInfo) : rawDocInfo\n } catch {\n throw new KordocError(\"비밀번호가 일치하지 않거나 암호화 데이터가 손상되었습니다.\")\n }\n assertDecryptedDocInfo(docInfoData, readRecords)\n }\n\n return { cfb, lenientCfb, header, compressed, distribution, encrypted, findStream, warnings }\n}\n\n/** DocInfo 원시 레코드 (best-effort) — 렌더 어댑터가 FACE_NAME·BORDER_FILL 등 파서가 안 쓰는 레코드를 읽는다 */\nexport function readHwp5DocInfoRecords(c: Hwp5Container): HwpRecord[] | null {\n try {\n const raw = c.findStream(\"/DocInfo\")\n if (!raw) return null\n return readRecords(c.compressed ? decompressStream(raw) : raw)\n } catch {\n return null\n }\n}\n\n/** 섹션 원시 스트림 — 배포용은 복호+압축해제 완료, 그 외는 readHwp5SectionRecords 가 압축을 푼다 */\nexport function readHwp5SectionStreams(c: Hwp5Container): Buffer[] {\n return c.distribution\n ? (c.cfb ? findViewTextSections(c.cfb, c.compressed) : findViewTextSectionsLenient(c.lenientCfb!, c.compressed))\n : c.encrypted\n ? findSectionsVia(c.findStream)\n : (c.cfb ? findSections(c.cfb) : findSectionsLenient(c.lenientCfb!))\n}\n\n/** 섹션 스트림 → 레코드 (누적 압축해제 상한). 실패한 섹션은 null + PARTIAL_PARSE 경고 */\nexport function readHwp5SectionRecords(c: Hwp5Container, sections: Buffer[], warnings: ParseWarning[]): (HwpRecord[] | null)[] {\n const out: (HwpRecord[] | null)[] = []\n let totalDecompressed = 0\n for (let si = 0; si < sections.length; si++) {\n try {\n // 배포용 문서는 findViewTextSections에서 이미 복호화+압축해제 완료\n const data = (!c.distribution && c.compressed) ? decompressStream(Buffer.from(sections[si])) : Buffer.from(sections[si])\n totalDecompressed += data.length\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\n out.push(readRecords(data))\n } catch (secErr) {\n if (secErr instanceof KordocError) throw secErr\n out.push(null)\n warnings.push({ page: si + 1, message: `섹션 ${si + 1} 파싱 실패: ${secErr instanceof Error ? secErr.message : \"알 수 없는 오류\"}`, code: \"PARTIAL_PARSE\" })\n }\n }\n return out\n}\n\n/** BinData 스토리지 — storageId(16진 BIN%04X) → 바이트(항목별 압축 정규화) */\nexport function readHwp5BinData(c: Hwp5Container): Map<number, { data: Buffer; name: string }> {\n const readStream = c.encrypted ? c.findStream : undefined\n return c.cfb ? collectHwp5BinData(c.cfb.FileIndex, readStream) : collectHwp5BinDataLenient(c.lenientCfb!, readStream)\n}\n\nexport function parseHwp5Document(buffer: Buffer, options?: ParseOptions): InternalParseResult {\n const c = openHwp5Container(buffer, options)\n const { cfb, lenientCfb, compressed, encrypted, findStream, warnings } = c\n\n const metadata: DocumentMetadata = {\n version: `${c.header.versionMajor}.x`,\n }\n if (cfb) extractHwp5Metadata(cfb, metadata)\n\n // DocInfo 파싱 (스타일 정보 추출)\n // 암호 문서는 복호를 거치는 findStream 경로로 — cfb 직접 접근은 암호문을 읽는다\n const docInfo = cfb && !encrypted\n ? parseDocInfoStream(cfb, compressed)\n : parseDocInfoFromStream(findStream(\"/DocInfo\"), compressed)\n\n const sections = readHwp5SectionStreams(c)\n if (sections.length === 0) throw new KordocError(\"섹션 스트림을 찾을 수 없습니다\")\n\n // (#66) 1단계: 섹션 레코드 확보 + 실제 페이지 프리패스 — 조판 캐시(PARA_LINE_SEG)가\n // 전 섹션에서 신뢰 가능하면 layout 모드(실제 페이지), 아니면 종전 섹션 근사.\n const sectionRecords = readHwp5SectionRecords(c, sections, warnings)\n const sectionDetects: (Hwp5SectionPageDetect | null)[] = sectionRecords.map(r => r ? detectHwp5SectionPages(r) : null)\n // 표 순번(sourceId) 프리패스 — 렌더 어댑터(#75)와 같은 규칙(table-ids.ts)\n const sectionTableIds: Map<number, string>[] = []\n {\n let base = 0\n for (const r of sectionRecords) {\n const ix = indexHwp5Tables(r ?? [], base)\n sectionTableIds.push(ix.ids)\n base += ix.count\n }\n }\n\n const layoutPages = sectionDetects.length > 0 && sectionDetects.every(d => d != null && d.usable)\n metadata.pageMode = layoutPages ? \"layout\" : \"section\"\n metadata.pageCount = layoutPages\n ? Math.max(sectionDetects.reduce((sum, d) => sum + (d?.pages ?? 0), 0), 1)\n : sections.length\n const pageFilter = options?.pages ? parsePageRange(options.pages, metadata.pageCount) : null\n if (pageFilter && !layoutPages) {\n warnings.push({ code: \"PAGE_BOUNDARY_APPROXIMATE\", message: \"조판 캐시가 없어 pages 필터를 섹션 단위 근사로 적용했습니다\" })\n }\n\n // 2단계: 본문 파싱 — layout 모드는 전 섹션 파싱 후 블록 단위 필터,\n // 섹션 근사는 종전(v4.7.2까지)처럼 섹션 스킵\n let bodyBlocks: IRBlock[] = []\n const doc = createHwp5DocState()\n doc.keepTrailingEmptyCols = options?.keepTrailingEmptyCols\n doc.includeFieldPlaceholders = options?.includeFieldPlaceholders\n let parsedSections = 0\n let pageBase = 0\n for (let si = 0; si < sections.length; si++) {\n const records = sectionRecords[si]\n if (!records) continue\n const detect = sectionDetects[si]!\n if (!layoutPages && pageFilter && !pageFilter.has(si + 1)) continue\n try {\n const sectionBlocks = parseSection(records, docInfo, warnings, si + 1, doc,\n layoutPages ? { base: pageBase, pageAtPara: detect.pageAtPara } : undefined, sectionTableIds[si])\n bodyBlocks.push(...sectionBlocks)\n parsedSections++\n options?.onProgress?.(parsedSections, sections.length)\n } catch (secErr) {\n if (secErr instanceof KordocError) throw secErr\n warnings.push({ page: si + 1, message: `섹션 ${si + 1} 파싱 실패: ${secErr instanceof Error ? secErr.message : \"알 수 없는 오류\"}`, code: \"PARTIAL_PARSE\" })\n } finally {\n pageBase += detect.pages\n }\n }\n\n // layout 모드 페이지 필터 — 실제 페이지 기준 블록 필터링\n if (pageFilter && layoutPages) {\n bodyBlocks = bodyBlocks.filter(b => b.pageNumber != null && pageFilter.has(b.pageNumber))\n }\n\n // 머리말은 문서 맨 앞, 꼬리말은 맨 뒤에 1회 출력\n const blocks: IRBlock[] = [...doc.headerBlocks, ...bodyBlocks, ...doc.footerBlocks]\n\n // BinData에서 이미지 추출 — 전체 파싱 시 본문 미참조 BinData 이미지도 스윕\n const images = cfb\n ? extractHwp5Images(cfb.FileIndex, blocks, warnings, !pageFilter, encrypted ? findStream : undefined)\n : extractHwp5ImagesLenient(lenientCfb!, blocks, warnings, !pageFilter, encrypted ? findStream : undefined)\n\n // 레이아웃 테이블 해체 (heading 감지 전에 수행하여 해체된 텍스트도 heading 감지 대상). 칸 테두리가 보이는 표는 풀지 않는다 —\n // 종전엔 3행 이하·글 많은 표를 테두리와 상관없이 풀어 같은 문서의 HWPX 표 118개가 사라지고 틀 안 중첩표가 바깥 단부터 풀렸다\n let flatBlocks = flattenLayoutTables(blocks)\n // 보이지 않는 틀 표 풀기 (v4.17.0) — 칸 테두리로 선 없는 틀은 글로, 칸으로 조립한 분수는 수식으로\n if (options?.layoutTables !== \"keep\") flatBlocks = unframeLayoutTables(flatBlocks, !!options?.keepTrailingEmptyCols)\n // 페이지 레이아웃 표의 반복 러닝 헤더 중복 제거 — opt-in (기본 off).\n // 위치 정보가 없는 HWP5 특성상 정당한 번호매김 반복(붙임별 재번호)까지 오삭제할 수\n // 있어 옵션이 켜졌을 때만 수행하고, 실제 제거가 있으면 경고로 가시화한다.\n if (options?.dedupeRunningHeaders) {\n const deduped = dedupeRunningHeaders(flatBlocks)\n const removed = flatBlocks.length - deduped.length\n if (removed > 0) warnings.push({ message: `반복 러닝 헤더 ${removed}개 제거`, code: \"HIDDEN_TEXT_FILTERED\" })\n flatBlocks = deduped\n }\n\n // 스타일 기반 헤딩 감지\n if (docInfo) {\n detectHwp5Headings(flatBlocks, docInfo, doc.tocTexts)\n }\n\n // outline 구축\n const outline: OutlineItem[] = flatBlocks\n .filter(b => b.type === \"heading\" && b.level && b.text)\n .map(b => ({ level: b.level!, text: b.text!, pageNumber: b.pageNumber }))\n\n let markdown = blocksToMarkdown(flatBlocks, { tableFormat: options?.tableFormat })\n // 이미지 인라인 옵션 — BMP→PNG 압축 후 base64 data URI 로 치환 (AI 에이전트 자체 완결형 마크다운)\n if (options?.inlineImages && options.images !== false && images.length > 0) {\n try {\n markdown = inlineImagesIntoMarkdown(markdown, images, { compress: true })\n } catch (inlineErr) {\n // 설계 계약상 bmpToPng 는 실패 시 null 을 반환하지만, 극단 입력·Node 버전차로 할당이\n // throw 하면 전체 파싱이 죽지 않도록 원본(비인라인) 마크다운을 유지하고 경고만 남긴다.\n warnings.push({ message: `이미지 인라인 실패 — 원본 파일 참조로 폴백: ${inlineErr instanceof Error ? inlineErr.message : \"알 수 없는 오류\"}`, code: \"SKIPPED_IMAGE\" })\n }\n }\n return { markdown, blocks: flatBlocks, metadata, outline: outline.length > 0 ? outline : undefined, warnings: warnings.length > 0 ? warnings : undefined, images: images.length > 0 ? images : undefined }\n}\n\n/** DocInfo 스트림 파싱 (best-effort) */\nfunction parseDocInfoStream(cfb: CfbContainer, compressed: boolean): HwpDocInfo | null {\n try {\n const entry = CFB.find(cfb, \"/DocInfo\")\n if (!entry?.content) return null\n const data = compressed ? decompressStream(Buffer.from(entry.content)) : Buffer.from(entry.content)\n const records = readRecords(data)\n return parseDocInfo(records)\n } catch {\n return null\n }\n}\n\n/** DocInfo — Buffer에서 직접 파싱 (lenient용) */\nfunction parseDocInfoFromStream(raw: Buffer | null, compressed: boolean): HwpDocInfo | null {\n if (!raw) return null\n try {\n const data = compressed ? decompressStream(raw) : raw\n return parseDocInfo(readRecords(data))\n } catch {\n return null\n }\n}\n\n/** 스타일 기반 헤딩 감지 — 큰 폰트 + 짧은 텍스트 → heading */\nfunction detectHwp5Headings(blocks: IRBlock[], docInfo: HwpDocInfo, tocTexts?: Set<string>): void {\n // 기본(본문) 폰트 크기 = 블록 폰트 크기의 텍스트 길이 가중 최빈값.\n // 공문서는 바탕글(10pt)과 다른 크기(13-14pt)로 본문을 쓰는 경우가 많아\n // 바탕글 스타일을 기준으로 삼으면 본문 전체가 헤딩으로 오검출된다 (실증: 보도자료 24/24).\n let baseFontSize = 0\n const sizeFreq = new Map<number, number>()\n for (const b of blocks) {\n if (b.style?.fontSize && b.text) {\n sizeFreq.set(b.style.fontSize, (sizeFreq.get(b.style.fontSize) || 0) + b.text.length)\n }\n }\n let maxWeight = 0\n for (const [size, weight] of sizeFreq) {\n if (weight > maxWeight) { maxWeight = weight; baseFontSize = size }\n }\n\n // 블록 스타일이 전혀 없으면 \"바탕글\", \"본문\" 등 본문 스타일로 폴백\n if (baseFontSize === 0) {\n for (const style of docInfo.styles) {\n const name = (style.nameKo || style.name).toLowerCase()\n if (name.includes(\"바탕\") || name.includes(\"본문\") || name === \"normal\" || name === \"body\") {\n const cs = docInfo.charShapes[style.charShapeId]\n // cs.fontSize는 0.1pt 단위 → pt로 변환 (블록의 style.fontSize와 동일 단위)\n if (cs?.fontSize > 0) { baseFontSize = cs.fontSize / 10; break }\n }\n }\n }\n\n if (baseFontSize <= 0) return\n\n for (const block of blocks) {\n // 개요 수준(outlineLevel)으로 이미 heading이 된 블록은 스킵\n if (block.type === \"heading\") continue\n if (block.type !== \"paragraph\" || !block.text || TOC_ENTRY_BLOCKS.has(block) || tocTexts?.has(block.text.trim())) continue\n const text = block.text.trim()\n if (text.length === 0 || text.length > 200) continue\n if (/^\\d+$/.test(text)) continue\n\n let level = 0\n\n // 폰트 크기 비율 기반 헤딩 감지 (스타일 정보가 있을 때만)\n if (block.style?.fontSize && baseFontSize > 0) {\n const ratio = block.style.fontSize / baseFontSize\n if (ratio >= HEADING_RATIO_H1) level = 1\n else if (ratio >= HEADING_RATIO_H2) level = 2\n else if (ratio >= HEADING_RATIO_H3) level = 3\n }\n\n // \"제N장/절/편\" 패턴 → H2, \"제N조\" 패턴 → H3 (스타일 유무 무관)\n if (/^제\\d+[장절편]\\s/.test(text) && text.length <= 50) {\n if (level === 0) level = 2\n } else if (/^제\\d+(조의?\\d*)\\s*[\\((]/.test(text) && text.length <= 80) {\n if (level === 0) level = 3\n }\n\n if (level > 0) {\n block.type = \"heading\"\n block.level = level\n }\n }\n}\n\n// ─── 메타데이터 추출 (best-effort) ───────────────────\n\nconst VT_I2 = 0x02, VT_LPSTR = 0x1e, VT_LPWSTR = 0x1f, VT_FILETIME = 0x40\n/** VT_LPSTR 코드 페이지 → TextDecoder 레이블 (1250~1258 은 windows-N) */\nconst CODE_PAGE_LABELS: Record<number, string> = { 949: \"euc-kr\", 932: \"shift_jis\", 936: \"gbk\", 950: \"big5\", 65001: \"utf-8\" }\n\n/**\n * OLE2 SummaryInformation 스트림에서 제목/작성자/설명/키워드/날짜 추출.\n * HWP5는 \\005HwpSummaryInformation 또는 \\005SummaryInformation에 저장.\n * OLE2 Property Set 포맷의 간이 파싱 — 실패 시 조용히 무시.\n * 한컴 HwpSummaryInformation(FMTID 9FA2B660-1061-11D4-B4C6-006097C09D8C)은 코드 페이지 속성 없이 문자열을 전부\n * VT_LPWSTR 로 쓴다(코퍼스 1,435건 전부) — 종전엔 VT_LPSTR 만 읽어 제목·지은이가 한 번도 안 나왔다.\n */\nfunction extractHwp5Metadata(cfb: CfbContainer, metadata: DocumentMetadata): void {\n try {\n // HWP 전용 SummaryInformation 먼저, 없으면 표준 OLE2\n const summaryEntry =\n CFB.find(cfb, \"/\\x05HwpSummaryInformation\") ||\n CFB.find(cfb, \"/\\x05SummaryInformation\")\n if (!summaryEntry?.content) return\n\n const data = Buffer.from(summaryEntry.content)\n if (data.length < 48) return\n\n // OLE2 Property Set Header: byte order(2) + version(2) + OS(4) + CLSID(16) + numSets(4) = 28\n // Then FMTID(16) + offset(4)\n const numSets = data.readUInt32LE(24)\n if (numSets === 0) return\n\n const setOffset = data.readUInt32LE(44)\n if (setOffset >= data.length - 8) return\n\n // Property Set: size(4) + numProperties(4) + [propertyId(4) + offset(4)] * N\n const numProps = data.readUInt32LE(setOffset + 4)\n if (numProps === 0 || numProps > 100) return\n\n // 속성 ID → 값 위치(type 4바이트부터). 코드 페이지(1)가 문자열 뒤에 올 수 있어 표를 먼저 모은다\n const props = new Map<number, number>()\n for (let i = 0; i < numProps; i++) {\n const entryOffset = setOffset + 8 + i * 8\n if (entryOffset + 8 > data.length) break\n const propOffset = setOffset + data.readUInt32LE(entryOffset + 4)\n if (propOffset + 8 <= data.length) props.set(data.readUInt32LE(entryOffset), propOffset)\n }\n const cpAt = props.get(1) // PID_CODEPAGE (VT_I2) — VT_LPSTR 해석용\n const codePage = cpAt !== undefined && data.readUInt32LE(cpAt) === VT_I2 ? data.readUInt16LE(cpAt + 4) : 0\n const text = (id: number) => { const at = props.get(id); return at === undefined ? undefined : readPropString(data, at, codePage) }\n const time = (id: number) => { const at = props.get(id); return at === undefined ? undefined : readPropFileTime(data, at) }\n\n // SummaryInformation 번호(한컴 FMTID 도 같음): 2 제목·3 주제·4 지은이·5 키워드·6 설명·12 만든 날짜·13 마지막 저장.\n // HWPX 짝 content.hpf 의 opf:title·subject·creator·keyword·description·CreatedDate 와 같은 값(한컴 저장 쌍 209건 실측).\n // 설명이 비면 주제 — HWPX metadata.ts(description → subject)·PDF(Subject) 와 같은 자리\n const title = text(2), author = text(4), description = text(6) || text(3)\n if (title) metadata.title = title\n if (author) metadata.author = author\n if (description) metadata.description = description\n const keywords = text(5)?.split(/[,;]/).map(k => k.trim()).filter(Boolean)\n if (keywords?.length) metadata.keywords = keywords\n const createdAt = time(12), modifiedAt = time(13)\n if (createdAt) metadata.createdAt = createdAt\n if (modifiedAt) metadata.modifiedAt = modifiedAt\n } catch {\n // best-effort — 실패 시 조용히 무시\n }\n}\n\n/**\n * 문자열 속성 값 (at = type 위치). VT_LPWSTR: 글자 수(UTF-16 단위, NUL 포함) + UTF-16LE,\n * VT_LPSTR: 바이트 수(NUL 포함) + 코드 페이지 문자열(1200 = UTF-16LE, 없음·모름 = UTF-8 — 종전 동작).\n * 값 뒤 4바이트 정렬 패딩은 오프셋 표로 찾으므로 무관\n */\nfunction readPropString(data: Buffer, at: number, codePage: number): string | undefined {\n const type = data.readUInt32LE(at)\n const unit = type === VT_LPWSTR ? 2 : type === VT_LPSTR ? 1 : 0\n const count = data.readUInt32LE(at + 4)\n if (!unit || count === 0 || count > 10000 || at + 8 + count * unit > data.length) return undefined\n const bytes = data.subarray(at + 8, at + 8 + count * unit)\n let s: string\n if (unit === 2 || codePage === 1200) s = bytes.toString(\"utf16le\")\n else {\n const label = CODE_PAGE_LABELS[codePage] ?? (codePage >= 1250 && codePage <= 1258 ? `windows-${codePage}` : \"utf-8\")\n try { s = new TextDecoder(label).decode(bytes) } catch { s = bytes.toString(\"utf8\") } // ICU 없는 Node — 레이블 미지원\n }\n const nul = s.indexOf(\"\\0\")\n return (nul >= 0 ? s.slice(0, nul) : s).trim() || undefined\n}\n\n/** VT_FILETIME — 1601-01-01 UTC 부터 100ns 단위 u64 → ISO 8601 초 단위(content.hpf CreatedDate 꼴). 0(미기록)은 undefined */\nfunction readPropFileTime(data: Buffer, at: number): string | undefined {\n if (data.readUInt32LE(at) !== VT_FILETIME || at + 12 > data.length) return undefined\n const ticks = data.readBigUInt64LE(at + 4)\n if (ticks === 0n) return undefined\n // 초로 먼저 나눠 정수 연산 — u64 를 double 로 옮기면 정각 값이 1초 앞당겨질 수 있다\n return new Date((Number(ticks / 10000000n) - 11644473600) * 1000).toISOString().replace(\".000Z\", \"Z\")\n}\n\n/** 메타데이터만 추출 (전체 파싱 없이) — MCP parse_metadata용 */\nexport function extractHwp5MetadataOnly(buffer: Buffer): DocumentMetadata {\n const cfb = CFB.parse(buffer)\n const headerEntry = CFB.find(cfb, \"/FileHeader\")\n if (!headerEntry?.content) throw new KordocError(\"FileHeader 스트림 없음\")\n const header = parseFileHeader(Buffer.from(headerEntry.content))\n if (header.signature !== \"HWP Document File\") throw new KordocError(\"HWP 시그니처 불일치\")\n\n const metadata: DocumentMetadata = {\n version: `${header.versionMajor}.x`,\n }\n extractHwp5Metadata(cfb, metadata)\n\n const sections = findSections(cfb)\n metadata.pageCount = sections.length\n\n return metadata\n}\n\n/** 배포용 문서: ViewText/Section{N} 스트림을 복호화하여 반환 */\nfunction findViewTextSections(cfb: CfbContainer, compressed: boolean): Buffer[] {\n const sections: Array<{ idx: number; content: Buffer }> = []\n\n for (let i = 0; i < MAX_SECTIONS; i++) {\n const entry = CFB.find(cfb, `/ViewText/Section${i}`)\n if (!entry?.content) break\n try {\n const decrypted = decryptViewText(Buffer.from(entry.content), compressed)\n sections.push({ idx: i, content: decrypted })\n } catch {\n // 복호화 실패 시 해당 섹션 스킵\n break\n }\n }\n\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\n}\n\n/** 스트림 리더로 BodyText 섹션 수집 — 압축 해제는 호출부가 맡는다(findSections와 같은 계약) */\nfunction findSectionsVia(read: (path: string) => Buffer | null): Buffer[] {\n const sections: Buffer[] = []\n for (let i = 0; i < MAX_SECTIONS; i++) {\n const raw = read(`/BodyText/Section${i}`)\n if (!raw) break\n sections.push(raw)\n }\n return sections\n}\n\nfunction findSections(cfb: CfbContainer): Buffer[] {\n const sections: Array<{ idx: number; content: Buffer }> = []\n\n for (let i = 0; i < MAX_SECTIONS; i++) {\n const entry = CFB.find(cfb, `/BodyText/Section${i}`)\n if (!entry?.content) break\n sections.push({ idx: i, content: Buffer.from(entry.content) })\n }\n\n if (sections.length === 0 && cfb.FileIndex) {\n for (const entry of cfb.FileIndex) {\n if (sections.length >= MAX_SECTIONS) break\n if (entry.name?.startsWith(\"Section\") && entry.content) {\n const idx = parseInt(entry.name.replace(\"Section\", \"\"), 10) || 0\n sections.push({ idx, content: Buffer.from(entry.content) })\n }\n }\n }\n\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\n}\n\n/** Lenient CFB: raw BodyText streams — readHwp5SectionRecords applies decompression once. */\nfunction findSectionsLenient(lcfb: LenientCfbContainer): Buffer[] {\n const sections: Array<{ idx: number; content: Buffer }> = []\n let totalBytes = 0\n for (let i = 0; i < MAX_SECTIONS; i++) {\n const raw = lcfb.findStream(`/BodyText/Section${i}`) ?? lcfb.findStream(`Section${i}`)\n if (!raw) break\n totalBytes += raw.length\n if (totalBytes > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\n sections.push({ idx: i, content: raw })\n }\n if (sections.length === 0) {\n // fallback: 이름에 \"Section\" 포함된 스트림\n for (const e of lcfb.entries()) {\n if (sections.length >= MAX_SECTIONS) break\n if (e.name.startsWith(\"Section\")) {\n const idx = parseInt(e.name.replace(\"Section\", \"\"), 10) || 0\n const raw = lcfb.findStream(e.name)\n if (raw) {\n totalBytes += raw.length\n if (totalBytes > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\n sections.push({ idx, content: raw })\n }\n }\n }\n }\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\n}\n\n/** Lenient CFB: ViewText/Section{N} 복호화 — 누적 크기 추적 */\nfunction findViewTextSectionsLenient(lcfb: LenientCfbContainer, compressed: boolean): Buffer[] {\n const sections: Array<{ idx: number; content: Buffer }> = []\n let totalDecompressed = 0\n for (let i = 0; i < MAX_SECTIONS; i++) {\n const raw = lcfb.findStream(`/ViewText/Section${i}`) ?? lcfb.findStream(`Section${i}`)\n if (!raw) break\n try {\n const content = decryptViewText(raw, compressed)\n totalDecompressed += content.length\n if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError(\"총 압축 해제 크기 초과 (decompression bomb 의심)\")\n sections.push({ idx: i, content })\n } catch { break }\n }\n return sections.sort((a, b) => a.idx - b.idx).map(s => s.content)\n}\n","/**\n * HWP5 렌더 어댑터 (#75 Task 7) — BodyText 레코드(PARA_LINE_SEG·CTRL_HEADER/TABLE/LIST_HEADER·SHAPE_COMPONENT(_PICTURE))를\n * HWPX 조판 캐시와 같은 뜻의 section DOM 으로 합성해 공용 SVG 렌더러(svg-render)에 넘긴다.\n *\n * 근거: 같은 문서를 한컴이 HWPX 로 저장하면 lineseg(textpos/vertpos/horzpos/horzsize)·cellAddr/cellSz·pos/sz/outMargin 이\n * HWP5 레코드 값과 1:1 이다(bench/corpus/pairs hwp↔hwpx 실측, 2026-09-06). 그리기(페이지 분할·표 격자·이미지·region)는\n * svg-render 하나가 맡고, 이 모듈은 레코드 → 속성 번역만 한다. 목표는 픽셀 파리티가 아니라 **결정적·페이지 로컬 region**.\n *\n * 레코드 레이아웃(pairs 실측 · 스펙 5.0 rev1.3):\n * - PARA_LINE_SEG 36B: textpos u32 | vertpos i32 | lineHeight | textHeight | baseline | spacing | horzpos | horzsize | flags\n * - CTRL_HEADER 개체 공통 attr u32@4: bit0 treatAsChar · bit3-4 vertRelTo(0 PAPER/1 PAGE/2 PARA) · bit5-7 vertAlign ·\n * bit8-9 horzRelTo(0 PAPER/1 PAGE/2 COLUMN/3 PARA) · bit10-12 horzAlign · bit21-23 textWrap(0 SQUARE/1 TOP_AND_BOTTOM/2 BEHIND/3 FRONT);\n * vertOffset i32@8 · horzOffset@12 · width u32@16 · height@20 · outMargin u16×4@28(l r t b)\n * - TABLE: rows u16@4 · cols@6 · inMargin u16×4@10. LIST_HEADER(셀): attr u32@2(bit5-6 vertAlign) · col u16@8 row@10 cs@12 rs@14 ·\n * w u32@16 h@20 · margin u16×4@24 · borderFillId u16@32 (parser.ts parseCell 과 동일)\n * - SHAPE_COMPONENT(gso 직속): \"gso \"@0 · chid@4 · offset i32@8/@12 · orgSz u32@20/@24 · curSz@28/@32. 묶음($con) 안 자식은 4바이트\n * 접두가 없어 chid@0 · offset@4/@8 · orgSz@16/@20 · curSz@24/@28 (licbyl 18018145 실측, 중첩 묶음도 같은 꼴). PICTURE: crop i32×4@44 · binDataId u16@71\n * - DocInfo BORDER_FILL: 속성 u16@0 · 변 4개(왼/오/위/아래) = @2+6k (type u8 · width u8 · COLORREF) · 대각선 @26 · 채우기 type u32@32 ·\n * 배경 COLORREF@36 (pairs header.xml 대조: type 1=SOLID·8=DOUBLE_SLIM, width 1=0.12mm·6=0.4mm·7=0.5mm, 배경 0xFFFFFFFF=없음).\n * CHAR_SHAPE: faceId u16@0 · ratio u8@14 · spacing i8@21 · relSize u8@28 · baseSize@42(1/100pt) · attr@46(bit0 italic·bit1 bold·bit2-3 underline) · color@52\n *\n * 한계(HWPX 경로와 동일): 머리말·꼬리말·각주 미렌더, 수식·OLE 경고, 조판 캐시(LINE_SEG) 없는 비한컴 저장본 문단은 reflow 폴백.\n * 한컴 접힘 PUA-A(결재란 \"(인)\" 등)는 글리프 복원을 우선해 그 문단 안에서만 슬롯이 어긋날 수 있다.\n */\n\nimport {\n TAG_PARA_HEADER, TAG_PARA_TEXT, TAG_CHAR_SHAPE, TAG_PARA_LINE_SEG, TAG_CTRL_HEADER, TAG_LIST_HEADER, TAG_TABLE,\n TAG_SHAPE_COMPONENT, TAG_SHAPE_COMPONENT_PICTURE, TAG_ID_MAPPINGS, TAG_FACE_NAME, TAG_DOC_CHAR_SHAPE, TAG_DOC_PARA_SHAPE,\n isExtendedOnlyCtrlChar, parseDocInfo, type HwpRecord, type HwpDocInfo,\n} from \"../hwp5/record.js\"\nimport { openHwp5Container, readHwp5DocInfoRecords, readHwp5SectionStreams, readHwp5SectionRecords, readHwp5BinData } from \"../hwp5/parser.js\"\nimport { indexHwp5Tables } from \"../hwp5/table-ids.js\"\nimport { detectImageMime } from \"../hwp5/images.js\"\nimport { mapPuaText } from \"../shared/pua.js\"\nimport { createXmlParser } from \"../hwpx/parser-shared.js\"\nimport { hwpFaceToCssStack, DEFAULT_PARA_GEOM, type RenderStyles, type RenderBorderEdge, type RenderBorderFill, type ParaAlign } from \"./head-styles.js\"\nimport { renderSectionRoots, assemblePageSvgs, escapeXml, type RenderSvgOptions, type HwpxPagesResult, type SectionRoot, type RenderImages } from \"./svg-render.js\"\nimport { KordocError } from \"../utils.js\"\nimport type { ParseWarning } from \"../types.js\"\n\n// record.ts 미정의 태그 (HWPTAG_BEGIN = 0x10)\nconst TAG_BORDER_FILL = 0x0014 // +4\nconst TAG_PAGE_DEF = 0x0049 // +57\nconst TAG_SHAPE_COMPONENT_LINE = 0x004e // +62\nconst TAG_SHAPE_COMPONENT_POLYGON = 0x0052 // +66\nconst TAG_SHAPE_COMPONENT_CURVE = 0x0053 // +67\n\n/** 4바이트 ASCII → u32 (LE 저장을 readUInt32LE 로 읽은 값과 비교) */\nfunction cid(s: string): number {\n return ((s.charCodeAt(0) << 24) | (s.charCodeAt(1) << 16) | (s.charCodeAt(2) << 8) | s.charCodeAt(3)) >>> 0\n}\nfunction swap32(id: number): number {\n return (((id & 0xff) << 24) | (((id >>> 8) & 0xff) << 16) | (((id >>> 16) & 0xff) << 8) | ((id >>> 24) & 0xff)) >>> 0\n}\nconst CTRL_TBL = cid(\"tbl \"), CTRL_GSO = cid(\"gso \"), CTRL_SECD = cid(\"secd\"), CTRL_COLD = cid(\"cold\"), CTRL_EQED = cid(\"eqed\")\nconst CHID_PIC = cid(\"$pic\"), CHID_REC = cid(\"$rec\"), CHID_LIN = cid(\"$lin\"), CHID_ELL = cid(\"$ell\"), CHID_POL = cid(\"$pol\"),\n CHID_CUR = cid(\"$cur\"), CHID_ARC = cid(\"$arc\"), CHID_CON = cid(\"$con\")\nconst KNOWN = new Set([CTRL_TBL, CTRL_GSO, CTRL_SECD, CTRL_COLD, CTRL_EQED, CHID_PIC, CHID_REC, CHID_LIN, CHID_ELL, CHID_POL, CHID_CUR, CHID_ARC, CHID_CON])\n/** 비표준 작성기의 BE 저장 id 방어 — 알려진 id 로 스왑 일치할 때만 */\nfunction normId(raw: number): number {\n if (KNOWN.has(raw)) return raw\n const sw = swap32(raw)\n return KNOWN.has(sw) ? sw : raw\n}\n\n/** COLORREF(0x00BBGGRR) → #RRGGBB. 상위 바이트가 있으면(0xFFFFFFFF 등 \"없음\") null */\nfunction colorHex(c: number): string | null {\n if ((c >>> 24) !== 0) return null\n const h = (v: number) => v.toString(16).padStart(2, \"0\").toUpperCase()\n return `#${h(c & 0xff)}${h((c >>> 8) & 0xff)}${h((c >>> 16) & 0xff)}`\n}\n\n// ─── DocInfo → RenderStyles ────────────────────────\n\n/** 테두리선 굵기 코드(u8) → mm (스펙 표 26) */\nconst BORDER_WIDTH_MM = [0.1, 0.12, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0]\n/** 테두리선 종류 코드(u8) → HWPX type 명 (0 없음 · 1 실선 · 8 이중선, pairs 실측) */\nconst BORDER_TYPE = [\"NONE\", \"SOLID\", \"DASH\", \"DOT\", \"DASH_DOT\", \"DASH_DOT_DOT\", \"LONG_DASH\", \"CIRCLE\", \"DOUBLE_SLIM\", \"SLIM_THICK\", \"THICK_SLIM\", \"SLIM_THICK_SLIM\", \"WAVE\", \"DOUBLE_WAVE\", \"THICK_3D\", \"THICK_3D_LIFTED\", \"3D\", \"3D_LIFTED\"]\nconst MM_TO_PT = 2.834645\n\nfunction borderEdge(d: Buffer, o: number): RenderBorderEdge | undefined {\n const type = d[o]\n if (type === 0) return undefined\n const mm = BORDER_WIDTH_MM[d[o + 1]] ?? 0.12\n return { type: BORDER_TYPE[type] ?? \"SOLID\", widthPt: mm * MM_TO_PT, color: colorHex(d.readUInt32LE(o + 2)) ?? \"#000000\" }\n}\n\nfunction borderFillOf(d: Buffer): RenderBorderFill {\n const bf: RenderBorderFill = {}\n if (d.length < 26) return bf\n const l = borderEdge(d, 2), r = borderEdge(d, 8), t = borderEdge(d, 14), b = borderEdge(d, 20)\n if (l) bf.left = l\n if (r) bf.right = r\n if (t) bf.top = t\n if (b) bf.bottom = b\n if (d.length >= 40) {\n const fillType = d.readUInt32LE(32)\n if (fillType & 1) { const bg = colorHex(d.readUInt32LE(36)); if (bg) bf.fill = bg }\n }\n return bf\n}\n\n/** FACE_NAME: attr u8@0 · 이름(len u16 + WCHAR)@1 */\nfunction readFaceName(d: Buffer): string {\n if (d.length < 3) return \"\"\n const len = d.readUInt16LE(1)\n const end = 3 + len * 2\n return end <= d.length ? d.subarray(3, end).toString(\"utf16le\") : \"\"\n}\n\nconst PARA_ALIGNS: ParaAlign[] = [\"JUSTIFY\", \"LEFT\", \"RIGHT\", \"CENTER\", \"DISTRIBUTE\", \"DISTRIBUTE_SPACE\"]\nconst LINE_SPACING_TYPES = [\"PERCENT\", \"FIXED\", \"BETWEEN_LINES\", \"AT_LEAST\"]\n\n/** DocInfo 레코드 → 렌더 스타일 테이블. id 는 HWPX 와 같은 규약(charPr/paraPr 0-based · borderFill 1-based) */\nexport function buildHwp5RenderStyles(records: HwpRecord[]): RenderStyles {\n const idm = records.find(r => r.tagId === TAG_ID_MAPPINGS)\n const hangulCount = idm && idm.data.length >= 8 ? idm.data.readUInt32LE(4) : Number.POSITIVE_INFINITY\n const faces: string[] = []\n for (const r of records) {\n if (r.tagId !== TAG_FACE_NAME) continue\n if (faces.length >= hangulCount) break\n faces.push(readFaceName(r.data))\n }\n const charPr: RenderStyles[\"charPr\"] = new Map()\n let ci = 0\n for (const r of records) {\n if (r.tagId !== TAG_DOC_CHAR_SHAPE) continue\n const d = r.data\n const id = String(ci++)\n if (d.length < 50) { charPr.set(id, { height: 1000, bold: false, italic: false, underline: false, ratio: 100, spacing: 0 }); continue }\n const face = faces[d.readUInt16LE(0)]\n const rel = d[28] || 100\n const attr = d.readUInt32LE(46)\n const color = d.length >= 56 ? colorHex(d.readUInt32LE(52)) : null\n charPr.set(id, {\n height: Math.max(100, Math.round(d.readInt32LE(42) * rel / 100)),\n bold: (attr & 2) !== 0, italic: (attr & 1) !== 0, underline: ((attr >>> 2) & 3) !== 0,\n ratio: d[14] || 100, spacing: d.readInt8(21),\n ...(color && color !== \"#000000\" ? { color } : {}),\n ...(face ? { fontFamily: hwpFaceToCssStack(face), face } : {}),\n })\n }\n const paraAlign: RenderStyles[\"paraAlign\"] = new Map()\n const paraGeom: RenderStyles[\"paraGeom\"] = new Map()\n let pi = 0\n for (const r of records) {\n if (r.tagId !== TAG_DOC_PARA_SHAPE) continue\n const d = r.data\n const id = String(pi++)\n if (d.length < 28) { paraAlign.set(id, \"JUSTIFY\"); paraGeom.set(id, { ...DEFAULT_PARA_GEOM }); continue }\n const attr1 = d.readUInt32LE(0)\n paraAlign.set(id, PARA_ALIGNS[(attr1 >>> 2) & 7] ?? \"JUSTIFY\")\n paraGeom.set(id, {\n lineSpacingType: LINE_SPACING_TYPES[attr1 & 3] ?? \"PERCENT\",\n lineSpacingValue: d.length >= 54 ? d.readUInt32LE(50) : d.readInt32LE(24),\n marginLeft: d.readInt32LE(4), marginRight: d.readInt32LE(8), marginIntent: d.readInt32LE(12),\n spaceBefore: d.readInt32LE(16), spaceAfter: d.readInt32LE(20),\n })\n }\n const borderFill: RenderStyles[\"borderFill\"] = new Map()\n let bi = 0\n for (const r of records) if (r.tagId === TAG_BORDER_FILL) borderFill.set(String(++bi), borderFillOf(r.data))\n return { charPr, paraAlign, paraGeom, borderFill }\n}\n\n// ─── BodyText → section DOM(XML) ───────────────────\n\nexport interface Hwp5SectionXmlContext {\n /** CTRL_HEADER 레코드 인덱스 → 표 sourceId (table-ids.ts) */\n tableIds: Map<number, string>\n /** binDataId(DocInfo 1-based) → 이미지 참조(binaryItemIDRef). null 이면 외부 연결 등 미참조 */\n imageRef: (binDataId: number) => string | null\n warnings: string[]\n}\n\ninterface CtrlRef { idx: number; id: number; start: number; end: number; used?: boolean }\n\nconst VREL = [\"PAPER\", \"PAGE\", \"PARA\", \"PARA\"], HREL = [\"PAPER\", \"PAGE\", \"COLUMN\", \"PARA\"]\nconst VALIGN = [\"TOP\", \"CENTER\", \"BOTTOM\", \"TOP\", \"BOTTOM\"], HALIGN = [\"LEFT\", \"CENTER\", \"RIGHT\", \"LEFT\", \"RIGHT\"]\nconst WRAP = [\"SQUARE\", \"TOP_AND_BOTTOM\", \"BEHIND_TEXT\", \"IN_FRONT_OF_TEXT\"]\n\ninterface ObjCommon { tac: boolean; wrap: string; w: number; h: number; posXml: string }\n\n/** 개체 공통 속성(CTRL_HEADER) → hp:sz/pos/outMargin */\nfunction objCommon(d: Buffer): ObjCommon {\n if (d.length < 36) return { tac: true, wrap: \"SQUARE\", w: 0, h: 0, posXml: `<hp:sz width=\"0\" height=\"0\"/><hp:pos treatAsChar=\"1\" vertRelTo=\"PARA\" horzRelTo=\"PARA\" vertAlign=\"TOP\" horzAlign=\"LEFT\" vertOffset=\"0\" horzOffset=\"0\"/>` }\n const a = d.readUInt32LE(4)\n const tac = (a & 1) !== 0\n const w = d.readUInt32LE(16), h = d.readUInt32LE(20)\n const posXml = `<hp:sz width=\"${w}\" height=\"${h}\"/>` +\n `<hp:pos treatAsChar=\"${tac ? 1 : 0}\" vertRelTo=\"${VREL[(a >>> 3) & 3]}\" horzRelTo=\"${HREL[(a >>> 8) & 3]}\" vertAlign=\"${VALIGN[(a >>> 5) & 7] ?? \"TOP\"}\" horzAlign=\"${HALIGN[(a >>> 10) & 7] ?? \"LEFT\"}\" vertOffset=\"${d.readInt32LE(8)}\" horzOffset=\"${d.readInt32LE(12)}\"/>` +\n `<hp:outMargin left=\"${d.readUInt16LE(28)}\" right=\"${d.readUInt16LE(30)}\" top=\"${d.readUInt16LE(32)}\" bottom=\"${d.readUInt16LE(34)}\"/>`\n return { tac, wrap: WRAP[(a >>> 21) & 7] ?? \"SQUARE\", w, h, posXml }\n}\n\n/** 한컴 PUA-A 접힘 영역 (record.ts 와 동일 범위) */\nconst FOLD_START = 0xa000, FOLD_END = 0xa48c, FOLD_SHIFT = 0xf0000 - 0xa000\n\n/** 텍스트 1글자 (제어문자 아님). XML 에 못 들어가는 코드는 U+FFFD */\nfunction textChar(ch: number): string {\n if (ch >= FOLD_START && ch <= FOLD_END) {\n const mapped = mapPuaText(String.fromCodePoint(ch + FOLD_SHIFT))\n // 매핑 없으면 PUA 그대로 돌아온다 — 글리프가 없으니 비운다\n return /[\\u{F0000}-\\u{FFFFF}]/u.test(mapped) ? \"\" : mapped\n }\n if (ch === 0xfffe || ch === 0xffff || (ch >= 0xd800 && ch <= 0xdfff)) return \"�\"\n return String.fromCharCode(ch)\n}\n\n/** [start,end) 안의 `level` 문단 전부 → hp:p 나열 */\nfunction paragraphsXml(records: HwpRecord[], start: number, end: number, level: number, ctx: Hwp5SectionXmlContext, depth: number): string {\n if (depth > 16) return \"\"\n let out = \"\"\n let i = start\n while (i < end) {\n const r = records[i]\n if (r.tagId === TAG_PARA_HEADER && r.level === level) {\n let j = i + 1\n while (j < end && records[j].level > level) j++\n out += paragraphXml(records, i, j, level, ctx, depth)\n i = j\n } else i++\n }\n return out\n}\n\nfunction paragraphXml(records: HwpRecord[], i: number, j: number, level: number, ctx: Hwp5SectionXmlContext, depth: number): string {\n const hdr = records[i]\n const paraShapeId = hdr.data.length >= 10 ? hdr.data.readUInt16LE(8) : 0\n const texts: Buffer[] = []\n const shapes: Array<[number, number]> = []\n const segs: Buffer[] = []\n const ctrls: CtrlRef[] = []\n let k = i + 1\n while (k < j) {\n const r = records[k]\n if (r.level === level + 1) {\n if (r.tagId === TAG_PARA_TEXT) texts.push(r.data)\n else if (r.tagId === TAG_CHAR_SHAPE) { for (let o = 0; o + 8 <= r.data.length; o += 8) shapes.push([r.data.readUInt32LE(o), r.data.readUInt32LE(o + 4)]) }\n else if (r.tagId === TAG_PARA_LINE_SEG) segs.push(r.data)\n else if (r.tagId === TAG_CTRL_HEADER && r.data.length >= 4) {\n let e = k + 1\n while (e < j && records[e].level > level + 1) e++\n ctrls.push({ idx: k, id: normId(r.data.readUInt32LE(0)), start: k + 1, end: e })\n k = e\n continue\n }\n }\n k++\n }\n const text = texts.length === 1 ? texts[0] : Buffer.concat(texts)\n\n // 확장 컨트롤 문자(ctrlIdx 순) ↔ CTRL_HEADER 순서 매핑 — record.ts 의 resolver 규약, 어긋나면 같은 id 의 미사용 컨트롤\n const ctrlElement = (ctrlIdx: number, rawId: number): string => {\n const id = normId(rawId)\n let c: CtrlRef | undefined = ctrls[ctrlIdx]\n if (!c || c.used || c.id !== id) c = ctrls.find(x => !x.used && x.id === id)\n if (!c) return \"<hp:ctrl/>\"\n c.used = true\n switch (c.id) {\n case CTRL_TBL: return tableXml(records, c, level, ctx, depth)\n case CTRL_GSO: return gsoXml(records, c, level, ctx, depth)\n case CTRL_SECD: return secPrXml(records, c)\n case CTRL_COLD: { const a = records[c.idx].data.length >= 8 ? records[c.idx].data.readUInt32LE(4) : 0; return `<hp:colPr colCount=\"${Math.max(1, (a >>> 2) & 0xff)}\"/>` }\n case CTRL_EQED: { const oc = objCommon(records[c.idx].data); return `<hp:equation textWrap=\"${oc.wrap}\">${oc.posXml}</hp:equation>` }\n default: return \"<hp:ctrl/>\"\n }\n }\n\n // 슬롯 모델(record.ts appendParaText 와 동일): 문자 1 · 문자형 제어 1 · 인라인/확장 제어 8. charShape 경계에서 run 교체\n let out = `<hp:p paraPrIDRef=\"${paraShapeId}\" styleIDRef=\"0\">`\n let shapeIdx = 0\n let run: string[] = []\n let textBuf = \"\"\n const flushText = () => { if (textBuf) { run.push(`<hp:t>${escapeXml(textBuf)}</hp:t>`); textBuf = \"\" } }\n const openRun = (id: number) => { out += `<hp:run charPrIDRef=\"${id}\">` }\n const closeRun = () => { flushText(); out += run.join(\"\") + \"</hp:run>\"; run = [] }\n openRun(shapes[0]?.[1] ?? 0)\n let pos = 0, ctrlIdx = 0, o = 0\n const switchShape = () => {\n while (shapeIdx + 1 < shapes.length && pos >= shapes[shapeIdx + 1][0]) { shapeIdx++; closeRun(); openRun(shapes[shapeIdx][1]) }\n }\n while (o + 1 < text.length) {\n switchShape()\n const ch = text.readUInt16LE(o)\n o += 2\n if (ch === 0x0d) break\n if (ch >= 0x20) {\n if (ch >= 0xd800 && ch <= 0xdbff && o + 1 < text.length) {\n const lo = text.readUInt16LE(o)\n if (lo >= 0xdc00 && lo <= 0xdfff) { textBuf += String.fromCharCode(ch, lo); o += 2; pos += 2; continue }\n }\n textBuf += textChar(ch)\n pos++\n continue\n }\n switch (ch) {\n case 0x09: flushText(); run.push(\"<hp:tab/>\"); o += 14; pos += 8; break\n case 0x0a: {\n flushText(); run.push(\"<hp:lineBreak/>\"); pos++\n // 수식 placeholder 0x0a+0x0b (record.ts 와 동일 특례)\n if (o + 16 <= text.length && text.readUInt16LE(o) === 0x000b) { run.push(ctrlElement(ctrlIdx++, text.readUInt32LE(o + 2))); o += 16; pos += 8 }\n break\n }\n case 0x00: flushText(); run.push(\"<hp:lineBreak/>\"); pos++; break\n case 0x1e: flushText(); run.push(\"<hp:nbSpace/>\"); pos++; break\n case 0x1f: flushText(); run.push(\"<hp:fwSpace/>\"); pos++; break\n case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: flushText(); run.push(\"<hp:hyphen/>\"); pos++; break\n default: {\n const isInline = (ch >= 4 && ch <= 9) || ch === 19 || ch === 20\n if ((isExtendedOnlyCtrlChar(ch) || isInline) && o + 14 <= text.length) {\n flushText()\n if (isExtendedOnlyCtrlChar(ch)) run.push(ctrlElement(ctrlIdx++, text.readUInt32LE(o)))\n else run.push(\"<hp:ctrl/>\")\n o += 14\n pos += 8\n } else pos++\n }\n }\n }\n closeRun()\n // 텍스트가 비어 있어도 컨트롤(secd 등)은 문자 스트림에 있다 — 문자 스트림에 없는 잔여 컨트롤은 문단 끝에 붙인다\n const rest = ctrls.filter(c => !c.used && (c.id === CTRL_TBL || c.id === CTRL_GSO || c.id === CTRL_SECD || c.id === CTRL_COLD))\n if (rest.length > 0) {\n openRun(shapes[0]?.[1] ?? 0)\n for (const c of rest) { const el = ctrlElement(ctrls.indexOf(c), c.id); run.push(el) }\n closeRun()\n }\n if (segs.length > 0) {\n out += \"<hp:linesegarray>\"\n for (const s of segs) {\n for (let off = 0; off + 36 <= s.length; off += 36) {\n out += `<hp:lineseg textpos=\"${s.readUInt32LE(off)}\" vertpos=\"${s.readInt32LE(off + 4)}\" vertsize=\"${s.readInt32LE(off + 8)}\" textheight=\"${s.readInt32LE(off + 12)}\" baseline=\"${s.readInt32LE(off + 16)}\" spacing=\"${s.readInt32LE(off + 20)}\" horzpos=\"${s.readInt32LE(off + 24)}\" horzsize=\"${s.readInt32LE(off + 28)}\" flags=\"${s.readUInt32LE(off + 32)}\"/>`\n }\n }\n out += \"</hp:linesegarray>\"\n }\n return out + \"</hp:p>\"\n}\n\n/** 구역 정의(secd) → hp:secPr/pagePr (PAGE_DEF: width u32@0 height@4 left@8 right@12 top@16 bottom@20 header@24 footer@28 gutter@32 attr@36) */\nfunction secPrXml(records: HwpRecord[], c: CtrlRef): string {\n for (let k = c.start; k < c.end; k++) {\n const r = records[k]\n if (r.tagId !== TAG_PAGE_DEF || r.data.length < 36) continue\n const d = r.data\n const u = (o: number) => d.readUInt32LE(o)\n const landscape = d.length >= 40 && (u(36) & 1) === 1 ? \"NARROWLY\" : \"WIDELY\"\n return `<hp:secPr><hp:pagePr landscape=\"${landscape}\" width=\"${u(0)}\" height=\"${u(4)}\"><hp:margin left=\"${u(8)}\" right=\"${u(12)}\" top=\"${u(16)}\" bottom=\"${u(20)}\" header=\"${u(24)}\" footer=\"${u(28)}\" gutter=\"${u(32)}\"/></hp:pagePr></hp:secPr>`\n }\n return \"<hp:secPr/>\"\n}\n\nconst CELL_VALIGN = [\"TOP\", \"CENTER\", \"BOTTOM\", \"TOP\"]\n\n/** 표 컨트롤 → hp:tbl (셀 = TABLE 레코드 뒤 LIST_HEADER + 같은 level 의 문단들, parser.ts parseTableControl 과 같은 분할) */\nfunction tableXml(records: HwpRecord[], c: CtrlRef, level: number, ctx: Hwp5SectionXmlContext, depth: number): string {\n const common = objCommon(records[c.idx].data)\n let tableIdx = -1\n for (let k = c.start; k < c.end; k++) if (records[k].tagId === TAG_TABLE && records[k].data.length >= 18) { tableIdx = k; break }\n if (tableIdx < 0) return \"<hp:ctrl/>\"\n const t = records[tableIdx].data\n const rows = t.readUInt16LE(4), cols = t.readUInt16LE(6)\n const inMargin = `<hp:inMargin left=\"${t.readUInt16LE(10)}\" right=\"${t.readUInt16LE(12)}\" top=\"${t.readUInt16LE(14)}\" bottom=\"${t.readUInt16LE(16)}\"/>`\n const cellLevel = level + 2\n let cells = \"\"\n let k = tableIdx + 1\n while (k < c.end) {\n const r = records[k]\n if (r.tagId === TAG_LIST_HEADER && r.level === cellLevel) {\n let e = k + 1\n while (e < c.end) {\n const x = records[e]\n if (x.level < cellLevel) break\n if (x.level === cellLevel && (x.tagId === TAG_LIST_HEADER || x.tagId === TAG_TABLE)) break\n e++\n }\n cells += cellXml(records, k, e, cellLevel, ctx, depth)\n k = e\n continue\n }\n k++\n }\n if (!cells) return \"<hp:ctrl/>\"\n const id = ctx.tableIds.get(c.idx)\n return `<hp:tbl${id ? ` id=\"${id}\"` : \"\"} textWrap=\"${common.wrap}\" rowCnt=\"${rows}\" colCnt=\"${cols}\" cellSpacing=\"0\">${common.posXml}${inMargin}<hp:tr>${cells}</hp:tr></hp:tbl>`\n}\n\nfunction cellXml(records: HwpRecord[], lh: number, end: number, cellLevel: number, ctx: Hwp5SectionXmlContext, depth: number): string {\n const d = records[lh].data\n if (d.length < 34) return \"\"\n const attr = d.readUInt32LE(2)\n const vAlign = CELL_VALIGN[(attr >>> 5) & 3]\n const paras = paragraphsXml(records, lh + 1, end, cellLevel, ctx, depth + 1)\n return `<hp:tc borderFillIDRef=\"${d.readUInt16LE(32)}\"><hp:subList vertAlign=\"${vAlign}\">${paras}</hp:subList>` +\n `<hp:cellAddr colAddr=\"${d.readUInt16LE(8)}\" rowAddr=\"${d.readUInt16LE(10)}\"/><hp:cellSpan colSpan=\"${Math.max(1, d.readUInt16LE(12))}\" rowSpan=\"${Math.max(1, d.readUInt16LE(14))}\"/>` +\n `<hp:cellSz width=\"${d.readUInt32LE(16)}\" height=\"${d.readUInt32LE(20)}\"/><hp:cellMargin left=\"${d.readUInt16LE(24)}\" right=\"${d.readUInt16LE(26)}\" top=\"${d.readUInt16LE(28)}\" bottom=\"${d.readUInt16LE(30)}\"/></hp:tc>`\n}\n\ninterface ShapeComp { idx: number; chid: number; xoff: number; yoff: number; orgW: number; orgH: number; curW: number; curH: number }\n\n/** nested=묶음 안 자식(4바이트 접두 없음) */\nfunction readShapeComp(records: HwpRecord[], idx: number, nested = false): ShapeComp | null {\n const d = records[idx].data\n const b = nested ? 0 : 4\n if (d.length < b + 32) return null\n return { idx, chid: normId(d.readUInt32LE(b)), xoff: d.readInt32LE(b + 4), yoff: d.readInt32LE(b + 8), orgW: d.readUInt32LE(b + 16), orgH: d.readUInt32LE(b + 20), curW: d.readUInt32LE(b + 24), curH: d.readUInt32LE(b + 28) }\n}\n\n/** 묶음 자식들 — 컨테이너 SHAPE_COMPONENT 바로 아래 level 의 SHAPE_COMPONENT 만, 각 자식 범위는 다음 형제 전까지(중첩 묶음 재귀) */\nfunction containerChildrenXml(records: HwpRecord[], scIdx: number, end: number, ctx: Hwp5SectionXmlContext): string {\n const childLevel = records[scIdx].level + 1\n let out = \"\"\n for (let k = scIdx + 1; k < end; k++) {\n if (records[k].tagId !== TAG_SHAPE_COMPONENT || records[k].level !== childLevel) continue\n const child = readShapeComp(records, k, true)\n if (!child) continue\n let e = k + 1\n while (e < end && !(records[e].tagId === TAG_SHAPE_COMPONENT && records[e].level <= childLevel)) e++\n const sizeXml = `<hp:sz width=\"${child.curW}\" height=\"${child.curH}\"/><hp:offset x=\"${child.xoff}\" y=\"${child.yoff}\"/>`\n out += child.chid === CHID_CON\n ? `<hp:container>${sizeXml}${containerChildrenXml(records, k, e, ctx)}</hp:container>`\n : shapeElement(records, child, k, e, sizeXml, \"\", ctx)\n k = e - 1\n }\n return out\n}\n\n/** 그리기 개체(gso) → hp:pic / rect / ellipse / line / polygon / curv / arc / container / ole */\nfunction gsoXml(records: HwpRecord[], c: CtrlRef, level: number, ctx: Hwp5SectionXmlContext, depth: number): string {\n const common = objCommon(records[c.idx].data)\n let scIdx = -1\n for (let k = c.start; k < c.end; k++) if (records[k].tagId === TAG_SHAPE_COMPONENT) { scIdx = k; break }\n const sc = scIdx >= 0 ? readShapeComp(records, scIdx) : null\n if (!sc) return `<hp:ole textWrap=\"${common.wrap}\">${common.posXml}</hp:ole>`\n // 글상자 문단 리스트: SHAPE_COMPONENT 뒤 첫 LIST_HEADER(같은 level+2)\n let textList = -1\n for (let k = scIdx + 1; k < c.end; k++) if (records[k].tagId === TAG_LIST_HEADER && records[k].level === level + 2) { textList = k; break }\n const geomEnd = textList >= 0 ? textList : c.end\n const drawText = textList >= 0 ? `<hp:drawText><hp:subList>${paragraphsXml(records, textList + 1, c.end, level + 2, ctx, depth + 1)}</hp:subList></hp:drawText>` : \"\"\n if (sc.chid === CHID_CON) return `<hp:container textWrap=\"${common.wrap}\">${common.posXml}${containerChildrenXml(records, scIdx, geomEnd, ctx)}</hp:container>`\n return shapeElement(records, sc, scIdx, geomEnd, common.posXml, drawText, ctx, common.wrap)\n}\n\n/** chid 별 개체 요소. geometry 레코드는 [scIdx, end) 에서 찾는다 */\nfunction shapeElement(records: HwpRecord[], sc: ShapeComp, scIdx: number, end: number, posXml: string, drawText: string, ctx: Hwp5SectionXmlContext, wrap?: string): string {\n const sizes = `<hp:orgSz width=\"${sc.orgW}\" height=\"${sc.orgH}\"/><hp:curSz width=\"${sc.curW}\" height=\"${sc.curH}\"/>`\n // 부유 개체의 textWrap — 없으면 렌더러가 TOP_AND_BOTTOM(밀어내기 역산)으로 보고 BEHIND/FRONT 개체(직인·워터마크)를 위로 올린다\n const wa = wrap ? ` textWrap=\"${wrap}\"` : \"\"\n const find = (tag: number): Buffer | null => { for (let k = scIdx + 1; k < end; k++) if (records[k].tagId === tag) return records[k].data; return null }\n if (sc.chid === CHID_PIC) {\n const p = find(TAG_SHAPE_COMPONENT_PICTURE)\n let img = \"<hp:img/>\", clip = \"\"\n if (p && p.length >= 73) {\n const ref = ctx.imageRef(p.readUInt16LE(71))\n img = ref ? `<hp:img binaryItemIDRef=\"${ref}\"/>` : \"<hp:img/>\"\n const cl = p.readInt32LE(44), ct = p.readInt32LE(48), cr = p.readInt32LE(52), cb = p.readInt32LE(56)\n if ((cl > 0 || ct > 0 || cr > 0 || cb > 0) && sc.orgW - cr > cl && sc.orgH - cb > ct) {\n clip = `<hp:imgClip left=\"${Math.max(0, cl)}\" top=\"${Math.max(0, ct)}\" right=\"${sc.orgW - Math.max(0, cr)}\" bottom=\"${sc.orgH - Math.max(0, cb)}\"/>`\n }\n }\n return `<hp:pic${wa}>${posXml}${sizes}<hp:imgDim dimwidth=\"${sc.orgW}\" dimheight=\"${sc.orgH}\"/>${clip}${img}</hp:pic>`\n }\n const line = `<hp:lineShape color=\"#000000\" width=\"12\" style=\"SOLID\"/>`\n if (sc.chid === CHID_LIN) {\n const l = find(TAG_SHAPE_COMPONENT_LINE)\n const pts = l && l.length >= 16\n ? `<hp:startPt x=\"${l.readInt32LE(0)}\" y=\"${l.readInt32LE(4)}\"/><hp:endPt x=\"${l.readInt32LE(8)}\" y=\"${l.readInt32LE(12)}\"/>`\n : `<hp:startPt x=\"0\" y=\"0\"/><hp:endPt x=\"${sc.orgW}\" y=\"${sc.orgH}\"/>`\n return `<hp:line${wa}>${posXml}${sizes}${line}${pts}</hp:line>`\n }\n if (sc.chid === CHID_POL || sc.chid === CHID_CUR) {\n const g = find(sc.chid === CHID_POL ? TAG_SHAPE_COMPONENT_POLYGON : TAG_SHAPE_COMPONENT_CURVE)\n let pts = \"\"\n if (g && g.length >= 2) {\n const n = Math.min(g.readInt16LE(0), 4096)\n for (let k = 0; k < n && 2 + k * 8 + 8 <= g.length; k++) pts += `<hp:pt x=\"${g.readInt32LE(2 + k * 8)}\" y=\"${g.readInt32LE(6 + k * 8)}\"/>`\n }\n const tag = sc.chid === CHID_POL ? \"polygon\" : \"curv\"\n return `<hp:${tag}${wa}>${posXml}${sizes}${line}${pts}${drawText}</hp:${tag}>`\n }\n const tag = sc.chid === CHID_REC ? \"rect\" : sc.chid === CHID_ELL ? \"ellipse\" : sc.chid === CHID_ARC ? \"arc\" : \"ole\"\n return `<hp:${tag}${wa}>${posXml}${sizes}${line}${drawText}</hp:${tag}>`\n}\n\n/** 섹션 레코드 → HWPX 동형 section XML (svg-render 가 읽는 요소·속성만) */\nexport function hwp5SectionToXml(records: HwpRecord[], ctx: Hwp5SectionXmlContext): string {\n return `<hs:sec xmlns:hs=\"http://www.hancom.co.kr/hwpml/2011/section\" xmlns:hp=\"http://www.hancom.co.kr/hwpml/2011/paragraph\" xmlns:hc=\"http://www.hancom.co.kr/hwpml/2011/core\">` +\n paragraphsXml(records, 0, records.length, 0, ctx, 0) + \"</hs:sec>\"\n}\n\n// ─── 엔트리 ───────────────────────────────────────\n\nexport interface RenderHwp5Options extends RenderSvgOptions {\n /** 암호 문서 열기 암호 */\n password?: string\n}\n\n/**\n * 레코드 단위 렌더 — 컨테이너를 이미 연 호출자(테스트·재사용)용. `secRecords` 의 null 구역은 생략.\n * 이미지는 참조된 BinData 만 dataURI 로 싣는다(HWPX 경로와 같은 장당·누적 한도).\n */\nexport function renderHwp5Records(\n docInfo: HwpDocInfo | null,\n styles: RenderStyles,\n secRecords: (HwpRecord[] | null)[],\n binData: Map<number, { data: Buffer; name: string }>,\n options?: RenderHwp5Options,\n select?: Set<number> | ((pageCount: number) => Set<number>),\n warnings: string[] = [],\n): HwpxPagesResult {\n const maxImg = options?.maxImageBytes ?? 40 * 1024 * 1024\n const MAX_TOTAL_IMAGE_BYTES = 128 * 1024 * 1024\n const images: RenderImages = new Map()\n let totalImgBytes = 0\n const imageRef = (binDataId: number): string | null => {\n if (binDataId <= 0) return null\n const item = docInfo?.binData[binDataId - 1]\n if (item?.kind === \"link\") return null\n const storageId = item && item.storageId > 0 ? item.storageId : binDataId\n const ref = `bin${storageId}`\n if (images.has(ref)) return ref\n const entry = binData.get(storageId)\n if (!entry) return ref // 누락은 렌더러가 placeholder + 경고\n if (entry.data.length > maxImg) { warnings.push(`이미지 ${entry.name} ${(entry.data.length / 1048576).toFixed(1)}MB — 한도 초과로 생략`); return ref }\n if (totalImgBytes + entry.data.length > MAX_TOTAL_IMAGE_BYTES) { warnings.push(`이미지 누적 ${Math.round(MAX_TOTAL_IMAGE_BYTES / 1048576)}MB 한도 초과 — 이후 생략`); return ref }\n totalImgBytes += entry.data.length\n images.set(ref, { dataUri: `data:${detectImageMime(entry.data) ?? \"image/jpeg\"};base64,${entry.data.toString(\"base64\")}` })\n return ref\n }\n const roots: SectionRoot[] = []\n let base = 0\n for (let si = 0; si < secRecords.length; si++) {\n const recs = secRecords[si]\n if (!recs) continue\n const { ids, count } = indexHwp5Tables(recs, base)\n base += count\n const xml = hwp5SectionToXml(recs, { tableIds: ids, imageRef, warnings })\n const doc = createXmlParser().parseFromString(xml, \"text/xml\")\n const root = doc.documentElement as unknown as Element\n if (!root) { warnings.push(`구역 ${si} 합성 DOM 생성 실패 — 생략`); continue }\n roots.push({ root, index: si })\n }\n if (roots.length === 0) throw new KordocError(\"렌더할 구역이 없습니다 — HWP 가 손상되었을 수 있습니다\")\n const r = renderSectionRoots(roots, { styles, images, warnings, reflow: !!options?.reflow, reflowMode: options?.reflowMode ?? \"keep\", highlights: options?.highlights })\n return assemblePageSvgs(r, \"hwp\", select)\n}\n\n/** HWP5(OLE2) → 페이지별 독립 SVG + RenderScene. renderHwpxPages 와 같은 계약 (#75 Task 7) */\nexport function renderHwp5Pages(\n input: ArrayBuffer | Uint8Array,\n options?: RenderHwp5Options,\n select?: Set<number> | ((pageCount: number) => Set<number>),\n): HwpxPagesResult {\n const buf = Buffer.from(input instanceof Uint8Array ? input : new Uint8Array(input))\n const c = openHwp5Container(buf, options)\n const warnings: string[] = c.warnings.map(w => w.message)\n const docInfoRecords = readHwp5DocInfoRecords(c)\n if (!docInfoRecords) warnings.push(\"DocInfo 없음 — 기본 스타일로 렌더\")\n const styles = buildHwp5RenderStyles(docInfoRecords ?? [])\n const docInfo = docInfoRecords ? parseDocInfo(docInfoRecords) : null\n const sections = readHwp5SectionStreams(c)\n if (sections.length === 0) throw new KordocError(\"섹션 스트림을 찾을 수 없습니다\")\n const parseWarnings: ParseWarning[] = []\n const secRecords = readHwp5SectionRecords(c, sections, parseWarnings)\n for (const w of parseWarnings) warnings.push(w.message)\n return renderHwp5Records(docInfo, styles, secRecords, readHwp5BinData(c), options, select, warnings)\n}\n","/**\n * 통합 문서 렌더 API (#75 Task 3·8) — 입력(경로/버퍼) → 포맷 감지 → 어댑터 → RenderScene + 페이지 SVG\n * → SVG/HTML/PNG/JPEG/PDF 자산. 다운스트림은 RenderScene 만 알면 된다.\n *\n * hwpx → HWPX 어댑터(svg-render, 조판 캐시 재생·reflow 폴백)\n * hwp → HWP5 어댑터(hwp5-scene: 레코드 → 동형 section DOM → 같은 렌더러)\n * 기타 → 미지원\n */\n\nimport { readFile } from \"node:fs/promises\"\nimport { detectFormat, detectZipFormat } from \"../detect.js\"\nimport { KordocError, toArrayBuffer } from \"../utils.js\"\nimport { parsePageRange } from \"../page-range.js\"\nimport { renderHwpxPages } from \"./svg-render.js\"\nimport { renderHwp5Pages } from \"./hwp5-scene.js\"\nimport type { WrapMode } from \"../hwpx/text-metrics.js\"\nimport type { RenderScene, RenderSourceFormat } from \"./scene.js\"\nimport { renderSceneToHtml } from \"./html.js\"\nimport { rasterizePageSvg } from \"./rasterize.js\"\nimport { renderHtmlToPdf } from \"./pdf.js\"\n\nexport type RenderFormat = \"svg\" | \"html\" | \"png\" | \"jpeg\" | \"pdf\"\n\nexport interface SceneRenderOptions {\n /** 1-based 페이지 선택 — [2,3] 또는 \"1-3,7\". 미지정이면 전 페이지 */\n pages?: number[] | string\n /** 조판 캐시 없는 HWPX 를 순수 TS 조판으로 렌더 (기본 true — 캐시가 있으면 무시) */\n reflow?: boolean\n /** 암호 HWP5 열기 암호 */\n password?: string\n reflowMode?: WrapMode\n highlights?: string[]\n maxImageBytes?: number\n}\n\nexport interface SceneRenderResult {\n scene: RenderScene\n /** 선택된 페이지의 standalone SVG (페이지 로컬 좌표) */\n pageSvgs: Map<number, string>\n}\n\nexport interface RenderDocumentOptions extends SceneRenderOptions {\n format: RenderFormat\n /** 래스터 출력 최대 폭 px */\n maxWidthPx?: number\n /** JPEG 품질 */\n quality?: number\n /** PDF 용 Chromium 실행 파일 */\n browserExecutablePath?: string\n /** HTML/PDF 문서 제목 */\n title?: string\n}\n\nexport interface RenderAsset {\n format: RenderFormat\n /** 페이지 단위 산출은 page, 문서 단위(HTML/PDF)는 없음 */\n page?: number\n data: string | Buffer\n /** 픽셀(래스터) 또는 pt(SVG) */\n width?: number\n height?: number\n /** 래스터 pt→px 실배율 */\n scale?: number\n mimeType?: string\n}\n\nexport interface RenderDocumentResult {\n scene: RenderScene\n assets: RenderAsset[]\n}\n\nexport type RenderInput = string | ArrayBuffer | Buffer | Uint8Array\n\nasync function loadBuffer(input: RenderInput): Promise<ArrayBuffer> {\n if (typeof input === \"string\") {\n try {\n return toArrayBuffer(await readFile(input))\n } catch (err) {\n const code = err instanceof Error && \"code\" in err ? (err as NodeJS.ErrnoException).code : undefined\n throw new KordocError(code === \"ENOENT\" ? `파일을 찾을 수 없습니다: ${input}` : `파일 읽기 실패: ${input}`)\n }\n }\n if (Buffer.isBuffer(input)) return toArrayBuffer(input)\n if (input instanceof Uint8Array) return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength) as ArrayBuffer\n return input\n}\n\n/** 입력 포맷 → 렌더 어댑터 종류 (hwpx | hwp). OLE2 는 HWP5 로 본다 — 세부 판정(HWP3·암호)은 어댑터가 */\nasync function sourceFormatOf(buffer: ArrayBuffer): Promise<RenderSourceFormat> {\n const format = detectFormat(buffer)\n if (format === \"hwpx\") {\n const zip = await detectZipFormat(buffer)\n if (zip === \"hwpx\" || zip === \"unknown\") return \"hwpx\"\n throw new KordocError(`렌더 미지원 형식(${zip}) — 레이아웃 렌더는 HWPX·HWP 만 지원합니다`)\n }\n if (format === \"hwp\") return \"hwp\"\n throw new KordocError(`렌더 미지원 형식(${format}) — 레이아웃 렌더는 HWPX·HWP 만 지원합니다`)\n}\n\n/**\n * 문서 → RenderScene + 선택 페이지 SVG. 페이지 선택은 조립 단계에서 걸러 비선택 페이지의 SVG 문자열을 만들지 않는다.\n * 선택 결과가 비면(범위 밖) KordocError.\n */\nexport async function renderDocumentToScene(input: RenderInput, options: SceneRenderOptions = {}): Promise<SceneRenderResult> {\n const buffer = await loadBuffer(input)\n const source = await sourceFormatOf(buffer)\n const select = options.pages !== undefined\n ? (pageCount: number) => {\n const set = parsePageRange(options.pages!, pageCount)\n if (set.size === 0) throw new KordocError(`선택한 페이지가 없습니다 — 문서는 ${pageCount}쪽 (pages: ${JSON.stringify(options.pages)})`)\n return set\n }\n : undefined\n const renderOptions = { reflow: options.reflow ?? true, reflowMode: options.reflowMode, highlights: options.highlights, maxImageBytes: options.maxImageBytes }\n const { scene, pageSvgs } = source === \"hwp\"\n ? renderHwp5Pages(buffer, { ...renderOptions, password: options.password }, select)\n : await renderHwpxPages(buffer, renderOptions, select)\n return { scene, pageSvgs }\n}\n\n/** 통합 렌더 — 포맷별 자산. 래스터·PDF 는 선택 페이지만 처리한다 */\nexport async function renderDocument(input: RenderInput, options: RenderDocumentOptions): Promise<RenderDocumentResult> {\n const { scene, pageSvgs } = await renderDocumentToScene(input, options)\n const pageNos = [...pageSvgs.keys()].sort((a, b) => a - b)\n const sizeOf = (n: number) => scene.pages.find(p => p.page === n)!\n const assets: RenderAsset[] = []\n switch (options.format) {\n case \"svg\":\n for (const n of pageNos) { const p = sizeOf(n); assets.push({ format: \"svg\", page: n, data: pageSvgs.get(n)!, width: p.width, height: p.height, mimeType: \"image/svg+xml\" }) }\n break\n case \"html\":\n assets.push({ format: \"html\", data: renderSceneToHtml(scene, pageSvgs, { title: options.title }), mimeType: \"text/html\" })\n break\n case \"png\":\n case \"jpeg\":\n for (const n of pageNos) {\n const p = sizeOf(n)\n const r = await rasterizePageSvg(pageSvgs.get(n)!, p.width, p.height, { format: options.format, maxWidthPx: options.maxWidthPx, quality: options.quality })\n assets.push({ format: options.format, page: n, data: r.data, width: r.widthPx, height: r.heightPx, scale: r.scale, mimeType: r.mimeType })\n }\n break\n case \"pdf\": {\n const html = renderSceneToHtml(scene, pageSvgs, { title: options.title })\n assets.push({ format: \"pdf\", data: await renderHtmlToPdf(html, { browserExecutablePath: options.browserExecutablePath }), mimeType: \"application/pdf\" })\n break\n }\n default:\n throw new KordocError(`알 수 없는 렌더 포맷: ${String(options.format)} (svg|html|png|jpeg|pdf)`)\n }\n return { scene, assets }\n}\n","/** 매직 바이트 기반 파일 포맷 감지 */\n\nimport JSZip from \"jszip\"\nimport type { FileType } from \"./types.js\"\nimport { parseLenientCfb } from \"./hwp5/cfb-lenient.js\"\n\n/** 매직 바이트 뷰 생성 (복사 없이 view) */\nfunction magicBytes(buffer: ArrayBuffer): Uint8Array {\n return new Uint8Array(buffer, 0, Math.min(4, buffer.byteLength))\n}\n\n/** ZIP 파일 여부: PK\\x03\\x04 */\nexport function isZipFile(buffer: ArrayBuffer): boolean {\n const b = magicBytes(buffer)\n return b[0] === 0x50 && b[1] === 0x4b && b[2] === 0x03 && b[3] === 0x04\n}\n\n/** HWPX (ZIP 기반 한컴 문서): PK\\x03\\x04 — 하위 호환용 */\nexport function isHwpxFile(buffer: ArrayBuffer): boolean {\n return isZipFile(buffer)\n}\n\n/** HWP 5.x (OLE2 바이너리 한컴 문서): \\xD0\\xCF\\x11\\xE0 */\nexport function isOldHwpFile(buffer: ArrayBuffer): boolean {\n const b = magicBytes(buffer)\n return b[0] === 0xd0 && b[1] === 0xcf && b[2] === 0x11 && b[3] === 0xe0\n}\n\n/**\n * HWP 3.x (한글 워드프로세서 3.0): \"HWP Document File V3.00 \\x1A\\x01\\x02\\x03\\x04\\x05\" 30 byte.\n * CFB(OLE2) 컨테이너 아닌 단일 binary stream — isOldHwpFile 과 magic 이 다르다.\n */\nconst HWP3_PREFIX = new TextEncoder().encode(\"HWP Document File V3.00\")\nexport function isHwp3File(buffer: ArrayBuffer): boolean {\n if (buffer.byteLength < HWP3_PREFIX.length) return false\n const head = new Uint8Array(buffer, 0, HWP3_PREFIX.length)\n for (let i = 0; i < HWP3_PREFIX.length; i++) {\n if (head[i] !== HWP3_PREFIX[i]) return false\n }\n return true\n}\n\n/** PDF 문서: %PDF */\nexport function isPdfFile(buffer: ArrayBuffer): boolean {\n const b = magicBytes(buffer)\n return b[0] === 0x25 && b[1] === 0x50 && b[2] === 0x44 && b[3] === 0x46\n}\n\n/** 래스터 이미지 (PNG/JPEG/WebP) — OCR 파싱 경로로 라우팅 */\nexport function isImageFile(buffer: ArrayBuffer): boolean {\n if (buffer.byteLength < 12) return false\n const b = new Uint8Array(buffer, 0, 12)\n // PNG: \\x89PNG\n if (b[0] === 0x89 && b[1] === 0x50 && b[2] === 0x4e && b[3] === 0x47) return true\n // JPEG: \\xFF\\xD8\\xFF\n if (b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff) return true\n // WebP: RIFF....WEBP\n if (b[0] === 0x52 && b[1] === 0x49 && b[2] === 0x46 && b[3] === 0x46 &&\n b[8] === 0x57 && b[9] === 0x45 && b[10] === 0x42 && b[11] === 0x50) return true\n return false\n}\n\n/** HWPML (XML 기반 한컴 문서): <?xml ... <HWPML */\nexport function isHwpmlFile(buffer: ArrayBuffer): boolean {\n const bytes = new Uint8Array(buffer, 0, Math.min(512, buffer.byteLength))\n const head = new TextDecoder(\"utf-8\", { fatal: false }).decode(bytes).replace(/^\\uFEFF/, \"\")\n return head.trimStart().startsWith(\"<?xml\") && head.includes(\"<HWPML\")\n}\n\n/** 동기 포맷 감지 — ZIP은 모두 \"hwpx\"로 반환 (하위 호환) */\nexport function detectFormat(buffer: ArrayBuffer): FileType {\n if (buffer.byteLength < 4) return \"unknown\"\n if (isHwp3File(buffer)) return \"hwp3\"\n if (isZipFile(buffer)) return \"hwpx\"\n if (isOldHwpFile(buffer)) return \"hwp\"\n if (isPdfFile(buffer)) return \"pdf\"\n if (isHwpmlFile(buffer)) return \"hwpml\"\n if (isImageFile(buffer)) return \"image\"\n return \"unknown\"\n}\n\n/**\n * OLE2 컨테이너 내부 스트림 기반 포맷 세분화.\n * HWP 5.x, XLS 모두 OLE2이므로 스트림 이름으로 구분.\n * - \"Workbook\" 또는 \"Book\" → 'xls'\n * - 그 외 (FileHeader 등) → 'hwp'\n */\nexport function detectOle2Format(buffer: ArrayBuffer): \"hwp\" | \"xls\" | \"unknown\" {\n try {\n const cfb = parseLenientCfb(Buffer.from(buffer))\n const names = cfb.entries().map(e => e.name)\n if (names.includes(\"Workbook\") || names.includes(\"Book\")) return \"xls\"\n if (names.includes(\"FileHeader\")) return \"hwp\"\n // FileHeader 없어도 BodyText/DocInfo 있으면 hwp\n if (names.some(n => n === \"DocInfo\" || n.startsWith(\"Section\"))) return \"hwp\"\n return \"unknown\"\n } catch {\n return \"unknown\"\n }\n}\n\n/**\n * ZIP 내부 구조 기반 포맷 세분화.\n * HWPX, XLSX, DOCX, PPTX 모두 ZIP이므로 내부 파일로 구분.\n */\nexport async function detectZipFormat(buffer: ArrayBuffer): Promise<\"hwpx\" | \"xlsx\" | \"docx\" | \"pptx\" | \"unknown\"> {\n try {\n const zip = await JSZip.loadAsync(buffer)\n // XLSX: xl/workbook.xml\n if (zip.file(\"xl/workbook.xml\")) return \"xlsx\"\n // DOCX: word/document.xml\n if (zip.file(\"word/document.xml\")) return \"docx\"\n // PPTX: HWPX로 오인하지 않도록 presentation.xml 로 감지\n if (zip.file(\"ppt/presentation.xml\")) return \"pptx\"\n // HWPX: Contents/ 또는 content.hpf 또는 mimetype\n if (zip.file(\"Contents/content.hpf\") || zip.file(\"mimetype\")) return \"hwpx\"\n // 기타 ZIP 내에 section 파일이 있으면 HWPX로 추정\n const hasSection = Object.keys(zip.files).some(f => f.startsWith(\"Contents/\"))\n if (hasSection) return \"hwpx\"\n return \"unknown\"\n } catch {\n return \"unknown\"\n }\n}\n","/**\n * 레이아웃 보존 HTML — 페이지별 standalone SVG 를 `.kordoc-page[data-page]` 로 감싼 자급자족 문서 (#75 Task 4).\n * 의미 HTML(IRBlock → <h1>/<p>/<table>) 이 아니다 — 원본 조판 재현이 목적. CDN·외부 자원 없음.\n * 인쇄 CSS(@page 크기 = 첫 페이지 크기, 페이지마다 break-after) 를 포함해 PDF 경로가 그대로 소비한다.\n */\n\nimport type { RenderScene } from \"./scene.js\"\nimport { escapeXml } from \"./svg-render.js\"\n\nexport interface SceneHtmlOptions {\n /** <title> — 기본 \"kordoc\" */\n title?: string\n}\n\n/**\n * @param scene 페이지 크기 메타(전 페이지)\n * @param pageSvgs 페이지 번호 → standalone SVG (선택된 페이지만 있어도 된다 — 있는 페이지만 방출)\n */\nexport function renderSceneToHtml(scene: RenderScene, pageSvgs: Map<number, string>, options: SceneHtmlOptions = {}): string {\n const title = escapeXml(options.title ?? \"kordoc\")\n const first = scene.pages.find(p => pageSvgs.has(p.page)) ?? scene.pages[0]\n const pageSize = first ? `${first.width}pt ${first.height}pt` : \"auto\"\n const pages = [...pageSvgs.keys()].sort((a, b) => a - b).map(n => {\n const meta = scene.pages.find(p => p.page === n)\n const size = meta ? ` style=\"width:${meta.width}pt;height:${meta.height}pt\"` : \"\"\n return `<div class=\"kordoc-page\" data-page=\"${n}\"${size}>${pageSvgs.get(n)}</div>`\n })\n return `<!doctype html>\n<html lang=\"ko\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${title}</title>\n<style>\nhtml, body { margin: 0; background: #e9e8e6; }\n.kordoc-document { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 16px 0; }\n.kordoc-page { position: relative; background: white; box-shadow: 0 1px 4px rgba(0,0,0,.25); }\n.kordoc-page > svg { display: block; width: 100%; height: 100%; }\n@page { size: ${pageSize}; margin: 0; }\n@media print {\n html, body { background: white; }\n .kordoc-document { gap: 0; padding: 0; }\n .kordoc-page { box-shadow: none; break-after: page; page-break-after: always; }\n .kordoc-page:last-child { break-after: auto; page-break-after: auto; }\n}\n</style>\n</head>\n<body>\n<div class=\"kordoc-document\">\n${pages.join(\"\\n\")}\n</div>\n</body>\n</html>\n`\n}\n","/**\n * Print Renderer — Markdown / IRBlock[] → PDF (puppeteer-core 기반).\n *\n * 흐름:\n * blocks → markdown (blocksToMarkdown)\n * markdown → HTML (markdown-it)\n * HTML + 프리셋 CSS → PDF (puppeteer-core)\n *\n * puppeteer-core는 optional peer dep. 미설치 시 markdownToPdf는 명확한 에러를 던지지만\n * `renderHtml()`은 항상 동작 (외부 PDF 엔진과 결합용).\n *\n * 참조: docs/SPEC.md §1.3\n */\n\nimport { existsSync } from \"fs\"\nimport MarkdownIt from \"markdown-it\"\nimport type { IRBlock } from \"../types.js\"\nimport { blocksToMarkdown } from \"../table/builder.js\"\nimport { KordocError, escapeHtml } from \"../utils.js\"\n\n// ─── 타입 ─────────────────────────────────────────\n\nexport type PrintPreset = \"default\" | \"gov-formal\" | \"compact\"\n\nexport interface PageMargin {\n top: string | number // mm 또는 CSS string (\"20mm\", \"1in\")\n right: string | number\n bottom: string | number\n left: string | number\n}\n\nexport interface PrintOptions {\n preset?: PrintPreset\n pageSize?: \"A4\" | \"Letter\"\n orientation?: \"portrait\" | \"landscape\"\n margin?: PageMargin\n /** 페이지 머리글 (HTML 허용, gov-formal 프리셋에서 자동 표시) */\n header?: string\n /** 페이지 바닥글 (HTML 허용) */\n footer?: string\n /** 워터마크 텍스트 (대각선 회색) */\n watermark?: string\n /** 사용자 정의 추가 CSS */\n extraCss?: string\n}\n\n// ─── 프리셋 CSS ───────────────────────────────────\n\nconst PRESETS: Record<PrintPreset, string> = {\n default: `\n @page { size: A4; margin: 20mm; }\n body { font-family: 'Pretendard', 'Malgun Gothic', '맑은 고딕', sans-serif; font-size: 11pt; line-height: 1.6; color: #111; }\n h1 { font-size: 20pt; margin: 1em 0 0.5em; }\n h2 { font-size: 16pt; margin: 1em 0 0.4em; }\n h3 { font-size: 13pt; margin: 0.8em 0 0.3em; }\n p { margin: 0.4em 0; }\n table { border-collapse: collapse; margin: 0.6em 0; width: 100%; }\n th, td { border: 1px solid #555; padding: 4px 8px; text-align: left; vertical-align: top; }\n th { background: #f0f0f0; }\n code { background: #f5f5f5; padding: 1px 4px; border-radius: 2px; font-family: 'D2Coding', Consolas, monospace; }\n pre { background: #f5f5f5; padding: 8px; border-radius: 4px; overflow-x: auto; }\n blockquote { border-left: 3px solid #ccc; padding-left: 12px; color: #555; margin: 0.6em 0; }\n img { max-width: 100%; }\n `,\n \"gov-formal\": `\n @page { size: A4; margin: 25mm 20mm; }\n body { font-family: '함초롬바탕', 'HCR Batang', 'Batang', 'Malgun Gothic', serif; font-size: 11pt; line-height: 1.7; color: #000; }\n h1 { font-size: 18pt; text-align: center; margin: 0.5em 0 1em; letter-spacing: 0.05em; }\n h2 { font-size: 14pt; margin: 1em 0 0.4em; border-bottom: 1px solid #999; padding-bottom: 2px; }\n h3 { font-size: 12pt; margin: 0.8em 0 0.3em; }\n p { margin: 0.3em 0; text-indent: 1em; }\n table { border-collapse: collapse; margin: 0.8em 0; width: 100%; }\n th, td { border: 1px solid #000; padding: 5px 8px; vertical-align: top; }\n th { background: #e8e8e8; font-weight: normal; }\n blockquote { border-left: 2px solid #555; padding-left: 12px; margin: 0.6em 0; }\n `,\n compact: `\n @page { size: A4; margin: 10mm; }\n body { font-family: 'Pretendard', 'Malgun Gothic', sans-serif; font-size: 9pt; line-height: 1.4; color: #111; }\n h1 { font-size: 14pt; margin: 0.5em 0 0.3em; }\n h2 { font-size: 12pt; margin: 0.5em 0 0.3em; }\n h3 { font-size: 10pt; margin: 0.4em 0 0.2em; }\n p { margin: 0.2em 0; }\n table { border-collapse: collapse; margin: 0.3em 0; width: 100%; font-size: 8pt; }\n th, td { border: 1px solid #777; padding: 2px 4px; }\n th { background: #f0f0f0; }\n `,\n}\n\n// ─── HTML 생성 ─────────────────────────────────\n\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n breaks: false,\n})\n\n// Markdown can come from documents. Keep only the small HTML vocabulary emitted by\n// blocksToMarkdown for merged/nested tables and underlining; render other raw tags as text.\n// Image cells may point into the CLI's per-document folder images/<document>/ (#98).\nconst SAFE_RAW_TAG = /^(?:<\\/?(?:table|thead|tbody|tfoot|tr|th|td|u)>|<br\\s*\\/?>|<(?:th|td)(?: (?:colspan|rowspan)=\"[1-9]\\d*\"){1,2}>|<img src=\"(?:images\\/(?:[^\"'<>\\/\\s]+\\/)?)?image_\\d+\\.(?:png|jpe?g|gif|webp|bmp)\" alt=\"image\">)$/i\nfunction sanitizeRawHtml(raw: string): string {\n return raw.replace(/<[^>]*>|</g, tag => SAFE_RAW_TAG.test(tag) ? tag : escapeHtml(tag))\n}\nmd.renderer.rules.html_block = (tokens, idx) => sanitizeRawHtml(tokens[idx].content)\nmd.renderer.rules.html_inline = (tokens, idx) => sanitizeRawHtml(tokens[idx].content)\n\n/**\n * Markdown 또는 IRBlock[] → HTML 문자열.\n * 외부 PDF 엔진(weasyprint, wkhtmltopdf 등)과 결합 가능.\n */\nexport function renderHtml(\n markdown: string,\n options?: PrintOptions,\n): string {\n const preset = options?.preset ?? \"default\"\n const css = PRESETS[preset] + (options?.extraCss ?? \"\")\n const body = md.render(markdown)\n\n const watermark = options?.watermark\n ? `<div class=\"watermark\">${escapeHtml(options.watermark)}</div>`\n : \"\"\n const watermarkCss = options?.watermark\n ? `\n .watermark {\n position: fixed;\n top: 50%; left: 50%;\n transform: translate(-50%, -50%) rotate(-30deg);\n font-size: 80pt;\n color: rgba(0,0,0,0.08);\n pointer-events: none;\n z-index: 9999;\n white-space: nowrap;\n }`\n : \"\"\n\n return `<!DOCTYPE html>\n<html lang=\"ko\">\n<head>\n<meta charset=\"utf-8\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; img-src 'self' data:; font-src 'self' data:; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'\">\n<style>${css}${watermarkCss}</style>\n</head>\n<body>\n${watermark}\n${body}\n</body>\n</html>`\n}\n\n// ─── PDF 생성 ─────────────────────────────────\n\n/**\n * 인쇄용 Chromium 을 띄워 잠근 페이지를 준다 — print(htmlToPdf)·render(renderHtmlToPdf) 두 기동 지점 공용.\n * 페이지 글은 문서에서 온 글이라 정적 조판만 필요하다: JS 를 끄고 data:·about: 밖의 요청(원격 http·로컬 file:)은\n * 전부 막는다. v4.14.4 까지는 문서가 병합 셀·마크다운 이미지로 심은 <img onerror>·<script>·![](http://…) 가\n * 헤드리스 Chrome 에서 실행되고 밖으로 요청했다 — KORDOC_OFFLINE(assertNetworkAllowed)은 Node fetch 만 막아\n * Chromium 요청은 샜다. 기동 timeout 90초: CI(Node 22)가 부하 중 기본 30초 안에 WS 엔드포인트를 못 받아\n * 실패했다(같은 커밋 재실행은 통과한 플레이크)\n */\nexport async function launchLockedPage(puppeteer: typeof import(\"puppeteer-core\"), executablePath: string) {\n const browser = await puppeteer.default.launch({\n executablePath,\n headless: true,\n timeout: 90_000,\n args: [\"--no-sandbox\", \"--disable-setuid-sandbox\"],\n })\n try {\n const page = await browser.newPage()\n await page.setJavaScriptEnabled(false)\n await page.setRequestInterception(true)\n page.on(\"request\", (req) => {\n if (/^(?:data|about):/i.test(req.url())) void req.continue()\n else void req.abort()\n })\n return { browser, page }\n } catch (err) {\n await browser.close()\n throw err\n }\n}\n\n/**\n * puppeteer-core로 HTML → PDF 변환.\n * 환경에 Chromium 실행 파일 필요 (PUPPETEER_EXECUTABLE_PATH 환경변수 또는 자동 감지).\n */\nasync function htmlToPdf(html: string, options?: PrintOptions): Promise<Buffer> {\n let puppeteer: typeof import(\"puppeteer-core\")\n try {\n puppeteer = await import(\"puppeteer-core\")\n } catch {\n throw new KordocError(\n \"PDF 생성에 puppeteer-core가 필요합니다. 설치: npm install puppeteer-core\",\n )\n }\n\n const executablePath =\n process.env.PUPPETEER_EXECUTABLE_PATH ?? findChromiumPath()\n if (!executablePath) {\n throw new KordocError(\n \"Chromium 실행 파일을 찾을 수 없습니다. PUPPETEER_EXECUTABLE_PATH 환경변수를 설정하세요.\",\n )\n }\n\n const { browser, page } = await launchLockedPage(puppeteer, executablePath)\n try {\n // puppeteer 25 부터 setContent 의 waitUntil 에서 networkidle0 이 빠짐. 같은 의미\n // (연결 0개가 500ms 유지)를 waitForNetworkIdle 로.\n await page.setContent(html, { waitUntil: \"load\" })\n await page.waitForNetworkIdle({ idleTime: 500, concurrency: 0 })\n\n const margin = options?.margin\n const pdf = await page.pdf({\n format: options?.pageSize ?? \"A4\",\n landscape: options?.orientation === \"landscape\",\n printBackground: true,\n margin: margin\n ? {\n top: toCss(margin.top),\n right: toCss(margin.right),\n bottom: toCss(margin.bottom),\n left: toCss(margin.left),\n }\n : undefined,\n displayHeaderFooter: !!(options?.header || options?.footer),\n headerTemplate: options?.header ?? \"<div></div>\",\n footerTemplate:\n options?.footer ??\n '<div style=\"font-size:8pt;width:100%;text-align:center;color:#777;\"><span class=\"pageNumber\"></span>/<span class=\"totalPages\"></span></div>',\n })\n return Buffer.from(pdf)\n } finally {\n await browser.close()\n }\n}\n\nfunction toCss(v: string | number): string {\n return typeof v === \"number\" ? `${v}mm` : v\n}\n\n/** Windows/Mac/Linux의 일반적 Chrome 경로 자동 감지 */\nexport function findChromiumPath(): string | null {\n // 사용자가 명시 안 했으면 OS 표준 경로 시도\n // Windows\n const win = [\n \"C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n \"C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n process.env.LOCALAPPDATA + \"\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe\",\n \"C:\\\\Program Files\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe\",\n \"C:\\\\Program Files (x86)\\\\Microsoft\\\\Edge\\\\Application\\\\msedge.exe\",\n ]\n // Mac\n const mac = [\n \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\",\n \"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\",\n ]\n // Linux\n const linux = [\"/usr/bin/google-chrome\", \"/usr/bin/chromium-browser\", \"/usr/bin/chromium\"]\n\n const candidates =\n process.platform === \"win32\" ? win : process.platform === \"darwin\" ? mac : linux\n\n for (const p of candidates) {\n if (p && existsSync(p)) return p\n }\n return null\n}\n\n/** Markdown → PDF (Buffer). */\nexport async function markdownToPdf(\n markdown: string,\n options?: PrintOptions,\n): Promise<Buffer> {\n const html = renderHtml(markdown, options)\n return htmlToPdf(html, options)\n}\n\n/** IRBlock[] → PDF (Buffer). */\nexport async function blocksToPdf(\n blocks: IRBlock[],\n options?: PrintOptions,\n): Promise<Buffer> {\n const markdown = blocksToMarkdown(blocks)\n return markdownToPdf(markdown, options)\n}\n","/**\n * 레이아웃 HTML → PDF (#75 Task 6). 그리기를 다시 구현하지 않고 페이지 SVG 를 담은 HTML 을 Chromium 이 인쇄한다.\n * puppeteer-core 는 optional peer dep, Chromium 은 번들하지 않는다 — 없으면 KordocError(조치 안내).\n */\n\nimport { KordocError } from \"../utils.js\"\nimport { findChromiumPath, launchLockedPage } from \"../print/renderer.js\"\n\nexport interface ScenePdfOptions {\n /** Chromium 실행 파일 — 미지정 시 PUPPETEER_EXECUTABLE_PATH → OS 표준 경로 자동 탐지 */\n browserExecutablePath?: string\n}\n\n/** 레이아웃 HTML(renderSceneToHtml 산출) → PDF. @page 크기·break-after 는 HTML 의 인쇄 CSS 가 담당 */\nexport async function renderHtmlToPdf(html: string, options: ScenePdfOptions = {}): Promise<Buffer> {\n let puppeteer: typeof import(\"puppeteer-core\")\n try {\n puppeteer = await import(\"puppeteer-core\")\n } catch {\n throw new KordocError(\"PDF 렌더에는 puppeteer-core 가 필요합니다 (npm install puppeteer-core). SVG/HTML/PNG 는 Chromium 없이 됩니다\")\n }\n const executablePath = options.browserExecutablePath ?? process.env.PUPPETEER_EXECUTABLE_PATH ?? findChromiumPath()\n if (!executablePath) {\n throw new KordocError(\"Chromium 실행 파일을 찾을 수 없습니다 — browserExecutablePath 옵션 또는 PUPPETEER_EXECUTABLE_PATH 환경변수를 지정하세요\")\n }\n // JS 끔·data:/about: 밖 요청 차단·기동 90초 — 인쇄 렌더러와 같은 잠금 (launchLockedPage 주석)\n const { browser, page } = await launchLockedPage(puppeteer, executablePath)\n try {\n await page.setContent(html, { waitUntil: \"load\" })\n const pdf = await page.pdf({ printBackground: true, preferCSSPageSize: true })\n return Buffer.from(pdf)\n } finally {\n await browser.close()\n }\n}\n","/**\n * region crop (#75 Task 5) — RenderScene 의 페이지 로컬 bbox 를 래스터 실배율로 픽셀 환산해 잘라낸다.\n * DPI 를 여기서 다시 추정하지 않는다: pixel = pt × rasterize 가 돌려준 scale.\n * 의미 판단(진짜 표인지 조직도인지)은 하지 않는다 — 렌더러가 표라고 아는 개체를 자르는 것뿐.\n */\n\nimport { KordocError } from \"../utils.js\"\nimport type { PageBBox, RenderObjectType, RenderRegion } from \"./scene.js\"\nimport { renderDocumentToScene, type RenderInput, type SceneRenderOptions } from \"./document.js\"\nimport { loadSharp, rasterizePageSvg, type RasterFormat } from \"./rasterize.js\"\n\nexport interface ExtractRegionOptions extends Pick<SceneRenderOptions, \"pages\" | \"reflow\" | \"reflowMode\"> {\n /** 기본 전 유형 */\n types?: RenderObjectType[]\n format?: RasterFormat\n /** bbox 둘레 여백(pt) — 페이지 안으로 클램프 */\n paddingPt?: number\n /** 페이지 래스터 최대 폭 px (crop 해상도) */\n maxWidthPx?: number\n quality?: number\n /** region 선별 — sourceId·id 로 특정 개체만 자를 때 (extractTables). types 필터 뒤에 적용 */\n filter?: (region: RenderRegion) => boolean\n}\n\nexport interface RegionAsset {\n region: RenderRegion\n page: number\n bbox: PageBBox\n mimeType: \"image/png\" | \"image/jpeg\"\n data: Buffer\n widthPx: number\n heightPx: number\n}\n\nexport interface PixelRect { left: number; top: number; width: number; height: number }\n\n/**\n * bbox(pt) → 페이지 픽셀 사각형. 여백 포함 후 페이지 안으로 클램프, 최소 1×1px.\n * 페이지 밖 bbox 는 페이지 가장자리 1px 로 잘려 sharp.extract 에 음수·범위 밖이 절대 가지 않는다.\n */\nexport function cropRect(bbox: PageBBox, scale: number, pageWidthPx: number, pageHeightPx: number, paddingPt = 0): PixelRect {\n const W = Math.max(1, Math.floor(pageWidthPx)), H = Math.max(1, Math.floor(pageHeightPx))\n const clamp = (v: number, hi: number) => Math.min(Math.max(0, v), hi)\n let left = clamp(Math.floor((bbox.x - paddingPt) * scale), W - 1)\n let top = clamp(Math.floor((bbox.y - paddingPt) * scale), H - 1)\n const right = clamp(Math.ceil((bbox.x + bbox.width + paddingPt) * scale), W)\n const bottom = clamp(Math.ceil((bbox.y + bbox.height + paddingPt) * scale), H)\n const width = Math.max(1, right - left), height = Math.max(1, bottom - top)\n if (left + width > W) left = W - width\n if (top + height > H) top = H - height\n return { left, top, width, height }\n}\n\n/** 렌더러가 아는 개체(표·이미지·문단·도형)를 페이지 이미지에서 잘라낸다. 페이지당 래스터 1회 */\nexport async function extractRenderedRegions(input: RenderInput, options: ExtractRegionOptions = {}): Promise<RegionAsset[]> {\n const { scene, pageSvgs } = await renderDocumentToScene(input, { pages: options.pages, reflow: options.reflow, reflowMode: options.reflowMode })\n const types = options.types ? new Set(options.types) : null\n const format = options.format ?? \"png\"\n const wanted = scene.regions.filter(r => (!types || types.has(r.type)) && (!options.filter || options.filter(r)))\n const byPage = new Map<number, Array<{ region: RenderRegion; bbox: PageBBox }>>()\n for (const r of wanted) for (const b of r.regions) if (pageSvgs.has(b.page)) (byPage.get(b.page) ?? byPage.set(b.page, []).get(b.page)!).push({ region: r, bbox: b })\n if (byPage.size === 0) return []\n const sharp = await loadSharp()\n const out: RegionAsset[] = []\n for (const [page, items] of [...byPage.entries()].sort((a, b) => a[0] - b[0])) {\n const meta = scene.pages.find(p => p.page === page)\n if (!meta) throw new KordocError(`페이지 ${page} 메타 없음`)\n const raster = await rasterizePageSvg(pageSvgs.get(page)!, meta.width, meta.height, { format, maxWidthPx: options.maxWidthPx, quality: options.quality })\n for (const { region, bbox } of items) {\n const rect = cropRect(bbox, raster.scale, raster.widthPx, raster.heightPx, options.paddingPt ?? 0)\n const pipeline = sharp(raster.data).extract(rect)\n const data = format === \"jpeg\" ? await pipeline.jpeg({ quality: options.quality ?? 85 }).toBuffer() : await pipeline.png().toBuffer()\n out.push({ region, page, bbox, mimeType: raster.mimeType, data, widthPx: rect.width, heightPx: rect.height })\n }\n }\n // 문서(region) 순서 유지 — 페이지 순회로 섞인 것을 region 등장 순으로 되돌린다\n const order = new Map(scene.regions.map((r, i) => [r.id, i]))\n out.sort((a, b) => (order.get(a.region.id)! - order.get(b.region.id)!) || (a.page - b.page))\n return out\n}\n"],"mappings":"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,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAAK,MAAO;AAAA,MAC3I,MAAO;AAAA,MAAK,OAAO;AAAA,MAAK,OA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;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;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;A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","names":["depth","ln","elements","num","x","y","pt","pageNo","MAX_DECOMPRESS_SIZE","TAG_ID_MAPPINGS","readRecords","CTRL_ID_TBL","num","num","swap32","num","i","table","require","TAG_BORDER_FILL","cid","swap32","CTRL_TBL","CTRL_GSO","CTRL_SECD","CTRL_COLD","CTRL_EQED","o","ctrlIdx","k","tag","JSZip","JSZip"]}