kordoc 4.18.1 → 4.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +7 -6
  2. package/dist/{-X4KTLQC6.js → -WOKB25R2.js} +13 -11
  3. package/dist/{chunk-UTVMLJZE.js → chunk-2I2RTJJ5.js} +433 -71
  4. package/dist/chunk-2I2RTJJ5.js.map +1 -0
  5. package/dist/{chunk-Q5N4YS3T.cjs → chunk-2ZBLQVAS.cjs} +20 -13
  6. package/dist/chunk-2ZBLQVAS.cjs.map +1 -0
  7. package/dist/{chunk-KXR4CTFT.js → chunk-3FZGL3IL.js} +2 -2
  8. package/dist/{chunk-R2IFWAHI.js → chunk-5X7XWYR2.js} +2 -2
  9. package/dist/{chunk-PWRY4W6Q.js → chunk-6GLTGOP6.js} +2 -2
  10. package/dist/{chunk-SQQ4ITJ5.js → chunk-AZQWESRH.js} +2 -2
  11. package/dist/{chunk-KBZ3MQJX.js → chunk-BGOR5GCF.js} +2 -2
  12. package/dist/{chunk-WWOTHS2E.js → chunk-BJ6RYYXV.js} +205 -7
  13. package/dist/chunk-BJ6RYYXV.js.map +1 -0
  14. package/dist/{chunk-VDIXNEWV.js → chunk-CAN5P6N6.js} +10 -5
  15. package/dist/chunk-CAN5P6N6.js.map +1 -0
  16. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  17. package/dist/{chunk-SIZ63E5G.cjs → chunk-HZV52V2X.cjs} +3 -3
  18. package/dist/chunk-HZV52V2X.cjs.map +1 -0
  19. package/dist/{chunk-X625PCZM.js → chunk-IXX3WHIE.js} +3 -3
  20. package/dist/{chunk-6V7QRQY3.js → chunk-J2A46VCA.js} +125 -87
  21. package/dist/chunk-J2A46VCA.js.map +1 -0
  22. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  23. package/dist/{chunk-AQUNBGXH.js → chunk-KSJ7GKNQ.js} +2 -2
  24. package/dist/{chunk-Z4KCEERM.js → chunk-NES5U4JK.js} +3 -3
  25. package/dist/{chunk-MTTYJEKA.js → chunk-OOX2GZZ4.js} +2 -2
  26. package/dist/{chunk-HUGBJVAA.js → chunk-PSJLB3SZ.js} +16 -9
  27. package/dist/chunk-PSJLB3SZ.js.map +1 -0
  28. package/dist/{chunk-NQR3VKWN.js → chunk-RDBTOE5X.js} +2 -2
  29. package/dist/{chunk-2VFZQ634.js → chunk-RRURZDEK.js} +2 -2
  30. package/dist/{chunk-6N5BOPJI.js → chunk-RSDUH4XF.js} +4 -3
  31. package/dist/chunk-RSDUH4XF.js.map +1 -0
  32. package/dist/{chunk-PCSOJ523.cjs → chunk-SHRRD2TD.cjs} +10 -5
  33. package/dist/chunk-SHRRD2TD.cjs.map +1 -0
  34. package/dist/{chunk-TMC4A3FM.js → chunk-UVUKT3IQ.js} +13 -7
  35. package/dist/chunk-UVUKT3IQ.js.map +1 -0
  36. package/dist/{chunk-IXAXI2S3.cjs → chunk-V4BKT65D.cjs} +3 -3
  37. package/dist/chunk-V4BKT65D.cjs.map +1 -0
  38. package/dist/{chunk-I7YWXECO.js → chunk-VGDTONTB.js} +10 -5
  39. package/dist/chunk-VGDTONTB.js.map +1 -0
  40. package/dist/{chunk-K35WIYBD.js → chunk-W3RADOLO.js} +5 -4
  41. package/dist/chunk-W3RADOLO.js.map +1 -0
  42. package/dist/{chunk-5PNQIF3V.js → chunk-W7P4GPOV.js} +4 -4
  43. package/dist/{chunk-7DC2KL3V.cjs → chunk-WN5YLP7H.cjs} +161 -43
  44. package/dist/chunk-WN5YLP7H.cjs.map +1 -0
  45. package/dist/{chunk-IUMV45TX.cjs → chunk-ZD5WNQNC.cjs} +12 -12
  46. package/dist/chunk-ZD5WNQNC.cjs.map +1 -0
  47. package/dist/{chunk-CWJ5KY2F.js → chunk-ZN3LHNJG.js} +176 -43
  48. package/dist/chunk-ZN3LHNJG.js.map +1 -0
  49. package/dist/chunks-OQDRDICD.js +10 -0
  50. package/dist/cli.js +55 -34
  51. package/dist/cli.js.map +1 -1
  52. package/dist/{formula-W3GQC6L6.cjs → formula-BQ5RW5XD.cjs} +9 -9
  53. package/dist/formula-BQ5RW5XD.cjs.map +1 -0
  54. package/dist/{formula-LICM6K27.js → formula-GPCOJJXN.js} +3 -3
  55. package/dist/{formula-T4Q2UHWO.js → formula-QZAOKPBX.js} +4 -4
  56. package/dist/{image-ocr-WZJURDX6.js → image-ocr-POCME5I4.js} +6 -6
  57. package/dist/{image-ocr-XB2NFKGZ.js → image-ocr-TWNNVTZS.js} +9 -9
  58. package/dist/{image-ocr-7PNHBD2G.cjs → image-ocr-VKIUBLBX.cjs} +15 -15
  59. package/dist/image-ocr-VKIUBLBX.cjs.map +1 -0
  60. package/dist/index.cjs +1264 -778
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +7 -5
  63. package/dist/index.d.ts +7 -5
  64. package/dist/index.js +606 -120
  65. package/dist/index.js.map +1 -1
  66. package/dist/mcp.js +30 -31
  67. package/dist/mcp.js.map +1 -1
  68. package/dist/{model-bundle-O2XYR54X.js → model-bundle-THYUL7GG.js} +5 -5
  69. package/dist/{models-SKK3NH3V.js → models-U22N4RXX.js} +5 -5
  70. package/dist/page-range-QHHFTL7X.cjs.map +1 -1
  71. package/dist/{parser-PJUZSMFR.cjs → parser-JOMFOZLH.cjs} +213 -252
  72. package/dist/parser-JOMFOZLH.cjs.map +1 -0
  73. package/dist/{parser-4D2M5BKY.js → parser-NKP2NCSV.js} +27 -66
  74. package/dist/parser-NKP2NCSV.js.map +1 -0
  75. package/dist/{parser-5G5G7HHG.js → parser-SCXRLOW7.js} +24 -63
  76. package/dist/parser-SCXRLOW7.js.map +1 -0
  77. package/dist/pdf-ocr-NF4AY5MI.cjs +16 -0
  78. package/dist/pdf-ocr-NF4AY5MI.cjs.map +1 -0
  79. package/dist/pdf-ocr-OLII3WAG.js +15 -0
  80. package/dist/pdf-ocr-VJL7ON2Q.js +19 -0
  81. package/dist/{profile-io-BAQTYPK7.js → profile-io-UI3TZBYH.js} +3 -3
  82. package/dist/{rasterize-A2ZEACKV.js → rasterize-XMBV5H2I.js} +3 -3
  83. package/dist/{redact-doc-26KN2LSZ.js → redact-doc-7ZZMHDBO.js} +11 -11
  84. package/dist/{render-62ZUDMHB.js → render-SWRTUDAV.js} +7 -7
  85. package/dist/seal-UKKJZHKE.js +10 -0
  86. package/dist/{setup-2A3E6WRE.js → setup-43JJEEU7.js} +3 -3
  87. package/dist/visual-D2563HCJ.js +20 -0
  88. package/dist/{watch-D2T5FEKT.js → watch-NRATQ472.js} +14 -14
  89. package/dist/watch-NRATQ472.js.map +1 -0
  90. package/package.json +1 -1
  91. package/dist/chunk-6N5BOPJI.js.map +0 -1
  92. package/dist/chunk-6V7QRQY3.js.map +0 -1
  93. package/dist/chunk-7DC2KL3V.cjs.map +0 -1
  94. package/dist/chunk-CWJ5KY2F.js.map +0 -1
  95. package/dist/chunk-HUGBJVAA.js.map +0 -1
  96. package/dist/chunk-I7YWXECO.js.map +0 -1
  97. package/dist/chunk-IUMV45TX.cjs.map +0 -1
  98. package/dist/chunk-IXAXI2S3.cjs.map +0 -1
  99. package/dist/chunk-K35WIYBD.js.map +0 -1
  100. package/dist/chunk-PCSOJ523.cjs.map +0 -1
  101. package/dist/chunk-Q5N4YS3T.cjs.map +0 -1
  102. package/dist/chunk-SIZ63E5G.cjs.map +0 -1
  103. package/dist/chunk-TMC4A3FM.js.map +0 -1
  104. package/dist/chunk-UTVMLJZE.js.map +0 -1
  105. package/dist/chunk-VDIXNEWV.js.map +0 -1
  106. package/dist/chunk-WWOTHS2E.js.map +0 -1
  107. package/dist/chunks-ACUC63N3.js +0 -10
  108. package/dist/formula-W3GQC6L6.cjs.map +0 -1
  109. package/dist/image-ocr-7PNHBD2G.cjs.map +0 -1
  110. package/dist/parser-4D2M5BKY.js.map +0 -1
  111. package/dist/parser-5G5G7HHG.js.map +0 -1
  112. package/dist/parser-PJUZSMFR.cjs.map +0 -1
  113. package/dist/pdf-ocr-EGWOIB6O.cjs +0 -16
  114. package/dist/pdf-ocr-EGWOIB6O.cjs.map +0 -1
  115. package/dist/pdf-ocr-NJTFXBRC.js +0 -19
  116. package/dist/pdf-ocr-ZXR6Q6BK.js +0 -15
  117. package/dist/seal-3GXC2API.js +0 -10
  118. package/dist/visual-4ZBZRMCM.js +0 -20
  119. package/dist/watch-D2T5FEKT.js.map +0 -1
  120. /package/dist/{-X4KTLQC6.js.map → -WOKB25R2.js.map} +0 -0
  121. /package/dist/{chunk-KXR4CTFT.js.map → chunk-3FZGL3IL.js.map} +0 -0
  122. /package/dist/{chunk-R2IFWAHI.js.map → chunk-5X7XWYR2.js.map} +0 -0
  123. /package/dist/{chunk-PWRY4W6Q.js.map → chunk-6GLTGOP6.js.map} +0 -0
  124. /package/dist/{chunk-SQQ4ITJ5.js.map → chunk-AZQWESRH.js.map} +0 -0
  125. /package/dist/{chunk-KBZ3MQJX.js.map → chunk-BGOR5GCF.js.map} +0 -0
  126. /package/dist/{chunk-X625PCZM.js.map → chunk-IXX3WHIE.js.map} +0 -0
  127. /package/dist/{chunk-AQUNBGXH.js.map → chunk-KSJ7GKNQ.js.map} +0 -0
  128. /package/dist/{chunk-Z4KCEERM.js.map → chunk-NES5U4JK.js.map} +0 -0
  129. /package/dist/{chunk-MTTYJEKA.js.map → chunk-OOX2GZZ4.js.map} +0 -0
  130. /package/dist/{chunk-NQR3VKWN.js.map → chunk-RDBTOE5X.js.map} +0 -0
  131. /package/dist/{chunk-2VFZQ634.js.map → chunk-RRURZDEK.js.map} +0 -0
  132. /package/dist/{chunk-5PNQIF3V.js.map → chunk-W7P4GPOV.js.map} +0 -0
  133. /package/dist/{chunks-ACUC63N3.js.map → chunks-OQDRDICD.js.map} +0 -0
  134. /package/dist/{formula-LICM6K27.js.map → formula-GPCOJJXN.js.map} +0 -0
  135. /package/dist/{formula-T4Q2UHWO.js.map → formula-QZAOKPBX.js.map} +0 -0
  136. /package/dist/{image-ocr-WZJURDX6.js.map → image-ocr-POCME5I4.js.map} +0 -0
  137. /package/dist/{image-ocr-XB2NFKGZ.js.map → image-ocr-TWNNVTZS.js.map} +0 -0
  138. /package/dist/{model-bundle-O2XYR54X.js.map → model-bundle-THYUL7GG.js.map} +0 -0
  139. /package/dist/{models-SKK3NH3V.js.map → models-U22N4RXX.js.map} +0 -0
  140. /package/dist/{pdf-ocr-NJTFXBRC.js.map → pdf-ocr-OLII3WAG.js.map} +0 -0
  141. /package/dist/{pdf-ocr-ZXR6Q6BK.js.map → pdf-ocr-VJL7ON2Q.js.map} +0 -0
  142. /package/dist/{profile-io-BAQTYPK7.js.map → profile-io-UI3TZBYH.js.map} +0 -0
  143. /package/dist/{rasterize-A2ZEACKV.js.map → rasterize-XMBV5H2I.js.map} +0 -0
  144. /package/dist/{redact-doc-26KN2LSZ.js.map → redact-doc-7ZZMHDBO.js.map} +0 -0
  145. /package/dist/{render-62ZUDMHB.js.map → render-SWRTUDAV.js.map} +0 -0
  146. /package/dist/{seal-3GXC2API.js.map → seal-UKKJZHKE.js.map} +0 -0
  147. /package/dist/{setup-2A3E6WRE.js.map → setup-43JJEEU7.js.map} +0 -0
  148. /package/dist/{visual-4ZBZRMCM.js.map → visual-D2563HCJ.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  blocksToPages
4
- } from "./chunk-PWRY4W6Q.js";
4
+ } from "./chunk-6GLTGOP6.js";
5
5
  import {
6
6
  allLinesegRemovalSplices,
7
7
  applySplices,
@@ -15,7 +15,7 @@ import {
15
15
  paraTextPureT,
16
16
  patchZipEntries,
17
17
  scanSectionXml
18
- } from "./chunk-Z4KCEERM.js";
18
+ } from "./chunk-NES5U4JK.js";
19
19
  import {
20
20
  CONVERT_MAP,
21
21
  FLAG_COMPRESSED,
@@ -31,6 +31,8 @@ import {
31
31
  TAG_PARA_HEADER,
32
32
  TAG_PARA_TEXT,
33
33
  TAG_TABLE,
34
+ TOC_ENTRY_BLOCKS,
35
+ TOC_LEADER,
34
36
  appendParaText,
35
37
  blocksPlainText,
36
38
  buildAddressedTable,
@@ -61,19 +63,21 @@ import {
61
63
  prepareWrap,
62
64
  renderDocumentToScene,
63
65
  romanNumeral,
64
- simulateWrap
65
- } from "./chunk-CWJ5KY2F.js";
66
+ simulateWrap,
67
+ takeTocLeaders
68
+ } from "./chunk-ZN3LHNJG.js";
66
69
  import {
67
70
  CELL_EDGES,
68
71
  HEADING_RATIO_H1,
69
72
  HEADING_RATIO_H2,
70
73
  HEADING_RATIO_H3,
74
+ escapeLiteralTags,
71
75
  plainScripts,
72
76
  stripScriptTags,
73
77
  tidyScriptTags,
74
78
  unframeLayoutTables,
75
79
  wrapScript
76
- } from "./chunk-TMC4A3FM.js";
80
+ } from "./chunk-UVUKT3IQ.js";
77
81
  import {
78
82
  parsePageRange
79
83
  } from "./chunk-VQFA55IC.js";
