kordoc 3.16.0 → 3.16.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.
- package/README.md +1 -1
- package/dist/{-H7N62NZU.js → -C4FVXHIP.js} +7 -7
- package/dist/{chunk-IC56KHBL.js → chunk-4WI7QXEG.js} +2 -2
- package/dist/{chunk-BIP4JAVG.cjs → chunk-72LLOLFX.cjs} +13 -6
- package/dist/chunk-72LLOLFX.cjs.map +1 -0
- package/dist/{chunk-5RHRQ7HK.js → chunk-E4Y73XRH.js} +8 -4
- package/dist/chunk-E4Y73XRH.js.map +1 -0
- package/dist/{chunk-Q7EGGW3H.js → chunk-EJ2ZIYTO.js} +13 -6
- package/dist/chunk-EJ2ZIYTO.js.map +1 -0
- package/dist/{chunk-WD5NUUU6.js → chunk-EUEKDZ7O.js} +315 -262
- package/dist/chunk-EUEKDZ7O.js.map +1 -0
- package/dist/{chunk-JFNWJFKP.js → chunk-HECUYZZK.js} +2 -2
- package/dist/{chunk-LYL4IL3E.js → chunk-IS3IK7UT.js} +13 -6
- package/dist/chunk-IS3IK7UT.js.map +1 -0
- package/dist/{chunk-FGL7BH4P.js → chunk-WR5LHRXO.js} +49 -20
- package/dist/chunk-WR5LHRXO.js.map +1 -0
- package/dist/cli.js +56 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +586 -502
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +358 -274
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +25 -16
- package/dist/mcp.js.map +1 -1
- package/dist/{parser-HBHCD7TE.js → parser-CMPNM6ZL.js} +25 -12
- package/dist/parser-CMPNM6ZL.js.map +1 -0
- package/dist/{parser-5G247XYD.js → parser-GWL72XYS.js} +24 -11
- package/dist/parser-GWL72XYS.js.map +1 -0
- package/dist/{parser-6V7F3GET.cjs → parser-YVRTKGKO.cjs} +36 -23
- package/dist/parser-YVRTKGKO.cjs.map +1 -0
- package/dist/render-A2LFMJCP.js +10 -0
- package/dist/seal-KA4VAV2Z.js +10 -0
- package/dist/{watch-HIYNOSFD.js → watch-WLI6K3IR.js} +7 -7
- package/package.json +2 -5
- package/dist/chunk-5RHRQ7HK.js.map +0 -1
- package/dist/chunk-BIP4JAVG.cjs.map +0 -1
- package/dist/chunk-FGL7BH4P.js.map +0 -1
- package/dist/chunk-LYL4IL3E.js.map +0 -1
- package/dist/chunk-Q7EGGW3H.js.map +0 -1
- package/dist/chunk-WD5NUUU6.js.map +0 -1
- package/dist/parser-5G247XYD.js.map +0 -1
- package/dist/parser-6V7F3GET.cjs.map +0 -1
- package/dist/parser-HBHCD7TE.js.map +0 -1
- package/dist/render-VA4NHROW.js +0 -10
- package/dist/seal-UVDRGHEF.js +0 -10
- /package/dist/{-H7N62NZU.js.map → -C4FVXHIP.js.map} +0 -0
- /package/dist/{chunk-IC56KHBL.js.map → chunk-4WI7QXEG.js.map} +0 -0
- /package/dist/{chunk-JFNWJFKP.js.map → chunk-HECUYZZK.js.map} +0 -0
- /package/dist/{render-VA4NHROW.js.map → render-A2LFMJCP.js.map} +0 -0
- /package/dist/{seal-UVDRGHEF.js.map → seal-KA4VAV2Z.js.map} +0 -0
- /package/dist/{watch-HIYNOSFD.js.map → watch-WLI6K3IR.js.map} +0 -0
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ MCP 등록 대신 스킬(SKILL.md) 형태로 쓰려면:
|
|
|
86
86
|
## v3.16.0 변경사항
|
|
87
87
|
|
|
88
88
|
- **📊 차트 생성**: 마크다운 ```chart 펜스(type/cat/계열 라인)가 한컴 네이티브 차트(OOXML chartSpace)로 생성됩니다 — 막대·선·원·도넛·영역·분산·방사형 등 20종, 계열/조각 색 지정, 잘못된 펜스는 코드블록 폴백.
|
|
89
|
-
- **🔴 도장/서명 자동 날인**: `kordoc seal` — "(인)"·"서명 또는 인" 앵커를 찾아 도장 PNG를 글 앞 부유로 배치. 표/페이지를 키우지 않아 날인 후 서식이 밀리지 않습니다 (MCP `place_seal` 포함).
|
|
89
|
+
- **🔴 도장/서명 자동 날인**: `kordoc seal` — "(인)"·"서명 또는 인" 앵커를 찾아 도장 PNG를 글 앞 부유로 배치. 표/페이지를 키우지 않아 날인 후 서식이 밀리지 않습니다 (MCP `place_seal` 포함). 중첩표·글상자·탭/줄바꿈 문단은 위치가 근사이며 결과 `warnings` 로 고지됩니다 — 한컴에서 확인 후 `--dx`/`--dy`(dx_mm/dy_mm)로 미세조정하세요.
|
|
90
90
|
- **🔌 Claude Code 플러그인**: `/plugin marketplace add chrisryugj/kordoc` → `.hwp`/`.hwpx`/공문서 요청에 kordoc 스킬 자동 활성화.
|
|
91
91
|
|
|
92
92
|
## v3.15.0 변경사항
|
|
@@ -33,10 +33,10 @@ import {
|
|
|
33
33
|
patchHwpxBlocks,
|
|
34
34
|
renderHtml,
|
|
35
35
|
validateHwpx
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-EUEKDZ7O.js";
|
|
37
37
|
import {
|
|
38
38
|
blocksToMarkdown
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-IS3IK7UT.js";
|
|
40
40
|
import {
|
|
41
41
|
detectFormat,
|
|
42
42
|
detectOle2Format,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
buildRangeSplices,
|
|
54
54
|
placeSealHwpx,
|
|
55
55
|
scanSectionXml
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-WR5LHRXO.js";
|
|
57
57
|
import {
|
|
58
58
|
SPACE_EM_FIXED,
|
|
59
59
|
SPACE_EM_FONT,
|
|
@@ -63,11 +63,11 @@ import {
|
|
|
63
63
|
renderHwpxToSvg,
|
|
64
64
|
simulateWrap,
|
|
65
65
|
simulateWrapKeepWord
|
|
66
|
-
} from "./chunk-
|
|
67
|
-
import "./chunk-
|
|
66
|
+
} from "./chunk-E4Y73XRH.js";
|
|
67
|
+
import "./chunk-HECUYZZK.js";
|
|
68
68
|
import {
|
|
69
69
|
VERSION
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-4WI7QXEG.js";
|
|
71
71
|
export {
|
|
72
72
|
HwpxSession,
|
|
73
73
|
PRESET_ALIAS,
|
|
@@ -125,4 +125,4 @@ export {
|
|
|
125
125
|
simulateWrapKeepWord,
|
|
126
126
|
validateHwpx
|
|
127
127
|
};
|
|
128
|
-
//# sourceMappingURL=-
|
|
128
|
+
//# sourceMappingURL=-C4FVXHIP.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/utils.ts
|
|
4
|
-
var VERSION = true ? "3.16.
|
|
4
|
+
var VERSION = true ? "3.16.2" : "0.0.0-dev";
|
|
5
5
|
function toArrayBuffer(buf) {
|
|
6
6
|
if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
|
|
7
7
|
return buf.buffer;
|
|
@@ -121,4 +121,4 @@ export {
|
|
|
121
121
|
safeMax,
|
|
122
122
|
classifyError
|
|
123
123
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-4WI7QXEG.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/utils.ts
|
|
2
|
-
var VERSION = true ? "3.16.
|
|
2
|
+
var VERSION = true ? "3.16.2" : "0.0.0-dev";
|
|
3
3
|
function toArrayBuffer(buf) {
|
|
4
4
|
if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
|
|
5
5
|
return buf.buffer;
|
|
@@ -343,7 +343,14 @@ function convertTableToText(rows) {
|
|
|
343
343
|
).filter(Boolean).join("\n");
|
|
344
344
|
}
|
|
345
345
|
function escapeGfm(text) {
|
|
346
|
-
|
|
346
|
+
const NUL = String.fromCharCode(0);
|
|
347
|
+
const spans = [];
|
|
348
|
+
const masked = text.replace(/\$\$[^$]*\$\$|\$[^$\n]*\$/g, (m) => {
|
|
349
|
+
spans.push(m);
|
|
350
|
+
return NUL + (spans.length - 1) + NUL;
|
|
351
|
+
});
|
|
352
|
+
const escaped = masked.replace(/([~*])/g, "\\$1");
|
|
353
|
+
return escaped.replace(new RegExp(NUL + "(\\d+)" + NUL, "g"), (_, n) => spans[Number(n)]);
|
|
347
354
|
}
|
|
348
355
|
var HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\s?개체|묶음\s?개체|글상자|수식\s?개체|OLE\s?개체)\s?입니다\.?/g;
|
|
349
356
|
function sanitizeText(text) {
|
|
@@ -436,15 +443,15 @@ function blocksToMarkdown(blocks) {
|
|
|
436
443
|
if (/^\[별표\s*\d+/.test(text)) {
|
|
437
444
|
const nextBlock = blocks[i + 1];
|
|
438
445
|
if (_optionalChain([nextBlock, 'optionalAccess', _20 => _20.type]) === "paragraph" && nextBlock.text && /관련\)?$/.test(nextBlock.text)) {
|
|
439
|
-
lines.push("", `## ${text} ${nextBlock.text}`, "");
|
|
446
|
+
lines.push("", `## ${escapeGfm(text)} ${escapeGfm(nextBlock.text)}`, "");
|
|
440
447
|
i++;
|
|
441
448
|
} else {
|
|
442
|
-
lines.push("", `## ${text}`, "");
|
|
449
|
+
lines.push("", `## ${escapeGfm(text)}`, "");
|
|
443
450
|
}
|
|
444
451
|
continue;
|
|
445
452
|
}
|
|
446
453
|
if (/^\([^)]*조[^)]*관련\)$/.test(text)) {
|
|
447
|
-
lines.push(`*${text}*`, "");
|
|
454
|
+
lines.push(`*${escapeGfm(text)}*`, "");
|
|
448
455
|
continue;
|
|
449
456
|
}
|
|
450
457
|
if (block.href) {
|
|
@@ -641,4 +648,4 @@ var HEADING_RATIO_H3 = 1.15;
|
|
|
641
648
|
|
|
642
649
|
|
|
643
650
|
exports.VERSION = VERSION; exports.toArrayBuffer = toArrayBuffer; exports.KordocError = KordocError; exports.isPathTraversal = isPathTraversal; exports.normalizeSectionHref = normalizeSectionHref; exports.compareSectionPaths = compareSectionPaths; exports.precheckZipSize = precheckZipSize; exports.stripDtd = stripDtd; exports.sanitizeHref = sanitizeHref; exports.safeMin = safeMin; exports.safeMax = safeMax; exports.classifyError = classifyError; exports.mapPuaText = mapPuaText; exports.MAX_COLS = MAX_COLS; exports.MAX_ROWS = MAX_ROWS; exports.buildTable = buildTable; exports.convertTableToText = convertTableToText; exports.flattenLayoutTables = flattenLayoutTables; exports.blocksToMarkdown = blocksToMarkdown; exports.HEADING_RATIO_H1 = HEADING_RATIO_H1; exports.HEADING_RATIO_H2 = HEADING_RATIO_H2; exports.HEADING_RATIO_H3 = HEADING_RATIO_H3;
|
|
644
|
-
//# sourceMappingURL=chunk-
|
|
651
|
+
//# sourceMappingURL=chunk-72LLOLFX.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/chris_gomdori/workspace/kordoc/dist/chunk-72LLOLFX.cjs","../src/utils.ts","../src/shared/pua.ts","../src/table/builder.ts","../src/types.ts"],"names":[],"mappings":"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","file":"/Users/chris_gomdori/workspace/kordoc/dist/chunk-72LLOLFX.cjs","sourcesContent":[null,"/** kordoc 공용 유틸리티 */\n\n/** 빌드 타임에 tsup define으로 주입되는 버전 */\ndeclare const __KORDOC_VERSION__: string\nexport const VERSION: string = typeof __KORDOC_VERSION__ !== \"undefined\" ? __KORDOC_VERSION__ : \"0.0.0-dev\"\n\n/**\n * Node.js Buffer → ArrayBuffer 변환\n * pool Buffer의 공유 ArrayBuffer 문제를 안전하게 처리.\n * offset=0이고 전체 ArrayBuffer를 차지하면 복사 없이 직접 반환.\n */\nexport function toArrayBuffer(buf: Buffer): ArrayBuffer {\n if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {\n return buf.buffer as ArrayBuffer\n }\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n}\n\n/**\n * kordoc 내부 에러 클래스 — 사용자에게 노출해도 안전한 메시지만 포함.\n * MCP 에러 정제에서 instanceof로 판별하여 allowlist 패턴 매칭 없이 안전하게 통과.\n */\nexport class KordocError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"KordocError\"\n }\n}\n\n/**\n * 에러 메시지 정제 — KordocError는 그대로, 나머지는 일반 메시지로 대체.\n * 파일시스템 경로, 스택 트레이스 등 내부 정보 노출 방지.\n */\nexport function sanitizeError(err: unknown): string {\n if (err instanceof KordocError) return err.message\n return \"문서 처리 중 오류가 발생했습니다\"\n}\n\n/**\n * ZIP 엔트리 경로의 경로 순회 여부 판별.\n * 백슬래시 정규화, .., 절대경로, Windows 드라이브 문자 모두 차단.\n */\nexport function isPathTraversal(name: string): boolean {\n if (name.includes(\"\\x00\")) return true\n const normalized = name.replace(/\\\\/g, \"/\")\n const segments = normalized.split(\"/\")\n return segments.some(s => s === \"..\") || normalized.startsWith(\"/\") || /^[A-Za-z]:/.test(normalized)\n}\n\n// ─── HWPX 섹션 href 해석 (parser/roundtrip 공용) ────────────────────\n\n/**\n * manifest href를 본문 섹션 경로(`Contents/sectionN.xml`)로 정규화.\n * 비본문 XML(header/script/settings)·path traversal·드라이브 경로는 null로 거른다.\n * 백슬래시 구분자를 슬래시로 통일해 parser와 roundtrip이 동일한 목록을 만든다.\n */\nexport function normalizeSectionHref(href: string): string | null {\n if (!href) return null\n let normalized = href.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\")\n if (isPathTraversal(normalized)) return null\n if (/^[Ss]ection\\d+\\.xml$/.test(normalized)) normalized = \"Contents/\" + normalized\n return /(?:^|\\/)[Ss]ection\\d+\\.xml$/.test(normalized) ? normalized : null\n}\n\n/** sectionN.xml을 N 숫자 순서로 정렬 (section10 > section2). */\nexport function compareSectionPaths(a: string, b: string): number {\n const ai = Number(a.match(/[Ss]ection(\\d+)\\.xml$/)?.[1] ?? Number.MAX_SAFE_INTEGER)\n const bi = Number(b.match(/[Ss]ection(\\d+)\\.xml$/)?.[1] ?? Number.MAX_SAFE_INTEGER)\n return ai === bi ? a.localeCompare(b) : ai - bi\n}\n\n// ─── ZIP 안전 로딩 (ZIP bomb 방지) ────────────────────\n\n/**\n * ZIP bomb 사전 검사 — Central Directory에서 비압축 합계와 엔트리 수 확인.\n * HWPX/XLSX/DOCX 등 모든 ZIP 기반 포맷에서 공통 사용.\n */\nexport function precheckZipSize(\n buffer: ArrayBuffer,\n maxUncompressedSize = 100 * 1024 * 1024,\n maxEntries = 500,\n): { totalUncompressed: number; entryCount: number } {\n try {\n const data = new DataView(buffer)\n const len = buffer.byteLength\n // EOCD 시그니처 역방향 스캔\n let eocdOffset = -1\n for (let i = len - 22; i >= Math.max(0, len - 65557); i--) {\n if (data.getUint32(i, true) === 0x06054b50) { eocdOffset = i; break }\n }\n if (eocdOffset < 0) return { totalUncompressed: 0, entryCount: 0 }\n\n const entryCount = data.getUint16(eocdOffset + 10, true)\n if (entryCount > maxEntries) {\n throw new KordocError(`ZIP 엔트리 수 초과: ${entryCount} (최대 ${maxEntries})`)\n }\n\n const cdSize = data.getUint32(eocdOffset + 12, true)\n const cdOffset = data.getUint32(eocdOffset + 16, true)\n if (cdOffset + cdSize > len) return { totalUncompressed: 0, entryCount }\n\n let totalUncompressed = 0\n let pos = cdOffset\n for (let i = 0; i < entryCount && pos + 46 <= cdOffset + cdSize; i++) {\n if (data.getUint32(pos, true) !== 0x02014b50) break\n totalUncompressed += data.getUint32(pos + 24, true)\n const nameLen = data.getUint16(pos + 28, true)\n const extraLen = data.getUint16(pos + 30, true)\n const commentLen = data.getUint16(pos + 32, true)\n pos += 46 + nameLen + extraLen + commentLen\n }\n\n if (totalUncompressed > maxUncompressedSize) {\n throw new KordocError(`ZIP 비압축 크기 초과: ${(totalUncompressed / 1024 / 1024).toFixed(1)}MB (최대 ${maxUncompressedSize / 1024 / 1024}MB)`)\n }\n\n return { totalUncompressed, entryCount }\n } catch (err) {\n if (err instanceof KordocError) throw err\n return { totalUncompressed: 0, entryCount: 0 }\n }\n}\n\n/** XXE/Billion Laughs 방지 — DOCTYPE 제거 (내부 DTD 서브셋 포함) */\nexport function stripDtd(xml: string): string {\n return xml.replace(/<!DOCTYPE\\s[^[>]*(\\[[\\s\\S]*?\\])?\\s*>/gi, \"\")\n}\n\n/** 하이퍼링크 URL 살균 — javascript: 등 XSS 위험 스킴 차단 */\nconst SAFE_HREF_RE = /^(?:https?:|mailto:|tel:|#)/i\nexport function sanitizeHref(href: string): string | null {\n const trimmed = href.trim()\n if (!trimmed || !SAFE_HREF_RE.test(trimmed)) return null\n return trimmed\n}\n\n// ─── 안전한 min/max (스택 오버플로 방지) ─────────────\n\n/** Math.min(...arr) 대체 — 대형 배열에서 스택 오버플로 방지 */\nexport function safeMin(arr: number[]): number {\n let min = Infinity\n for (let i = 0; i < arr.length; i++) if (arr[i] < min) min = arr[i]\n return min\n}\n\n/** Math.max(...arr) 대체 — 대형 배열에서 스택 오버플로 방지 */\nexport function safeMax(arr: number[]): number {\n let max = -Infinity\n for (let i = 0; i < arr.length; i++) if (arr[i] > max) max = arr[i]\n return max\n}\n\n// ─── 에러 분류 ──────────────────────────────────────\n\nimport type { ErrorCode } from \"./types.js\"\n\n/** 에러를 구조화된 ErrorCode로 분류 — KordocError 메시지 패턴 매칭 */\nexport function classifyError(err: unknown): ErrorCode {\n if (!(err instanceof Error)) return \"PARSE_ERROR\"\n const msg = err.message\n if (msg.includes(\"암호화\")) return \"ENCRYPTED\"\n if (msg.includes(\"DRM\")) return \"DRM_PROTECTED\"\n if (msg.includes(\"ZIP bomb\") || msg.includes(\"ZIP 비압축 크기 초과\") || msg.includes(\"ZIP 엔트리 수 초과\")) return \"ZIP_BOMB\"\n if (msg.includes(\"bomb\") || msg.includes(\"크기 초과\") || msg.includes(\"압축 해제\")) return \"DECOMPRESSION_BOMB\"\n if (msg.includes(\"이미지 기반\")) return \"IMAGE_BASED_PDF\"\n if (msg.includes(\"섹션\") && (msg.includes(\"찾을 수 없\") || msg.includes(\"없음\"))) return \"NO_SECTIONS\"\n if (msg.includes(\"시그니처\") || msg.includes(\"복구할 수 없\")) return \"CORRUPTED\"\n return \"PARSE_ERROR\"\n}\n","/**\n * 한컴 PUA(Private Use Area) 문자 → 유니코드 표준 문자 매핑.\n *\n * 한글(HWP)은 글머리표·문자표 기호를 Symbol/Wingdings 계열 PUA 코드포인트\n * (U+F020~U+F0FF)와 한컴 자체 Supplementary PUA-A 영역(U+F0000대)에 저장한다.\n * 매핑 없이 마크다운에 내보내면 빈 네모로 깨진다.\n *\n * 매핑 출처: rhwp (MIT, https://github.com/edwardkim/rhwp)\n * paragraph_layout.rs map_pua_bullet_char — 한컴 PDF 정답지 시각 검증 테이블.\n */\n\n/** BMP Symbol 영역 (U+F020~U+F0FF) 매핑 — 키는 (code - 0xF000) */\nconst BMP_SYMBOL_MAP: Record<number, string> = {\n // 도형/기호\n 0x6c: \"●\", // ●\n 0x6d: \"●\", // ● (그림자 원 근사)\n 0x6e: \"■\", // ■\n 0x6f: \"□\", // □\n 0x70: \"□\", // □ (굵은 흰 사각 근사)\n 0x71: \"□\", // □ (그림자 근사)\n 0x72: \"□\", // □ (그림자 근사)\n 0x73: \"⬧\", // ⬧\n 0x74: \"⧫\", // ⧫\n 0x75: \"◆\", // ◆\n 0x76: \"❖\", // ❖\n 0x77: \"⬥\", // ⬥\n // 체크/별/점\n 0x9e: \"·\", // ·\n 0x9f: \"•\", // •\n 0xa0: \"·\", // · (한컴 PDF 정답지 정합 — ▪ 아님)\n 0xa1: \"⚪\", // ⚪\n 0xa2: \"○\", // ○\n 0xa3: \"○\", // ○\n 0xa4: \"◉\", // ◉\n 0xa5: \"◎\", // ◎\n 0xa7: \"▪\", // ▪\n 0xa8: \"◻\", // ◻\n 0xaa: \"✦\", // ✦\n 0xab: \"★\", // ★\n 0xac: \"✶\", // ✶\n 0xad: \"✴\", // ✴\n 0xae: \"✹\", // ✹\n // 손 모양\n 0x45: \"☜\", // ☜\n 0x46: \"☞\", // ☞\n 0x47: \"☝\", // ☝\n 0x48: \"☟\", // ☟\n // 체크마크\n 0xfb: \"✗\", // ✗\n 0xfc: \"✔\", // ✔\n 0xfd: \"☒\", // ☒\n 0xfe: \"☑\", // ☑\n // 화살표\n 0xe8: \"➔\", // ➔ (heavy wide-headed — 한컴 PDF 정답지 정합)\n 0xef: \"⇦\", // ⇦\n 0xf0: \"⇨\", // ⇨\n 0xf1: \"⇧\", // ⇧\n 0xf2: \"⇩\", // ⇩\n // 기타\n 0x22: \"✂\", // ✂\n 0x36: \"⌛\", // ⌛\n 0x4a: \"☺\", // ☺\n 0x4e: \"☠\", // ☠\n 0x52: \"☼\", // ☼\n 0x54: \"❄\", // ❄\n 0x58: \"✠\", // ✠\n 0x59: \"✡\", // ✡\n}\n\n/** Supplementary PUA-A (U+F0000대) — 한컴 자체 영역 매핑 */\nconst SUPPLEMENTARY_MAP: Record<number, string> = {\n 0xf003b: \"↓\", // ↓\n 0xf02ef: \"·\", // ·\n 0xf0854: \"《\", // 《\n 0xf0855: \"》\", // 》\n 0xf00da: \"▸\", // ▸\n 0xf080f: \"━\", // ━\n 0xf0827: \"■\", // ■\n}\n\n/** 단일 코드포인트 매핑 — 매핑 없으면 원본 유지 */\nexport function mapPuaChar(code: number): string | undefined {\n if (code >= 0xf020 && code <= 0xf0ff) {\n return BMP_SYMBOL_MAP[code - 0xf000]\n }\n if (code >= 0xf0000 && code <= 0xf09ff) {\n return SUPPLEMENTARY_MAP[code]\n }\n return undefined\n}\n\n/** BMP PUA 영역 여부 (U+E000~U+F8FF) */\nexport function isBmpPua(code: number): boolean {\n return code >= 0xe000 && code <= 0xf8ff\n}\n\n/**\n * 문자열 내 한컴 PUA 문자를 표준 유니코드로 치환.\n * 매핑 없는 BMP PUA는 원본 유지(옛한글 한양PUA 가능성 — 제거하면 안 됨),\n * 매핑 없는 Supplementary PUA-A는 호출부의 기존 제거 로직에 위임한다.\n */\nexport function mapPuaText(text: string): string {\n let out = \"\"\n for (const ch of text) {\n const code = ch.codePointAt(0)!\n out += mapPuaChar(code) ?? ch\n }\n return out\n}\n","/** 2-pass colSpan/rowSpan 테이블 빌더 및 Markdown 변환 */\n\nimport type { CellContext, IRBlock, IRCell, IRTable } from \"../types.js\"\nimport { sanitizeHref } from \"../utils.js\"\nimport { mapPuaText } from \"../shared/pua.js\"\n\n/** 테이블 열 수 상한 — 한국 공공문서 기준 충분한 값 */\nexport const MAX_COLS = 200\n/** 테이블 행 수 상한 — 메모리 폭주 방지 */\nexport const MAX_ROWS = 10000\n\nexport function buildTable(rows: CellContext[][]): IRTable {\n if (rows.length > MAX_ROWS) rows = rows.slice(0, MAX_ROWS)\n const numRows = rows.length\n\n // colAddr/rowAddr가 있으면 직접 배치 (HWPX cellAddr, HWP5 colAddr/rowAddr)\n const hasAddr = rows.some(row => row.some(c => c.colAddr !== undefined && c.rowAddr !== undefined))\n if (hasAddr) return buildTableDirect(rows, numRows)\n\n // Pass 1: maxCols 계산 — 2D 배열 사용 (동적 확장)\n let maxCols = 0\n const tempOccupied: boolean[][] = Array.from({ length: numRows }, () => [])\n\n for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {\n let colIdx = 0\n for (const cell of rows[rowIdx]) {\n while (colIdx < MAX_COLS && tempOccupied[rowIdx][colIdx]) colIdx++\n if (colIdx >= MAX_COLS) break\n\n for (let r = rowIdx; r < Math.min(rowIdx + cell.rowSpan, numRows); r++) {\n for (let c = colIdx; c < Math.min(colIdx + cell.colSpan, MAX_COLS); c++) {\n tempOccupied[r][c] = true\n }\n }\n colIdx += cell.colSpan\n if (colIdx > maxCols) maxCols = colIdx\n }\n }\n\n if (maxCols === 0) return { rows: 0, cols: 0, cells: [], hasHeader: false }\n\n // Pass 2: 실제 배치\n const grid: IRCell[][] = Array.from({ length: numRows }, () =>\n Array.from({ length: maxCols }, () => ({ text: \"\", colSpan: 1, rowSpan: 1 }))\n )\n const occupied: boolean[][] = Array.from({ length: numRows }, () => Array(maxCols).fill(false))\n\n for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {\n let colIdx = 0\n let cellIdx = 0\n\n while (colIdx < maxCols && cellIdx < rows[rowIdx].length) {\n while (colIdx < maxCols && occupied[rowIdx][colIdx]) colIdx++\n if (colIdx >= maxCols) break\n\n const cell = rows[rowIdx][cellIdx]\n grid[rowIdx][colIdx] = {\n text: cell.text.trim(),\n colSpan: cell.colSpan,\n rowSpan: cell.rowSpan,\n }\n\n for (let r = rowIdx; r < Math.min(rowIdx + cell.rowSpan, numRows); r++) {\n for (let c = colIdx; c < Math.min(colIdx + cell.colSpan, maxCols); c++) {\n occupied[r][c] = true\n }\n }\n\n colIdx += cell.colSpan\n cellIdx++\n }\n }\n\n return trimAndReturn(grid, numRows, maxCols)\n}\n\n/** colAddr/rowAddr 절대 좌표 기반 직접 배치 */\nfunction buildTableDirect(rows: CellContext[][], numRows: number): IRTable {\n // 전체 셀에서 maxCols 계산 (MAX_COLS 상한 적용)\n let maxCols = 0\n for (const row of rows) {\n for (const cell of row) {\n const end = (cell.colAddr ?? 0) + cell.colSpan\n if (end > maxCols) maxCols = end\n }\n }\n if (maxCols > MAX_COLS) maxCols = MAX_COLS\n if (maxCols === 0) return { rows: 0, cols: 0, cells: [], hasHeader: false }\n\n const grid: IRCell[][] = Array.from({ length: numRows }, () =>\n Array.from({ length: maxCols }, () => ({ text: \"\", colSpan: 1, rowSpan: 1 }))\n )\n\n for (const row of rows) {\n for (const cell of row) {\n const r = cell.rowAddr ?? 0\n const c = cell.colAddr ?? 0\n if (r >= numRows || c >= maxCols || r < 0 || c < 0) continue\n\n grid[r][c] = { text: cell.text.trim(), colSpan: cell.colSpan, rowSpan: cell.rowSpan }\n\n // 병합 영역 마킹\n for (let dr = 0; dr < cell.rowSpan; dr++) {\n for (let dc = 0; dc < cell.colSpan; dc++) {\n if (dr === 0 && dc === 0) continue\n if (r + dr < numRows && c + dc < maxCols) {\n grid[r + dr][c + dc] = { text: \"\", colSpan: 1, rowSpan: 1 }\n }\n }\n }\n }\n }\n\n return trimAndReturn(grid, numRows, maxCols)\n}\n\n/** 빈 후행 열 제거 후 IRTable 반환 */\nfunction trimAndReturn(grid: IRCell[][], numRows: number, maxCols: number): IRTable {\n let effectiveCols = maxCols\n while (effectiveCols > 0) {\n const colEmpty = grid.every(row => !row[effectiveCols - 1]?.text?.trim())\n if (!colEmpty) break\n effectiveCols--\n }\n if (effectiveCols < maxCols && effectiveCols > 0) {\n const trimmed = grid.map(row => row.slice(0, effectiveCols))\n return { rows: numRows, cols: effectiveCols, cells: trimmed, hasHeader: numRows > 1 }\n }\n return { rows: numRows, cols: maxCols, cells: grid, hasHeader: numRows > 1 }\n}\n\nexport function convertTableToText(rows: CellContext[][]): string {\n return rows\n .map(row =>\n row\n .map(c => c.text.trim().replace(/\\n/g, \" \").replace(/\\|/g, \"\\\\|\"))\n .filter(Boolean)\n .join(\" / \")\n )\n .filter(Boolean)\n .join(\"\\n\")\n}\n\n/** 마크다운 GFM 특수문자 이스케이프 — remark-gfm 오해석 방지 */\nfunction escapeGfm(text: string): string {\n // ~ → \\~ (GFM strikethrough 방지), * → \\* (emphasis/HR·마스킹 별표 \"******\" 방지).\n // 단 $...$ / $$...$$ 수식 스팬은 KaTeX 문법이라 이스케이프하면 파스 에러가 나므로 보호한다\n // (스팬을 임시 필러로 가린 뒤 escape → 복원). NUL 필러는 마크다운 본문에 등장하지 않는다.\n const NUL = String.fromCharCode(0) // 마크다운 본문에 없는 안전한 필러 (소스에 raw NUL 미기입)\n const spans: string[] = []\n const masked = text.replace(/\\$\\$[^$]*\\$\\$|\\$[^$\\n]*\\$/g, (m) => {\n spans.push(m)\n return NUL + (spans.length - 1) + NUL\n })\n const escaped = masked.replace(/([~*])/g, \"\\\\$1\")\n return escaped.replace(new RegExp(NUL + \"(\\\\d+)\" + NUL, \"g\"), (_, n) => spans[Number(n)])\n}\n\n/** HWP 자동생성 도형/개체 대체텍스트 정규식 — 한컴오피스가 삽입하는 모든 알려진 패턴 */\nconst HWP_SHAPE_ALT_TEXT_RE = /(?:모서리가 둥근 |둥근 )?(?:사각형|직사각형|정사각형|원|타원|삼각형|이등변 삼각형|직각 삼각형|선|직선|곡선|화살표|굵은 화살표|이중 화살표|오각형|육각형|팔각형|별|[4-8]점별|십자|십자형|구름|구름형|마름모|도넛|평행사변형|사다리꼴|부채꼴|호|반원|물결|번개|하트|빗금|블록 화살표|수식|표|그림|개체|그리기\\s?개체|묶음\\s?개체|글상자|수식\\s?개체|OLE\\s?개체)\\s?입니다\\.?/g\n\n/** HWP PUA 특수문자 및 도형 대체텍스트 제거 — 모든 포맷 공통 */\nfunction sanitizeText(text: string): string {\n // 한컴 PUA → 표준 유니코드 매핑 (rhwp 검증 테이블) — 제거 regex보다 먼저 적용\n let result = mapPuaText(text)\n // Supplementary Private Use Area (U+F0000-U+FFFFD) — HWP 전용 기호 (매핑 안 된 잔여분)\n .replace(/[\\u{F0000}-\\u{FFFFD}]/gu, \"\")\n // HWP 도형/개체 자동생성 대체텍스트 제거\n .replace(HWP_SHAPE_ALT_TEXT_RE, \"\")\n .replace(/ +/g, \" \")\n .trim()\n // 균등배분 스페이스 정리 (\"현 장 대 응 단 장\" → \"현장대응단장\")\n // 짧은 텍스트(30자 이하)에서 70%+ 토큰이 한글 1글자면 균등배분으로 판단\n if (result.length <= 30 && result.includes(\" \")) {\n const tokens = result.split(\" \")\n // 한글 1글자 토큰만 카운트 — ASCII 특수문자(< > & 등)는 균등배분이 아님\n const koreanSingleCharCount = tokens.filter(t => t.length === 1 && /[\\uAC00-\\uD7AF\\u3131-\\u318E]/.test(t)).length\n if (tokens.length >= 3 && koreanSingleCharCount / tokens.length >= 0.7) {\n result = tokens.join(\"\")\n }\n }\n return result\n}\n\n/**\n * 레이아웃 테이블 감지 및 해체 — IRBlock 레벨에서 수행\n * 적은 행(≤3) + 셀 내 줄바꿈 다량 → table 블록을 paragraph 블록들로 분해\n * heading 감지 전에 호출해야 해체된 텍스트에 heading 감지 적용 가능\n *\n * 호출 정책(의도): HWP5 파서만 호출한다. 구형 HWP5 문서는 제목/본문을 표로\n * 감싼 레이아웃 표가 흔하지만, HWPX는 그 관행이 드물고 무엇보다 patchHwpx/\n * fillHwpx 무손실 라운드트립이 \"파서 렌더 = 소스맵 표 서수\" 대응에 의존하므로\n * HWPX에서 표를 문단으로 해체하면 표 매핑이 깨진다. HWPX 적용은 코퍼스\n * 전/후 정량 비교 + 라운드트립 e2e 검증이 선행되어야 한다.\n */\nexport function flattenLayoutTables(blocks: IRBlock[]): IRBlock[] {\n const result: IRBlock[] = []\n\n for (const block of blocks) {\n if (block.type !== \"table\" || !block.table) {\n result.push(block)\n continue\n }\n\n const { rows: numRows, cols: numCols, cells } = block.table\n\n // 1x1 테이블은 기존 로직(tableToMarkdown)에서 처리\n if (numRows === 1 && numCols === 1) {\n result.push(block)\n continue\n }\n\n // 레이아웃 테이블 휴리스틱\n if (numRows <= 3) {\n let totalNewlines = 0\n let totalTextLen = 0\n for (let r = 0; r < numRows; r++) {\n for (let c = 0; c < numCols; c++) {\n const t = cells[r]?.[c]?.text || \"\"\n totalNewlines += (t.match(/\\n/g) || []).length\n totalTextLen += t.length\n }\n }\n\n // 레이아웃 테이블 판정: 많은 줄바꿈(>5), 또는 적은 행에 비해 총 텍스트 과다(>300)\n // 단, 열이 4개 이상이면 헤더-값 구조의 데이터 표일 가능성이 높아 해체하지 않는다\n // (실증: 2×10 모집프로그램 표가 문단으로 해체되어 헤더↔값 연결 파괴)\n if (numCols < 4 && (totalNewlines > 5 || (numRows <= 2 && totalTextLen > 300))) {\n // 레이아웃 테이블 → 각 셀을 paragraph 블록으로 분해\n for (let r = 0; r < numRows; r++) {\n for (let c = 0; c < numCols; c++) {\n const cellText = cells[r]?.[c]?.text?.trim()\n if (!cellText) continue\n // 셀 내 줄바꿈을 별도 paragraph로 분리\n for (const line of cellText.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed) continue\n result.push({ type: \"paragraph\", text: trimmed, pageNumber: block.pageNumber })\n }\n }\n }\n continue\n }\n }\n\n result.push(block)\n }\n\n return result\n}\n\nexport function blocksToMarkdown(blocks: IRBlock[]): string {\n const lines: string[] = []\n\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i]\n\n // 헤딩 블록 — escapeGfm 필수: 마스킹 별표(\"홍**\")가 볼드로 소비·삭제되는 것 방지\n if (block.type === \"heading\" && block.text) {\n const prefix = \"#\".repeat(Math.min(block.level || 2, 6))\n const headingText = sanitizeText(block.text)\n if (headingText) lines.push(\"\", `${prefix} ${escapeGfm(headingText)}`, \"\")\n continue\n }\n\n // 이미지 블록 —  참조\n if (block.type === \"image\" && block.text) {\n lines.push(\"\", ``, \"\")\n continue\n }\n\n // 구분선 블록\n if (block.type === \"separator\") {\n lines.push(\"\", \"---\", \"\")\n continue\n }\n\n // 리스트 블록\n if (block.type === \"list\" && block.text) {\n const listText = sanitizeText(block.text)\n if (!listText) continue\n // 텍스트가 이미 번호로 시작하면 그대로 출력 (원래 번호 보존)\n const alreadyNumbered = block.listType === \"ordered\" && /^\\d+\\.\\s/.test(listText)\n const prefix = alreadyNumbered ? \"\" : block.listType === \"ordered\" ? \"1. \" : \"- \"\n lines.push(`${prefix}${escapeGfm(listText)}`)\n if (block.children) {\n for (const child of block.children) {\n const childPrefix = child.listType === \"ordered\" ? \"1.\" : \"-\"\n lines.push(` ${childPrefix} ${escapeGfm(child.text || \"\")}`)\n }\n }\n continue\n }\n\n if (block.type === \"paragraph\" && block.text) {\n let text = sanitizeText(block.text)\n if (!text) continue\n\n // 별표 패턴 (기존 호환)\n if (/^\\[별표\\s*\\d+/.test(text)) {\n const nextBlock = blocks[i + 1]\n if (nextBlock?.type === \"paragraph\" && nextBlock.text && /관련\\)?$/.test(nextBlock.text)) {\n lines.push(\"\", `## ${escapeGfm(text)} ${escapeGfm(nextBlock.text)}`, \"\")\n i++\n } else {\n lines.push(\"\", `## ${escapeGfm(text)}`, \"\")\n }\n continue\n }\n\n if (/^\\([^)]*조[^)]*관련\\)$/.test(text)) {\n lines.push(`*${escapeGfm(text)}*`, \"\")\n continue\n }\n\n // 하이퍼링크가 있으면 텍스트에 링크 삽입 (javascript: 등 위험 스킴 제거)\n if (block.href) {\n const href = sanitizeHref(block.href)\n if (href) text = `[${text}](${href})`\n }\n\n // 각주가 있으면 괄호로 인라인 삽입\n if (block.footnoteText) {\n text += ` (주: ${block.footnoteText})`\n }\n\n lines.push(escapeGfm(text), \"\")\n } else if (block.type === \"table\" && block.table) {\n // 테이블 앞에 빈 줄 보장 (마크다운 렌더링 필수)\n if (lines.length > 0 && lines[lines.length - 1] !== \"\") {\n lines.push(\"\")\n }\n // 표 캡션 — 표 위에 강조 문단으로 출력 (v3.0)\n if (block.table.caption) {\n const caption = sanitizeText(block.table.caption)\n if (caption) lines.push(`**${escapeGfm(caption)}**`, \"\")\n }\n const tableMd = tableToMarkdown(block.table)\n if (tableMd) {\n lines.push(tableMd)\n lines.push(\"\")\n }\n }\n }\n\n return lines.join(\"\\n\").trim()\n}\n\n/** 병합 셀 존재 여부 확인 */\nfunction hasMergedCells(table: IRTable): boolean {\n for (const row of table.cells) {\n for (const cell of row) {\n if (cell.colSpan > 1 || cell.rowSpan > 1) return true\n }\n }\n return false\n}\n\n/** 셀 내부에 중첩 표 블록 존재 여부 — v3.0 */\nfunction hasNestedTables(table: IRTable): boolean {\n for (const row of table.cells) {\n for (const cell of row) {\n if (cell.blocks?.some(b => b.type === \"table\" && b.table)) return true\n }\n }\n return false\n}\n\n/** 셀 내부 콘텐츠 → HTML — blocks(중첩표/다중문단) 있으면 구조 보존 재귀 렌더링 */\nfunction cellInnerHtml(cell: IRCell): string {\n if (cell.blocks?.length) {\n return cell.blocks\n .map(b => {\n if (b.type === \"table\" && b.table) {\n // 중첩표 캡션도 보존 — 표 위에 텍스트로\n const cap = b.table.caption ? sanitizeText(b.table.caption) : \"\"\n return (cap ? cap + \"<br>\" : \"\") + tableToHtml(b.table)\n }\n if (b.type === \"image\" && b.text) return `<img src=\"${b.text}\" alt=\"image\">`\n const t = sanitizeText(b.text ?? \"\")\n return t ? t.replace(/\\n/g, \"<br>\") : \"\"\n })\n .filter(Boolean)\n .join(\"<br>\")\n }\n return sanitizeText(cell.text).replace(/\\n/g, \"<br>\")\n}\n\nfunction containsInlineMath(text: string): boolean {\n return /(^|[^\\\\])\\$(?=\\S)(?:\\\\.|[^$\\n])+?\\S\\$/.test(text)\n}\n\nfunction tableContainsInlineMath(table: IRTable): boolean {\n for (const row of table.cells) {\n for (const cell of row) {\n if (containsInlineMath(cell.text)) return true\n }\n }\n return false\n}\n\n/** 병합 테이블 → HTML <table> 출력 (rowspan/colspan 보존) */\nfunction tableToHtml(table: IRTable): string {\n const { cells, rows: numRows, cols: numCols } = table\n const skip = new Set<string>()\n const lines: string[] = [\"<table>\"]\n\n for (let r = 0; r < numRows; r++) {\n const tag = r === 0 ? \"th\" : \"td\"\n const rowHtml: string[] = []\n for (let c = 0; c < numCols; c++) {\n if (skip.has(`${r},${c}`)) continue\n const cell = cells[r]?.[c]\n if (!cell) continue\n\n // 병합 영역 skip 마킹\n for (let dr = 0; dr < cell.rowSpan; dr++) {\n for (let dc = 0; dc < cell.colSpan; dc++) {\n if (dr === 0 && dc === 0) continue\n if (r + dr < numRows && c + dc < numCols) skip.add(`${r + dr},${c + dc}`)\n }\n }\n\n const text = cellInnerHtml(cell)\n const attrs: string[] = []\n if (cell.colSpan > 1) attrs.push(`colspan=\"${cell.colSpan}\"`)\n if (cell.rowSpan > 1) attrs.push(`rowspan=\"${cell.rowSpan}\"`)\n const attrStr = attrs.length ? \" \" + attrs.join(\" \") : \"\"\n rowHtml.push(`<${tag}${attrStr}>${text}</${tag}>`)\n }\n if (rowHtml.length) lines.push(`<tr>${rowHtml.join(\"\")}</tr>`)\n }\n\n lines.push(\"</table>\")\n return lines.join(\"\\n\")\n}\n\nfunction tableToMarkdown(table: IRTable): string {\n if (table.rows === 0 || table.cols === 0) return \"\"\n\n const { cells, rows: numRows, cols: numCols } = table\n\n // 병합 셀·중첩표가 있으면 HTML 테이블로 출력하되, 수식이 있으면 GFM 표로 출력한다.\n // 많은 Markdown 렌더러가 raw HTML table 내부의 $...$를 수식으로 다시 처리하지 않는다.\n if ((hasMergedCells(table) || hasNestedTables(table)) && !tableContainsInlineMath(table)) {\n return tableToHtml(table)\n }\n\n // 1행 1열 → 구조화된 텍스트 (빈 셀이면 스킵)\n if (numRows === 1 && numCols === 1) {\n const content = sanitizeText(cells[0][0].text)\n if (!content) return \"\"\n return content\n .split(/\\n/)\n .map(line => {\n const trimmed = line.trim()\n if (!trimmed) return \"\"\n if (/^\\d+\\.\\s/.test(trimmed)) return `**${escapeGfm(trimmed)}**`\n if (/^[가-힣]\\.\\s/.test(trimmed)) return ` ${escapeGfm(trimmed)}`\n return escapeGfm(trimmed)\n })\n .filter(Boolean)\n .join(\"\\n\")\n }\n\n // 1열 다행 테이블 → 각 행을 별도 라인으로 출력 (목록성 데이터)\n if (numCols === 1 && numRows >= 2) {\n return cells\n .map(row => escapeGfm(sanitizeText(row[0].text)).replace(/\\n/g, \" \"))\n .filter(Boolean)\n .join(\"\\n\")\n }\n\n // 병합 셀: 행/열 병합된 셀은 빈 칸으로\n const display: string[][] = Array.from({ length: numRows }, () => Array(numCols).fill(\"\"))\n const skip = new Set<string>()\n\n for (let r = 0; r < numRows; r++) {\n for (let c = 0; c < numCols; c++) {\n if (skip.has(`${r},${c}`)) continue\n const cell = cells[r]?.[c]\n if (!cell) continue\n display[r][c] = escapeGfm(sanitizeText(cell.text)).replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \"<br>\")\n\n // colSpan/rowSpan: 병합된 열은 빈 칸으로 유지 (텍스트 중복 방지)\n for (let dr = 0; dr < cell.rowSpan; dr++) {\n for (let dc = 0; dc < cell.colSpan; dc++) {\n if (dr === 0 && dc === 0) continue\n if (r + dr < numRows && c + dc < numCols) {\n skip.add(`${r + dr},${c + dc}`)\n }\n }\n }\n // colSpan > 1이면 display 열 인덱스를 건너뜀\n c += cell.colSpan - 1\n }\n }\n\n // rowSpan 잔류 처리:\n // 1) 완전 빈 행 제거\n // 2) \"첫 열만 값, 나머지 빈\" 행 → 다음 데이터 행의 첫 열에 값을 전파\n // 단, colSpan으로 인한 빈 열(skip 셀)은 이 대상이 아님\n const uniqueRows: string[][] = []\n let pendingLabelRow: string[] | null = null\n for (let r = 0; r < display.length; r++) {\n const row = display[r]\n const isEmptyPlaceholder = row.every(cell => cell === \"\")\n if (isEmptyPlaceholder) continue\n\n // 첫 열만 값이 있고 나머지 모두 빈 행 → 다음 데이터 행의 첫 열에 전파\n // 단, colSpan으로 인한 빈 열(skip 셀)은 \"진짜 빈\"이 아니므로 제외\n const nonEmptyCols = row.filter(cell => cell !== \"\")\n const hasSkipInRow = row.some((_, c) => skip.has(`${r},${c}`))\n if (!hasSkipInRow && nonEmptyCols.length === 1 && row[0] !== \"\" && row.slice(1).every(c => c === \"\")) {\n if (pendingLabelRow) uniqueRows.push(pendingLabelRow) // 연속 보류 — 앞 행 소실 방지\n pendingLabelRow = row\n continue\n }\n\n // 보류한 첫 열 값을 현재 행의 빈 첫 열에 전파, 전파 불가면 보류 행 그대로 출력\n if (pendingLabelRow) {\n if (row[0] === \"\") row[0] = pendingLabelRow[0]\n else uniqueRows.push(pendingLabelRow)\n pendingLabelRow = null\n }\n uniqueRows.push(row)\n }\n if (pendingLabelRow) uniqueRows.push(pendingLabelRow) // 표 끝 보류 행 소실 방지\n\n if (uniqueRows.length === 0) return \"\"\n\n const md: string[] = []\n md.push(\"| \" + uniqueRows[0].join(\" | \") + \" |\")\n md.push(\"| \" + uniqueRows[0].map(() => \"---\").join(\" | \") + \" |\")\n for (let i = 1; i < uniqueRows.length; i++) {\n md.push(\"| \" + uniqueRows[i].join(\" | \") + \" |\")\n }\n return md.join(\"\\n\")\n}\n","/** kordoc 공통 타입 정의 */\n\n// ─── 중간 표현 (Intermediate Representation) ─────────\n\nexport interface CellContext {\n text: string\n colSpan: number\n rowSpan: number\n /** HWP5 셀 열 주소 (0-based) — 병합 테이블 배치용 */\n colAddr?: number\n /** HWP5 셀 행 주소 (0-based) — 병합 테이블 배치용 */\n rowAddr?: number\n}\n\n/** 블록 타입 — v2.0에서 heading, list, image, separator 추가 */\nexport type IRBlockType = \"paragraph\" | \"table\" | \"heading\" | \"list\" | \"image\" | \"separator\"\n\nexport interface IRBlock {\n type: IRBlockType\n text?: string\n table?: IRTable\n /** 헤딩 레벨 (1-6), type=\"heading\"일 때 사용 */\n level?: number\n /** 원본 페이지 번호 (1-based) */\n pageNumber?: number\n /** 바운딩 박스 — PDF에서만 제공 */\n bbox?: BoundingBox\n /** 텍스트 스타일 정보 (선택) */\n style?: InlineStyle\n /** 리스트 타입, type=\"list\"일 때 사용 */\n listType?: \"ordered\" | \"unordered\"\n /** 중첩 리스트 아이템 */\n children?: IRBlock[]\n /** 하이퍼링크 URL */\n href?: string\n /** 각주/미주 텍스트 (인라인 삽입용) */\n footnoteText?: string\n /** 이미지 데이터 (type=\"image\"일 때) */\n imageData?: ImageData\n}\n\n/** 추출된 이미지 바이너리 데이터 */\nexport interface ImageData {\n /** 이미지 바이너리 */\n data: Uint8Array\n /** MIME 타입 (image/png, image/jpeg, image/gif, image/bmp, image/wmf, image/emf) */\n mimeType: string\n /** 원본 파일명 (있는 경우) */\n filename?: string\n}\n\n/** 바운딩 박스 — PDF 포인트 단위 (72pt = 1인치) */\nexport interface BoundingBox {\n page: number\n x: number\n y: number\n width: number\n height: number\n}\n\n/** 인라인 텍스트 스타일 */\nexport interface InlineStyle {\n bold?: boolean\n italic?: boolean\n fontSize?: number\n fontName?: string\n}\n\nexport interface IRTable {\n rows: number\n cols: number\n cells: IRCell[][]\n /** 첫 행을 헤더로 렌더링할지 여부 (현재: rows > 1이면 true — 의미적 감지가 아닌 레이아웃 힌트) */\n hasHeader: boolean\n /** 표 캡션 (예: \"표 1. 부서별 예산\") — v3.0 */\n caption?: string\n}\n\nexport interface IRCell {\n text: string\n colSpan: number\n rowSpan: number\n /**\n * 셀 내부 블록 콘텐츠 — v3.0.\n * 중첩 표·이미지·다중 문단을 구조 그대로 보존한다.\n * blocks가 있으면 text는 blocks의 평탄화 텍스트(하위 호환용)다.\n */\n blocks?: IRBlock[]\n /** 제목 셀 여부 (HWP5 width_ref bit2 / HWPX header 속성) — v3.0 */\n isHeader?: boolean\n}\n\n// ─── 메타데이터 ─────────────────────────────────────\n\n/** 문서 메타데이터 — 각 포맷에서 추출 가능한 필드만 채워짐 */\nexport interface DocumentMetadata {\n /** 문서 제목 */\n title?: string\n /** 작성자 */\n author?: string\n /** 작성 프로그램 (예: \"한글 2020\", \"Adobe Acrobat\") */\n creator?: string\n /** 생성일시 (ISO 8601) */\n createdAt?: string\n /** 수정일시 (ISO 8601) */\n modifiedAt?: string\n /** 페이지/섹션 수 */\n pageCount?: number\n /** 문서 포맷 버전 (예: HWP \"5.1.0.1\") */\n version?: string\n /** 설명 */\n description?: string\n /** 키워드 */\n keywords?: string[]\n}\n\n// ─── 파싱 옵션 ──────────────────────────────────────\n\n/** 파싱 옵션 — parse() 함수에 전달 */\nexport interface ParseOptions {\n /**\n * 파싱할 페이지/섹션 범위 (1-based).\n * - 배열: [1, 2, 3]\n * - 문자열: \"1-3\", \"1,3,5-7\"\n *\n * PDF: 정확한 페이지 단위. HWP/HWPX: 섹션 단위 근사치.\n */\n pages?: number[] | string\n /** 이미지 기반 PDF용 OCR 프로바이더 (선택) */\n ocr?: OcrProvider\n /** 진행률 콜백 — current: 현재 페이지/섹션, total: 전체 수 */\n onProgress?: (current: number, total: number) => void\n /** PDF 머리글/바닥글 자동 제거 */\n removeHeaderFooter?: boolean\n /** 원본 파일 경로 (DRM COM fallback에 필요, 내부 전용) */\n filePath?: string\n /**\n * PDF 수식 OCR 활성화 (기본 false).\n *\n * 활성화 시 각 PDF 페이지를 이미지로 렌더링 → YOLOv8 기반 수식 영역 검출 →\n * TrOCR 기반 LaTeX 인식. 감지된 수식은 `$...$` (inline) / `$$...$$` (display) 로\n * 블록 텍스트에 삽입된다.\n *\n * 필수 optional 의존성: `onnxruntime-node`, `@huggingface/transformers`,\n * `@hyzyla/pdfium`, `sharp`. 미설치 시 parse 에 실패하지 않고 **경고만** 남기고\n * 수식 인식은 skip 한다 (일반 텍스트 추출은 정상 동작).\n *\n * 모델(~155MB) 은 첫 사용 시 HuggingFace 에서 자동 다운로드 되어\n * `~/.cache/kordoc/models/pix2text/` 에 SHA-256 검증과 함께 저장된다.\n */\n formulaOcr?: boolean\n}\n\n// ─── 파싱 경고 ──────────────────────────────────────\n\n/** 파싱 중 스킵/실패한 요소 보고 */\nexport interface ParseWarning {\n /** 관련 페이지 번호 (알 수 있는 경우) */\n page?: number\n /** 경고 메시지 */\n message: string\n /** 구조화된 경고 코드 */\n code: WarningCode\n}\n\nexport type WarningCode =\n | \"SKIPPED_IMAGE\"\n | \"SKIPPED_OLE\"\n | \"TRUNCATED_TABLE\"\n | \"OCR_FALLBACK\"\n | \"UNSUPPORTED_ELEMENT\"\n | \"BROKEN_ZIP_RECOVERY\"\n | \"HIDDEN_TEXT_FILTERED\"\n | \"MALFORMED_XML\"\n | \"PARTIAL_PARSE\"\n | \"LENIENT_CFB_RECOVERY\"\n | \"NEEDS_OCR\"\n\n/** 문서 구조 (헤딩 트리) */\nexport interface OutlineItem {\n level: number\n text: string\n pageNumber?: number\n}\n\n// ─── 에러 코드 ──────────────────────────────────────\n\n/** 구조화된 에러 코드 — 프로그래밍적 에러 핸들링용 */\nexport type ErrorCode =\n | \"EMPTY_INPUT\"\n | \"UNSUPPORTED_FORMAT\"\n | \"ENCRYPTED\"\n | \"DRM_PROTECTED\"\n | \"CORRUPTED\"\n | \"DECOMPRESSION_BOMB\"\n | \"ZIP_BOMB\"\n | \"IMAGE_BASED_PDF\"\n | \"NO_SECTIONS\"\n | \"PARSE_ERROR\"\n | \"MISSING_DEPENDENCY\"\n\n// ─── 파싱 결과 (discriminated union) ────────────────\n\nexport type FileType = \"hwpx\" | \"hwp\" | \"hwp3\" | \"hwpml\" | \"pdf\" | \"xlsx\" | \"xls\" | \"docx\" | \"unknown\"\n\ninterface ParseResultBase {\n fileType: FileType\n /** 페이지/섹션 수 — PDF: 실제 페이지 수, HWP/HWPX: 섹션 수, XLSX: 시트 수 */\n pageCount?: number\n /** 이미지 기반 PDF 여부 (텍스트 추출 불가) */\n isImageBased?: boolean\n}\n\nexport interface ParseSuccess extends ParseResultBase {\n success: true\n /** 추출된 마크다운 텍스트 */\n markdown: string\n /** 중간 표현 블록 (구조화된 데이터 접근용) */\n blocks: IRBlock[]\n /** 문서 메타데이터 */\n metadata?: DocumentMetadata\n /** 문서 구조 (헤딩 트리) — v2.0 */\n outline?: OutlineItem[]\n /** 파싱 중 발생한 경고 — v2.0 */\n warnings?: ParseWarning[]\n /** 추출된 이미지 목록 — 마크다운에서 파일명으로 참조됨 */\n images?: ExtractedImage[]\n /** 페이지별 텍스트 품질 신호 — PDF에서만 제공 */\n pageQuality?: PageQuality[]\n /** 문서 단위 품질 요약 — PDF에서만 제공 */\n qualitySummary?: DocumentQualitySummary\n}\n\n/** 페이지별 텍스트 품질 신호 (PDF 전용). 자세한 설명은 src/pdf/quality.ts */\nexport interface PageQuality {\n page: number\n textChars: number\n hangulRatio: number\n controlCharRatio: number\n replacementCharRatio: number\n puaRatio: number\n needsOcr: boolean\n ocrReason?: \"low_text\" | \"high_pua\" | \"high_control\" | \"high_replacement\"\n}\n\n/** 문서 단위 품질 요약 (PDF 전용). */\nexport interface DocumentQualitySummary {\n totalPages: number\n totalTextChars: number\n avgHangulRatio: number\n avgControlCharRatio: number\n avgReplacementCharRatio: number\n avgPuaRatio: number\n lowTextPageCount: number\n highPuaPageCount: number\n needsOcr: boolean\n ocrCandidatePages: number[]\n}\n\n/** 추출된 이미지 — ParseSuccess.images에 포함 */\nexport interface ExtractedImage {\n /** 마크다운에서 참조되는 파일명 (예: image_001.png) */\n filename: string\n /** 이미지 바이너리 */\n data: Uint8Array\n /** MIME 타입 */\n mimeType: string\n}\n\nexport interface ParseFailure extends ParseResultBase {\n success: false\n /** 오류 메시지 */\n error: string\n /** 구조화된 에러 코드 */\n code?: ErrorCode\n}\n\nexport type ParseResult = ParseSuccess | ParseFailure\n\n// ─── 문서 비교 (Diff) ───────────────────────────────\n\nexport type DiffChangeType = \"added\" | \"removed\" | \"modified\" | \"unchanged\"\n\nexport interface BlockDiff {\n type: DiffChangeType\n /** 원본 블록 (added이면 undefined) */\n before?: IRBlock\n /** 변경 후 블록 (removed이면 undefined) */\n after?: IRBlock\n /** modified 테이블의 셀 단위 diff */\n cellDiffs?: CellDiff[][]\n /** 유사도 (0-1) */\n similarity?: number\n}\n\nexport interface CellDiff {\n type: DiffChangeType\n before?: string\n after?: string\n}\n\nexport interface DiffResult {\n stats: { added: number; removed: number; modified: number; unchanged: number }\n diffs: BlockDiff[]\n}\n\n// ─── 라운드트립 패치 (v3.0) ─────────────────────────\n\n/** 패치 중 매핑 실패/미지원으로 건너뛴 항목 — silent 실패 금지 */\nexport interface PatchSkip {\n /** 건너뛴 사유 */\n reason: string\n /** 원본 쪽 내용 요약 (최대 80자) */\n before?: string\n /** 편집 쪽 내용 요약 (최대 80자) */\n after?: string\n /**\n * 부분 적용 표시 — 변경이 적용은 됐지만(applied 계상) 편집 원형 그대로는\n * 아님 (예: 셀 내 줄 추가를 마지막 문단에 병합). 완전 미적용 skip과 구분.\n */\n partial?: boolean\n}\n\n/** patchHwpx 옵션 */\nexport interface PatchOptions {\n /** 패치 후 재파싱 자동 검증 (기본 true) */\n verify?: boolean\n}\n\n/** patchHwpx / patchBlocks 결과 */\nexport interface PatchResult {\n success: boolean\n /** 패치된 HWPX (success=true) */\n data?: Uint8Array\n /** 적용된 변경 수 */\n applied: number\n /** 매핑 실패 항목 (이유 포함) */\n skipped: PatchSkip[]\n /**\n * 무손실 검증 (patchHwpx/patchHwp 전용): 패치본 재파싱 vs 편집 마크다운의\n * 잔차 diff — modified/added/removed가 0이어야 의도가 전부 반영된 것.\n * session.patchBlocks는 이 필드를 채우지 않는다 (changes 참조).\n */\n verification?: DiffResult\n /**\n * 변경 가시화 (session.patchBlocks 전용): 패치 전 → 후 문서 diff —\n * 적용된 편집 수만큼 modified가 나오는 것이 정상. verification과 의미가\n * 정반대이므로 혼용 금지.\n */\n changes?: DiffResult\n /** 실패 사유 (success=false) */\n error?: string\n}\n\n// ─── 양식 인식 ──────────────────────────────────────\n\nexport interface FormField {\n label: string\n value: string\n /** 0-based 소스 행 */\n row: number\n /** 0-based 소스 열 */\n col: number\n /** 매칭된 입력 키(정규화) — 모호 라벨 거부 가드(fillWithUniqueGuard)의 집계 기준 */\n key?: string\n}\n\nexport interface FormResult {\n fields: FormField[]\n /** 양식 확신도 (0-1) */\n confidence: number\n}\n\n// ─── OCR 프로바이더 ─────────────────────────────────\n\n/** 사용자 제공 OCR 함수 — 페이지 이미지를 받아 텍스트 반환 */\nexport type OcrProvider = (\n pageImage: Uint8Array,\n pageNumber: number,\n mimeType: \"image/png\"\n) => Promise<string>\n\n// ─── Watch 모드 ─────────────────────────────────────\n\nexport interface WatchOptions {\n dir: string\n outDir?: string\n webhook?: string\n format?: \"markdown\" | \"json\"\n pages?: string\n silent?: boolean\n}\n\n// ─── 헤딩 감지 공통 임계값 ──────────────────────────\n\n/** 폰트 크기 비율 → heading level (전 파서 공통) */\nexport const HEADING_RATIO_H1 = 1.5\nexport const HEADING_RATIO_H2 = 1.3\nexport const HEADING_RATIO_H3 = 1.15\n\n// ─── 내부 파서 반환 타입 ─────────────────────────────\n\n/** 내부 파서가 index.ts에 반환하는 공통 타입 (HWP5/HWPX/PDF/XLSX/DOCX) */\nexport interface InternalParseResult {\n markdown: string\n blocks: IRBlock[]\n metadata?: DocumentMetadata\n outline?: OutlineItem[]\n warnings?: ParseWarning[]\n images?: ExtractedImage[]\n /** PDF 전용: 이미지 기반 PDF 여부 */\n isImageBased?: boolean\n /** PDF 전용: 페이지별 품질 신호 */\n pageQuality?: PageQuality[]\n /** PDF 전용: 문서 단위 품질 요약 */\n qualitySummary?: DocumentQualitySummary\n}\n"]}
|
|
@@ -3,13 +3,15 @@ import {
|
|
|
3
3
|
DEFAULT_CHAR,
|
|
4
4
|
DEFAULT_PARA_GEOM,
|
|
5
5
|
MAX_DECOMPRESS_SIZE,
|
|
6
|
+
MAX_ZIP_ENTRIES,
|
|
6
7
|
createXmlParser,
|
|
7
8
|
findChildByLocalName,
|
|
8
9
|
parseRenderStyles
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HECUYZZK.js";
|
|
10
11
|
import {
|
|
11
|
-
KordocError
|
|
12
|
-
|
|
12
|
+
KordocError,
|
|
13
|
+
precheckZipSize
|
|
14
|
+
} from "./chunk-4WI7QXEG.js";
|
|
13
15
|
|
|
14
16
|
// src/render/svg-render.ts
|
|
15
17
|
import JSZip from "jszip";
|
|
@@ -1021,6 +1023,8 @@ function sniffMime(name, bytes) {
|
|
|
1021
1023
|
}
|
|
1022
1024
|
async function renderHwpxToSvg(input, options) {
|
|
1023
1025
|
const maxImg = options?.maxImageBytes ?? 40 * 1024 * 1024;
|
|
1026
|
+
const ab = input instanceof Uint8Array ? input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength) : input;
|
|
1027
|
+
precheckZipSize(ab, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES);
|
|
1024
1028
|
let zip;
|
|
1025
1029
|
try {
|
|
1026
1030
|
zip = await JSZip.loadAsync(input);
|
|
@@ -1156,4 +1160,4 @@ export {
|
|
|
1156
1160
|
fitRatioForFewerLines,
|
|
1157
1161
|
renderHwpxToSvg
|
|
1158
1162
|
};
|
|
1159
|
-
//# sourceMappingURL=chunk-
|
|
1163
|
+
//# sourceMappingURL=chunk-E4Y73XRH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/render/svg-render.ts","../src/render/layout.ts","../src/hwpx/text-metrics.ts","../src/render/reflow.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, type WrapMode } from \"../hwpx/text-metrics.js\"\nimport { parseRenderStyles, DEFAULT_CHAR, type RenderStyles, type RenderBorderEdge } from \"./head-styles.js\"\nimport { reflowSection } from \"./reflow.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'(어절, Windows 한글·공문서) / 'charAll'(글자, macOS·전자결재) */\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 }\n}\n\n// ─── XML 헬퍼 ─────────────────────────────────────\n\nfunction ln(el: Element): string {\n return (el.tagName || \"\").replace(/^[^:]+:/, \"\")\n}\n\nfunction 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\nfunction num(el: Element | null, attr: string, fallback = 0): number {\n return el ? toInt32(el.getAttribute(attr) ?? undefined, fallback) : fallback\n}\n\nfunction 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\nfunction escapeXml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\")\n}\n\n// ─── 내부 모델 ────────────────────────────────────\n\ninterface PageGeom { PW: number; PH: number; ML: number; MT: number; BODY_W: number; BODY_H: number }\n\nexport interface Seg { textpos: number; vertpos: number; horzpos: number; horzsize: number; textheight: number; baseline: number }\n\nexport interface ParaChar { ch: string; prId: string | null }\n\n/** 렌더 대상 개체 태그 — 이 외(도형류)는 경고 후 생략 */\nconst OBJ_TAGS = new Set([\"tbl\", \"pic\", \"container\", \"equation\", \"rect\", \"ellipse\", \"polygon\", \"curv\", \"line\", \"arc\", \"ole\", \"textart\"])\n\ninterface ParaObj { el: Element; tag: string; index: number; inline: boolean; width: number; height: number }\n\nexport interface ParaModel { chars: ParaChar[]; segs: Seg[]; objs: ParaObj[]; paraPrId: string | null }\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 }\n}\n\nconst pt = (u: number): string => String(Math.round(u) / 100)\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/** hp:t 안에서 1슬롯을 차지하는 문자형 컨트롤 (HWP5 문자 스트림 모델) —\n * 탭(0x09)은 char가 아니라 inline 컨트롤 = 8슬롯(16바이트)이라 여기 넣으면 안 된다\n * (record.ts 0x09 처리의 i+=14와 같은 모델. 1슬롯로 세면 탭당 7슬롯씩 줄 경계가 밀린다) */\nconst CHAR_CTRL_1SLOT = new Set([\"lineBreak\", \"hyphen\", \"nbSpace\", \"fwSpace\"])\n\n/** lineseg textpos 정합용 0폭 필러 슬롯 — 컨트롤이 차지하는 문자 위치를 채운다 */\nfunction 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 * (탭 폭은 탭스톱 미해석으로 0 — 경계 정합이 우선, 폭 오차는 줄 스케일이 흡수)\n */\nfunction pushTextSlots(t: Element, chars: ParaChar[], prId: string | null, depth: number): 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) {\n for (const cp of c.textContent ?? \"\") {\n chars.push({ ch: 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 === \"tab\") {\n pushFillers(chars, 8, prId) // inline 컨트롤 8슬롯 (전부 폭 0 — 탭스톱 미해석)\n } else if (CHAR_CTRL_1SLOT.has(tag)) {\n chars.push({ ch: tag === \"nbSpace\" || tag === \"fwSpace\" ? \" \" : \"\", prId })\n } else {\n pushTextSlots(el, chars, prId, depth + 1)\n }\n }\n }\n}\n\nexport function buildPara(p: Element): ParaModel {\n const chars: ParaChar[] = []\n const objs: ParaObj[] = []\n let segs: Seg[] = []\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 pushTextSlots(ch, chars, prId, 0)\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 objs.push({\n el: ch, tag: cn, index: chars.length,\n inline: pos?.getAttribute(\"treatAsChar\") === \"1\",\n width: w, height: h,\n })\n // 확장 컨트롤(GSO 등)은 문자 스트림에서 8슬롯 — 실측: 데모 코퍼스 1,132개\n // 멀티라인 문단에서 textpos 가 8슬롯 블록 중간에 걸린 경계 0건\n pushFillers(chars, 8, prId)\n } else {\n // secPr·ctrl(구역/단 정의)·필드 등 나머지 run 자식도 확장/인라인 컨트롤 8슬롯\n pushFillers(chars, 8, prId)\n }\n }\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// ─── 줄 렌더 (run별 charPr + 정렬) ──────────────────\n\ninterface LinePlan {\n seg: Seg\n /** 줄 시작 x 보정 (정렬) */\n xoff: number\n /** 텍스트 조각 폭 스케일 */\n scale: number\n start: number\n end: 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 })\n}\n\n/** 줄 자연폭 = 텍스트 조각 + 인라인 개체 폭 (개체 폭은 스케일 불변) */\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.width\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 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 const nat = lineNaturalWidth(m, styles, start, end)\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 })\n }\n return plans\n}\n\n/** 줄 안 [start, upto) 구간의 전진폭 (텍스트×스케일 + 인라인 개체) */\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++) x += charW(m.chars[i], styles) * plan.scale\n for (const o of m.objs) if (o.inline && o.index >= plan.start && o.index < upto) x += o.width\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) drawObject(o, ox, oy, 0, areaW, ctx, depth)\n return\n }\n const plans = planLines(m, ctx.styles)\n const baseV = m.segs[0].vertpos\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 // 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 // 필러 슬롯(컨트롤·서로게이트 자리)은 그리지 않고 건너뛴다 —\n // 인라인 개체의 폭 전진은 개체 첫 슬롯에서 1회 수행\n if (m.chars[i].ch === \"\") {\n for (const o of m.objs) if (o.inline && o.index === i) cursor += o.width\n i++\n continue\n }\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 !== \"\") { 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 }) * plan.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 ([...text].length > 1 && sw > 50) {\n attrs.push(`textLength=\"${pt(sw)}\"`, `lengthAdjust=\"${plan.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\n // 개체 배치 — 인라인은 소속 줄 위치, 앵커는 hp:pos 해석\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 const x = ox + plan.seg.horzpos + plan.xoff + advanceTo(m, ctx.styles, plan, o.index)\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}\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 const w = o.width, h = 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.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 // 그리기개체 묶음 — 자식 개체를 컨테이너 원점 기준으로 재귀 배치\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\") }\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// ─── 그리기 도형 (rect/ellipse/line/polygon/curv/arc) ────────────────\n// geometry 좌표는 개체 로컬(orgSz 기준). 실제 크기 = curSz(있으면)로 스케일.\n// lineShape=선(color/width/style), fillBrush>winBrush=채움(faceColor). 회전은 근사 생략.\n\nconst SHAPE_TAGS = new Set([\"rect\", \"ellipse\", \"line\", \"polygon\", \"curv\", \"arc\"])\n\n/** lineShape width(1/100 mm) → pt */\nfunction shapeStrokePt(v: number): number {\n return Math.max(0.2, (v / 100) * 2.834645)\n}\n\nfunction drawShape(o: ParaObj, x: number, y: number, ctx: Ctx, depth: number): void {\n const el = o.el\n const orgSz = findChildByLocalName(el, \"orgSz\")\n const curSz = findChildByLocalName(el, \"curSz\")\n const ow = num(orgSz, \"width\"), oh = num(orgSz, \"height\")\n const w = num(curSz, \"width\") || ow || o.width\n const h = num(curSz, \"height\") || oh || o.height\n const sx = ow > 0 ? w / ow : 1\n const sy = oh > 0 ? h / oh : 1\n\n const lineShape = findChildByLocalName(el, \"lineShape\")\n const lstyle = lineShape?.getAttribute(\"style\") ?? \"SOLID\"\n const strokeCol = lineShape?.getAttribute(\"color\") || \"#000000\"\n const hasStroke = lstyle !== \"NONE\"\n const strokeW = hasStroke ? shapeStrokePt(lineShape ? num(lineShape, \"width\") : 33) : 0\n const dash = /DASH|DOT/.test(lstyle) ? ` stroke-dasharray=\"${lstyle.includes(\"DOT\") ? \"1,1.5\" : \"3,1.5\"}\"` : \"\"\n const strokeAttr = hasStroke ? ` stroke=\"${escapeXml(strokeCol)}\" stroke-width=\"${strokeW.toFixed(2)}\"${dash}` : \"\"\n\n const fillBrush = findChildByLocalName(el, \"fillBrush\")\n const winBrush = fillBrush ? findChildByLocalName(fillBrush, \"winBrush\") : null\n const face = winBrush?.getAttribute(\"faceColor\")\n const fill = face && face.toLowerCase() !== \"none\" ? face : \"none\"\n const fillAttr = ` fill=\"${fill === \"none\" ? \"none\" : escapeXml(fill)}\"`\n\n if (o.tag === \"rect\") {\n emit(ctx, `<rect x=\"${pt(x)}\" y=\"${pt(y)}\" width=\"${pt(w)}\" height=\"${pt(h)}\"${fillAttr}${strokeAttr}/>`)\n } else if (o.tag === \"ellipse\") {\n emit(ctx, `<ellipse cx=\"${pt(x + w / 2)}\" cy=\"${pt(y + h / 2)}\" rx=\"${pt(w / 2)}\" ry=\"${pt(h / 2)}\"${fillAttr}${strokeAttr}/>`)\n } else if (o.tag === \"line\") {\n const s = findChildByLocalName(el, \"startPt\"), e = findChildByLocalName(el, \"endPt\")\n const x1 = x + num(s, \"x\") * sx, y1 = y + num(s, \"y\") * sy\n const x2 = x + num(e, \"x\") * sx, y2 = y + num(e, \"y\") * sy\n emit(ctx, `<line x1=\"${pt(x1)}\" y1=\"${pt(y1)}\" x2=\"${pt(x2)}\" y2=\"${pt(y2)}\" stroke=\"${escapeXml(strokeCol)}\" stroke-width=\"${(strokeW || 0.3).toFixed(2)}\"${dash}/>`)\n } else if (o.tag === \"polygon\" || o.tag === \"curv\") {\n const pts: string[] = []\n for (const c of elements(el)) if (ln(c) === \"pt\") pts.push(`${pt(x + num(c, \"x\") * sx)},${pt(y + num(c, \"y\") * sy)}`)\n if (pts.length >= 2) emit(ctx, `<polygon points=\"${pts.join(\" \")}\"${fillAttr}${strokeAttr}/>`)\n } else if (o.tag === \"arc\") {\n // 호는 외접 박스 타원으로 근사 (start/sweep 각 미해석)\n emit(ctx, `<ellipse cx=\"${pt(x + w / 2)}\" cy=\"${pt(y + h / 2)}\" rx=\"${pt(w / 2)}\" ry=\"${pt(h / 2)}\" fill=\"none\"${strokeAttr || ` stroke=\"${escapeXml(strokeCol)}\" stroke-width=\"0.3\"`}/>`)\n }\n\n // 도형 안 텍스트(drawText>subList) — 조판 캐시 있으면 그린다\n const dt = findChildByLocalName(el, \"drawText\")\n const sub = dt ? findChildByLocalName(dt, \"subList\") : null\n if (sub) {\n for (const p of elements(sub)) if (ln(p) === \"p\") drawPara(p, x, y, w, ctx, depth + 1)\n }\n}\n\n// ─── 표 ───────────────────────────────────────────\n\ninterface 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/** 셀 콘텐츠 세로 범위 — 줄(vp+th)과 PARA 앵커 개체(anchor+h) 최대값 */\nfunction cellContentExtent(cell: CellModel): number {\n if (!cell.sub) return 0\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) continue\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 return ext\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\n/** tbl의 셀 모델 수집 — drawTable과 measureTableHeight가 같은 셀 해석을 공유 */\nfunction 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 ca: num(addr, \"colAddr\"), ra: 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): number {\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) : undefined })),\n nRows,\n )\n let sum = 0\n for (const h of rowH) sum += h\n return sum\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) : 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 // 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)\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}\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 const w = num(sz, \"width\", 5669), h = num(sz, \"height\", 5669)\n const img = findFirst(pic, \"img\")\n const ref = img?.getAttribute(\"binaryItemIDRef\")\n const loaded = ref != null ? ctx.images.get(ref) : undefined\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 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}\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\")) return \"image/gif\"\n if (lower.endsWith(\".svg\")) return \"image/svg+xml\"\n return \"image/jpeg\"\n}\n\n// ─── 엔트리 ───────────────────────────────────────\n\n/**\n * HWPX(한컴 저장본) → 레이아웃 보존 SVG (1페이지).\n * 조판 캐시(linesegarray)가 없는 파일(예: markdownToHwpx 산출물)은 KordocError.\n */\nexport async function renderHwpxToSvg(input: ArrayBuffer | Uint8Array, options?: RenderSvgOptions): Promise<RenderSvgResult> {\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 const secFile = zip.file(\"Contents/section0.xml\") ??\n zip.file(/Contents\\/section\\d+\\.xml$/i).sort((a, b) => a.name.localeCompare(b.name))[0]\n if (!secFile) throw new KordocError(\"Contents/section0.xml 없음 — HWPX가 아니거나 손상됨\")\n const secXml = await secFile.async(\"string\")\n if (secXml.length > MAX_DECOMPRESS_SIZE) throw new KordocError(\"섹션 XML이 허용 크기를 초과\")\n // 태그 마크업만 매치 — 본문 텍스트에 \"linesegarray\"가 있어도 캐시로 오판하지 않는다\n // (본문의 <는 XML에서 <로 이스케이프되므로 리터럴 <…linesegarray는 태그뿐)\n const hasCache = /<(?:[A-Za-z][\\w.-]*:)?linesegarray[\\s/>]/.test(secXml)\n if (!hasCache && !options?.reflow) {\n throw new KordocError(\"조판 캐시(linesegarray) 없음 — 한컴에서 저장한 HWPX만 렌더 가능 (reflow 옵션으로 합성 렌더 가능)\")\n }\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 // 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 // 참조된 이미지만 선로딩 (섹션 문자열 정규식 — 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 = new Map<string, { dataUri: string }>()\n const refs = new Set<string>()\n for (const m of secXml.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 const doc = createXmlParser().parseFromString(secXml, \"text/xml\")\n const root = doc.documentElement as unknown as Element\n if (!root) throw new KordocError(\"섹션 XML 파싱 실패\")\n\n const pagePr = findFirst(root, \"pagePr\")\n const margin = pagePr ? findChildByLocalName(pagePr, \"margin\") : null\n const PW = num(pagePr, \"width\", 59528), PH = num(pagePr, \"height\", 84188)\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\n // Tier-2 reflow — 캐시 없는 문단에 linesegarray 합성 주입(한컴본은 이 경로 미진입).\n // 이후 페이지 프리패스·drawPara가 합성 캐시를 그대로 소비한다.\n if (!hasCache) reflowSection(root, styles, { BODY_W, BODY_H }, options?.reflowMode ?? \"keep\")\n\n // 페이지 분할 프리패스 — 최상위 lineseg vertpos는 페이지 로컬(페이지마다 0부터)이라\n // 역행 지점이 곧 페이지 경계다. 다단(colCount>1)은 단 이동도 vertpos가 리셋되지만\n // horzpos가 오른쪽으로 점프하므로, horzpos가 왼쪽으로 돌아올 때만 새 페이지로 본다.\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 for (const s of segEls) {\n const v = num(s, \"vertpos\")\n const h = num(s, \"horzpos\")\n if (v < prevV && (!multiCol || h <= prevH)) cur++\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 const ctx: Ctx = {\n pages: Array.from({ length: nPages }, () => []), page: 0,\n geom: { PW, PH, ML, MT, BODY_W, BODY_H }, styles, images, defs: [],\n highlights: (options?.highlights ?? []).map(s => s.trim().toLowerCase()).filter(s => s.length > 0),\n warnings, warned: new Set(), stats: { texts: 0, images: 0, tables: 0 },\n }\n\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\n // 리셋이 없는데 본문이 페이지를 넘는 파일(누적 vertpos 기록본) 방어 —\n // 한 페이지로 두되 캔버스만 내용 끝까지 늘려 잘림을 막는다.\n const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2000) : PH\n const GAP = 2400 // 페이지 사이 시각 간격 (24pt)\n const totalH = nPages * pageH + (nPages - 1) * GAP\n\n const pagesSvg = ctx.pages.map((buf, k) =>\n `<g data-page=\"${k + 1}\" transform=\"translate(0 ${pt(k * (pageH + GAP))})\">` +\n `<rect width=\"${pt(PW)}\" height=\"${pt(pageH)}\" fill=\"white\" stroke=\"#c9c7c4\" stroke-width=\"0.75\"/>` +\n `<g clip-path=\"url(#pgclip)\">\\n${buf.join(\"\\n\")}\\n</g></g>`,\n ).join(\"\\n\")\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(PW)} ${pt(totalH)}\" width=\"${pt(PW)}pt\" height=\"${pt(totalH)}pt\" font-family=\"'HCR Batang','함초롬바탕','Hancom Batang',AppleMyungjo,'Noto Serif CJK KR',serif\" xml:space=\"preserve\">\\n` +\n `<defs><clipPath id=\"pgclip\"><rect x=\"0\" y=\"0\" width=\"${pt(PW)}\" height=\"${pt(pageH)}\"/></clipPath>${ctx.defs.join(\"\")}</defs>\\n` +\n `${pagesSvg}\\n</svg>`\n return { svg, width: Math.round(PW) / 100, height: Math.round(totalH) / 100, pageCount: nPages, warnings, stats: ctx.stats }\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 * 한글 조판 텍스트 폭 계산 — 함초롬바탕(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\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 >= 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\nexport interface MeasureOptions {\n /** 공백 폭(em×1000). 기본 SPACE_EM_FIXED(500) = useFontSpace 0 */\n spaceEm?: number\n /** 자간(charPr spacing %) — 글자폭의 %가 문자마다 추가 */\n spacingPct?: number\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 let em = 0\n for (const ch of text) {\n const cp = ch.codePointAt(0)!\n const w = cp === 0x20 ? spaceEm : charWidthEm1000(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\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=KEEP_WORD(어절 단위, 공문서 모드·Windows 한글),\n * mode 'charAll' = 글자 단위(BREAK_WORD·macOS 한글·전자결재 변환기의 실동작).\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?: MeasureOptions,\n): WrapResult {\n const EPS = 0.5\n const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED\n const spacing = opts?.spacingPct ?? 0\n const k = (height * ratioPct) / 100 / 1000\n const cwCp = (cp: number): number =>\n (cp === 0x20 ? spaceEm : charWidthEm1000(cp)) * (1 + spacing / 100) * k\n const charW = (ch: string): number => cwCp(ch.codePointAt(0)!)\n const rangeW = (from: number, to: number): number => {\n let w = 0\n for (const ch of text.slice(from, to)) w += charW(ch)\n return w\n }\n\n const units = text.match(mode === \"keep\" ? / +|[^ ]+/g : / +|[^ ]/g) ?? []\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 // 시작금칙: 줄머리 금지 문자면 직전 글자 1개를 함께 내린다 (밀어내기)\n if (u !== undefined && FORBID_START.has(u) && bp - 1 > lineStart() && text[bp - 1] !== \" \") 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 += charW(\" \") * 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 let sub = 0\n for (const ch of u) {\n const c = charW(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 * 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 2: 최상위 텍스트 문단. 표 셀·개체 밀어내기·자동 페이지 분할은 Phase 3.\n */\n\nimport { buildPara, measureTableHeight } from \"./svg-render.js\"\nimport { simulateWrap, 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 // 실텍스트 + UTF-16 유닛 → chars 슬롯 인덱스 매핑 (서로게이트 쌍은 슬롯 1개, 유닛 2개)\n const realIdx: number[] = []\n let text = \"\"\n for (let i = 0; i < m.chars.length; i++) {\n const ch = m.chars[i].ch\n if (ch === \"\") continue\n for (let u = 0; u < ch.length; u++) realIdx.push(i)\n text += ch\n }\n\n const geom = styles.paraGeom.get(m.paraPrId ?? \"\") ?? DEFAULT_PARA_GEOM\n // 문단 지배 charPr — 첫 실문자 우선(height/장평/자간)\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 const height = domChar.height || 1000\n const ratio = domChar.ratio || 100\n const spacingPct = domChar.spacing || 0\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 const wrap = text.length === 0\n ? { lines: 1, starts: [0], lastLineWidth: 0 }\n : simulateWrap(text, firstWidth, contWidth, height, ratio, mode, { spacingPct })\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 const lsa = doc.createElement(\"hp:linesegarray\")\n for (let li = 0; li < wrap.starts.length; li++) {\n const startReal = wrap.starts[li]\n const textpos = startReal < realIdx.length ? realIdx[startReal] : m.chars.length\n const vertpos = startV + 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 // 문단 바닥 = 텍스트 줄 흐름과 개체(표·이미지) 높이 중 큰 쪽 (표 밀어내기 반영).\n // 표는 셀 콘텐츠 성장으로 선언 sz보다 커질 수 있어 drawTable과 같은 실효 높이를 쓴다\n // (셀 lineseg 필요 — reflowBlockFlow가 reflowTablesIn을 먼저 돌린다).\n const textBottom = startV + wrap.starts.length * pitch\n let objBottom = startV\n for (const o of m.objs) {\n const h = o.tag === \"tbl\" ? Math.max(o.height, measureTableHeight(o.el)) : o.height\n objBottom = Math.max(objBottom, startV + h)\n }\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 const paraH = res.paraBottom - startV\n // 페이지 넘김: 문단이 현재 페이지를 넘치고, 문단 자체는 한 페이지에 들어가면 다음 페이지로.\n if (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/**\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"],"mappings":";;;;;;;;;;;;;;;;AAsBA,OAAO,WAAW;;;ACfX,SAAS,QAAQ,GAAuB,WAAW,GAAW;AACnE,MAAI,KAAK,QAAQ,MAAM,GAAI,QAAO;AAClC,QAAM,IAAI,OAAO,CAAC;AAClB,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,IAAI,aAAa,IAAI,aAAc;AAC5C;AAeO,SAAS,gBAAgB,aAA+B,OAAe,OAA0B;AACtG,QAAM,IAAI,IAAI,MAA0B,QAAQ,CAAC,EAAE,KAAK,MAAS;AACjE,IAAE,CAAC,IAAI;AACP,MAAI,SAAS,QAAQ,QAAQ,EAAG,GAAE,KAAK,IAAI;AAC3C,MAAI,UAAU;AACd,MAAI,QAAQ;AACZ,SAAO,WAAW,UAAU,QAAQ,GAAG;AACrC,cAAU;AACV,eAAW,KAAK,aAAa;AAC3B,UAAI,EAAE,IAAI,KAAK,EAAE,IAAI,SAAS,EAAE,KAAK,EAAE,EAAG;AAC1C,YAAM,KAAK,EAAE,EAAE,CAAC;AAChB,YAAM,KAAK,EAAE,EAAE,CAAC;AAChB,UAAI,MAAM,QAAQ,MAAM,MAAM;AAC5B,UAAE,EAAE,CAAC,IAAI,KAAK,EAAE;AAChB,kBAAU;AAAA,MACZ,WAAW,MAAM,QAAQ,MAAM,MAAM;AACnC,UAAE,EAAE,CAAC,IAAI,KAAK,EAAE;AAChB,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,MAAI,IAAI;AACR,SAAO,KAAK,OAAO;AACjB,QAAI,EAAE,CAAC,KAAK,MAAM;AAAE;AAAK;AAAA,IAAS;AAClC,QAAI,KAAK,IAAI;AACb,QAAI,KAAK;AACT,WAAO,MAAM,SAAS,EAAE,EAAE,KAAK,KAAM;AACrC,UAAM,MAAM,EAAE,EAAE;AAChB,UAAM,MAAM,MAAM,QAAS,EAAE,EAAE,IAAe,OAAO,KAAK,MAAM;AAChE,UAAM,IAAI,KAAK;AACf,aAAS,IAAI,GAAG,IAAI,GAAG,IAAK,GAAE,KAAK,CAAC,IAAI,OAAQ,MAAM,OAAO,IAAK;AAClE,QAAI,KAAK,MAAO,GAAE,KAAK,IAAI;AAC3B,QAAI;AAAA,EACN;AAEA,QAAM,MAAM;AACZ,WAAS,IAAI,GAAG,KAAK,OAAO,IAAK,KAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAG,KAAI,CAAC,IAAI,IAAI,IAAI,CAAC;AAC5E,SAAO;AACT;AAQO,SAAS,gBACd,OACA,UACU;AACV,QAAM,IAAI,IAAI,MAAc,QAAQ,EAAE,KAAK,CAAC;AAC5C,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,YAAY,KAAK,EAAE,WAAW,KAAK,EAAE,UAAU,UAAU;AAC7D,QAAE,EAAE,OAAO,IAAI,KAAK,IAAI,EAAE,EAAE,OAAO,GAAG,EAAE,MAAM;AAAA,IAChD;AAAA,EACF;AAEA,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,WAAW,EAAG;AACpB,UAAM,OAAiB,CAAC;AACxB,aAAS,IAAI,EAAE,SAAS,IAAI,KAAK,IAAI,EAAE,UAAU,EAAE,SAAS,QAAQ,GAAG,IAAK,MAAK,KAAK,CAAC;AACvF,UAAM,QAAQ,KAAK,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,CAAC,GAAG,CAAC;AAC/C,UAAM,UAAU,KAAK,OAAO,OAAK,EAAE,CAAC,MAAM,CAAC;AAC3C,QAAI,QAAQ,SAAS,KAAK,EAAE,SAAS,OAAO;AAC1C,YAAM,QAAQ,EAAE,SAAS,SAAS,QAAQ;AAC1C,iBAAW,KAAK,QAAS,GAAE,CAAC,IAAI;AAAA,IAClC;AAAA,EACF;AAEA,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,YAAY,KAAK,EAAE,YAAY,QAAQ,EAAE,WAAW,KAAK,EAAE,UAAU,UAAU;AACnF,UAAI,EAAE,WAAW,EAAE,EAAE,OAAO,EAAG,GAAE,EAAE,OAAO,IAAI,EAAE;AAAA,IAClD;AAAA,EACF;AACA,SAAO;AACT;;;ACnFA,IAAM,UAAU;AAAA,EACd;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;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;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;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;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;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;AAC7E;AAGA,IAAM,QAAgC;AAAA,EACpC,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EACxE,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAAK,KAAM;AAAA,EAC7D,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EACtF,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EACtF,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAK,MAAQ;AAAA,EAAM,MAAQ;AAClD;AAGO,SAAS,gBAAgB,IAAoB;AAClD,MAAI,MAAM,MAAQ,MAAM,IAAM,QAAO,QAAQ,KAAK,EAAI;AACtD,QAAM,MAAM,MAAM,EAAE;AACpB,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,MAAM,SAAU,MAAM,MAAQ,QAAO;AACzC,MAAI,MAAM,QAAU,MAAM,KAAQ,QAAO;AACzC,MAAI,MAAM,SAAU,MAAM,MAAQ,QAAO;AACzC,MAAK,MAAM,SAAU,MAAM,SAAY,MAAM,SAAU,MAAM,MAAS,QAAO;AAC7E,MAAK,MAAM,SAAU,MAAM,SAAY,MAAM,SAAU,MAAM,MAAS,QAAO;AAC7E,MAAI,OAAO,MAAQ,QAAO;AAC1B,MAAI,MAAM,QAAU,MAAM,KAAQ,QAAO;AACzC,MAAI,MAAM,QAAU,MAAM,KAAQ,QAAO;AACzC,MAAI,MAAM,QAAU,MAAM,KAAQ,QAAO;AACzC,MAAI,MAAM,SAAU,MAAM,MAAQ,QAAO;AACzC,MAAI,MAAM,SAAU,MAAM,MAAQ,QAAO;AACzC,SAAO,MAAM,QAAS,MAAM;AAC9B;AAGO,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAatB,SAAS,iBACd,MACA,QACA,UACA,MACQ;AACR,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,UAAU,MAAM,cAAc;AACpC,MAAI,KAAK;AACT,aAAW,MAAM,MAAM;AACrB,UAAM,KAAK,GAAG,YAAY,CAAC;AAC3B,UAAM,IAAI,OAAO,KAAO,UAAU,gBAAgB,EAAE;AACpD,UAAM,KAAK,IAAI,UAAU;AAAA,EAC3B;AACA,SAAQ,KAAK,MAAQ,UAAU,WAAW;AAC5C;AAcA,IAAM,eAAe,oBAAI,IAAI,CAAC,GAAG,uHAAwC,CAAC;AAE1E,IAAM,aAAa,oBAAI,IAAI,CAAC,GAAG,4DAAmB,CAAC;AAyB5C,SAAS,aACd,MACA,YACA,WACA,QACA,UACA,OAAiB,QACjB,MACY;AACZ,QAAM,MAAM;AACZ,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,UAAU,MAAM,cAAc;AACpC,QAAM,IAAK,SAAS,WAAY,MAAM;AACtC,QAAM,OAAO,CAAC,QACX,OAAO,KAAO,UAAU,gBAAgB,EAAE,MAAM,IAAI,UAAU,OAAO;AACxE,QAAMA,SAAQ,CAAC,OAAuB,KAAK,GAAG,YAAY,CAAC,CAAE;AAC7D,QAAM,SAAS,CAAC,MAAc,OAAuB;AACnD,QAAI,IAAI;AACR,eAAW,MAAM,KAAK,MAAM,MAAM,EAAE,EAAG,MAAKA,OAAM,EAAE;AACpD,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,KAAK,MAAM,SAAS,SAAS,cAAc,UAAU,KAAK,CAAC;AAEzE,QAAM,SAAS,CAAC,CAAC;AACjB,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,QAAM,YAAY,MAAc,OAAO,OAAO,SAAS,CAAC;AAGxD,QAAM,cAAc,CAAC,SAAiB,MAAoB;AACxD,QAAI,KAAK;AACT,UAAM,IAAI,KAAK,OAAO;AAEtB,QAAI,MAAM,UAAa,aAAa,IAAI,CAAC,KAAK,KAAK,IAAI,UAAU,KAAK,KAAK,KAAK,CAAC,MAAM,IAAK;AAE5F,WAAO,KAAK,IAAI,UAAU,KAAK,WAAW,IAAI,KAAK,KAAK,CAAC,CAAC,EAAG;AAC7D,QAAI,MAAM,UAAU,EAAG,MAAK;AAC5B,WAAO,KAAK,EAAE;AACd,YAAQ;AACR,YAAQ,OAAO,IAAI,OAAO,IAAI;AAAA,EAChC;AAEA,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,CAAC,MAAM,KAAK;AAChB,eAASA,OAAM,GAAG,IAAI,EAAE;AACxB,aAAO,EAAE;AACT;AAAA,IACF;AACA,UAAM,IAAI,OAAO,KAAK,MAAM,EAAE,MAAM;AACpC,QAAI,QAAQ,KAAK,QAAQ,KAAK;AAC5B,eAAS;AACT,aAAO,EAAE;AACT;AAAA,IACF;AACA,QAAI,UAAU,KAAK,IAAI,YAAY,KAAK;AAEtC,UAAI,MAAM;AACV,iBAAW,MAAM,GAAG;AAClB,cAAM,IAAIA,OAAM,EAAE;AAClB,YAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,EAAG,aAAY,MAAM,KAAK,CAAC;AAClE,iBAAS;AACT,eAAO,GAAG;AAAA,MACZ;AACA,aAAO,EAAE;AACT;AAAA,IACF;AACA,gBAAY,KAAK,CAAC;AAClB,WAAO,EAAE;AAAA,EACX;AACA,SAAO,EAAE,OAAO,OAAO,QAAQ,QAAQ,eAAe,MAAM;AAC9D;AAGO,SAAS,qBACd,MACA,YACA,WACA,QACA,UACA,MACY;AACZ,SAAO,aAAa,MAAM,YAAY,WAAW,QAAQ,UAAU,QAAQ,IAAI;AACjF;AASO,SAAS,sBACd,MACA,YACA,WACA,QACA,WACA,UACA,MACe;AACf,QAAM,OAAO,aAAa,MAAM,YAAY,WAAW,QAAQ,WAAW,QAAQ,IAAI;AACtF,MAAI,KAAK,QAAQ,EAAG,QAAO;AAC3B,WAAS,IAAI,YAAY,GAAG,KAAK,UAAU,KAAK;AAC9C,UAAM,MAAM,aAAa,MAAM,YAAY,WAAW,QAAQ,GAAG,QAAQ,IAAI;AAC7E,QAAI,IAAI,QAAQ,KAAK,MAAO,QAAO;AAAA,EACrC;AACA,SAAO;AACT;;;ACrNA,IAAM,gBAAgB;AAEtB,IAAM,iBAAiB;AAEvB,SAAS,GAAG,IAAqB;AAC/B,UAAQ,GAAG,WAAW,IAAI,QAAQ,WAAW,EAAE;AACjD;AAEA,SAAS,SAAS,IAAwB;AACxC,QAAM,MAAiB,CAAC;AACxB,QAAM,OAAO,GAAG;AAChB,MAAI,CAAC,KAAM,QAAO;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,KAAI,KAAK,CAAC,EAAE,aAAa,EAAG,KAAI,KAAK,KAAK,CAAC,CAAY;AAC7F,SAAO;AACT;AAEA,SAAS,IAAI,IAAoB,MAAc,WAAW,GAAW;AACnE,SAAO,KAAK,QAAQ,GAAG,aAAa,IAAI,KAAK,QAAW,QAAQ,IAAI;AACtE;AAGA,SAAS,cAAc,GAAY,OAAqB;AACtD,aAAW,OAAO,SAAS,CAAC,GAAG;AAC7B,QAAI,GAAG,GAAG,MAAM,eAAgB;AAChC,eAAW,OAAO,SAAS,GAAG,GAAG;AAC/B,UAAI,GAAG,GAAG,MAAM,UAAW;AAC3B,UAAI,aAAa,WAAW,OAAO,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC;AAAA,IACjE;AAAA,EACF;AACF;AAQA,SAAS,SAAS,QAAgB,MAA8B;AAC9D,QAAM,IAAI,KAAK;AACf,UAAQ,KAAK,iBAAiB;AAAA,IAC5B,KAAK;AAAW,aAAO,KAAK,MAAO,SAAS,IAAK,GAAG;AAAA,IACpD,KAAK;AAAS,aAAO,IAAI,IAAI,IAAI,KAAK,MAAM,SAAS,GAAG;AAAA;AAAA,IACxD,KAAK;AAAY,aAAO,KAAK,IAAI,GAAG,MAAM;AAAA,IAC1C;AAAS,aAAO,KAAK,MAAM,SAAS,GAAG;AAAA,EACzC;AACF;AAMA,SAAS,WACP,GACA,KACA,QACA,OACA,QACA,MACmD;AACnD,QAAM,IAAI,UAAU,CAAC;AACrB,MAAI,EAAE,KAAK,SAAS,EAAG,QAAO;AAG9B,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,EAAE,MAAM,QAAQ,KAAK;AACvC,UAAM,KAAK,EAAE,MAAM,CAAC,EAAE;AACtB,QAAI,OAAO,GAAI;AACf,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,IAAK,SAAQ,KAAK,CAAC;AAClD,YAAQ;AAAA,EACV;AAEA,QAAM,OAAO,OAAO,SAAS,IAAI,EAAE,YAAY,EAAE,KAAK;AAEtD,MAAI,UAAU;AACd,aAAW,KAAK,EAAE,OAAO;AACvB,QAAI,EAAE,OAAO,MAAM,EAAE,QAAQ,MAAM;AACjC,YAAM,KAAK,OAAO,OAAO,IAAI,EAAE,IAAI;AACnC,UAAI,IAAI;AAAE,kBAAU;AAAI;AAAA,MAAM;AAAA,IAChC;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,aAAa,QAAQ,WAAW;AAEtC,QAAM,UAAU,KAAK;AACrB,QAAM,QAAQ,KAAK,IAAI,KAAM,QAAQ,UAAU,KAAK,WAAW;AAE/D,QAAM,aAAa;AACnB,QAAM,YAAY,KAAK,IAAI,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,YAAY,CAAC;AACtE,QAAM,WAAW,UAAU,KAAK,IAAI,GAAG,KAAK,YAAY;AAExD,QAAM,OAAO,KAAK,WAAW,IACzB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC,GAAG,eAAe,EAAE,IAC1C,aAAa,MAAM,YAAY,WAAW,QAAQ,OAAO,MAAM,EAAE,WAAW,CAAC;AAEjF,QAAM,QAAQ,SAAS,QAAQ,IAAI;AACnC,QAAM,WAAW,KAAK,MAAM,SAAS,cAAc;AACnD,QAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,MAAM;AAE1C,QAAM,MAAM,IAAI,cAAc,iBAAiB;AAC/C,WAAS,KAAK,GAAG,KAAK,KAAK,OAAO,QAAQ,MAAM;AAC9C,UAAM,YAAY,KAAK,OAAO,EAAE;AAChC,UAAM,UAAU,YAAY,QAAQ,SAAS,QAAQ,SAAS,IAAI,EAAE,MAAM;AAC1E,UAAM,UAAU,SAAS,KAAK;AAC9B,UAAM,UAAU,OAAO;AACvB,UAAM,MAAM,IAAI,cAAc,YAAY;AAC1C,QAAI,aAAa,WAAW,OAAO,OAAO,CAAC;AAC3C,QAAI,aAAa,WAAW,OAAO,OAAO,CAAC;AAC3C,QAAI,aAAa,YAAY,OAAO,MAAM,CAAC;AAC3C,QAAI,aAAa,cAAc,OAAO,MAAM,CAAC;AAC7C,QAAI,aAAa,YAAY,OAAO,QAAQ,CAAC;AAC7C,QAAI,aAAa,WAAW,OAAO,OAAO,CAAC;AAC3C,QAAI,aAAa,WAAW,OAAO,UAAU,UAAU,QAAQ,CAAC;AAChE,QAAI,aAAa,YAAY,OAAO,UAAU,aAAa,SAAS,CAAC;AACrE,QAAI,aAAa,SAAS,aAAa;AACvC,QAAI,YAAY,GAAG;AAAA,EACrB;AACA,IAAE,YAAY,GAAG;AAKjB,QAAM,aAAa,SAAS,KAAK,OAAO,SAAS;AACjD,MAAI,YAAY;AAChB,aAAW,KAAK,EAAE,MAAM;AACtB,UAAM,IAAI,EAAE,QAAQ,QAAQ,KAAK,IAAI,EAAE,QAAQ,mBAAmB,EAAE,EAAE,CAAC,IAAI,EAAE;AAC7E,gBAAY,KAAK,IAAI,WAAW,SAAS,CAAC;AAAA,EAC5C;AACA,SAAO,EAAE,YAAY,KAAK,IAAI,YAAY,SAAS,GAAG,YAAY,KAAK,WAAW;AACpF;AAGA,SAAS,eAAe,GAAY,KAAe,QAAsB,MAAgB,SAA8B;AACrH,aAAW,OAAO,SAAS,CAAC,GAAG;AAC7B,QAAI,GAAG,GAAG,MAAM,MAAO;AACvB,eAAW,OAAO,SAAS,GAAG,GAAG;AAC/B,UAAI,GAAG,GAAG,MAAM,MAAO;AACvB,iBAAW,MAAM,SAAS,GAAG,GAAG;AAC9B,YAAI,GAAG,EAAE,MAAM,KAAM;AACrB,mBAAW,MAAM,SAAS,EAAE,GAAG;AAC7B,cAAI,GAAG,EAAE,MAAM,KAAM;AACrB,gBAAM,MAAM,qBAAqB,IAAI,QAAQ;AAC7C,gBAAM,KAAK,qBAAqB,IAAI,YAAY;AAChD,gBAAM,QAAQ,IAAI,KAAK,OAAO;AAC9B,gBAAM,KAAK,KAAK,IAAI,IAAI,QAAQ,GAAG,IAAI;AACvC,gBAAM,KAAK,KAAK,IAAI,IAAI,SAAS,GAAG,IAAI;AACxC,gBAAM,QAAQ,KAAK,IAAI,KAAK,QAAQ,KAAK,EAAE;AAC3C,gBAAM,MAAM,qBAAqB,IAAI,SAAS;AAC9C,cAAI,IAAK,iBAAgB,KAAK,KAAK,QAAQ,OAAO,MAAM,SAAS,CAAC;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAAS,gBACP,WACA,KACA,QACA,OACA,MACA,SACA,OACM;AACN,MAAI,UAAU;AACd,MAAI,iBAAiB;AACrB,aAAW,KAAK,SAAS,SAAS,GAAG;AACnC,QAAI,GAAG,CAAC,MAAM,IAAK;AAGnB,mBAAe,GAAG,KAAK,QAAQ,MAAM,OAAO;AAC5C,UAAM,IAAI,OAAO,SAAS,IAAI,EAAE,aAAa,aAAa,KAAK,EAAE;AACjE,UAAM,SAAS,UAAU,kBAAkB,GAAG,eAAe;AAC7D,UAAM,MAAM,WAAW,GAAG,KAAK,QAAQ,OAAO,QAAQ,IAAI;AAC1D,QAAI,KAAK;AACP,YAAM,QAAQ,IAAI,aAAa;AAE/B,UAAI,QAAQ,KAAK,SAAS,KAAK,IAAI,aAAa,SAAS,SAAS,OAAO;AACvE,sBAAc,GAAG,CAAC,MAAM;AACxB,kBAAU;AAAA,MACZ,OAAO;AACL,kBAAU,IAAI;AAAA,MAChB;AACA,uBAAiB,IAAI;AACrB,cAAQ;AAAA,IACV;AAAA,EACF;AACF;AAOO,SAAS,cACd,MACA,QACA,MACA,OAAiB,QACT;AACR,QAAM,MAAM,KAAK;AACjB,QAAM,UAAU,EAAE,GAAG,EAAE;AACvB,kBAAgB,MAAM,KAAK,QAAQ,KAAK,QAAQ,MAAM,SAAS,KAAK,MAAM;AAC1E,SAAO,QAAQ;AACjB;;;AHjLA,SAASC,IAAG,IAAqB;AAC/B,UAAQ,GAAG,WAAW,IAAI,QAAQ,WAAW,EAAE;AACjD;AAEA,SAASC,UAAS,IAAwB;AACxC,QAAM,MAAiB,CAAC;AACxB,QAAM,WAAW,GAAG;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,SAAS,CAAC,EAAE,aAAa,EAAG,KAAI,KAAK,SAAS,CAAC,CAAY;AAAA,EACjE;AACA,SAAO;AACT;AAEA,SAASC,KAAI,IAAoB,MAAc,WAAW,GAAW;AACnE,SAAO,KAAK,QAAQ,GAAG,aAAa,IAAI,KAAK,QAAW,QAAQ,IAAI;AACtE;AAEA,SAAS,UAAU,IAAa,MAAc,QAAQ,GAAmB;AACvE,MAAI,QAAQ,GAAI,QAAO;AACvB,aAAW,MAAMD,UAAS,EAAE,GAAG;AAC7B,QAAID,IAAG,EAAE,MAAM,KAAM,QAAO;AAC5B,UAAM,QAAQ,UAAU,IAAI,MAAM,QAAQ,CAAC;AAC3C,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAEA,SAAS,UAAU,GAAmB;AACpC,SAAO,EAAE,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,QAAQ;AACpG;AAWA,IAAM,WAAW,oBAAI,IAAI,CAAC,OAAO,OAAO,aAAa,YAAY,QAAQ,WAAW,WAAW,QAAQ,QAAQ,OAAO,OAAO,SAAS,CAAC;AAuBvI,IAAM,KAAK,CAAC,MAAsB,OAAO,KAAK,MAAM,CAAC,IAAI,GAAG;AAE5D,SAAS,KAAK,KAAU,GAAiB;AACvC,MAAI,MAAM,IAAI,IAAI,EAAE,KAAK,CAAC;AAC5B;AAEA,SAAS,SAAS,KAAU,KAAa,KAAmB;AAC1D,MAAI,IAAI,OAAO,IAAI,GAAG,EAAG;AACzB,MAAI,OAAO,IAAI,GAAG;AAClB,MAAI,SAAS,KAAK,GAAG;AACvB;AAOA,IAAM,kBAAkB,oBAAI,IAAI,CAAC,aAAa,UAAU,WAAW,SAAS,CAAC;AAG7E,SAAS,YAAY,OAAmB,GAAW,MAA2B;AAC5E,WAAS,IAAI,GAAG,IAAI,GAAG,IAAK,OAAM,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC;AACzD;AAQA,SAAS,cAAc,GAAY,OAAmB,MAAqB,OAAqB;AAC9F,MAAI,QAAQ,GAAI;AAChB,QAAM,OAAO,EAAE;AACf,MAAI,CAAC,KAAM;AACX,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,EAAE,aAAa,GAAG;AACpB,iBAAW,MAAM,EAAE,eAAe,IAAI;AACpC,cAAM,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC;AAC3B,YAAI,GAAG,WAAW,EAAG,OAAM,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC;AAAA,MAClD;AAAA,IACF,WAAW,EAAE,aAAa,GAAG;AAC3B,YAAM,KAAK;AACX,YAAM,MAAMA,IAAG,EAAE;AACjB,UAAI,QAAQ,OAAO;AACjB,oBAAY,OAAO,GAAG,IAAI;AAAA,MAC5B,WAAW,gBAAgB,IAAI,GAAG,GAAG;AACnC,cAAM,KAAK,EAAE,IAAI,QAAQ,aAAa,QAAQ,YAAY,MAAM,IAAI,KAAK,CAAC;AAAA,MAC5E,OAAO;AACL,sBAAc,IAAI,OAAO,MAAM,QAAQ,CAAC;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,UAAU,GAAuB;AAC/C,QAAM,QAAoB,CAAC;AAC3B,QAAM,OAAkB,CAAC;AACzB,MAAI,OAAc,CAAC;AACnB,aAAW,SAASC,UAAS,CAAC,GAAG;AAC/B,UAAM,MAAMD,IAAG,KAAK;AACpB,QAAI,QAAQ,OAAO;AACjB,YAAM,OAAO,MAAM,aAAa,aAAa;AAC7C,iBAAW,MAAMC,UAAS,KAAK,GAAG;AAChC,cAAM,KAAKD,IAAG,EAAE;AAChB,YAAI,OAAO,KAAK;AACd,wBAAc,IAAI,OAAO,MAAM,CAAC;AAAA,QAClC,WAAW,SAAS,IAAI,EAAE,GAAG;AAC3B,gBAAM,KAAK,qBAAqB,IAAI,IAAI;AACxC,gBAAM,MAAM,qBAAqB,IAAI,KAAK;AAE1C,gBAAM,IAAIE,KAAI,IAAI,OAAO,KAAKA,KAAI,qBAAqB,IAAI,OAAO,GAAG,OAAO,KAAKA,KAAI,qBAAqB,IAAI,OAAO,GAAG,OAAO;AAC/H,gBAAM,IAAIA,KAAI,IAAI,QAAQ,KAAKA,KAAI,qBAAqB,IAAI,OAAO,GAAG,QAAQ,KAAKA,KAAI,qBAAqB,IAAI,OAAO,GAAG,QAAQ;AAClI,eAAK,KAAK;AAAA,YACR,IAAI;AAAA,YAAI,KAAK;AAAA,YAAI,OAAO,MAAM;AAAA,YAC9B,QAAQ,KAAK,aAAa,aAAa,MAAM;AAAA,YAC7C,OAAO;AAAA,YAAG,QAAQ;AAAA,UACpB,CAAC;AAGD,sBAAY,OAAO,GAAG,IAAI;AAAA,QAC5B,OAAO;AAEL,sBAAY,OAAO,GAAG,IAAI;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,WAAW,QAAQ,gBAAgB;AACjC,aAAOD,UAAS,KAAK,EAAE,OAAO,OAAKD,IAAG,CAAC,MAAM,SAAS,EAAE,IAAI,QAAM;AAAA,QAChE,SAASE,KAAI,GAAG,SAAS;AAAA,QAAG,SAASA,KAAI,GAAG,SAAS;AAAA,QAAG,SAASA,KAAI,GAAG,SAAS;AAAA,QACjF,UAAUA,KAAI,GAAG,UAAU;AAAA,QAAG,YAAYA,KAAI,GAAG,cAAc,GAAI;AAAA,QAAG,UAAUA,KAAI,GAAG,YAAY,GAAG;AAAA,MACxG,EAAE;AAAA,IACJ;AAAA,EACF;AACA,SAAO,EAAE,OAAO,MAAM,MAAM,UAAU,EAAE,aAAa,aAAa,EAAE;AACtE;AAcA,SAAS,MAAM,GAAa,QAA8B;AACxD,QAAM,MAAM,EAAE,QAAQ,OAAO,OAAO,OAAO,IAAI,EAAE,IAAI,IAAI,WAAc;AACvE,SAAO,iBAAiB,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,EAAE,YAAY,GAAG,QAAQ,CAAC;AAC/E;AAGA,SAAS,iBAAiB,GAAc,QAAsB,OAAe,KAA4C;AACvH,MAAI,OAAO;AACX,WAAS,IAAI,OAAO,IAAI,OAAO,IAAI,EAAE,MAAM,QAAQ,IAAK,SAAQ,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM;AACxF,MAAI,MAAM;AACV,aAAW,KAAK,EAAE,KAAM,KAAI,EAAE,UAAU,EAAE,SAAS,SAAS,EAAE,QAAQ,IAAK,QAAO,EAAE;AACpF,SAAO,EAAE,MAAM,IAAI;AACrB;AAEA,SAAS,UAAU,GAAc,QAAkC;AACjE,QAAM,SAAS,EAAE,YAAY,OAAO,OAAO,UAAU,IAAI,EAAE,QAAQ,IAAI,WAAc;AACrF,QAAM,QAAoB,CAAC;AAC3B,WAAS,IAAI,GAAG,IAAI,EAAE,KAAK,QAAQ,KAAK;AACtC,UAAM,MAAM,EAAE,KAAK,CAAC;AACpB,UAAM,QAAQ,IAAI;AAClB,UAAM,MAAM,IAAI,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,IAAI,CAAC,EAAE,UAAU,KAAK,IAAI,EAAE,MAAM,QAAQ,KAAK;AAC1F,UAAM,MAAM,iBAAiB,GAAG,QAAQ,OAAO,GAAG;AAClD,UAAM,SAAS,MAAM,EAAE,KAAK,SAAS;AACrC,QAAI,OAAO;AACX,QAAI,QAAQ;AACZ,UAAM,QAAQ,IAAI,WAAW,IAAI;AACjC,QAAI,IAAI,OAAO,MAAM,CAAC,UAAU,UAAU,gBAAgB,UAAU,qBAAqB;AAEvF,cAAQ,QAAQ,IAAI,QAAQ,IAAI,OAAO;AAAA,IACzC,WAAW,IAAI,OAAO,IAAI,MAAM,KAAK,QAAQ;AAC3C,YAAM,IAAI,IAAI,OAAO,IAAI;AACzB,UAAI,UAAU,SAAU,QAAO,KAAK,IAAI,IAAI,IAAI,WAAW,KAAK,CAAC;AAAA,eACxD,UAAU,QAAS,QAAO,KAAK,IAAI,GAAG,IAAI,WAAW,CAAC;AAAA,IACjE;AACA,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,SAAQ;AACnD,YAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,KAAK,CAAC;AACzC,UAAM,KAAK,EAAE,KAAK,MAAM,OAAO,OAAO,IAAI,CAAC;AAAA,EAC7C;AACA,SAAO;AACT;AAGA,SAAS,UAAU,GAAc,QAAsB,MAAgB,MAAsB;AAC3F,MAAI,IAAI;AACR,WAAS,IAAI,KAAK,OAAO,IAAI,QAAQ,IAAI,EAAE,MAAM,QAAQ,IAAK,MAAK,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,IAAI,KAAK;AACpG,aAAW,KAAK,EAAE,KAAM,KAAI,EAAE,UAAU,EAAE,SAAS,KAAK,SAAS,EAAE,QAAQ,KAAM,MAAK,EAAE;AACxF,SAAO;AACT;AAQA,SAAS,SAAS,GAAY,IAAY,IAAY,OAAe,KAAU,OAAe,UAA2B;AACvH,MAAI,QAAQ,IAAI;AAAE,aAAS,KAAK,SAAS,4EAAqB;AAAG;AAAA,EAAO;AACxE,QAAM,IAAI,UAAU,CAAC;AACrB,MAAI,EAAE,KAAK,WAAW,GAAG;AAEvB,QAAI,EAAE,MAAM,KAAK,OAAK,EAAE,OAAO,EAAE,GAAG;AAClC,eAAS,KAAK,cAAc,sJAAwC;AAAA,IACtE;AACA,eAAW,KAAK,EAAE,KAAM,YAAW,GAAG,IAAI,IAAI,GAAG,OAAO,KAAK,KAAK;AAClE;AAAA,EACF;AACA,QAAM,QAAQ,UAAU,GAAG,IAAI,MAAM;AACrC,QAAM,QAAQ,EAAE,KAAK,CAAC,EAAE;AAExB,WAAS,KAAK,GAAG,KAAK,MAAM,QAAQ,MAAM;AACxC,UAAM,OAAO,MAAM,EAAE;AACrB,QAAI,YAAY,SAAS,EAAE,MAAM,OAAW,KAAI,OAAO,SAAS,EAAE;AAClE,UAAM,EAAE,IAAI,IAAI;AAEhB,QAAI,IAAI,KAAK;AACb,QAAI,SAAS,KAAK,IAAI,UAAU,KAAK;AACrC,UAAM,IAAI,KAAK,IAAI,UAAU,IAAI;AACjC,WAAO,IAAI,KAAK,OAAO,IAAI,EAAE,MAAM,QAAQ;AAGzC,UAAI,EAAE,MAAM,CAAC,EAAE,OAAO,IAAI;AACxB,mBAAW,KAAK,EAAE,KAAM,KAAI,EAAE,UAAU,EAAE,UAAU,EAAG,WAAU,EAAE;AACnE;AACA;AAAA,MACF;AACA,YAAM,OAAO,EAAE,MAAM,CAAC,EAAE;AACxB,UAAI,IAAI;AACR,UAAI,QAAQ;AAEZ,aAAO,IAAI,KAAK,OAAO,IAAI,EAAE,MAAM,UAAU,EAAE,MAAM,CAAC,EAAE,SAAS,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,IAAI;AAAE,iBAAS,EAAE,MAAM,CAAC,EAAE;AAAI;AAAA,MAAI;AAI7H;AACE,cAAM,MAAM,MAAM,OAAO,OAAO;AAChC,YAAI,MAAM,GAAG;AAAE,kBAAQ,MAAM,MAAM,GAAG,GAAG;AAAG,cAAI,IAAI;AAAA,QAAI,WAC/C,QAAQ,GAAG;AAClB,gBAAM,SAAS,MAAM,MAAM,KAAK,EAAG,CAAC,EAAE;AACtC,kBAAQ,MAAM,MAAM,GAAG,MAAM;AAAG,cAAI,IAAI;AAAA,QAC1C;AAAA,MACF;AACA,YAAM,MAAM,QAAQ,OAAO,IAAI,OAAO,OAAO,IAAI,IAAI,IAAI,WAAc;AAKvE,YAAM,YAAY,CAAC,MAAc,IAAY,QAAyB;AACpE,cAAM,KAAK,iBAAiB,MAAM,GAAG,QAAQ,GAAG,OAAO,EAAE,YAAY,GAAG,QAAQ,CAAC,IAAI,KAAK;AAC1F,YAAI,KAAK;AACP,eAAK,KAAK,YAAY,GAAG,EAAE,CAAC,QAAQ,GAAG,KAAK,IAAI,OAAO,CAAC,YAAY,GAAG,EAAE,CAAC,aAAa,GAAG,IAAI,UAAU,CAAC,wCAAwC;AAAA,QACnJ;AACA,YAAI,KAAK,KAAK,EAAE,SAAS,GAAG;AAC1B,gBAAM,QAAkB,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,cAAc,GAAG,GAAG,MAAM,CAAC,GAAG;AACxF,cAAI,CAAC,GAAG,IAAI,EAAE,SAAS,KAAK,KAAK,IAAI;AACnC,kBAAM,KAAK,eAAe,GAAG,EAAE,CAAC,KAAK,iBAAiB,KAAK,QAAQ,IAAI,qBAAqB,SAAS,GAAG;AAAA,UAC1G;AACA,cAAI,GAAG,KAAM,OAAM,KAAK,oBAAoB;AAC5C,cAAI,GAAG,OAAQ,OAAM,KAAK,qBAAqB;AAC/C,cAAI,GAAG,UAAW,OAAM,KAAK,6BAA6B;AAC1D,cAAI,GAAG,MAAO,OAAM,KAAK,SAAS,UAAU,GAAG,KAAK,CAAC,GAAG;AACxD,eAAK,KAAK,SAAS,MAAM,KAAK,GAAG,CAAC,IAAI,UAAU,IAAI,CAAC,SAAS;AAC9D,cAAI,MAAM;AAAA,QACZ;AACA,eAAO;AAAA,MACT;AAGA,YAAM,SAAkC,CAAC;AACzC,UAAI,IAAI,WAAW,SAAS,KAAK,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,cAAM,QAAiC,CAAC;AACxC,cAAM,QAAQ,MAAM,YAAY;AAChC,mBAAW,QAAQ,IAAI,YAAY;AACjC,mBAAS,IAAI,MAAM,QAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,MAAM,QAAQ,MAAM,IAAI,KAAK,MAAM,GAAG;AACpF,kBAAM,KAAK,CAAC,GAAG,IAAI,KAAK,MAAM,CAAC;AAAA,UACjC;AAAA,QACF;AACA,cAAM,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAChC,mBAAW,CAAC,GAAG,CAAC,KAAK,OAAO;AAC1B,gBAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,cAAI,QAAQ,KAAK,KAAK,CAAC,EAAG,MAAK,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC;AAAA,cAClD,QAAO,KAAK,CAAC,GAAG,CAAC,CAAC;AAAA,QACzB;AAAA,MACF;AAEA,UAAI,OAAO,WAAW,GAAG;AACvB,kBAAU,UAAU,OAAO,QAAQ,KAAK;AAAA,MAC1C,OAAO;AACL,YAAI,SAAS;AACb,YAAI,OAAO;AACX,mBAAW,CAAC,GAAG,CAAC,KAAK,QAAQ;AAC3B,oBAAU,UAAU,MAAM,MAAM,MAAM,CAAC,GAAG,QAAQ,KAAK;AACvD,oBAAU,UAAU,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,IAAI;AACnD,iBAAO;AAAA,QACT;AACA,kBAAU,UAAU,MAAM,MAAM,IAAI,GAAG,QAAQ,KAAK;AACpD,iBAAS;AAAA,MACX;AACA,UAAI;AAAA,IACN;AAAA,EACF;AAGA,aAAW,KAAK,EAAE,MAAM;AACtB,QAAI,EAAE,QAAQ;AACZ,UAAI,UAAU;AACd,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,KAAK,MAAM,CAAC;AAClB,YAAI,GAAG,SAAS,EAAE,UAAU,EAAE,QAAQ,GAAG,OAAO,MAAM,MAAM,SAAS,GAAI,WAAU;AAAA,MACrF;AACA,YAAM,OAAO,MAAM,OAAO;AAC1B,UAAI,YAAY,SAAS,OAAO,MAAM,OAAW,KAAI,OAAO,SAAS,OAAO;AAC5E,YAAM,IAAI,KAAK,KAAK,IAAI,UAAU,KAAK,OAAO,UAAU,GAAG,IAAI,QAAQ,MAAM,EAAE,KAAK;AAEpF,YAAM,OAAO,KAAK,KAAK,IAAI,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,EAAE,MAAM;AAC7E,iBAAW,GAAG,GAAG,MAAM,OAAO,OAAO,KAAK,KAAK;AAAA,IACjD,OAAO;AAEL,UAAI,YAAY,SAAS,CAAC,MAAM,OAAW,KAAI,OAAO,SAAS,CAAC;AAChE,YAAM,EAAE,GAAG,EAAE,IAAI,aAAa,GAAG,IAAI,IAAI,OAAO,OAAO,GAAG;AAC1D,iBAAW,GAAG,GAAG,GAAG,OAAO,OAAO,KAAK,KAAK;AAAA,IAC9C;AAAA,EACF;AACF;AAGA,SAAS,aAAa,GAAY,IAAY,IAAY,OAAe,OAAe,KAAoC;AAC1H,QAAM,EAAE,IAAI,IAAI,IAAI,IAAI,QAAQ,OAAO,IAAI,IAAI;AAC/C,QAAM,MAAM,qBAAqB,EAAE,IAAI,KAAK;AAC5C,QAAM,KAAK,qBAAqB,EAAE,IAAI,WAAW;AACjD,QAAM,MAAMA,KAAI,IAAI,KAAK,GAAG,MAAMA,KAAI,IAAI,QAAQ;AAClD,QAAM,IAAI,EAAE,OAAO,IAAI,EAAE;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,GAAG,IAAI,GAAG,KAAK,MAAM;AACxC,QAAM,KAAKA,KAAI,KAAK,YAAY;AAChC,QAAM,KAAKA,KAAI,KAAK,YAAY;AAChC,QAAM,OAAO,IAAI,aAAa,WAAW,KAAK;AAC9C,QAAM,OAAO,IAAI,aAAa,WAAW,KAAK;AAC9C,QAAM,KAAK,IAAI,aAAa,WAAW,KAAK;AAC5C,QAAM,KAAK,IAAI,aAAa,WAAW,KAAK;AAC5C,QAAM,OAAO,EAAE,GAAG,aAAa,UAAU,KAAK;AAE9C,MAAI;AACJ,MAAI,SAAS,SAAS;AACpB,QAAI,OAAO,WAAW,KAAK,IAAI,KAAK,OAAO,YAAY,KAAK,KAAK,IAAI,KAAK;AAAA,EAC5E,WAAW,SAAS,QAAQ;AAC1B,QAAI,OAAO,WAAW,KAAK,SAAS,IAAI,KAAK,OAAO,WAAW,MAAM,SAAS,KAAK,IAAI,KAAK,KAAK;AAAA,EACnG,WAAW,SAAS,kBAAkB;AAGpC,UAAM,SAAS,SAAS,MAAM,IAAI;AAClC,UAAM,SAAS,UAAU,OAAO,SAAS;AACzC,QAAI,KAAK,SAAS,MAAM;AAAA,EAC1B,OAAO;AAEL,QAAI,KAAK,QAAQ;AAAA,EACnB;AAEA,MAAI;AACJ,MAAI,SAAS,QAAQ;AACnB,QAAI,OAAO,UAAU,KAAK,SAAS,IAAI,KAAK,OAAO,WAAW,MAAM,SAAS,KAAK,IAAI,KAAK,KAAK;AAAA,EAClG,WAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,UAAU,KAAK,IAAI,KAAK,OAAO,YAAY,KAAK,KAAK,IAAI,KAAK;AAAA,EAC3E,OAAO;AAGL,QAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,KAAK,OAAO,WAAW,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK;AAAA,EAChG;AACA,SAAO,EAAE,GAAG,EAAE;AAChB;AAEA,SAAS,WAAW,GAAY,GAAW,GAAW,OAAe,OAAe,KAAU,OAAqB;AACjH,MAAI,EAAE,QAAQ,MAAO,WAAU,EAAE,IAAI,GAAG,GAAG,KAAK,QAAQ,CAAC;AAAA,WAChD,EAAE,QAAQ,MAAO,SAAQ,EAAE,IAAI,GAAG,GAAG,GAAG;AAAA,WACxC,EAAE,QAAQ,aAAa;AAE9B,eAAW,MAAMD,UAAS,EAAE,EAAE,GAAG;AAC/B,YAAM,MAAMD,IAAG,EAAE;AACjB,UAAI,CAAC,SAAS,IAAI,GAAG,EAAG;AACxB,YAAM,KAAK,qBAAqB,IAAI,IAAI;AACxC,YAAM,MAAM,qBAAqB,IAAI,QAAQ;AAC7C,YAAM,MAAe,EAAE,IAAI,IAAI,KAAK,OAAO,GAAG,QAAQ,MAAM,OAAOE,KAAI,IAAI,OAAO,GAAG,QAAQA,KAAI,IAAI,QAAQ,EAAE;AAC/G,iBAAW,KAAK,IAAIA,KAAI,KAAK,GAAG,GAAG,IAAIA,KAAI,KAAK,GAAG,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC;AAAA,IACpF;AAAA,EACF,WAAW,EAAE,QAAQ,YAAY;AAC/B,aAAS,KAAK,YAAY,qFAAoB;AAAA,EAChD,WAAW,WAAW,IAAI,EAAE,GAAG,GAAG;AAChC,cAAU,GAAG,GAAG,GAAG,KAAK,KAAK;AAAA,EAC/B,OAAO;AACL,aAAS,KAAK,SAAS,EAAE,GAAG,IAAI,gBAAM,EAAE,GAAG,uDAAe;AAAA,EAC5D;AACF;AAMA,IAAM,aAAa,oBAAI,IAAI,CAAC,QAAQ,WAAW,QAAQ,WAAW,QAAQ,KAAK,CAAC;AAGhF,SAAS,cAAc,GAAmB;AACxC,SAAO,KAAK,IAAI,KAAM,IAAI,MAAO,QAAQ;AAC3C;AAEA,SAAS,UAAU,GAAY,GAAW,GAAW,KAAU,OAAqB;AAClF,QAAM,KAAK,EAAE;AACb,QAAM,QAAQ,qBAAqB,IAAI,OAAO;AAC9C,QAAM,QAAQ,qBAAqB,IAAI,OAAO;AAC9C,QAAM,KAAKA,KAAI,OAAO,OAAO,GAAG,KAAKA,KAAI,OAAO,QAAQ;AACxD,QAAM,IAAIA,KAAI,OAAO,OAAO,KAAK,MAAM,EAAE;AACzC,QAAM,IAAIA,KAAI,OAAO,QAAQ,KAAK,MAAM,EAAE;AAC1C,QAAM,KAAK,KAAK,IAAI,IAAI,KAAK;AAC7B,QAAM,KAAK,KAAK,IAAI,IAAI,KAAK;AAE7B,QAAM,YAAY,qBAAqB,IAAI,WAAW;AACtD,QAAM,SAAS,WAAW,aAAa,OAAO,KAAK;AACnD,QAAM,YAAY,WAAW,aAAa,OAAO,KAAK;AACtD,QAAM,YAAY,WAAW;AAC7B,QAAM,UAAU,YAAY,cAAc,YAAYA,KAAI,WAAW,OAAO,IAAI,EAAE,IAAI;AACtF,QAAM,OAAO,WAAW,KAAK,MAAM,IAAI,sBAAsB,OAAO,SAAS,KAAK,IAAI,UAAU,OAAO,MAAM;AAC7G,QAAM,aAAa,YAAY,YAAY,UAAU,SAAS,CAAC,mBAAmB,QAAQ,QAAQ,CAAC,CAAC,IAAI,IAAI,KAAK;AAEjH,QAAM,YAAY,qBAAqB,IAAI,WAAW;AACtD,QAAM,WAAW,YAAY,qBAAqB,WAAW,UAAU,IAAI;AAC3E,QAAM,OAAO,UAAU,aAAa,WAAW;AAC/C,QAAM,OAAO,QAAQ,KAAK,YAAY,MAAM,SAAS,OAAO;AAC5D,QAAM,WAAW,UAAU,SAAS,SAAS,SAAS,UAAU,IAAI,CAAC;AAErE,MAAI,EAAE,QAAQ,QAAQ;AACpB,SAAK,KAAK,YAAY,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,IAAI,QAAQ,GAAG,UAAU,IAAI;AAAA,EAC1G,WAAW,EAAE,QAAQ,WAAW;AAC9B,SAAK,KAAK,gBAAgB,GAAG,IAAI,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,IAAI,QAAQ,GAAG,UAAU,IAAI;AAAA,EAChI,WAAW,EAAE,QAAQ,QAAQ;AAC3B,UAAM,IAAI,qBAAqB,IAAI,SAAS,GAAG,IAAI,qBAAqB,IAAI,OAAO;AACnF,UAAM,KAAK,IAAIA,KAAI,GAAG,GAAG,IAAI,IAAI,KAAK,IAAIA,KAAI,GAAG,GAAG,IAAI;AACxD,UAAM,KAAK,IAAIA,KAAI,GAAG,GAAG,IAAI,IAAI,KAAK,IAAIA,KAAI,GAAG,GAAG,IAAI;AACxD,SAAK,KAAK,aAAa,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,aAAa,UAAU,SAAS,CAAC,oBAAoB,WAAW,KAAK,QAAQ,CAAC,CAAC,IAAI,IAAI,IAAI;AAAA,EACvK,WAAW,EAAE,QAAQ,aAAa,EAAE,QAAQ,QAAQ;AAClD,UAAM,MAAgB,CAAC;AACvB,eAAW,KAAKD,UAAS,EAAE,EAAG,KAAID,IAAG,CAAC,MAAM,KAAM,KAAI,KAAK,GAAG,GAAG,IAAIE,KAAI,GAAG,GAAG,IAAI,EAAE,CAAC,IAAI,GAAG,IAAIA,KAAI,GAAG,GAAG,IAAI,EAAE,CAAC,EAAE;AACpH,QAAI,IAAI,UAAU,EAAG,MAAK,KAAK,oBAAoB,IAAI,KAAK,GAAG,CAAC,IAAI,QAAQ,GAAG,UAAU,IAAI;AAAA,EAC/F,WAAW,EAAE,QAAQ,OAAO;AAE1B,SAAK,KAAK,gBAAgB,GAAG,IAAI,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,gBAAgB,cAAc,YAAY,UAAU,SAAS,CAAC,sBAAsB,IAAI;AAAA,EAC3L;AAGA,QAAM,KAAK,qBAAqB,IAAI,UAAU;AAC9C,QAAM,MAAM,KAAK,qBAAqB,IAAI,SAAS,IAAI;AACvD,MAAI,KAAK;AACP,eAAW,KAAKD,UAAS,GAAG,EAAG,KAAID,IAAG,CAAC,MAAM,IAAK,UAAS,GAAG,GAAG,GAAG,GAAG,KAAK,QAAQ,CAAC;AAAA,EACvF;AACF;AAcA,SAAS,kBAAkB,MAAyB;AAClD,MAAI,CAAC,KAAK,IAAK,QAAO;AACtB,MAAI,MAAM;AACV,aAAW,KAAKC,UAAS,KAAK,GAAG,GAAG;AAClC,QAAID,IAAG,CAAC,MAAM,IAAK;AACnB,UAAM,IAAI,UAAU,CAAC;AACrB,eAAW,KAAK,EAAE,KAAM,OAAM,KAAK,IAAI,KAAK,EAAE,UAAU,EAAE,UAAU;AACpE,UAAM,QAAQ,EAAE,KAAK,CAAC,GAAG,WAAW;AACpC,eAAW,KAAK,EAAE,MAAM;AACtB,UAAI,EAAE,OAAQ;AACd,YAAM,MAAM,qBAAqB,EAAE,IAAI,KAAK;AAC5C,WAAK,KAAK,aAAa,WAAW,KAAK,YAAY,OAAQ;AAC3D,YAAM,KAAK,qBAAqB,EAAE,IAAI,WAAW;AACjD,YAAM,SAAS,SAASE,KAAI,IAAI,KAAK,IAAI,EAAE,SAASA,KAAI,IAAI,QAAQ;AACpE,YAAM,SAAS,UAAU,OAAO,SAAS;AACzC,YAAM,KAAK,IAAI,KAAK,SAASA,KAAI,IAAI,KAAK,IAAIA,KAAI,KAAK,YAAY,IAAI,EAAE,MAAM;AAAA,IACjF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,IAAY,IAAY,IAAY,IAAY,GAA6B;AAC7F,QAAM,OAAO,WAAW,KAAK,EAAE,IAAI,IAAI,sBAAsB,EAAE,KAAK,SAAS,KAAK,IAAI,UAAU,OAAO,MAAM;AAC7G,SAAO,aAAa,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,CAAC,aAAa,UAAU,EAAE,KAAK,CAAC,mBAAmB,EAAE,QAAQ,QAAQ,CAAC,CAAC,IAAI,IAAI;AACvJ;AAGA,SAAS,aAAa,KAA2B;AAC/C,QAAM,WAAW,qBAAqB,KAAK,UAAU;AACrD,QAAM,OAAOA,KAAI,UAAU,QAAQ,GAAG,GAAG,OAAOA,KAAI,UAAU,SAAS,GAAG;AAC1E,QAAM,OAAOA,KAAI,UAAU,OAAO,GAAG,GAAG,OAAOA,KAAI,UAAU,UAAU,GAAG;AAE1E,QAAM,QAAqB,CAAC;AAC5B,aAAW,MAAMD,UAAS,GAAG,GAAG;AAC9B,QAAID,IAAG,EAAE,MAAM,KAAM;AACrB,eAAW,MAAMC,UAAS,EAAE,GAAG;AAC7B,UAAID,IAAG,EAAE,MAAM,KAAM;AACrB,YAAM,OAAO,qBAAqB,IAAI,UAAU;AAChD,YAAM,OAAO,qBAAqB,IAAI,UAAU;AAChD,YAAM,MAAM,qBAAqB,IAAI,QAAQ;AAC7C,YAAM,KAAK,qBAAqB,IAAI,YAAY;AAChD,UAAI,CAAC,QAAQ,CAAC,IAAK;AACnB,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QACJ,IAAIE,KAAI,MAAM,SAAS;AAAA,QAAG,IAAIA,KAAI,MAAM,SAAS;AAAA,QACjD,IAAI,KAAK,IAAI,GAAGA,KAAI,MAAM,WAAW,CAAC,CAAC;AAAA,QAAG,IAAI,KAAK,IAAI,GAAGA,KAAI,MAAM,WAAW,CAAC,CAAC;AAAA,QACjF,GAAGA,KAAI,KAAK,OAAO;AAAA,QAAG,GAAGA,KAAI,KAAK,QAAQ;AAAA,QAC1C,MAAM,GAAG,aAAa,iBAAiB;AAAA,QACvC,KAAK,qBAAqB,IAAI,SAAS;AAAA,QACvC,SAAS,KAAKA,KAAI,IAAI,QAAQ,IAAI,IAAI;AAAA,QAAM,SAAS,KAAKA,KAAI,IAAI,SAAS,IAAI,IAAI;AAAA,QACnF,SAAS,KAAKA,KAAI,IAAI,OAAO,IAAI,IAAI;AAAA,QAAM,SAAS,KAAKA,KAAI,IAAI,UAAU,IAAI,IAAI;AAAA,MACrF,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,mBAAmB,KAAsB;AACvD,QAAM,QAAQ,aAAa,GAAG;AAC9B,MAAI,MAAM,WAAW,KAAK,MAAM,SAAS,KAAM,QAAO;AACtD,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,OAAK,EAAE,KAAK,EAAE,EAAE,CAAC;AACrD,QAAM,OAAO;AAAA,IACX,MAAM,IAAI,QAAM,EAAE,SAAS,EAAE,IAAI,SAAS,EAAE,IAAI,QAAQ,EAAE,GAAG,UAAU,EAAE,OAAO,IAAI,kBAAkB,CAAC,IAAI,OAAU,EAAE;AAAA,IACvH;AAAA,EACF;AACA,MAAI,MAAM;AACV,aAAW,KAAK,KAAM,QAAO;AAC7B,SAAO;AACT;AAEA,SAAS,UAAU,KAAc,IAAY,IAAY,KAAU,OAAqB;AACtF,MAAI,QAAQ,IAAI;AAAE,aAAS,KAAK,SAAS,4EAAqB;AAAG;AAAA,EAAO;AACxE,MAAI,MAAM;AACV,QAAM,QAAQ,qBAAqB,KAAK,IAAI;AAC5C,QAAM,QAAQ,aAAa,GAAG;AAC9B,MAAI,MAAM,WAAW,KAAK,MAAM,SAAS,KAAM;AAE/C,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,OAAK,EAAE,KAAK,EAAE,EAAE,CAAC;AACrD,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,OAAK,EAAE,KAAK,EAAE,EAAE,CAAC;AACrD,QAAM,UAA4B,MAAM,IAAI,QAAM,EAAE,GAAG,EAAE,IAAI,GAAG,EAAE,KAAK,EAAE,IAAI,MAAM,EAAE,EAAE,EAAE;AACzF,QAAM,OAAO,gBAAgB,SAAS,OAAOA,KAAI,OAAO,OAAO,KAAK,MAAS;AAC7E,QAAM,OAAO;AAAA,IACX,MAAM,IAAI,QAAM,EAAE,SAAS,EAAE,IAAI,SAAS,EAAE,IAAI,QAAQ,EAAE,GAAG,UAAU,EAAE,OAAO,IAAI,kBAAkB,CAAC,IAAI,OAAU,EAAE;AAAA,IACvH;AAAA,EACF;AACA,QAAM,OAAiB,CAAC,CAAC;AACzB,WAAS,IAAI,GAAG,IAAI,OAAO,IAAK,MAAK,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;AAG3D,QAAM,OAAO,MAAM,IAAI,QAAM;AAAA,IAC3B;AAAA,IACA,GAAG,KAAK,KAAK,EAAE,EAAE;AAAA,IAAG,GAAG,KAAK,KAAK,EAAE,EAAE;AAAA,IACrC,GAAG,KAAK,KAAK,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,EAAE;AAAA,IACjD,GAAG,KAAK,KAAK,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,EAAE;AAAA,EACnD,EAAE;AACF,aAAW,KAAK,MAAM;AACpB,UAAM,KAAK,EAAE,EAAE,QAAQ,OAAO,IAAI,OAAO,WAAW,IAAI,EAAE,EAAE,IAAI,IAAI;AACpE,QAAI,IAAI,KAAM,MAAK,KAAK,YAAY,GAAG,EAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC,aAAa,GAAG,EAAE,CAAC,CAAC,WAAW,UAAU,GAAG,IAAI,CAAC,KAAK;AAAA,EACrI;AACA,aAAW,KAAK,MAAM;AACpB,UAAM,EAAE,EAAE,IAAI;AACd,QAAI,CAAC,EAAE,IAAK;AACZ,UAAM,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE;AACnC,UAAM,SAAS,kBAAkB,CAAC;AAClC,UAAM,KAAK,EAAE,IAAI,aAAa,WAAW,KAAK;AAC9C,QAAI,OAAO;AACX,QAAI,OAAO,SAAU,QAAO,KAAK,IAAI,IAAI,SAAS,UAAU,CAAC;AAAA,aACpD,OAAO,SAAU,QAAO,KAAK,IAAI,GAAG,SAAS,MAAM;AAC5D,eAAW,KAAKD,UAAS,EAAE,GAAG,GAAG;AAC/B,UAAID,IAAG,CAAC,MAAM,IAAK;AACnB,eAAS,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,KAAK,QAAQ,CAAC;AAAA,IAClG;AAAA,EACF;AACA,aAAW,KAAK,MAAM;AACpB,UAAM,KAAK,EAAE,EAAE,QAAQ,OAAO,IAAI,OAAO,WAAW,IAAI,EAAE,EAAE,IAAI,IAAI;AACpE,QAAI,CAAC,GAAI;AACT,QAAI,GAAG,IAAK,MAAK,KAAK,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,CAAC;AAChE,QAAI,GAAG,OAAQ,MAAK,KAAK,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC;AAClF,QAAI,GAAG,KAAM,MAAK,KAAK,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;AAClE,QAAI,GAAG,MAAO,MAAK,KAAK,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,KAAK,CAAC;AAAA,EAClF;AACF;AAUA,SAAS,YAAY,QAA6C,KAAkB;AAClF,MAAI,CAAC,OAAO,OAAO;AACjB,WAAO,QAAQ,MAAM,IAAI,KAAK,MAAM;AACpC,QAAI,KAAK;AAAA,MACP,eAAe,OAAO,KAAK,uHACyC,OAAO,OAAO;AAAA,IACpF;AAAA,EACF;AACA,SAAO,OAAO;AAChB;AAEA,SAAS,QAAQ,KAAc,GAAW,GAAW,KAAgB;AACnE,QAAM,KAAK,qBAAqB,KAAK,IAAI;AACzC,QAAM,IAAIE,KAAI,IAAI,SAAS,IAAI,GAAG,IAAIA,KAAI,IAAI,UAAU,IAAI;AAC5D,QAAM,MAAM,UAAU,KAAK,KAAK;AAChC,QAAM,MAAM,KAAK,aAAa,iBAAiB;AAC/C,QAAM,SAAS,OAAO,OAAO,IAAI,OAAO,IAAI,GAAG,IAAI;AACnD,MAAI,CAAC,QAAQ;AACX,SAAK,KAAK,YAAY,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,kDAAkD;AAC7H,aAAS,KAAK,OAAO,GAAG,IAAI,6DAAgB,OAAO,oBAAU,EAAE;AAC/D;AAAA,EACF;AACA,MAAI,MAAM;AAIV,QAAM,OAAO,qBAAqB,KAAK,SAAS;AAChD,QAAM,SAAS,qBAAqB,KAAK,QAAQ;AACjD,QAAM,QAAQ,qBAAqB,KAAK,OAAO;AAC/C,QAAM,OAAOA,KAAI,QAAQ,UAAU,GAAG,OAAOA,KAAI,QAAQ,WAAW;AAEpE,QAAM,OAAO,OAAO,IAAI,OAAOA,KAAI,OAAO,OAAO;AACjD,QAAM,OAAO,OAAO,IAAI,OAAOA,KAAI,OAAO,QAAQ;AAClD,QAAM,KAAKA,KAAI,MAAM,MAAM,GAAG,KAAKA,KAAI,MAAM,KAAK;AAClD,QAAM,KAAKA,KAAI,MAAM,SAAS,IAAI,GAAG,KAAKA,KAAI,MAAM,UAAU,IAAI;AAClE,QAAM,UACJ,OAAO,KAAK,OAAO,KAAK,QAAQ,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,QAAQ,KAAK,SAAS,KAAK,MAAM,KAAK;AAC1G,QAAM,QAAQ,YAAY,QAAQ,GAAG;AACrC,MAAI,SAAS;AAEX;AAAA,MAAK;AAAA,MACH,WAAW,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,cAAc,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC,4CAC7G,KAAK,wBAAwB,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;AAAA,IAC3E;AAAA,EACF,OAAO;AACL,SAAK,KAAK,eAAe,KAAK,QAAQ,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,KAAK;AAAA,EAClG;AACF;AAEA,SAAS,UAAU,MAAc,OAA2B;AAC1D,QAAM,QAAQ,KAAK,YAAY;AAC/B,MAAI,MAAM,SAAS,MAAM,KAAM,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM,OAAQ,MAAM,CAAC,MAAM,GAAO,QAAO;AACnG,MAAI,MAAM,SAAS,MAAM,KAAM,MAAM,SAAS,KAAK,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,GAAO,QAAO;AACnG,MAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,MAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,SAAO;AACT;AAQA,eAAsB,gBAAgB,OAAiC,SAAsD;AAC3H,QAAM,SAAS,SAAS,iBAAiB,KAAK,OAAO;AAIrD,QAAM,KAAK,iBAAiB,aACxB,MAAM,OAAO,MAAM,MAAM,YAAY,MAAM,aAAa,MAAM,UAAU,IACxE;AACJ,kBAAgB,IAAI,qBAAqB,eAAe;AACxD,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,KAAK;AAAA,EACnC,QAAQ;AACN,UAAM,IAAI,YAAY,yGAAmC;AAAA,EAC3D;AACA,QAAM,UAAU,IAAI,KAAK,uBAAuB,KAC9C,IAAI,KAAK,6BAA6B,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC;AACxF,MAAI,CAAC,QAAS,OAAM,IAAI,YAAY,kGAA2C;AAC/E,QAAM,SAAS,MAAM,QAAQ,MAAM,QAAQ;AAC3C,MAAI,OAAO,SAAS,oBAAqB,OAAM,IAAI,YAAY,qEAAmB;AAGlF,QAAM,WAAW,2CAA2C,KAAK,MAAM;AACvE,MAAI,CAAC,YAAY,CAAC,SAAS,QAAQ;AACjC,UAAM,IAAI,YAAY,uNAAsE;AAAA,EAC9F;AAEA,QAAM,WAAqB,CAAC;AAC5B,QAAM,WAAW,IAAI,KAAK,qBAAqB,KAAK,IAAI,KAAK,mBAAmB;AAChF,QAAM,SAAuB,WACzB,kBAAkB,MAAM,SAAS,MAAM,QAAQ,CAAC,IAChD,EAAE,QAAQ,oBAAI,IAAI,GAAG,WAAW,oBAAI,IAAI,GAAG,UAAU,oBAAI,IAAI,GAAG,YAAY,oBAAI,IAAI,EAAE;AAC1F,MAAI,CAAC,SAAU,UAAS,KAAK,mFAA4B;AAGzD,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,MAAM,IAAI,KAAK,gBAAgB,EAAE,CAAC;AACxC,MAAI,KAAK;AACP,UAAM,MAAM,MAAM,IAAI,MAAM,QAAQ;AACpC,eAAW,KAAK,IAAI,SAAS,2DAA2D,EAAG,QAAO,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;AAChH,eAAW,KAAK,IAAI,SAAS,2DAA2D,EAAG,QAAO,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;AAAA,EAClH;AAIA,QAAM,iBAAiB;AACvB,QAAM,wBAAwB,MAAM,OAAO;AAC3C,QAAM,SAAS,oBAAI,IAAiC;AACpD,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,OAAO,SAAS,4BAA4B,EAAG,MAAK,IAAI,EAAE,CAAC,CAAC;AAC5E,MAAI,gBAAgB;AACpB,aAAW,OAAO,MAAM;AACtB,QAAI,OAAO,QAAQ,gBAAgB;AACjC,eAAS,KAAK,sBAAO,KAAK,IAAI,iBAAO,cAAc,4FAAsB;AACzE;AAAA,IACF;AACA,QAAI,OAAO,OAAO,IAAI,GAAG;AACzB,QAAI,CAAC,MAAM;AACT,YAAM,OAAO,IAAI,KAAK,IAAI,OAAO,aAAa,IAAI,QAAQ,uBAAuB,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;AACnG,aAAO,MAAM;AAAA,IACf;AACA,QAAI,CAAC,KAAM;AACX,UAAM,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,cAAc,IAAI;AACvD,QAAI,CAAC,EAAG;AACR,UAAM,QAAQ,MAAM,EAAE,MAAM,YAAY;AACxC,QAAI,MAAM,SAAS,QAAQ;AACzB,eAAS,KAAK,sBAAO,IAAI,KAAK,MAAM,SAAS,SAAS,QAAQ,CAAC,CAAC,wDAAgB;AAChF;AAAA,IACF;AACA,QAAI,gBAAgB,MAAM,SAAS,uBAAuB;AACxD,eAAS,KAAK,mCAAU,KAAK,MAAM,wBAAwB,OAAO,CAAC,+DAAkB;AACrF;AAAA,IACF;AACA,qBAAiB,MAAM;AACvB,WAAO,IAAI,KAAK,EAAE,SAAS,QAAQ,UAAU,MAAM,KAAK,CAAC,WAAW,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ,CAAC,GAAG,CAAC;AAAA,EAC/G;AAEA,QAAM,MAAM,gBAAgB,EAAE,gBAAgB,QAAQ,UAAU;AAChE,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,OAAM,IAAI,YAAY,4CAAc;AAE/C,QAAM,SAAS,UAAU,MAAM,QAAQ;AACvC,QAAM,SAAS,SAAS,qBAAqB,QAAQ,QAAQ,IAAI;AACjE,QAAM,KAAKA,KAAI,QAAQ,SAAS,KAAK,GAAG,KAAKA,KAAI,QAAQ,UAAU,KAAK;AACxE,QAAM,KAAKA,KAAI,QAAQ,QAAQ,IAAI;AACnC,QAAM,KAAKA,KAAI,QAAQ,OAAO,IAAI,IAAIA,KAAI,QAAQ,UAAU,CAAC;AAC7D,QAAM,SAAS,KAAK,KAAKA,KAAI,QAAQ,UAAU,IAAI,IAAIA,KAAI,QAAQ,UAAU,CAAC;AAC9E,QAAM,SAAS,KAAK,KAAKA,KAAI,QAAQ,SAAS,IAAI;AAIlD,MAAI,CAAC,SAAU,eAAc,MAAM,QAAQ,EAAE,QAAQ,OAAO,GAAG,SAAS,cAAc,MAAM;AAK5F,QAAM,QAAQ,UAAU,MAAM,OAAO;AACrC,QAAM,WAAWA,KAAI,OAAO,YAAY,CAAC,IAAI;AAC7C,QAAM,eAAe,oBAAI,IAAuB;AAChD,MAAI,SAAS;AACb,MAAI,UAAU;AACd;AACE,QAAI,QAAQ,OAAO;AACnB,QAAI,QAAQ,OAAO;AACnB,QAAI,MAAM;AACV,eAAW,KAAKD,UAAS,IAAI,GAAG;AAC9B,UAAID,IAAG,CAAC,MAAM,IAAK;AACnB,YAAM,MAAM,qBAAqB,GAAG,cAAc;AAClD,YAAM,SAAS,MAAMC,UAAS,GAAG,EAAE,OAAO,OAAKD,IAAG,CAAC,MAAM,SAAS,IAAI,CAAC;AACvE,YAAM,UAAoB,CAAC;AAC3B,iBAAW,KAAK,QAAQ;AACtB,cAAM,IAAIE,KAAI,GAAG,SAAS;AAC1B,cAAM,IAAIA,KAAI,GAAG,SAAS;AAC1B,YAAI,IAAI,UAAU,CAAC,YAAY,KAAK,OAAQ;AAC5C,gBAAQ,KAAK,GAAG;AAChB,kBAAU,KAAK,IAAI,SAAS,IAAIA,KAAI,GAAG,cAAc,GAAI,CAAC;AAC1D,gBAAQ;AACR,gBAAQ;AAAA,MACV;AACA,mBAAa,IAAI,GAAG,OAAO;AAC3B,eAAS,KAAK,IAAI,QAAQ,MAAM,CAAC;AAAA,IACnC;AAAA,EACF;AAEA,QAAM,MAAW;AAAA,IACf,OAAO,MAAM,KAAK,EAAE,QAAQ,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA,IAAG,MAAM;AAAA,IACvD,MAAM,EAAE,IAAI,IAAI,IAAI,IAAI,QAAQ,OAAO;AAAA,IAAG;AAAA,IAAQ;AAAA,IAAQ,MAAM,CAAC;AAAA,IACjE,aAAa,SAAS,cAAc,CAAC,GAAG,IAAI,OAAK,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,OAAO,OAAK,EAAE,SAAS,CAAC;AAAA,IACjG;AAAA,IAAU,QAAQ,oBAAI,IAAI;AAAA,IAAG,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,EAAE;AAAA,EACvE;AAEA,aAAW,KAAKD,UAAS,IAAI,GAAG;AAC9B,QAAID,IAAG,CAAC,MAAM,IAAK;AACnB,aAAS,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG,aAAa,IAAI,CAAC,CAAC;AAAA,EACzD;AAIA,QAAM,QAAQ,WAAW,IAAI,KAAK,IAAI,IAAI,KAAK,UAAU,GAAI,IAAI;AACjE,QAAM,MAAM;AACZ,QAAM,SAAS,SAAS,SAAS,SAAS,KAAK;AAE/C,QAAM,WAAW,IAAI,MAAM;AAAA,IAAI,CAAC,KAAK,MACnC,iBAAiB,IAAI,CAAC,4BAA4B,GAAG,KAAK,QAAQ,IAAI,CAAC,mBACvD,GAAG,EAAE,CAAC,aAAa,GAAG,KAAK,CAAC;AAAA,EACX,IAAI,KAAK,IAAI,CAAC;AAAA;AAAA,EACjD,EAAE,KAAK,IAAI;AAGX,QAAM,MAAM,wDAAwD,GAAG,EAAE,CAAC,IAAI,GAAG,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC,eAAe,GAAG,MAAM,CAAC;AAAA,uDACzE,GAAG,EAAE,CAAC,aAAa,GAAG,KAAK,CAAC,iBAAiB,IAAI,KAAK,KAAK,EAAE,CAAC;AAAA,EACnH,QAAQ;AAAA;AACb,SAAO,EAAE,KAAK,OAAO,KAAK,MAAM,EAAE,IAAI,KAAK,QAAQ,KAAK,MAAM,MAAM,IAAI,KAAK,WAAW,QAAQ,UAAU,OAAO,IAAI,MAAM;AAC7H;","names":["charW","ln","elements","num"]}
|