@@ -92,7 +96,7 @@ import {
92
96
  findChildByLocalName,
93
97
  localName,
94
98
  rawTextContent
95
- } from "./chunk-6N5BOPJI.js";
99
+ } from "./chunk-RSDUH4XF.js";
96
100
  import {
97
101
  MAX_COLS,
98
102
  MAX_ROWS,
@@ -107,7 +111,7 @@ import {
107
111
  hasStructuredCellContent,
108
112
  mapPuaText,
109
113
  noteSuffix
110
- } from "./chunk-K35WIYBD.js";
114
+ } from "./chunk-W3RADOLO.js";
111
115
  import {
112
116
  KordocError,
113
117
  classifyError,
@@ -115,6 +119,7 @@ import {
115
119
  escapeHtml,
116
120
  isPathTraversal,
117
121
  normalizeSectionHref,
122
+ partExtension,
118
123
  precheckZipSize,
119
124
  sanitizeError,
120
125
  sanitizeHref,
@@ -122,20 +127,20 @@ import {
122
127
  toArrayBuffer,
123
128
  unescapeHtml,
124
129
  unzipLimitBytes
125
- } from "./chunk-I7YWXECO.js";
130
+ } from "./chunk-VGDTONTB.js";
126
131
 
127
132
  // src/table/visual.ts
128
133
  import { readFile as readFile2 } from "fs/promises";
129
134
 
130
135
  // src/plain-markdown.ts
131
136
  function toPlainMarkdown(md) {
132
- return plainScripts(md).replace(/!\[[^\]\n]*\]\([^)\n]*\)/g, "").replace(/<img\b[^>]*>/gi, "").replace(/(?<!!)\[([^\]\n]*)\]\((?:https?|mailto|ftp):[^)\s]*\)/g, "$1").replace(/<\/?u>/g, "").replace(/(?<!\\)\*\*(?=\S)([^\n]*?\S)(?<!\\)\*\*/g, "$1").replace(/(<br>)+(?=<\/t[dh]>)/g, "").replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
137
+ return plainScripts(md).replace(/!\[[^\]\n]*\]\([^)\n]*\)/g, "").replace(/<img\b[^>]*>/gi, "").replace(/(?<!!)\[([^\]\n]*)\]\((?:https?|mailto|ftp):[^)\s]*\)/g, "$1").replace(/(?<!\\)<\/?u>/g, "").replace(/(?<!\\)\*\*(?=\S)([^\n]*?\S)(?<!\\)\*\*/g, "$1").replace(/(<br>)+(?=<\/t[dh]>)/g, "").replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
133
138
  }
134
139
 
135
140
  // src/html-tables.ts
136
141
  var TABLE_TAG = /(<\/?(?:table|tr|th|td)\b[^>]*>)/i;
137
142
  function pipeCellToHtml(cell2) {
138
- return cell2.split(/(<br>|<\/?(?:u|sup|sub)>|<img\b[^>]*>)/).map((part, i) => i % 2 ? part : part.replace(/\\([\\`*_{}[\]()#+\-.!|~<>$])/g, "$1").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).join("");
143
+ return cell2.split(/(<br>|(?<!\\)<\/?(?:u|sup|sub)>|<img\b[^>]*>)/).map((part, i) => i % 2 ? part : part.replace(/\\([\\`*_{}[\]()#+\-.!|~<>$])/g, "$1").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).join("");
139
144
  }
140
145
  function splitPipeRow(line) {
141
146
  const t = line.trim().replace(/^\|/, "").replace(/(?<!\\)\|$/, "");
@@ -628,7 +633,7 @@ function parseParaHeadings(doc, map) {
628
633
  if (map.size > 0) break;
629
634
  }
630
635
  }
631
- function detectHwpxHeadings(blocks, styleMap) {
636
+ function detectHwpxHeadings(blocks, styleMap, tocTexts) {
632
637
  if (blocks.some((b) => b.type === "heading")) return;
633
638
  let baseFontSize = 0;
634
639
  const sizeFreq = /* @__PURE__ */ new Map();
@@ -645,7 +650,7 @@ function detectHwpxHeadings(blocks, styleMap) {
645
650
  }
646
651
  }
647
652
  for (const block of blocks) {
648
- if (block.type !== "paragraph" || !block.text) continue;
653
+ if (block.type !== "paragraph" || !block.text || TOC_ENTRY_BLOCKS.has(block) || tocTexts?.has(block.text.trim())) continue;
649
654
  const text = block.text.trim();
650
655
  if (text.length === 0 || text.length > 200 || /^\d+$/.test(text)) continue;
651
656
  let level = 0;
@@ -1183,7 +1188,7 @@ function extractRunSpans(para2, ctx, mode, requireMixed) {
1183
1188
  const tkids = rc.childNodes;
1184
1189
  for (let k = 0; k < (tkids?.length ?? 0); k++) {
1185
1190
  const tk = tkids[k];
1186
- if (tk.nodeType === 3 || tk.nodeType === 4) text += escapeLiteralDollar(tk.textContent || "");
1191
+ if (tk.nodeType === 3 || tk.nodeType === 4) text += escapeLiteralTags(escapeLiteralDollar(tk.textContent || ""));
1187
1192
  else if (tk.nodeType === 1) {
1188
1193
  const ttag = (tk.tagName || tk.localName || "").replace(/^[^:]+:/, "");
1189
1194
  if (ttag === "tab" || ttag === "fwSpace" || ttag === "hwSpace" || ttag === "nbSpace") text += " ";
@@ -1297,13 +1302,13 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1297
1302
  }
1298
1303
  if (tableCtx) tableStack.push(tableCtx);
1299
1304
  const newTable = { rows: [], currentRow: [], cell: null, sourceId: el.getAttribute("id") ?? void 0 };
1300
- walkSection(el, blocks, newTable, tableStack, ctx, depth + 1);
1305
+ inIndependentDeletionStory(ctx.shared.track, () => walkSection(el, blocks, newTable, tableStack, ctx, depth + 1));
1301
1306
  tableCtx = completeTable(newTable, tableStack, blocks, ctx);
1302
1307
  break;
1303
1308
  }
1304
1309
  // 표/도표 캡션 — IRTable.caption으로 보존 (v3.0, 기존 무음 드롭 수정)
1305
1310
  case "caption": {
1306
- const cap = collectSubListContent(el, ctx);
1311
+ const cap = inIndependentDeletionStory(ctx.shared.track, () => collectSubListContent(el, ctx));
1307
1312
  if (cap.text) {
1308
1313
  if (tableCtx) {
1309
1314
  tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + cap.text;
@@ -1356,13 +1361,16 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1356
1361
  case "p": {
1357
1362
  const paraPg = ctx.paraPage?.get(el);
1358
1363
  if (paraPg !== void 0 && ctx.pageBase !== void 0) ctx.page = ctx.pageBase + paraPg + 1;
1359
- const { text: rawText, href, footnote, style, segments, placeholderSpans } = extractParagraphInfo(el, ctx.styleMap, ctx);
1364
+ const { text: rawText, href, footnote, style, segments, placeholderSpans, tocEntry } = extractParagraphInfo(el, ctx.styleMap, ctx);
1360
1365
  let text = rawText;
1361
1366
  let headingLevel;
1362
1367
  const ph = resolveParaHeading(el, ctx);
1363
1368
  if (text) {
1364
1369
  if (ph?.prefix) text = ph.prefix + " " + text;
1365
- headingLevel = ph?.headingLevel;
1370
+ headingLevel = tocEntry ? void 0 : ph?.headingLevel;
1371
+ if (tocEntry) {
1372
+ for (const line of text.split("\n")) if (line.trim()) ctx.shared.tocTexts.add(line.trim());
1373
+ }
1366
1374
  }
1367
1375
  if (segments) {
1368
1376
  const cell2 = tableCtx?.cell ?? null;
@@ -1424,6 +1432,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1424
1432
  }
1425
1433
  const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.page };
1426
1434
  if (headingLevel) block.level = headingLevel;
1435
+ if (tocEntry) TOC_ENTRY_BLOCKS.add(block);
1427
1436
  if (style) block.style = style;
1428
1437
  if (href) block.href = href;
1429
1438
  if (footnote) block.footnoteText = footnote;
@@ -1447,6 +1456,15 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1447
1456
  if (spanMode !== "foreign" && el.getAttribute("paraPrIDRef") === KORDOC_PARA_QUOTE) block.quote = true;
1448
1457
  }
1449
1458
  if (!headingLevel && !block.spans && placeholderSpans) block.spans = withPrefixSpan(placeholderSpans, ph?.prefix);
1459
+ if (headingLevel && placeholderSpans) {
1460
+ const shown = withPrefixSpan(placeholderSpans.filter((s) => !s.placeholder), ph?.prefix).map((s) => s.text).join("").trim();
1461
+ if (shown) block.text = shown;
1462
+ else {
1463
+ block.type = "paragraph";
1464
+ delete block.level;
1465
+ block.spans = withPrefixSpan(placeholderSpans, ph?.prefix);
1466
+ }
1467
+ }
1450
1468
  blocks.push(block);
1451
1469
  } else {
1452
1470
  blocks.push({ type: "paragraph", text, pageNumber: ctx.page });
@@ -1688,10 +1706,10 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1688
1706
  }
1689
1707
  if (tableCtx) tableStack.push(tableCtx);
1690
1708
  const newTable = { rows: [], currentRow: [], cell: null, inline: isInlineTbl(el), sourceId: el.getAttribute("id") ?? void 0 };
1691
- walkSection(el, blocks, newTable, tableStack, ctx, d + 1);
1709
+ inIndependentDeletionStory(ctx.shared.track, () => walkSection(el, blocks, newTable, tableStack, ctx, d + 1));
1692
1710
  tableCtx = completeTable(newTable, tableStack, blocks, ctx);
1693
1711
  } else if (localTag === "caption" && !inShape) {
1694
- const cap = collectSubListContent(el, ctx);
1712
+ const cap = inIndependentDeletionStory(ctx.shared.track, () => collectSubListContent(el, ctx));
1695
1713
  if (cap.text) {
1696
1714
  if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: cap.text, pageNumber: ctx.page }]);
1697
1715
  else if (tableCtx) {
@@ -1856,7 +1874,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
1856
1874
  const open = openFields.pop();
1857
1875
  if (open?.rangeIdx !== void 0) linkRanges[open.rangeIdx].end = text.length;
1858
1876
  if (open?.guide !== void 0 && open.start !== void 0) {
1859
- const value = text.slice(open.start).replace(/\\\$/g, "$");
1877
+ const value = text.slice(open.start).replace(/\\([$<])/g, "$1");
1860
1878
  if (value && (value === open.guide || value.trimEnd() === open.guide)) {
1861
1879
  text = text.slice(0, open.start) + PH_OPEN + text.slice(open.start) + PH_CLOSE;
1862
1880
  }
@@ -1887,7 +1905,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
1887
1905
  // 아니므로 방출하지 않는다
1888
1906
  case "header":
1889
1907
  case "footer": {
1890
- if (!ctx) break;
1908
+ if (!ctx || isInDeletedRange(ctx)) break;
1891
1909
  const t = collectSubListText(k, ctx);
1892
1910
  if (t && hasPageAutoNum(k) && !/\p{L}/u.test(t)) break;
1893
1911
  if (t) {
@@ -1965,7 +1983,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
1965
1983
  ctx.warnings?.push({ page: ctx.page, message: "\uBCC0\uACBD\uCD94\uC801 \uC0AD\uC81C \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uC81C\uC678", code: "HIDDEN_TEXT_FILTERED" });
1966
1984
  }
1967
1985
  } else {
1968
- text += escapeLiteralDollar(t.replace(/\x1E/g, ""));
1986
+ text += escapeLiteralTags(escapeLiteralDollar(t.replace(/\x1E/g, "")));
1969
1987
  }
1970
1988
  continue;
1971
1989
  }
@@ -1985,8 +2003,9 @@ function extractParagraphInfo(para2, styleMap, ctx) {
1985
2003
  // 점 채움을 탭 하나로 바꾸고 쪽 번호를 남기는 것과 같다). 종전엔 채움 탭 뒤를 쪽 번호로 보고 잘라, 목차 쪽 번호만이 아니라
1986
2004
  // 일정표 "사용자 의견조사 ····· '26년 8~9월"(gate-fill 36646162)의 일정, 줄바꿈 뒤 다음 목차 항목
1987
2005
  // "<참고2> 직종별사업체노동력조사 개요"(rhwp issue6044)까지 사라졌다
2006
+ // (채움 탭은 목차 항목 판정까지 TOC_LEADER 표지로 들고 가다 보통 탭으로 바꾼다 — toc-entry.ts)
1988
2007
  case "tab":
1989
- text += " ";
2008
+ text += /^(?:|0|NONE)$/i.test(child.getAttribute("leader") ?? "") ? " " : TOC_LEADER;
1990
2009
  break;
1991
2010
  case "br":
1992
2011
  if ((child.getAttribute("type") || "line") === "line") text += "\n";
@@ -2008,9 +2027,10 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2008
2027
  if (isInlineTbl(child)) text += "";
2009
2028
  break;
2010
2029
  // 양식 선택 상자·라디오 단추 — 한컴이 그리는 상자 기호와 캡션 글 (종전엔 통째로 빠졌다: form-002 "원천기술형"·서울 결재 "부분공개")
2030
+ // 지운 구간의 양식 단추·수식은 글처럼 내지 않는다 (최종본)
2011
2031
  case "checkBtn":
2012
2032
  case "radioBtn":
2013
- text += formButtonText(child, tag === "radioBtn");
2033
+ if (!isInDeletedRange(ctx)) text += formButtonText(child, tag === "radioBtn");
2014
2034
  break;
2015
2035
  // 하이퍼링크
2016
2036
  case "hyperlink": {
@@ -2080,6 +2100,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2080
2100
  // 스크립트가 담겨 있음. hml-equation-parser 로 LaTeX 변환 후 `$...$`
2081
2101
  // 로 래핑. 실패/빈 스크립트면 무시 (대체 텍스트 누출 방지).
2082
2102
  case "equation": {
2103
+ if (isInDeletedRange(ctx)) break;
2083
2104
  const script = findChildByLocalName(child, "script");
2084
2105
  const raw = script ? extractTextFromNode(script) : "";
2085
2106
  if (raw.trim()) {
@@ -2133,6 +2154,9 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2133
2154
  if (applied.length) href = void 0;
2134
2155
  }
2135
2156
  text = tidyScriptTags(text);
2157
+ const toc = takeTocLeaders(text);
2158
+ text = toc.text;
2159
+ const tocEntry = toc.tocEntry;
2136
2160
  const cleanParaText = (raw) => {
2137
2161
  let t = raw.replace(/[ \t]+/g, " ").trim();
2138
2162
  if (/^그림입니다\.?\s*원본\s*그림의\s*(이름|크기)/.test(t)) t = "";
@@ -2168,7 +2192,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2168
2192
  if (!style.fontSize && !style.bold && !style.italic) style = void 0;
2169
2193
  }
2170
2194
  }
2171
- return { text: cleanText, href, footnote, style, segments, placeholderSpans };
2195
+ return { text: cleanText, href, footnote, style, segments, placeholderSpans, ...tocEntry ? { tocEntry } : {} };
2172
2196
  }
2173
2197
  function withPrefixSpan(spans, prefix) {
2174
2198
  return prefix ? [{ text: prefix + " " }, ...spans] : spans;
@@ -2647,7 +2671,7 @@ async function parseHwpxDocument(buffer, options) {
2647
2671
  applyPageText(blocks, shared);
2648
2672
  const images = await extractImagesFromZip(zip, blocks, decompressed, warnings, !options?.pages, shared.track.deletedImageRefs);
2649
2673
  const shown = options?.layoutTables === "keep" ? blocks : unframeLayoutTables(blocks, !!options?.keepTrailingEmptyCols);
2650
- detectHwpxHeadings(shown, styleMap);
2674
+ detectHwpxHeadings(shown, styleMap, shared.tocTexts);
2651
2675
  const outline = shown.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
2652
2676
  const markdown = blocksToMarkdown(shown);
2653
2677
  return { markdown, blocks: shown, metadata, outline: outline.length > 0 ? outline : void 0, warnings: warnings.length > 0 ? warnings : void 0, images: images.length > 0 ? images : void 0 };
@@ -15384,7 +15408,7 @@ function parseCharStream(reader, charCount, ctx, para2) {
15384
15408
  continue;
15385
15409
  }
15386
15410
  if (ch >= 32) {
15387
- para2.text += escapeLiteralDollar(decodeJohabText(ch) ?? "");
15411
+ para2.text += escapeLiteralTags(escapeLiteralDollar(decodeJohabText(ch) ?? ""));
15388
15412
  continue;
15389
15413
  }
15390
15414
  if (ch === 9) {
@@ -15732,7 +15756,7 @@ function collectRichText(root) {
15732
15756
  const el = children[i];
15733
15757
  const local = el.localName || el.tagName?.replace(/^[^:]+:/, "") || "";
15734
15758
  if (local === "rPh") continue;
15735
- if (local === "t") out += el.textContent ?? "";
15759
+ if (local === "t") out += escapeLiteralTags(escapeLiteralDollar(el.textContent ?? ""));
15736
15760
  else walk(el);
15737
15761
  }
15738
15762
  };
@@ -16356,14 +16380,14 @@ function extractSheetCells(records, bofIndex, sst, convertNum) {
16356
16380
  const h = readCellHeader(rec.data);
16357
16381
  if (h && rec.data.length >= 10) {
16358
16382
  const isst = rec.data.readUInt32LE(6);
16359
- cells.push({ row: h.row, col: h.col, value: sst[isst] ?? "" });
16383
+ cells.push({ row: h.row, col: h.col, value: escapeLiteralTags(escapeLiteralDollar(sst[isst] ?? "")) });
16360
16384
  }
16361
16385
  break;
16362
16386
  }
16363
16387
  case OP_LABEL: {
16364
16388
  const h = readCellHeader(rec.data);
16365
16389
  if (h) {
16366
- cells.push({ row: h.row, col: h.col, value: decodeLabelString(rec.data) });
16390
+ cells.push({ row: h.row, col: h.col, value: escapeLiteralTags(escapeLiteralDollar(decodeLabelString(rec.data))) });
16367
16391
  }
16368
16392
  break;
16369
16393
  }
@@ -16379,7 +16403,7 @@ function extractSheetCells(records, bofIndex, sst, convertNum) {
16379
16403
  cells.push({
16380
16404
  row: h.row,
16381
16405
  col: h.col,
16382
- value: decodeFormulaStringRecord(next.data)
16406
+ value: escapeLiteralTags(escapeLiteralDollar(decodeFormulaStringRecord(next.data)))
16383
16407
  });
16384
16408
  i = j;
16385
16409
  } else {
@@ -17091,6 +17115,7 @@ function isDisplayMath(el) {
17091
17115
  // src/docx/parser.ts
17092
17116
  var MAX_DECOMPRESS_SIZE4 = unzipLimitBytes(100 * 1024 * 1024);
17093
17117
  var MEDIA_PART_RE = /^word\/(?:media|embeddings)\//;
17118
+ var NEVER_READ_RE = /^word\/(?:embeddings\/.*|media\/.+\.(?:mp4|m4v|mov|avi|wmv|mpe?g|mkv|webm|mp3|wav|m4a|wma|aac|ogg|flac|mid|bin))$/i;
17094
17119
  function matchesLocal(el, localName3) {
17095
17120
  return el.localName === localName3 || (el.tagName?.endsWith(`:${localName3}`) ?? false);
17096
17121
  }
@@ -17282,7 +17307,7 @@ function parseFootnotes(xml) {
17282
17307
  const runs = findElements(p, "r");
17283
17308
  for (const r of runs) {
17284
17309
  const tElements = getChildElements(r, "t");
17285
- for (const t of tElements) texts.push(t.textContent ?? "");
17310
+ for (const t of tElements) texts.push(escapeLiteralTags(escapeLiteralDollar(t.textContent ?? "")));
17286
17311
  }
17287
17312
  }
17288
17313
  notes.set(id, texts.join("").trim());
@@ -17313,7 +17338,7 @@ function collectOmmlRoots(p) {
17313
17338
  function extractRun(r) {
17314
17339
  let text = "";
17315
17340
  for (const el of effectiveChildElements(r)) {
17316
- if (matchesLocal(el, "t")) text += el.textContent ?? "";
17341
+ if (matchesLocal(el, "t")) text += escapeLiteralTags(escapeLiteralDollar(el.textContent ?? ""));
17317
17342
  else if (matchesLocal(el, "br") || matchesLocal(el, "cr")) text += "\n";
17318
17343
  else if (matchesLocal(el, "tab")) text += " ";
17319
17344
  else if (matchesLocal(el, "sym")) text += symbolChar(getAttr(el, "font"), getAttr(el, "char"));
@@ -17609,7 +17634,7 @@ async function buildImageMap(zip, rels, doc, warnings, readBytes) {
17609
17634
  let data = null;
17610
17635
  if (readBytes) data = await imgFile.async("uint8array");
17611
17636
  imgIdx++;
17612
- const ext = imgPath.split(".").pop()?.toLowerCase() ?? "png";
17637
+ const ext = partExtension(imgPath);
17613
17638
  const mimeMap = {
17614
17639
  png: "image/png",
17615
17640
  jpg: "image/jpeg",
@@ -17708,7 +17733,7 @@ async function parseHeaderFooterBlocks(zip, doc, rels, kind, styles, numbering,
17708
17733
  }
17709
17734
  async function parseDocxDocument(buffer, options) {
17710
17735
  const readImages = options?.images !== false;
17711
- precheckZipSize(buffer, MAX_DECOMPRESS_SIZE4, void 0, { re: MEDIA_PART_RE, skip: !readImages });
17736
+ precheckZipSize(buffer, MAX_DECOMPRESS_SIZE4, void 0, { re: MEDIA_PART_RE, skip: !readImages, never: NEVER_READ_RE });
17712
17737
  const zip = await JSZip4.loadAsync(buffer);
17713
17738
  const warnings = [];
17714
17739
  const docFile = zip.file("word/document.xml");
@@ -17824,8 +17849,328 @@ async function parseDocxDocument(buffer, options) {
17824
17849
  };
17825
17850
  }
17826
17851
 
17827
- // src/hwpml/parser.ts
17852
+ // src/pptx/parser.ts
17853
+ import JSZip5 from "jszip";
17828
17854
  import { DOMParser as DOMParser3 } from "@xmldom/xmldom";
17855
+ var MAX_DECOMPRESS_SIZE5 = unzipLimitBytes(100 * 1024 * 1024);
17856
+ var MEDIA_PART_RE2 = /^ppt\/(?:media|embeddings)\//;
17857
+ var NEVER_READ_RE2 = /^ppt\/(?:embeddings\/.*|media\/.+\.(?:mp4|m4v|mov|avi|wmv|mpe?g|mkv|webm|mp3|wav|m4a|wma|aac|ogg|flac|mid|bin))$/i;
17858
+ var MAX_ZIP_ENTRIES2 = 1e4;
17859
+ var MAX_SLIDES = 2e3;
17860
+ var SKIP_PLACEHOLDERS = /* @__PURE__ */ new Set(["sldNum", "dt", "ftr", "hdr", "sldImg"]);
17861
+ var BULLET_PLACEHOLDERS = /* @__PURE__ */ new Set(["body", "obj"]);
17862
+ var REL_NOTES = "/notesSlide";
17863
+ var REL_IMAGE = "/image";
17864
+ var SLIDE_PART_RE = /^ppt\/slides\/slide(\d+)\.xml$/;
17865
+ var IMAGE_MIME = {
17866
+ png: "image/png",
17867
+ jpg: "image/jpeg",
17868
+ jpeg: "image/jpeg",
17869
+ gif: "image/gif",
17870
+ bmp: "image/bmp",
17871
+ wmf: "image/wmf",
17872
+ emf: "image/emf",
17873
+ tif: "image/tiff",
17874
+ tiff: "image/tiff",
17875
+ svg: "image/svg+xml"
17876
+ };
17877
+ function parseXml3(text) {
17878
+ return new DOMParser3().parseFromString(stripDtd(text), "text/xml");
17879
+ }
17880
+ function getAttr2(el, name) {
17881
+ const attrs = el.attributes;
17882
+ for (let i = 0; i < attrs.length; i++) {
17883
+ const attr = attrs[i];
17884
+ if (attr.localName === name || attr.name === name) return attr.value;
17885
+ }
17886
+ return null;
17887
+ }
17888
+ var NS_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
17889
+ function getRelId(el) {
17890
+ const attrs = el.attributes;
17891
+ for (let i = 0; i < attrs.length; i++) {
17892
+ const attr = attrs[i];
17893
+ if (attr.localName === "id" && (attr.namespaceURI === NS_REL || attr.name === "r:id")) return attr.value;
17894
+ }
17895
+ return null;
17896
+ }
17897
+ function findPath(el, ...names) {
17898
+ for (const name of names) {
17899
+ if (!el) return null;
17900
+ el = findChildByLocalName(el, name);
17901
+ }
17902
+ return el;
17903
+ }
17904
+ function resolvePart(baseDir, target) {
17905
+ if (target.startsWith("/")) return target.slice(1);
17906
+ const parts = baseDir ? baseDir.split("/") : [];
17907
+ for (const seg of target.split("/")) {
17908
+ if (seg === "..") parts.pop();
17909
+ else if (seg && seg !== ".") parts.push(seg);
17910
+ }
17911
+ return parts.join("/");
17912
+ }
17913
+ async function readRels(zip, part) {
17914
+ const map = /* @__PURE__ */ new Map();
17915
+ const slash = part.lastIndexOf("/");
17916
+ const dir = slash >= 0 ? part.slice(0, slash) : "";
17917
+ const file = zip.file(`${dir ? dir + "/" : ""}_rels/${part.slice(slash + 1)}.rels`);
17918
+ if (!file) return map;
17919
+ const doc = parseXml3(await file.async("text"));
17920
+ for (const rel of childrenByLocalName(doc.documentElement, "Relationship")) {
17921
+ const id = getAttr2(rel, "Id");
17922
+ const target = getAttr2(rel, "Target");
17923
+ if (!id || !target || getAttr2(rel, "TargetMode") === "External") continue;
17924
+ map.set(id, { type: getAttr2(rel, "Type") ?? "", target: resolvePart(dir, target) });
17925
+ }
17926
+ return map;
17927
+ }
17928
+ async function slideParts(zip) {
17929
+ const ordered = [];
17930
+ const presFile = zip.file("ppt/presentation.xml");
17931
+ if (presFile) {
17932
+ const pres = parseXml3(await presFile.async("text"));
17933
+ const rels = await readRels(zip, "ppt/presentation.xml");
17934
+ const list = findChildByLocalName(pres.documentElement, "sldIdLst");
17935
+ for (const sld of list ? childrenByLocalName(list, "sldId") : []) {
17936
+ const target = rels.get(getRelId(sld) ?? "")?.target;
17937
+ if (target && zip.file(target)) ordered.push(target);
17938
+ }
17939
+ }
17940
+ if (ordered.length === 0) {
17941
+ const names = Object.keys(zip.files).filter((n) => SLIDE_PART_RE.test(n));
17942
+ names.sort((a, b) => Number(SLIDE_PART_RE.exec(a)[1]) - Number(SLIDE_PART_RE.exec(b)[1]));
17943
+ ordered.push(...names);
17944
+ }
17945
+ if (ordered.length > MAX_SLIDES) throw new KordocError(`PPTX \uC2AC\uB77C\uC774\uB4DC \uC218 \uCD08\uACFC: ${ordered.length} (\uCD5C\uB300 ${MAX_SLIDES})`);
17946
+ return ordered;
17947
+ }
17948
+ function paragraphText(p) {
17949
+ let text = "";
17950
+ for (const child of elementChildren(p)) {
17951
+ const name = localName(child);
17952
+ if (name === "r" || name === "fld") {
17953
+ const t = findChildByLocalName(child, "t");
17954
+ const rPr = findChildByLocalName(child, "rPr");
17955
+ const baseline = Number(rPr ? getAttr2(rPr, "baseline") ?? 0 : 0);
17956
+ if (t) text += wrapScript(escapeLiteralTags(escapeLiteralDollar(rawTextContent(t))), baseline > 0 ? "sup" : baseline < 0 ? "sub" : null);
17957
+ } else if (name === "br") {
17958
+ text += " ";
17959
+ }
17960
+ }
17961
+ return tidyScriptTags(text.replace(/\s+/g, " ").trim());
17962
+ }
17963
+ function bodyParagraphs(txBody) {
17964
+ const out = [];
17965
+ for (const p of childrenByLocalName(txBody, "p")) {
17966
+ const text = paragraphText(p);
17967
+ if (!text) continue;
17968
+ const pPr = findChildByLocalName(p, "pPr");
17969
+ let bullet;
17970
+ if (pPr) {
17971
+ if (findChildByLocalName(pPr, "buNone")) bullet = "none";
17972
+ else if (findChildByLocalName(pPr, "buAutoNum")) bullet = "ordered";
17973
+ else if (findChildByLocalName(pPr, "buChar") || findChildByLocalName(pPr, "buBlip")) bullet = "unordered";
17974
+ }
17975
+ const lvl = Math.max(0, parseInt(pPr ? getAttr2(pPr, "lvl") ?? "0" : "0", 10) || 0);
17976
+ out.push({ text, lvl, bullet });
17977
+ }
17978
+ return out;
17979
+ }
17980
+ function placeholderType(sp) {
17981
+ const ph = findPath(sp, "nvSpPr", "nvPr", "ph");
17982
+ return ph ? getAttr2(ph, "type") ?? "obj" : null;
17983
+ }
17984
+ function pushParagraphs(paras, bulletDefault, page, out) {
17985
+ let lastList = null;
17986
+ for (const para2 of paras) {
17987
+ const bullet = para2.bullet ?? (bulletDefault ? "unordered" : "none");
17988
+ if (bullet === "none") {
17989
+ out.push({ type: "paragraph", text: para2.text, pageNumber: page });
17990
+ lastList = null;
17991
+ continue;
17992
+ }
17993
+ const item = { type: "list", text: para2.text, listType: bullet, pageNumber: page };
17994
+ if (para2.lvl > 0 && lastList) {
17995
+ ;
17996
+ (lastList.children ??= []).push(item);
17997
+ } else {
17998
+ out.push(item);
17999
+ lastList = item;
18000
+ }
18001
+ }
18002
+ }
18003
+ function parseTable2(tbl2, page, keepEmptyCols) {
18004
+ const rows = childrenByLocalName(tbl2, "tr");
18005
+ if (rows.length === 0) return null;
18006
+ const cellRows = rows.map((tr, r) => {
18007
+ const cells = [];
18008
+ childrenByLocalName(tr, "tc").forEach((tc2, c) => {
18009
+ if (getAttr2(tc2, "hMerge") === "1" || getAttr2(tc2, "vMerge") === "1") return;
18010
+ const txBody = findChildByLocalName(tc2, "txBody");
18011
+ const text = txBody ? bodyParagraphs(txBody).map((p) => p.text).join("\n") : "";
18012
+ cells.push({
18013
+ text,
18014
+ colSpan: Math.max(1, parseInt(getAttr2(tc2, "gridSpan") ?? "1", 10) || 1),
18015
+ rowSpan: Math.max(1, parseInt(getAttr2(tc2, "rowSpan") ?? "1", 10) || 1),
18016
+ colAddr: c,
18017
+ rowAddr: r
18018
+ });
18019
+ });
18020
+ return cells;
18021
+ });
18022
+ const table2 = buildTable(cellRows, { keepAnchoredEmptyCols: keepEmptyCols });
18023
+ if (table2.rows === 0 || table2.cols === 0) return null;
18024
+ return { type: "table", table: table2, pageNumber: page };
18025
+ }
18026
+ async function pictureBlock(pic, ctx) {
18027
+ const blip = findPath(pic, "blipFill", "blip");
18028
+ const embed = blip ? getAttr2(blip, "embed") : null;
18029
+ const rel = embed ? ctx.rels.get(embed) : void 0;
18030
+ if (!rel || !rel.type.endsWith(REL_IMAGE)) return null;
18031
+ let filename = ctx.imageNames.get(rel.target);
18032
+ if (!filename) {
18033
+ const file = ctx.zip.file(rel.target);
18034
+ if (!file) return null;
18035
+ const ext = partExtension(rel.target);
18036
+ filename = `image_${String(ctx.imageNames.size + 1).padStart(3, "0")}.${ext}`;
18037
+ try {
18038
+ if (ctx.options?.images !== false) {
18039
+ const data = await file.async("uint8array");
18040
+ ctx.images.push({ filename, data, mimeType: IMAGE_MIME[ext] ?? detectImageMime(data) ?? "application/octet-stream", source: rel.target });
18041
+ }
18042
+ } catch (err) {
18043
+ ctx.warnings.push({
18044
+ page: ctx.page,
18045
+ code: "SKIPPED_IMAGE",
18046
+ message: `PPTX \uC774\uBBF8\uC9C0 \uCD94\uCD9C \uC2E4\uD328 (${rel.target}): ${err instanceof Error ? err.message : String(err)}`
18047
+ });
18048
+ return null;
18049
+ }
18050
+ ctx.imageNames.set(rel.target, filename);
18051
+ }
18052
+ return { type: "image", text: filename, pageNumber: ctx.page };
18053
+ }
18054
+ async function walkShapes(tree, ctx, out, depth = 0) {
18055
+ if (depth > MAX_XML_DEPTH) return;
18056
+ for (const el of elementChildren(tree)) {
18057
+ switch (localName(el)) {
18058
+ case "grpSp":
18059
+ await walkShapes(el, ctx, out, depth + 1);
18060
+ break;
18061
+ case "AlternateContent": {
18062
+ const choice = findChildByLocalName(el, "Choice");
18063
+ if (choice) await walkShapes(choice, ctx, out, depth + 1);
18064
+ break;
18065
+ }
18066
+ case "sp": {
18067
+ const ph = placeholderType(el);
18068
+ const txBody = findChildByLocalName(el, "txBody");
18069
+ if (ph && SKIP_PLACEHOLDERS.has(ph) || !txBody) break;
18070
+ const paras = bodyParagraphs(txBody);
18071
+ if (paras.length === 0) break;
18072
+ if (ph === "title" || ph === "ctrTitle") {
18073
+ out.push({ type: "heading", level: ph === "ctrTitle" ? 1 : 2, text: paras.map((p) => p.text).join(" "), pageNumber: ctx.page });
18074
+ } else {
18075
+ pushParagraphs(paras, ph !== null && BULLET_PLACEHOLDERS.has(ph), ctx.page, out);
18076
+ }
18077
+ break;
18078
+ }
18079
+ case "graphicFrame": {
18080
+ const data = findPath(el, "graphic", "graphicData");
18081
+ const tbl2 = data ? findChildByLocalName(data, "tbl") : null;
18082
+ if (tbl2) {
18083
+ const block = parseTable2(tbl2, ctx.page, ctx.options?.keepTrailingEmptyCols);
18084
+ if (block) out.push(block);
18085
+ } else if (data) {
18086
+ const uri = getAttr2(data, "uri") ?? "";
18087
+ const kind = /chart/i.test(uri) ? "\uCC28\uD2B8" : /diagram/i.test(uri) ? "SmartArt" : "\uAC1C\uCCB4";
18088
+ ctx.warnings.push({ page: ctx.page, code: "UNSUPPORTED_ELEMENT", message: `PPTX ${kind}\uC758 \uAE00\uC740 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (${ctx.page}\uCABD)` });
18089
+ }
18090
+ break;
18091
+ }
18092
+ case "pic": {
18093
+ const block = await pictureBlock(el, ctx);
18094
+ if (block) out.push(block);
18095
+ break;
18096
+ }
18097
+ }
18098
+ }
18099
+ }
18100
+ async function notesText(zip, rels) {
18101
+ const rel = [...rels.values()].find((r) => r.type.endsWith(REL_NOTES));
18102
+ const file = rel ? zip.file(rel.target) : null;
18103
+ if (!file) return "";
18104
+ const tree = findPath(parseXml3(await file.async("text")).documentElement, "cSld", "spTree");
18105
+ const texts = [];
18106
+ for (const sp of tree ? childrenByLocalName(tree, "sp") : []) {
18107
+ const txBody = findChildByLocalName(sp, "txBody");
18108
+ if (placeholderType(sp) === "body" && txBody) texts.push(...bodyParagraphs(txBody).map((p) => p.text));
18109
+ }
18110
+ return texts.join(" ");
18111
+ }
18112
+ async function parsePptxDocument(buffer, options) {
18113
+ precheckZipSize(buffer, MAX_DECOMPRESS_SIZE5, MAX_ZIP_ENTRIES2, { re: MEDIA_PART_RE2, skip: options?.images === false, never: NEVER_READ_RE2 });
18114
+ const zip = await JSZip5.loadAsync(buffer);
18115
+ if (!zip.file("ppt/presentation.xml")) {
18116
+ throw new KordocError("\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 PPTX \uD30C\uC77C: ppt/presentation.xml\uC774 \uC5C6\uC2B5\uB2C8\uB2E4");
18117
+ }
18118
+ const warnings = [];
18119
+ const blocks = [];
18120
+ const imageNames = /* @__PURE__ */ new Map();
18121
+ const images = [];
18122
+ const slides = await slideParts(zip);
18123
+ const pageFilter = options?.pages ? parsePageRange(options.pages, slides.length) : null;
18124
+ for (let i = 0; i < slides.length; i++) {
18125
+ const page = i + 1;
18126
+ if (pageFilter && !pageFilter.has(page)) continue;
18127
+ const root = parseXml3(await zip.file(slides[i]).async("text")).documentElement;
18128
+ if (getAttr2(root, "show") === "0") {
18129
+ warnings.push({ page, code: "HIDDEN_TEXT_FILTERED", message: `PPTX \uC228\uAE34 \uC2AC\uB77C\uC774\uB4DC ${page}\uCABD\uC744 \uAC74\uB108\uB701\uB2C8\uB2E4` });
18130
+ continue;
18131
+ }
18132
+ const rels = await readRels(zip, slides[i]);
18133
+ const tree = findPath(root, "cSld", "spTree");
18134
+ if (tree) await walkShapes(tree, { zip, rels, page, warnings, options, imageNames, images }, blocks);
18135
+ const notes = await notesText(zip, rels);
18136
+ if (notes) blocks.push({ type: "paragraph", text: `\uBC1C\uD45C\uC790 \uB178\uD2B8: ${notes}`, quote: true, pageNumber: page });
18137
+ }
18138
+ const metadata = { pageCount: slides.length, pageMode: "layout" };
18139
+ const coreFile = zip.file("docProps/core.xml");
18140
+ if (coreFile) {
18141
+ try {
18142
+ const coreDoc = parseXml3(await coreFile.async("text"));
18143
+ const getFirst = (tag) => {
18144
+ const els = coreDoc.getElementsByTagName(tag);
18145
+ return els.length > 0 ? (els[0].textContent ?? "").trim() || void 0 : void 0;
18146
+ };
18147
+ metadata.title = getFirst("dc:title") || getFirst("dcterms:title");
18148
+ metadata.author = getFirst("dc:creator");
18149
+ metadata.description = getFirst("dc:description");
18150
+ const created = getFirst("dcterms:created");
18151
+ if (created) metadata.createdAt = created;
18152
+ const modified = getFirst("dcterms:modified");
18153
+ if (modified) metadata.modifiedAt = modified;
18154
+ } catch (err) {
18155
+ warnings.push({
18156
+ code: "PARTIAL_PARSE",
18157
+ message: `PPTX \uBA54\uD0C0\uB370\uC774\uD130(core.xml) \uD30C\uC2F1 \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}`
18158
+ });
18159
+ }
18160
+ }
18161
+ const outline = blocks.filter((b) => b.type === "heading").map((b) => ({ level: b.level ?? 2, text: b.text ?? "", pageNumber: b.pageNumber }));
18162
+ return {
18163
+ markdown: blocksToMarkdown(blocks),
18164
+ blocks,
18165
+ metadata,
18166
+ outline: outline.length > 0 ? outline : void 0,
18167
+ warnings: warnings.length > 0 ? warnings : void 0,
18168
+ images: images.length > 0 ? images : void 0
18169
+ };
18170
+ }
18171
+
18172
+ // src/hwpml/parser.ts
18173
+ import { DOMParser as DOMParser4 } from "@xmldom/xmldom";
17829
18174
  var MAX_TABLE_ROWS = 5e3;
17830
18175
  var MAX_TABLE_COLS = 500;
17831
18176
  var MAX_HWPML_BYTES = 50 * 1024 * 1024;
@@ -17837,7 +18182,7 @@ function parseHwpmlDocument(buffer, options) {
17837
18182
  const normalized = text.replace(/&nbsp;/g, "&#160;");
17838
18183
  const xml = stripDtd(normalized);
17839
18184
  const warnings = [];
17840
- const parser = new DOMParser3({
18185
+ const parser = new DOMParser4({
17841
18186
  onError: (_level, msg2) => {
17842
18187
  warnings.push({ message: `HWPML XML \uD30C\uC2F1 \uACBD\uACE0: ${msg2}`, code: "MALFORMED_XML" });
17843
18188
  }
@@ -17930,7 +18275,7 @@ function walkContent(node, blocks, paraShapeMap, sectionNum, warnings, inHeaderF
17930
18275
  }
17931
18276
  if (tag === "TABLE") {
17932
18277
  if (!inHeaderFooter) {
17933
- parseTable2(el, blocks, paraShapeMap, sectionNum, warnings, keep);
18278
+ parseTable3(el, blocks, paraShapeMap, sectionNum, warnings, keep);
17934
18279
  }
17935
18280
  continue;
17936
18281
  }
@@ -17949,7 +18294,7 @@ function walkTablesInP(node, blocks, paraShapeMap, sectionNum, warnings, keep, d
17949
18294
  if (el.nodeType !== 1) continue;
17950
18295
  const tag = localName(el);
17951
18296
  if (tag === "TABLE") {
17952
- parseTable2(el, blocks, paraShapeMap, sectionNum, warnings, keep);
18297
+ parseTable3(el, blocks, paraShapeMap, sectionNum, warnings, keep);
17953
18298
  continue;
17954
18299
  }
17955
18300
  if (tag === "FOOTNOTE" || tag === "ENDNOTE" || tag === "HEADER" || tag === "FOOTER") continue;
@@ -17982,7 +18327,7 @@ function collectCharText(node, parts, depth = 0) {
17982
18327
  if (tag === "CHAR") {
17983
18328
  for (const c of Array.from(el.childNodes)) {
17984
18329
  const t = c.nodeType === 1 ? localName(c) === "TAB" ? " " : rawTextContent(c) : c.nodeType === 3 || c.nodeType === 4 ? c.nodeValue ?? "" : "";
17985
- if (t) parts.push(t);
18330
+ if (t) parts.push(escapeLiteralTags(t));
17986
18331
  }
17987
18332
  } else if (tag === "TABLE" || tag === "PICTURE" || tag === "SHAPEOBJECT") {
17988
18333
  } else if (tag === "AUTONUM") {
@@ -17991,7 +18336,7 @@ function collectCharText(node, parts, depth = 0) {
17991
18336
  }
17992
18337
  }
17993
18338
  }
17994
- function parseTable2(el, blocks, _paraShapeMap, sectionNum, warnings, keep) {
18339
+ function parseTable3(el, blocks, _paraShapeMap, sectionNum, warnings, keep) {
17995
18340
  const table2 = buildHmlTable(el, warnings, keep, 0);
17996
18341
  if (!table2) return;
17997
18342
  const caption = extractShapeCaption(el);
@@ -18933,10 +19278,10 @@ function fillInlineFields(text, values, filled, matchedLabels, blockedLabels) {
18933
19278
  }
18934
19279
 
18935
19280
  // src/form/filler-hwpx.ts
18936
- import JSZip6 from "jszip";
19281
+ import JSZip7 from "jszip";
18937
19282
 
18938
19283
  // src/form/click-here.ts
18939
- import JSZip5 from "jszip";
19284
+ import JSZip6 from "jszip";
18940
19285
  var FIELD_BEGIN_RE = /<([A-Za-z0-9_]+:)?fieldBegin\b([^>]*?)(\/?)>/g;
18941
19286
  function attrOf2(attrs, name) {
18942
19287
  const m = attrs.match(new RegExp(`(?:^|\\s)${name}="([^"]*)"`));
@@ -19052,7 +19397,7 @@ function fillClickHereInXml(xml, cursor, blockedLabels) {
19052
19397
  }
19053
19398
  async function extractClickHereFields(hwpxBuffer) {
19054
19399
  precheckZipSize(hwpxBuffer);
19055
- const zip = await JSZip5.loadAsync(hwpxBuffer);
19400
+ const zip = await JSZip6.loadAsync(hwpxBuffer);
19056
19401
  const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
19057
19402
  const fields = [];
19058
19403
  for (let si = 0; si < sectionPaths.length; si++) {
@@ -19073,7 +19418,7 @@ async function extractClickHereFields(hwpxBuffer) {
19073
19418
  async function fillHwpx(hwpxBuffer, values, blockedLabels) {
19074
19419
  const u8 = new Uint8Array(hwpxBuffer);
19075
19420
  precheckZipSize(hwpxBuffer);
19076
- const zip = await JSZip6.loadAsync(hwpxBuffer);
19421
+ const zip = await JSZip7.loadAsync(hwpxBuffer);
19077
19422
  const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
19078
19423
  if (sectionPaths.length === 0) {
19079
19424
  throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
@@ -19532,7 +19877,7 @@ function chooseTableRepresentation(table2, opts = {}) {
19532
19877
  }
19533
19878
 
19534
19879
  // src/hwpx/generator.ts
19535
- import JSZip7 from "jszip";
19880
+ import JSZip8 from "jszip";
19536
19881
 
19537
19882
  // src/hwpx/geometry.ts
19538
19883
  var A4_W_HU = 59528;
@@ -22363,7 +22708,7 @@ function sanitizeText(text) {
22363
22708
  return result;
22364
22709
  }
22365
22710
  function normForMatch(text) {
22366
- return sanitizeText(text.replace(/\\\$/g, "$")).replace(/\s+/g, " ").trim();
22711
+ return sanitizeText(text.replace(/\\\$/g, "$").replace(/\\(?=<\/?(?:u|sup|sub)>)/g, "")).replace(/\s+/g, " ").trim();
22367
22712
  }
22368
22713
  function unescapeGfm(text) {
22369
22714
  return text.replace(/\\([~*_`|#<$\\])/g, "$1");
@@ -24204,7 +24549,8 @@ function renderListItem(block, blockIdx, ctx) {
24204
24549
  ctx.prevWasOrdered = false;
24205
24550
  }
24206
24551
  const indentPrefix = " ".repeat(indent);
24207
- return generateParagraph(indentPrefix + marker + (block.text || ""), PARA_LIST);
24552
+ const markerRun = `<hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t>${escapeTextXml(indentPrefix + marker)}</hp:t></hp:run>`;
24553
+ return `<hp:p paraPrIDRef="${PARA_LIST}" styleIDRef="0">${markerRun}${generateRuns(block.text || "")}</hp:p>`;
24208
24554
  }
24209
24555
  function renderHr(ctx) {
24210
24556
  return ctx.gongmun ? `<hp:p paraPrIDRef="${PARA_NORMAL}" styleIDRef="0"><hp:run charPrIDRef="${CHAR_NORMAL}"><hp:t></hp:t></hp:run></hp:p>` : `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0"><hp:t>\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500</hp:t></hp:run></hp:p>`;
@@ -25712,7 +26058,7 @@ async function markdownToHwpx(markdown, options) {
25712
26058
  if (remap2 && remap2.tables.length > 0 && remap2.tables.every((t) => !t.used)) {
25713
26059
  console.warn(`[kordoc] format profile: \uD504\uB85C\uD544 \uD45C ${remap2.tables.length}\uAC1C\uAC00 \uBB38\uC11C \uD45C\uC640 \uB9E4\uCE6D\uB418\uC9C0 \uC54A\uC544 \uBBF8\uC801\uC6A9 (\uD589\xB7\uC5F4/\uCCAB \uC140 \uD14D\uC2A4\uD2B8 \uBD88\uC77C\uCE58)`);
25714
26060
  }
25715
- const zip2 = new JSZip7();
26061
+ const zip2 = new JSZip8();
25716
26062
  zip2.file("mimetype", "application/hwp+zip", { compression: "STORE" });
25717
26063
  zip2.file("META-INF/container.xml", generateContainerXml());
25718
26064
  zip2.file("Contents/content.hpf", generateManifest(chartParts2, images2.manifestItems(), "gongmun"));
@@ -25759,7 +26105,7 @@ async function markdownToHwpx(markdown, options) {
25759
26105
  }
25760
26106
  }
25761
26107
  const extraBorderFills = [...remap?.borderFillXmls ?? [], ...bfReg?.emit() ?? []];
25762
- const zip = new JSZip7();
26108
+ const zip = new JSZip8();
25763
26109
  zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
25764
26110
  zip.file("META-INF/container.xml", generateContainerXml());
25765
26111
  zip.file("Contents/content.hpf", generateManifest(chartParts, images.manifestItems(), gongmun ? "gongmun" : "default"));
@@ -25979,7 +26325,7 @@ function readBuiltinTemplateSample(t) {
25979
26325
  }
25980
26326
 
25981
26327
  // src/hwpx/extract-profile.ts
25982
- import JSZip8 from "jszip";
26328
+ import JSZip9 from "jszip";
25983
26329
  function elemsByLocal(root, name) {
25984
26330
  const all = root.getElementsByTagName("*");
25985
26331
  const out = [];
@@ -26066,7 +26412,7 @@ function num(s) {
26066
26412
  const n = parseInt(s, 10);
26067
26413
  return Number.isFinite(n) ? n : void 0;
26068
26414
  }
26069
- function parseTable3(tbl2, tableIndex, borderFills, charPrs) {
26415
+ function parseTable4(tbl2, tableIndex, borderFills, charPrs) {
26070
26416
  const rows = num(tbl2.getAttribute("rowCnt")) ?? 0;
26071
26417
  const cols = num(tbl2.getAttribute("colCnt")) ?? 0;
26072
26418
  const sz = findChildByLocalName(tbl2, "sz");
@@ -26182,7 +26528,7 @@ function pick(map, keys) {
26182
26528
  }
26183
26529
  async function hwpxToProfile(input) {
26184
26530
  const buf = input instanceof ArrayBuffer ? input : toArrayBuffer(input);
26185
- const zip = await JSZip8.loadAsync(buf);
26531
+ const zip = await JSZip9.loadAsync(buf);
26186
26532
  const parser = createXmlParser();
26187
26533
  const headerFile = zip.file("Contents/header.xml") ?? zip.file(/[Hh]eader\.xml$/)?.[0];
26188
26534
  let headerXml = "<root/>";
@@ -26197,7 +26543,7 @@ async function hwpxToProfile(input) {
26197
26543
  const doc = parser.parseFromString(await f.async("text"), "text/xml");
26198
26544
  for (const tbl2 of elemsByLocal(doc, "tbl")) {
26199
26545
  if (!isTopLevelTable(tbl2)) continue;
26200
- tables.push(parseTable3(tbl2, tableIndex++, borderFills, charPrs));
26546
+ tables.push(parseTable4(tbl2, tableIndex++, borderFills, charPrs));
26201
26547
  }
26202
26548
  }
26203
26549
  return { schema_version: "0.3.0", tables };
@@ -26899,7 +27245,7 @@ function muncheLintWarnings(text, limit = 10) {
26899
27245
  }
26900
27246
 
26901
27247
  // src/roundtrip/patcher.ts
26902
- import JSZip9 from "jszip";
27248
+ import JSZip10 from "jszip";
26903
27249
 
26904
27250
  // src/roundtrip/table-rows.ts
26905
27251
  var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
@@ -28765,13 +29111,17 @@ async function patchHwpx(original, editedMarkdown, options) {
28765
29111
  const parsed = await parseHwpxDocument(u8ToArrayBuffer(original), { layoutTables: "keep" });
28766
29112
  origBlocks = parsed.blocks;
28767
29113
  const visual = await parseHwpxDocument(u8ToArrayBuffer(original));
29114
+ if (parsed.markdown !== visual.markdown && editedMarkdown === visual.markdown) {
29115
+ const verification2 = options?.verify === false ? void 0 : diffUnitLists(splitMarkdownUnits(visual.markdown), splitMarkdownUnits(editedMarkdown));
29116
+ return { success: true, data: new Uint8Array(original), applied: 0, skipped, verification: verification2 };
29117
+ }
28768
29118
  if (editedFromVisual(parsed.markdown, visual.markdown, editedMarkdown)) return { success: false, applied: 0, skipped, error: LAYOUT_MODE_MISMATCH };
28769
29119
  } catch (err) {
28770
29120
  return { success: false, applied: 0, skipped, error: `\uC6D0\uBCF8 HWPX \uD30C\uC2F1 \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}` };
28771
29121
  }
28772
29122
  let zip;
28773
29123
  try {
28774
- zip = await JSZip9.loadAsync(original);
29124
+ zip = await JSZip10.loadAsync(original);
28775
29125
  } catch {
28776
29126
  return { success: false, applied: 0, skipped, error: "ZIP \uB85C\uB4DC \uC2E4\uD328" };
28777
29127
  }
@@ -29216,8 +29566,8 @@ async function resolveHeaderEntryName(zip) {
29216
29566
  }
29217
29567
 
29218
29568
  // src/validate.ts
29219
- import JSZip10 from "jszip";
29220
- import { DOMParser as DOMParser4 } from "@xmldom/xmldom";
29569
+ import JSZip11 from "jszip";
29570
+ import { DOMParser as DOMParser5 } from "@xmldom/xmldom";
29221
29571
  var REQUIRED_FILES = [
29222
29572
  "mimetype",
29223
29573
  "META-INF/container.xml",
@@ -29234,7 +29584,7 @@ async function validateHwpx(buffer) {
29234
29584
  const issues = [];
29235
29585
  let zip;
29236
29586
  try {
29237
- zip = await JSZip10.loadAsync(buffer);
29587
+ zip = await JSZip11.loadAsync(buffer);
29238
29588
  } catch (err) {
29239
29589
  return {
29240
29590
  ok: false,
@@ -29263,7 +29613,7 @@ async function validateHwpx(buffer) {
29263
29613
  const text = await zip.files[name].async("string");
29264
29614
  let firstError = null;
29265
29615
  try {
29266
- new DOMParser4({
29616
+ new DOMParser5({
29267
29617
  onError(level, msg2) {
29268
29618
  if (level !== "warning" && firstError === null) firstError = String(msg2);
29269
29619
  }
@@ -29302,12 +29652,12 @@ async function validateHwpx(buffer) {
29302
29652
  }
29303
29653
 
29304
29654
  // src/roundtrip/session.ts
29305
- import JSZip11 from "jszip";
29655
+ import JSZip12 from "jszip";
29306
29656
  async function buildState(bytes) {
29307
29657
  const format2 = detectFormat(new Uint8Array(bytes.subarray(0, 512)).buffer);
29308
29658
  if (format2 !== "hwpx") throw new Error(`HWPX \uC138\uC158\uC740 HWPX \uBB38\uC11C\uB9CC \uC5FD\uB2C8\uB2E4 (\uAC10\uC9C0\uB41C \uD3EC\uB9F7: ${format2}) \u2014 HWP 5.x \uB294 patchHwp \uB85C \uD328\uCE58\uD558\uC138\uC694`);
29309
29659
  const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes), { layoutTables: "keep" });
29310
- const zip = await JSZip11.loadAsync(bytes);
29660
+ const zip = await JSZip12.loadAsync(bytes);
29311
29661
  const sectionPaths = await resolveSectionEntryNames(zip);
29312
29662
  if (sectionPaths.length === 0) {
29313
29663
  throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
@@ -29681,9 +30031,12 @@ async function parse(input, options) {
29681
30031
  }
29682
30032
  if (result.success && opts?.classifyTables) classifyTableTree(result.blocks);
29683
30033
  let out = result;
29684
- if (result.success && !result.pages) {
29685
- const pages = blocksToPages(result.blocks);
29686
- if (pages) out = { ...result, pages };
30034
+ if (result.success && opts?.pages && result.pageCount && parsePageRange(opts.pages, result.pageCount).size === 0) {
30035
+ out = { ...result, warnings: [...result.warnings ?? [], { code: "PARTIAL_PARSE", message: `\uC694\uCCAD\uD55C \uCABD \uBC94\uC704(${opts.pages})\uAC00 \uBB38\uC11C \uCABD(1~${result.pageCount})\uACFC \uACB9\uCE58\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4` }] };
30036
+ }
30037
+ if (out.success && !out.pages) {
30038
+ const pages = blocksToPages(out.blocks);
30039
+ if (pages) out = { ...out, pages };
29687
30040
  }
29688
30041
  if (out.success && opts?.plain) {
29689
30042
  out = { ...out, markdown: toPlainMarkdown(out.markdown), ...out.pages ? { pages: out.pages.map((p) => ({ ...p, markdown: toPlainMarkdown(p.markdown) })) } : {} };
@@ -29710,9 +30063,7 @@ async function dispatch(format2, buffer, opts) {
29710
30063
  const zipFormat = await detectZipFormat(buffer);
29711
30064
  if (zipFormat === "xlsx") return parseXlsx(buffer, opts);
29712
30065
  if (zipFormat === "docx") return parseDocx(buffer, opts);
29713
- if (zipFormat === "pptx") {
29714
- return { success: false, fileType: "pptx", error: "PPTX \uD30C\uC77C\uC740 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4.", code: "UNSUPPORTED_FORMAT" };
29715
- }
30066
+ if (zipFormat === "pptx") return parsePptx(buffer, opts);
29716
30067
  return parseHwpx(buffer, opts);
29717
30068
  }
29718
30069
  case "hwp": {
@@ -29737,7 +30088,7 @@ function scriptsOff(r, off) {
29737
30088
  }
29738
30089
  async function parseImage(buffer, options) {
29739
30090
  try {
29740
- const { parseImageDocument } = await import("./image-ocr-XB2NFKGZ.js");
30091
+ const { parseImageDocument } = await import("./image-ocr-TWNNVTZS.js");
29741
30092
  const { blocks, warnings } = await parseImageDocument(buffer, options);
29742
30093
  stripScriptTags({ blocks });
29743
30094
  return {
@@ -29797,7 +30148,7 @@ async function parseHwp(buffer, options) {
29797
30148
  async function parsePdf(buffer, options) {
29798
30149
  let parsePdfDocument;
29799
30150
  try {
29800
- const mod = await import("./parser-4D2M5BKY.js");
30151
+ const mod = await import("./parser-NKP2NCSV.js");
29801
30152
  parsePdfDocument = mod.parsePdfDocument;
29802
30153
  } catch {
29803
30154
  return {
@@ -29839,6 +30190,14 @@ async function parseDocx(buffer, options) {
29839
30190
  return { success: false, fileType: "docx", error: sanitizeError(err), code: classifyError(err) };
29840
30191
  }
29841
30192
  }
30193
+ async function parsePptx(buffer, options) {
30194
+ try {
30195
+ const { markdown, blocks, metadata, outline, warnings, images } = await parsePptxDocument(buffer, options);
30196
+ return scriptsOff({ success: true, fileType: "pptx", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0, pageCount: metadata?.pageCount }, options?.scriptTags === false);
30197
+ } catch (err) {
30198
+ return { success: false, fileType: "pptx", error: sanitizeError(err), code: classifyError(err) };
30199
+ }
30200
+ }
29842
30201
  async function parseHwpml(buffer, options) {
29843
30202
  try {
29844
30203
  const { markdown, blocks, metadata, outline, warnings } = parseHwpmlDocument(buffer, options);
@@ -29954,6 +30313,8 @@ async function extractTables(input, options = {}) {
29954
30313
  }
29955
30314
 
29956
30315
  export {
30316
+ toPlainMarkdown,
30317
+ toHtmlTables,
29957
30318
  extractHwpxMetadataOnly,
29958
30319
  formatFillValue,
29959
30320
  ValueCursor,
@@ -30006,7 +30367,8 @@ export {
30006
30367
  parseXlsx,
30007
30368
  parseXls,
30008
30369
  parseDocx,
30370
+ parsePptx,
30009
30371
  parseHwpml,
30010
30372
  fillForm
30011
30373
  };
30012
- //# sourceMappingURL=chunk-UTVMLJZE.js.map
30374
+ //# sourceMappingURL=chunk-2I2RTJJ5.js.map