kordoc 4.7.2 → 4.7.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 (131) hide show
  1. package/README.md +5 -1
  2. package/dist/{-CVPGGSAB.js → -WYNJOQ6P.js} +9 -9
  3. package/dist/{chunk-GLZMUKYL.js → chunk-3NKSSL44.js} +3 -3
  4. package/dist/{chunk-O4VLHQVG.js → chunk-3P5YCJQE.js} +2 -2
  5. package/dist/{chunk-JMWBMGNH.js → chunk-46F5RMU7.js} +2 -2
  6. package/dist/{chunk-QKHN7LEV.js → chunk-4HZN7EZZ.js} +2 -2
  7. package/dist/{chunk-4VNUQYUE.js → chunk-4KGGVCYX.js} +1 -1
  8. package/dist/chunk-4KGGVCYX.js.map +1 -0
  9. package/dist/{chunk-BSWJCSG7.js → chunk-CGJDCZA3.js} +2 -2
  10. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  11. package/dist/{chunk-DZBMQC2N.cjs → chunk-E6DONLTM.cjs} +2 -2
  12. package/dist/chunk-E6DONLTM.cjs.map +1 -0
  13. package/dist/{chunk-URTDBXV6.cjs → chunk-EV53YE3W.cjs} +1 -1
  14. package/dist/chunk-EV53YE3W.cjs.map +1 -0
  15. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  16. package/dist/{chunk-Y3VDFFAW.js → chunk-HJV4BVQI.js} +2 -2
  17. package/dist/{chunk-VXB52EWX.js → chunk-IMFIACHP.js} +2 -2
  18. package/dist/{chunk-IVSEGHI2.js → chunk-JKJEBDP7.js} +3 -3
  19. package/dist/{chunk-E2XTVK7H.cjs → chunk-KE6DLZAW.cjs} +11 -11
  20. package/dist/chunk-KE6DLZAW.cjs.map +1 -0
  21. package/dist/{chunk-P7HPLWEP.js → chunk-KEGAEHWO.js} +2 -2
  22. package/dist/{chunk-I4TP7HYB.js → chunk-KSATSTC5.js} +1 -1
  23. package/dist/chunk-KSATSTC5.js.map +1 -0
  24. package/dist/{chunk-7CWNBDWQ.js → chunk-LMHVMGEC.js} +3 -3
  25. package/dist/{chunk-IN2AOMX6.js → chunk-NYLYIN55.js} +3 -3
  26. package/dist/{chunk-PGDJUJ4L.js → chunk-P663PYWV.js} +2 -2
  27. package/dist/{chunk-PV6WSN6E.cjs → chunk-QETZP25M.cjs} +3 -3
  28. package/dist/chunk-QETZP25M.cjs.map +1 -0
  29. package/dist/{chunk-YU3PLOX4.cjs → chunk-QGATKC4R.cjs} +6 -6
  30. package/dist/chunk-QGATKC4R.cjs.map +1 -0
  31. package/dist/{chunk-46VU6YXF.cjs → chunk-R4LXZRYP.cjs} +4 -4
  32. package/dist/chunk-R4LXZRYP.cjs.map +1 -0
  33. package/dist/{chunk-TYIFZT5H.js → chunk-RQVFFSAO.js} +2 -2
  34. package/dist/{chunk-IMY4LQ3J.js → chunk-TXQQ7URG.js} +2 -2
  35. package/dist/{chunk-LJW5EQXS.js → chunk-VZ3AQVBN.js} +8 -3
  36. package/dist/chunk-VZ3AQVBN.js.map +1 -0
  37. package/dist/{chunk-QXC5NPO2.js → chunk-W5NZYPRQ.js} +386 -80
  38. package/dist/chunk-W5NZYPRQ.js.map +1 -0
  39. package/dist/{chunk-FJBOVJAG.js → chunk-XOO6YEZE.js} +3 -3
  40. package/dist/{chunk-VEBTOI3I.js → chunk-Y7T5Z4DC.js} +3 -3
  41. package/dist/chunks-LWAOHTK2.js +10 -0
  42. package/dist/cli.js +22 -22
  43. package/dist/cli.js.map +1 -1
  44. package/dist/{formula-RTGCS55B.js → formula-IFKZZPD2.js} +4 -4
  45. package/dist/{formula-7D3MLOSV.cjs → formula-PDMZMBIP.cjs} +9 -9
  46. package/dist/formula-PDMZMBIP.cjs.map +1 -0
  47. package/dist/{formula-GHGK7T3S.js → formula-V6EAYHMF.js} +3 -3
  48. package/dist/{image-ocr-PRI2JKDW.cjs → image-ocr-75WGMVU6.cjs} +12 -12
  49. package/dist/image-ocr-75WGMVU6.cjs.map +1 -0
  50. package/dist/{image-ocr-2UMWFGRS.js → image-ocr-B7TMIG3F.js} +8 -8
  51. package/dist/{image-ocr-TQ5THN35.js → image-ocr-XHBZONFZ.js} +6 -6
  52. package/dist/index.cjs +864 -553
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.d.cts +10 -2
  55. package/dist/index.d.ts +10 -2
  56. package/dist/index.js +388 -77
  57. package/dist/index.js.map +1 -1
  58. package/dist/mcp.js +19 -18
  59. package/dist/mcp.js.map +1 -1
  60. package/dist/{model-bundle-5RLZV7P2.js → model-bundle-Q4HQXJQG.js} +5 -5
  61. package/dist/{models-RSHR2BY7.js → models-QQA7OQHY.js} +5 -5
  62. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  63. package/dist/{parser-SCQ7NJUJ.cjs → parser-4CBOPTVY.cjs} +28 -28
  64. package/dist/parser-4CBOPTVY.cjs.map +1 -0
  65. package/dist/{parser-QPMTNRPU.js → parser-HT7GZOF2.js} +8 -8
  66. package/dist/{parser-FXEIK3VJ.js.map → parser-HT7GZOF2.js.map} +1 -1
  67. package/dist/{parser-FXEIK3VJ.js → parser-Z36MZUHS.js} +8 -8
  68. package/dist/{parser-QPMTNRPU.js.map → parser-Z36MZUHS.js.map} +1 -1
  69. package/dist/pdf-ocr-6ZNGDVC6.cjs +14 -0
  70. package/dist/pdf-ocr-6ZNGDVC6.cjs.map +1 -0
  71. package/dist/pdf-ocr-NTXRJI4T.js +13 -0
  72. package/dist/pdf-ocr-TYMCEUPH.js +16 -0
  73. package/dist/{profile-io-I4UGTNP6.js → profile-io-ACYRLCSR.js} +3 -3
  74. package/dist/{rasterize-QY3XJKCH.js → rasterize-QYCLEH2J.js} +2 -2
  75. package/dist/render-5Y2US4IM.js +10 -0
  76. package/dist/seal-VU6JS2QD.js +10 -0
  77. package/dist/{setup-T2BY3VME.js → setup-MG3YYKXU.js} +3 -3
  78. package/dist/{watch-JJE2UGVK.js → watch-AZIY6AWC.js} +10 -10
  79. package/package.json +1 -1
  80. package/dist/chunk-46VU6YXF.cjs.map +0 -1
  81. package/dist/chunk-4VNUQYUE.js.map +0 -1
  82. package/dist/chunk-DZBMQC2N.cjs.map +0 -1
  83. package/dist/chunk-E2XTVK7H.cjs.map +0 -1
  84. package/dist/chunk-I4TP7HYB.js.map +0 -1
  85. package/dist/chunk-LJW5EQXS.js.map +0 -1
  86. package/dist/chunk-PV6WSN6E.cjs.map +0 -1
  87. package/dist/chunk-QXC5NPO2.js.map +0 -1
  88. package/dist/chunk-URTDBXV6.cjs.map +0 -1
  89. package/dist/chunk-YU3PLOX4.cjs.map +0 -1
  90. package/dist/chunks-RNP6VLGK.js +0 -10
  91. package/dist/formula-7D3MLOSV.cjs.map +0 -1
  92. package/dist/image-ocr-PRI2JKDW.cjs.map +0 -1
  93. package/dist/parser-SCQ7NJUJ.cjs.map +0 -1
  94. package/dist/pdf-ocr-HPRM24SP.js +0 -16
  95. package/dist/pdf-ocr-I6DISTBR.js +0 -13
  96. package/dist/pdf-ocr-JNWL65YW.cjs +0 -14
  97. package/dist/pdf-ocr-JNWL65YW.cjs.map +0 -1
  98. package/dist/render-HEQX5DII.js +0 -10
  99. package/dist/seal-NB2YJ5F4.js +0 -10
  100. /package/dist/{-CVPGGSAB.js.map → -WYNJOQ6P.js.map} +0 -0
  101. /package/dist/{chunk-GLZMUKYL.js.map → chunk-3NKSSL44.js.map} +0 -0
  102. /package/dist/{chunk-O4VLHQVG.js.map → chunk-3P5YCJQE.js.map} +0 -0
  103. /package/dist/{chunk-JMWBMGNH.js.map → chunk-46F5RMU7.js.map} +0 -0
  104. /package/dist/{chunk-QKHN7LEV.js.map → chunk-4HZN7EZZ.js.map} +0 -0
  105. /package/dist/{chunk-BSWJCSG7.js.map → chunk-CGJDCZA3.js.map} +0 -0
  106. /package/dist/{chunk-Y3VDFFAW.js.map → chunk-HJV4BVQI.js.map} +0 -0
  107. /package/dist/{chunk-VXB52EWX.js.map → chunk-IMFIACHP.js.map} +0 -0
  108. /package/dist/{chunk-IVSEGHI2.js.map → chunk-JKJEBDP7.js.map} +0 -0
  109. /package/dist/{chunk-P7HPLWEP.js.map → chunk-KEGAEHWO.js.map} +0 -0
  110. /package/dist/{chunk-7CWNBDWQ.js.map → chunk-LMHVMGEC.js.map} +0 -0
  111. /package/dist/{chunk-IN2AOMX6.js.map → chunk-NYLYIN55.js.map} +0 -0
  112. /package/dist/{chunk-PGDJUJ4L.js.map → chunk-P663PYWV.js.map} +0 -0
  113. /package/dist/{chunk-TYIFZT5H.js.map → chunk-RQVFFSAO.js.map} +0 -0
  114. /package/dist/{chunk-IMY4LQ3J.js.map → chunk-TXQQ7URG.js.map} +0 -0
  115. /package/dist/{chunk-FJBOVJAG.js.map → chunk-XOO6YEZE.js.map} +0 -0
  116. /package/dist/{chunk-VEBTOI3I.js.map → chunk-Y7T5Z4DC.js.map} +0 -0
  117. /package/dist/{chunks-RNP6VLGK.js.map → chunks-LWAOHTK2.js.map} +0 -0
  118. /package/dist/{formula-RTGCS55B.js.map → formula-IFKZZPD2.js.map} +0 -0
  119. /package/dist/{formula-GHGK7T3S.js.map → formula-V6EAYHMF.js.map} +0 -0
  120. /package/dist/{image-ocr-2UMWFGRS.js.map → image-ocr-B7TMIG3F.js.map} +0 -0
  121. /package/dist/{image-ocr-TQ5THN35.js.map → image-ocr-XHBZONFZ.js.map} +0 -0
  122. /package/dist/{model-bundle-5RLZV7P2.js.map → model-bundle-Q4HQXJQG.js.map} +0 -0
  123. /package/dist/{models-RSHR2BY7.js.map → models-QQA7OQHY.js.map} +0 -0
  124. /package/dist/{pdf-ocr-HPRM24SP.js.map → pdf-ocr-NTXRJI4T.js.map} +0 -0
  125. /package/dist/{pdf-ocr-I6DISTBR.js.map → pdf-ocr-TYMCEUPH.js.map} +0 -0
  126. /package/dist/{profile-io-I4UGTNP6.js.map → profile-io-ACYRLCSR.js.map} +0 -0
  127. /package/dist/{rasterize-QY3XJKCH.js.map → rasterize-QYCLEH2J.js.map} +0 -0
  128. /package/dist/{render-HEQX5DII.js.map → render-5Y2US4IM.js.map} +0 -0
  129. /package/dist/{seal-NB2YJ5F4.js.map → seal-VU6JS2QD.js.map} +0 -0
  130. /package/dist/{setup-T2BY3VME.js.map → setup-MG3YYKXU.js.map} +0 -0
  131. /package/dist/{watch-JJE2UGVK.js.map → watch-AZIY6AWC.js.map} +0 -0
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  flattenLayoutTables,
9
9
  inlineImagesIntoMarkdown,
10
10
  mapPuaText
11
- } from "./chunk-PGDJUJ4L.js";
11
+ } from "./chunk-P663PYWV.js";
12
12
  import {
13
13
  parsePageRange
14
14
  } from "./chunk-GE43BE46.js";
@@ -16,7 +16,7 @@ import {
16
16
  HEADING_RATIO_H1,
17
17
  HEADING_RATIO_H2,
18
18
  HEADING_RATIO_H3
19
- } from "./chunk-I4TP7HYB.js";
19
+ } from "./chunk-KSATSTC5.js";
20
20
  import {
21
21
  KordocError,
22
22
  VERSION,
@@ -29,7 +29,7 @@ import {
29
29
  sanitizeHref,
30
30
  stripDtd,
31
31
  toArrayBuffer
32
- } from "./chunk-JMWBMGNH.js";
32
+ } from "./chunk-46F5RMU7.js";
33
33
 
34
34
  // src/index.ts
35
35
  import { readFile } from "fs/promises";
@@ -395,7 +395,7 @@ function comResultToParseResult(pages, pageCount, warnings) {
395
395
  }
396
396
  }
397
397
  const markdown = lines.join("\n\n");
398
- const metadata = { pageCount };
398
+ const metadata = { pageCount, pageMode: "layout" };
399
399
  warnings.push({
400
400
  message: "DRM \uBB38\uC11C: \uD55C\uCEF4 COM API\uB85C \uD14D\uC2A4\uD2B8 \uCD94\uCD9C (\uC11C\uC2DD/\uD45C \uC815\uBCF4 \uC81C\uD55C\uC801)",
401
401
  code: "DRM_COM_FALLBACK"
@@ -491,7 +491,12 @@ function clampSpan(val, max) {
491
491
  return Math.max(1, Math.min(val, max));
492
492
  }
493
493
  function createSectionShared() {
494
- return { numState: /* @__PURE__ */ new Map(), pageText: { headers: [], footers: [] }, track: { deleteDepth: 0, warned: false } };
494
+ return {
495
+ numState: /* @__PURE__ */ new Map(),
496
+ pageText: { headers: [], footers: [] },
497
+ track: { deleteDepth: 0, warned: false },
498
+ pageState: { base: 0, allUsable: true }
499
+ };
495
500
  }
496
501
  function createXmlParser(warnings) {
497
502
  return new DOMParser({
@@ -1482,11 +1487,11 @@ function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraph
1482
1487
  function completeTable(newTable, tableStack, blocks, ctx) {
1483
1488
  const parentTable = tableStack.length > 0 ? tableStack.pop() : null;
1484
1489
  if (newTable.rows.length === 0) {
1485
- if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.sectionNum });
1490
+ if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.page });
1486
1491
  return parentTable;
1487
1492
  }
1488
1493
  const ir = buildTableWithCellMeta(newTable, ctx.shared.keepTrailingEmptyCols, ctx.shared.keepEmptyParagraphs);
1489
- const block = { type: "table", table: ir, pageNumber: ctx.sectionNum };
1494
+ const block = { type: "table", table: ir, pageNumber: ctx.page };
1490
1495
  if (parentTable?.cell) {
1491
1496
  const cell2 = parentTable.cell;
1492
1497
  (cell2.blocks ??= []).push(block);
@@ -1504,6 +1509,133 @@ function completeTable(newTable, tableStack, blocks, ctx) {
1504
1509
  return parentTable;
1505
1510
  }
1506
1511
 
1512
+ // src/hwpx/page-boundary.ts
1513
+ var MIDPAGE_V = 2e3;
1514
+ var localName2 = (el) => {
1515
+ const n = el.nodeName;
1516
+ const i = n.indexOf(":");
1517
+ return i >= 0 ? n.slice(i + 1) : n;
1518
+ };
1519
+ var childElements = (parent) => {
1520
+ const out = [];
1521
+ for (let c = parent.firstChild; c; c = c.nextSibling) {
1522
+ if (c.nodeType === 1) out.push(c);
1523
+ }
1524
+ return out;
1525
+ };
1526
+ var numAttr = (el, name) => {
1527
+ const v = el.getAttribute(name);
1528
+ const n = v == null ? NaN : Number(v);
1529
+ return Number.isFinite(n) ? n : 0;
1530
+ };
1531
+ var findDescendant = (root, local, maxDepth = 8) => {
1532
+ if (maxDepth < 0) return null;
1533
+ for (const c of childElements(root)) {
1534
+ if (localName2(c) === local) return c;
1535
+ const found = findDescendant(c, local, maxDepth - 1);
1536
+ if (found) return found;
1537
+ }
1538
+ return null;
1539
+ };
1540
+ function linesegsOf(p) {
1541
+ const lsa = childElements(p).find((c) => localName2(c) === "linesegarray");
1542
+ return lsa ? childElements(lsa).filter((s) => localName2(s) === "lineseg") : [];
1543
+ }
1544
+ function cellFlowResets(subList) {
1545
+ let prevV = Number.NEGATIVE_INFINITY;
1546
+ let resets = 0;
1547
+ for (const p of childElements(subList)) {
1548
+ if (localName2(p) !== "p") continue;
1549
+ for (const s of linesegsOf(p)) {
1550
+ const v = numAttr(s, "vertpos");
1551
+ if (v < prevV) resets++;
1552
+ prevV = v;
1553
+ }
1554
+ }
1555
+ return resets;
1556
+ }
1557
+ function tableIntraBreaks(tbl2) {
1558
+ const rowMax = /* @__PURE__ */ new Map();
1559
+ for (const tr of childElements(tbl2)) {
1560
+ if (localName2(tr) !== "tr") continue;
1561
+ for (const tc2 of childElements(tr)) {
1562
+ if (localName2(tc2) !== "tc") continue;
1563
+ const addr = childElements(tc2).find((e) => localName2(e) === "cellAddr");
1564
+ const row = addr ? numAttr(addr, "rowAddr") : 0;
1565
+ const sub = childElements(tc2).find((e) => localName2(e) === "subList");
1566
+ if (!sub) continue;
1567
+ const r = cellFlowResets(sub);
1568
+ rowMax.set(row, Math.max(rowMax.get(row) ?? 0, r));
1569
+ }
1570
+ }
1571
+ let sum = 0;
1572
+ for (const v of rowMax.values()) sum += v;
1573
+ return sum;
1574
+ }
1575
+ function detectHwpxSectionPages(root) {
1576
+ const colPr = findDescendant(root, "colPr");
1577
+ const multiCol = colPr ? numAttr(colPr, "colCount") > 1 : false;
1578
+ const paraPage = /* @__PURE__ */ new Map();
1579
+ let cur = 0;
1580
+ let prevV = Number.NEGATIVE_INFINITY;
1581
+ let prevH = Number.NEGATIVE_INFINITY;
1582
+ let first = true;
1583
+ let suppressMidReset = false;
1584
+ let topParas = 0;
1585
+ let parasWithSegs = 0;
1586
+ for (const p of childElements(root)) {
1587
+ if (localName2(p) !== "p") continue;
1588
+ topParas++;
1589
+ const explicit = p.getAttribute("pageBreak") === "1";
1590
+ let brokeByExplicit = false;
1591
+ if (explicit && !first) {
1592
+ cur++;
1593
+ brokeByExplicit = true;
1594
+ suppressMidReset = false;
1595
+ }
1596
+ const segs = linesegsOf(p);
1597
+ if (segs.length > 0) parasWithSegs++;
1598
+ let paraFirst = true;
1599
+ let startPage = cur;
1600
+ for (const s of segs) {
1601
+ const v = numAttr(s, "vertpos");
1602
+ const h = numAttr(s, "horzpos");
1603
+ const brk = v < prevV ? !multiCol || h <= prevH : paraFirst && v === prevV && h <= prevH;
1604
+ if (brk && !(paraFirst && brokeByExplicit)) {
1605
+ if (paraFirst && suppressMidReset && v >= MIDPAGE_V) {
1606
+ } else {
1607
+ cur++;
1608
+ }
1609
+ }
1610
+ if (paraFirst) {
1611
+ suppressMidReset = false;
1612
+ startPage = cur;
1613
+ }
1614
+ paraFirst = false;
1615
+ prevV = v;
1616
+ prevH = h;
1617
+ }
1618
+ paraPage.set(p, startPage);
1619
+ first = false;
1620
+ let add = 0;
1621
+ for (const r of childElements(p)) {
1622
+ if (localName2(r) !== "run") continue;
1623
+ for (const t of childElements(r)) {
1624
+ if (localName2(t) === "tbl") add += tableIntraBreaks(t);
1625
+ }
1626
+ }
1627
+ if (add > 0) {
1628
+ cur += add;
1629
+ suppressMidReset = true;
1630
+ }
1631
+ }
1632
+ return {
1633
+ paraPage,
1634
+ pages: cur + 1,
1635
+ usable: topParas > 0 && parasWithSegs === topParas
1636
+ };
1637
+ }
1638
+
1507
1639
  // src/hwpx/section-walker.ts
1508
1640
  function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
1509
1641
  const parser = createXmlParser(warnings);
@@ -1511,6 +1643,13 @@ function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
1511
1643
  if (!doc.documentElement) return [];
1512
1644
  const ctx = { styleMap, warnings, sectionNum, shared: shared ?? createSectionShared() };
1513
1645
  ctx.shared.track.deleteDepth = 0;
1646
+ const detect = detectHwpxSectionPages(doc.documentElement);
1647
+ const ps = ctx.shared.pageState;
1648
+ if (!detect.usable) ps.allUsable = false;
1649
+ ctx.paraPage = detect.paraPage;
1650
+ ctx.pageBase = ps.base;
1651
+ ctx.page = ps.base + 1;
1652
+ ps.base += detect.pages;
1514
1653
  for (const tagName of ["hp:secPr", "secPr"]) {
1515
1654
  const els = doc.getElementsByTagName(tagName);
1516
1655
  if (els.length > 0) {
@@ -1557,7 +1696,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1557
1696
  const chan = kordocTableChannel(el, ctx);
1558
1697
  if (chan) {
1559
1698
  if (chan.kind === "heading" && chan.text) {
1560
- blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
1699
+ blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.page });
1561
1700
  }
1562
1701
  break;
1563
1702
  }
@@ -1576,7 +1715,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1576
1715
  tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + cap.text;
1577
1716
  if (cap.hasStructure) (tableCtx.captionBlocks ??= []).push(...cap.blocks);
1578
1717
  } else {
1579
- blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.sectionNum });
1718
+ blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.page });
1580
1719
  }
1581
1720
  }
1582
1721
  break;
@@ -1619,6 +1758,8 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1619
1758
  }
1620
1759
  break;
1621
1760
  case "p": {
1761
+ const paraPg = ctx.paraPage?.get(el);
1762
+ if (paraPg !== void 0 && ctx.pageBase !== void 0) ctx.page = ctx.pageBase + paraPg + 1;
1622
1763
  const { text: rawText, href, footnote, style, segments } = extractParagraphInfo(el, ctx.styleMap, ctx);
1623
1764
  let text = rawText;
1624
1765
  let headingLevel;
@@ -1640,7 +1781,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1640
1781
  const flush = () => {
1641
1782
  const s = segs[segIdx++];
1642
1783
  if (!s) return;
1643
- const block = { type: "paragraph", text: s, pageNumber: ctx.sectionNum };
1784
+ const block = { type: "paragraph", text: s, pageNumber: ctx.page };
1644
1785
  if (first && !cell2) {
1645
1786
  first = false;
1646
1787
  if (style) block.style = style;
@@ -1664,7 +1805,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1664
1805
  if (footnote) text += ` (\uC8FC: ${footnote})`;
1665
1806
  cell2.text += (cell2.text || cell2.paraSeen ? "\n" : "") + text;
1666
1807
  cell2.paraSeen = true;
1667
- const cellBlock = { type: "paragraph", text, pageNumber: ctx.sectionNum };
1808
+ const cellBlock = { type: "paragraph", text, pageNumber: ctx.page };
1668
1809
  const cellSpanMode = spanModeOf(ctx.shared.kordocLayout);
1669
1810
  if (cellSpanMode && !ph?.prefix) {
1670
1811
  const spans = extractRunSpans(el, ctx, cellSpanMode, cellSpanMode !== "kordoc");
@@ -1676,11 +1817,11 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1676
1817
  (cell2.blocks ??= []).push(cellBlock);
1677
1818
  } else if (!tableCtx) {
1678
1819
  if (ctx.shared.kordocLayout && /^─{10,}$/.test(text)) {
1679
- blocks.push({ type: "separator", pageNumber: ctx.sectionNum });
1820
+ blocks.push({ type: "separator", pageNumber: ctx.page });
1680
1821
  tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
1681
1822
  break;
1682
1823
  }
1683
- const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.sectionNum };
1824
+ const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.page };
1684
1825
  if (headingLevel) block.level = headingLevel;
1685
1826
  if (style) block.style = style;
1686
1827
  if (href) block.href = href;
@@ -1706,7 +1847,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1706
1847
  }
1707
1848
  blocks.push(block);
1708
1849
  } else {
1709
- blocks.push({ type: "paragraph", text, pageNumber: ctx.sectionNum });
1850
+ blocks.push({ type: "paragraph", text, pageNumber: ctx.page });
1710
1851
  }
1711
1852
  } else if (ctx.shared.keepEmptyParagraphs && !hasObjectDescendant(el)) {
1712
1853
  if (tableCtx?.cell) {
@@ -1714,9 +1855,9 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1714
1855
  if (cell2.text || cell2.paraSeen) cell2.text += "\n";
1715
1856
  cell2.paraSeen = true;
1716
1857
  cell2.lineOpen = false;
1717
- (cell2.blocks ??= []).push({ type: "paragraph", text: "", pageNumber: ctx.sectionNum });
1858
+ (cell2.blocks ??= []).push({ type: "paragraph", text: "", pageNumber: ctx.page });
1718
1859
  } else if (!tableCtx) {
1719
- blocks.push({ type: "paragraph", text: "", pageNumber: ctx.sectionNum });
1860
+ blocks.push({ type: "paragraph", text: "", pageNumber: ctx.page });
1720
1861
  }
1721
1862
  }
1722
1863
  tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
@@ -1739,7 +1880,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1739
1880
  case "memogroup":
1740
1881
  case "memo": {
1741
1882
  if (ctx.warnings && extractTextFromNode(el)) {
1742
- ctx.warnings.push({ page: ctx.sectionNum, message: "\uBA54\uBAA8 \uD14D\uC2A4\uD2B8 \uBCF8\uBB38 \uC81C\uC678: memogroup", code: "HIDDEN_TEXT_FILTERED" });
1883
+ ctx.warnings.push({ page: ctx.page, message: "\uBA54\uBAA8 \uD14D\uC2A4\uD2B8 \uBCF8\uBB38 \uC81C\uC678: memogroup", code: "HIDDEN_TEXT_FILTERED" });
1743
1884
  }
1744
1885
  break;
1745
1886
  }
@@ -1751,9 +1892,9 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1751
1892
  }
1752
1893
  function handleShape(el, sink, ctx) {
1753
1894
  const imgRef = extractImageRef(el);
1754
- const drawTextChild = findDescendant(el, "drawText");
1895
+ const drawTextChild = findDescendant2(el, "drawText");
1755
1896
  if (imgRef) {
1756
- const block = { type: "image", text: imgRef, pageNumber: ctx.sectionNum };
1897
+ const block = { type: "image", text: imgRef, pageNumber: ctx.page };
1757
1898
  const alt = userShapeComment(el);
1758
1899
  if (alt) block.footnoteText = alt;
1759
1900
  sink.push(block);
@@ -1764,11 +1905,11 @@ function handleShape(el, sink, ctx) {
1764
1905
  const capEl = findChildByLocalName(el, "caption");
1765
1906
  if (capEl) {
1766
1907
  const capText = collectSubListText(capEl, ctx);
1767
- if (capText) sink.push({ type: "paragraph", text: capText, pageNumber: ctx.sectionNum });
1908
+ if (capText) sink.push({ type: "paragraph", text: capText, pageNumber: ctx.page });
1768
1909
  }
1769
1910
  if (!imgRef && !drawTextChild && ctx.warnings && ctx.sectionNum) {
1770
1911
  const localTag = (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
1771
- ctx.warnings.push({ page: ctx.sectionNum, message: `\uC2A4\uD0B5\uB41C \uC694\uC18C: ${localTag}`, code: "SKIPPED_IMAGE" });
1912
+ ctx.warnings.push({ page: ctx.page, message: `\uC2A4\uD0B5\uB41C \uC694\uC18C: ${localTag}`, code: "SKIPPED_IMAGE" });
1772
1913
  }
1773
1914
  }
1774
1915
  function userShapeComment(el) {
@@ -1853,7 +1994,7 @@ function collectSubListContent(el, ctx, depth = 0) {
1853
1994
  const t = extractParagraphInfo(ch, ctx.styleMap, ctx).text;
1854
1995
  if (t) {
1855
1996
  parts.push(t);
1856
- out.blocks.push({ type: "paragraph", text: t, pageNumber: ctx.sectionNum });
1997
+ out.blocks.push({ type: "paragraph", text: t, pageNumber: ctx.page });
1857
1998
  }
1858
1999
  const tbls = [];
1859
2000
  findTopLevelTbls(ch, tbls);
@@ -1926,7 +2067,7 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1926
2067
  const chan = kordocTableChannel(el, ctx);
1927
2068
  if (chan) {
1928
2069
  if (chan.kind === "heading" && chan.text) {
1929
- blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
2070
+ blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.page });
1930
2071
  }
1931
2072
  continue;
1932
2073
  }
@@ -1938,11 +2079,11 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1938
2079
  } else if (localTag === "caption" && !inShape) {
1939
2080
  const cap = collectSubListContent(el, ctx);
1940
2081
  if (cap.text) {
1941
- if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: cap.text, pageNumber: ctx.sectionNum }]);
2082
+ if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: cap.text, pageNumber: ctx.page }]);
1942
2083
  else if (tableCtx) {
1943
2084
  tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + cap.text;
1944
2085
  if (cap.hasStructure) (tableCtx.captionBlocks ??= []).push(...cap.blocks);
1945
- } else blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.sectionNum });
2086
+ } else blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.page });
1946
2087
  }
1947
2088
  } else if (localTag === "pic" || localTag === "shape" || localTag === "drawingObject") {
1948
2089
  if (tableCtx?.cell) {
@@ -1987,7 +2128,7 @@ function hasObjectDescendant(node, depth = 0) {
1987
2128
  function isInlineTbl(tbl2) {
1988
2129
  return findChildByLocalName(tbl2, "pos")?.getAttribute("treatAsChar") === "1";
1989
2130
  }
1990
- function findDescendant(node, targetTag, depth = 0) {
2131
+ function findDescendant2(node, targetTag, depth = 0) {
1991
2132
  if (depth > 5) return null;
1992
2133
  const children = node.childNodes;
1993
2134
  if (!children) return null;
@@ -1996,7 +2137,7 @@ function findDescendant(node, targetTag, depth = 0) {
1996
2137
  if (child.nodeType !== 1) continue;
1997
2138
  const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
1998
2139
  if (tag === targetTag) return child;
1999
- const found = findDescendant(child, targetTag, depth + 1);
2140
+ const found = findDescendant2(child, targetTag, depth + 1);
2000
2141
  if (found) return found;
2001
2142
  }
2002
2143
  return null;
@@ -2017,7 +2158,7 @@ function extractDrawTextBlocks(drawTextNode, blocks, ctx) {
2017
2158
  const ph = resolveParaHeading(child, ctx);
2018
2159
  if (text) {
2019
2160
  if (ph?.prefix) text = ph.prefix + " " + text;
2020
- const block = { type: "paragraph", text, style: info.style ?? void 0, pageNumber: ctx.sectionNum };
2161
+ const block = { type: "paragraph", text, style: info.style ?? void 0, pageNumber: ctx.page };
2021
2162
  if (info.href) block.href = info.href;
2022
2163
  if (info.footnote) block.footnoteText = info.footnote;
2023
2164
  blocks.push(block);
@@ -2120,7 +2261,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2120
2261
  // 숨은 설명 — 본문 혼입 차단
2121
2262
  case "hiddenComment": {
2122
2263
  if (ctx?.warnings && extractTextFromNode(k)) {
2123
- ctx.warnings.push({ page: ctx.sectionNum, message: "\uC228\uC740 \uC124\uBA85 \uD14D\uC2A4\uD2B8 \uC81C\uC678: hiddenComment", code: "HIDDEN_TEXT_FILTERED" });
2264
+ ctx.warnings.push({ page: ctx.page, message: "\uC228\uC740 \uC124\uBA85 \uD14D\uC2A4\uD2B8 \uC81C\uC678: hiddenComment", code: "HIDDEN_TEXT_FILTERED" });
2124
2265
  }
2125
2266
  break;
2126
2267
  }
@@ -2140,7 +2281,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2140
2281
  // 미지원 요소 — 텍스트를 가졌으면 무음 손실 대신 경고
2141
2282
  default: {
2142
2283
  if (ctx?.warnings && extractTextFromNode(k)) {
2143
- ctx.warnings.push({ page: ctx.sectionNum, message: `\uBBF8\uC9C0\uC6D0 \uC81C\uC5B4 \uC694\uC18C\uC758 \uD14D\uC2A4\uD2B8 \uC190\uC2E4: ${ktag}`, code: "UNSUPPORTED_ELEMENT" });
2284
+ ctx.warnings.push({ page: ctx.page, message: `\uBBF8\uC9C0\uC6D0 \uC81C\uC5B4 \uC694\uC18C\uC758 \uD14D\uC2A4\uD2B8 \uC190\uC2E4: ${ktag}`, code: "UNSUPPORTED_ELEMENT" });
2144
2285
  }
2145
2286
  }
2146
2287
  }
@@ -2157,7 +2298,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2157
2298
  if (isInDeletedRange(ctx)) {
2158
2299
  if (t && ctx && !ctx.shared.track.warned) {
2159
2300
  ctx.shared.track.warned = true;
2160
- ctx.warnings?.push({ page: ctx.sectionNum, message: "\uBCC0\uACBD\uCD94\uC801 \uC0AD\uC81C \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uC81C\uC678", code: "HIDDEN_TEXT_FILTERED" });
2301
+ ctx.warnings?.push({ page: ctx.page, message: "\uBCC0\uACBD\uCD94\uC801 \uC0AD\uC81C \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uC81C\uC678", code: "HIDDEN_TEXT_FILTERED" });
2161
2302
  }
2162
2303
  } else {
2163
2304
  text += t.replace(/\x1E/g, "");
@@ -2269,7 +2410,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2269
2410
  } catch {
2270
2411
  if (ctx?.warnings) {
2271
2412
  ctx.warnings.push({
2272
- page: ctx.sectionNum,
2413
+ page: ctx.page,
2273
2414
  message: `\uC218\uC2DD LaTeX \uBCC0\uD658 \uC2E4\uD328 \u2014 \uC218\uC2DD \uD14D\uC2A4\uD2B8 \uC81C\uC678: ${raw.trim().slice(0, 40)}`,
2274
2415
  code: "PARTIAL_PARSE"
2275
2416
  });
@@ -2426,6 +2567,7 @@ import { inflateRawSync as inflateRawSync2, inflateSync } from "zlib";
2426
2567
  var TAG_PARA_HEADER = 66;
2427
2568
  var TAG_PARA_TEXT = 67;
2428
2569
  var TAG_CHAR_SHAPE = 68;
2570
+ var TAG_PARA_LINE_SEG = 69;
2429
2571
  var TAG_CTRL_HEADER = 71;
2430
2572
  var TAG_LIST_HEADER = 72;
2431
2573
  var TAG_TABLE = 77;
@@ -3060,7 +3202,9 @@ function extractFromBrokenZip(buffer) {
3060
3202
  totalDecompressed += content.length * 2;
3061
3203
  if (totalDecompressed > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC");
3062
3204
  sectionNum++;
3063
- blocks.push(...parseSectionXml(content, void 0, warnings, sectionNum, shared));
3205
+ const secBlocks = parseSectionXml(content, void 0, warnings, sectionNum, shared);
3206
+ for (const b of secBlocks) b.pageNumber = sectionNum;
3207
+ blocks.push(...secBlocks);
3064
3208
  } catch {
3065
3209
  continue;
3066
3210
  }
@@ -3199,33 +3343,48 @@ async function parseHwpxDocument(buffer, options) {
3199
3343
  const warnings = [];
3200
3344
  const sectionPaths = await resolveSectionPaths(zip);
3201
3345
  if (sectionPaths.length === 0) throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
3202
- metadata.pageCount = sectionPaths.length;
3203
- const pageFilter = options?.pages ? parsePageRange(options.pages, sectionPaths.length) : null;
3204
- const totalTarget = pageFilter ? pageFilter.size : sectionPaths.length;
3205
- const blocks = [];
3346
+ let allBlocks = [];
3206
3347
  const shared = createSectionShared();
3207
3348
  shared.kordocLayout = await readKordocLayout(zip);
3208
3349
  shared.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
3209
3350
  shared.keepEmptyParagraphs = options?.keepEmptyParagraphs;
3351
+ const sectionRanges = [];
3210
3352
  let parsedSections = 0;
3211
3353
  for (let si = 0; si < sectionPaths.length; si++) {
3212
- if (pageFilter && !pageFilter.has(si + 1)) continue;
3213
3354
  const file = zip.file(sectionPaths[si]);
3214
3355
  if (!file) continue;
3215
3356
  try {
3216
3357
  const xml = await file.async("text");
3217
3358
  decompressed.total += xml.length * 2;
3218
3359
  if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new ZipBombError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
3219
- blocks.push(...parseSectionXml(xml, styleMap, warnings, si + 1, shared));
3360
+ const start = allBlocks.length;
3361
+ allBlocks.push(...parseSectionXml(xml, styleMap, warnings, si + 1, shared));
3362
+ sectionRanges.push({ sectionNum: si + 1, start, end: allBlocks.length });
3220
3363
  parsedSections++;
3221
- options?.onProgress?.(parsedSections, totalTarget);
3364
+ options?.onProgress?.(parsedSections, sectionPaths.length);
3222
3365
  } catch (secErr) {
3223
3366
  if (secErr instanceof ZipBombError) throw secErr;
3224
3367
  warnings.push({ page: si + 1, message: `\uC139\uC158 ${si + 1} \uD30C\uC2F1 \uC2E4\uD328: ${secErr instanceof Error ? secErr.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`, code: "PARTIAL_PARSE" });
3225
3368
  }
3226
3369
  }
3370
+ const layoutPages = shared.pageState.allUsable && parsedSections > 0;
3371
+ metadata.pageMode = layoutPages ? "layout" : "section";
3372
+ metadata.pageCount = layoutPages ? Math.max(shared.pageState.base, 1) : sectionPaths.length;
3373
+ if (!layoutPages) {
3374
+ for (const r of sectionRanges) {
3375
+ for (let b = r.start; b < r.end; b++) allBlocks[b].pageNumber = r.sectionNum;
3376
+ }
3377
+ }
3378
+ if (options?.pages) {
3379
+ const pageFilter = parsePageRange(options.pages, metadata.pageCount);
3380
+ allBlocks = allBlocks.filter((b) => b.pageNumber != null && pageFilter.has(b.pageNumber));
3381
+ if (!layoutPages) {
3382
+ warnings.push({ code: "PAGE_BOUNDARY_APPROXIMATE", message: "\uC870\uD310 \uCE90\uC2DC\uAC00 \uC5C6\uC5B4 pages \uD544\uD130\uB97C \uC139\uC158 \uB2E8\uC704 \uADFC\uC0AC\uB85C \uC801\uC6A9\uD588\uC2B5\uB2C8\uB2E4" });
3383
+ }
3384
+ }
3385
+ const blocks = allBlocks;
3227
3386
  applyPageText(blocks, shared);
3228
- const images = await extractImagesFromZip(zip, blocks, decompressed, warnings, !pageFilter);
3387
+ const images = await extractImagesFromZip(zip, blocks, decompressed, warnings, !options?.pages);
3229
3388
  detectHwpxHeadings(blocks, styleMap);
3230
3389
  const outline = blocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
3231
3390
  const markdown = blocksToMarkdown(blocks);
@@ -4246,6 +4405,112 @@ function assertSupportedEncryptVersion(version) {
4246
4405
  }
4247
4406
  }
4248
4407
 
4408
+ // src/hwp5/page-boundary.ts
4409
+ var MIDPAGE_V2 = 2e3;
4410
+ var CTRL_ID_TBL = 1952607264;
4411
+ function tableIntraBreaks2(records, start, end) {
4412
+ const rowMax = /* @__PURE__ */ new Map();
4413
+ let curRow = -1;
4414
+ let prevV = Number.NEGATIVE_INFINITY;
4415
+ let resets = 0;
4416
+ const flush = () => {
4417
+ if (curRow >= 0) rowMax.set(curRow, Math.max(rowMax.get(curRow) ?? 0, resets));
4418
+ resets = 0;
4419
+ prevV = Number.NEGATIVE_INFINITY;
4420
+ };
4421
+ for (let i = start; i < end; i++) {
4422
+ const rec = records[i];
4423
+ if (rec.tagId === TAG_LIST_HEADER && rec.level === 2 && rec.data.length >= 12) {
4424
+ flush();
4425
+ curRow = rec.data.readUInt16LE(10);
4426
+ } else if (rec.tagId === TAG_PARA_LINE_SEG && rec.level === 3 && curRow >= 0) {
4427
+ for (let off = 0; off + 36 <= rec.data.length; off += 36) {
4428
+ const v = rec.data.readInt32LE(off + 4);
4429
+ if (v < prevV) resets++;
4430
+ prevV = v;
4431
+ }
4432
+ }
4433
+ }
4434
+ flush();
4435
+ let sum = 0;
4436
+ for (const v of rowMax.values()) sum += v;
4437
+ return sum;
4438
+ }
4439
+ function detectHwp5SectionPages(records) {
4440
+ const pageAtPara = [];
4441
+ let cur = 0;
4442
+ let prevV = Number.NEGATIVE_INFINITY;
4443
+ let prevH = Number.NEGATIVE_INFINITY;
4444
+ let first = true;
4445
+ let suppressMidReset = false;
4446
+ let parasWithSegs = 0;
4447
+ let i = 0;
4448
+ while (i < records.length) {
4449
+ const rec = records[i];
4450
+ if (rec.tagId !== TAG_PARA_HEADER || rec.level !== 0) {
4451
+ i++;
4452
+ continue;
4453
+ }
4454
+ let j = i + 1;
4455
+ while (j < records.length && records[j].level > 0) j++;
4456
+ const divideSort = rec.data.length >= 12 ? rec.data.readUInt8(11) : 0;
4457
+ const explicit = (divideSort & 4) !== 0;
4458
+ let brokeByExplicit = false;
4459
+ if (explicit && !first) {
4460
+ cur++;
4461
+ brokeByExplicit = true;
4462
+ suppressMidReset = false;
4463
+ }
4464
+ let paraFirst = true;
4465
+ let startPage = cur;
4466
+ let hadSegs = false;
4467
+ let intraAdd = 0;
4468
+ for (let k = i + 1; k < j; k++) {
4469
+ const r = records[k];
4470
+ if (r.tagId === TAG_PARA_LINE_SEG && r.level === 1) {
4471
+ hadSegs = true;
4472
+ for (let off = 0; off + 36 <= r.data.length; off += 36) {
4473
+ const v = r.data.readInt32LE(off + 4);
4474
+ const h = r.data.readInt32LE(off + 24);
4475
+ const brk = v < prevV || paraFirst && v === prevV && h <= prevH;
4476
+ if (brk && !(paraFirst && brokeByExplicit)) {
4477
+ if (paraFirst && suppressMidReset && v >= MIDPAGE_V2) {
4478
+ } else {
4479
+ cur++;
4480
+ }
4481
+ }
4482
+ if (paraFirst) {
4483
+ suppressMidReset = false;
4484
+ startPage = cur;
4485
+ }
4486
+ paraFirst = false;
4487
+ prevV = v;
4488
+ prevH = h;
4489
+ }
4490
+ }
4491
+ if (r.tagId === TAG_CTRL_HEADER && r.level === 1 && r.data.length >= 4 && r.data.readUInt32LE(0) === CTRL_ID_TBL) {
4492
+ let ce = k + 1;
4493
+ while (ce < j && records[ce].level > 1) ce++;
4494
+ intraAdd += tableIntraBreaks2(records, k + 1, ce);
4495
+ k = ce - 1;
4496
+ }
4497
+ }
4498
+ if (hadSegs) parasWithSegs++;
4499
+ pageAtPara.push(startPage);
4500
+ first = false;
4501
+ if (intraAdd > 0) {
4502
+ cur += intraAdd;
4503
+ suppressMidReset = true;
4504
+ }
4505
+ i = j;
4506
+ }
4507
+ return {
4508
+ pageAtPara,
4509
+ pages: cur + 1,
4510
+ usable: pageAtPara.length > 0 && parasWithSegs === pageAtPara.length
4511
+ };
4512
+ }
4513
+
4249
4514
  // src/hwp5/parser.ts
4250
4515
  import { createRequire } from "module";
4251
4516
  var require2 = createRequire(import.meta.url);
@@ -4371,31 +4636,63 @@ function parseHwp5Document(buffer, options) {
4371
4636
  const docInfo = cfb && !encrypted ? parseDocInfoStream(cfb, compressed) : parseDocInfoFromStream(findStream("/DocInfo"), compressed);
4372
4637
  const sections = distribution ? cfb ? findViewTextSections(cfb, compressed) : findViewTextSectionsLenient(lenientCfb, compressed) : encrypted ? findSectionsVia(findStream) : cfb ? findSections(cfb) : findSectionsLenient(lenientCfb, compressed);
4373
4638
  if (sections.length === 0) throw new KordocError("\uC139\uC158 \uC2A4\uD2B8\uB9BC\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
4374
- metadata.pageCount = sections.length;
4375
- const pageFilter = options?.pages ? parsePageRange(options.pages, sections.length) : null;
4376
- const totalTarget = pageFilter ? pageFilter.size : sections.length;
4377
- const bodyBlocks = [];
4378
- const doc = createHwp5DocState();
4379
- doc.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
4639
+ const sectionRecords = [];
4640
+ const sectionDetects = [];
4380
4641
  let totalDecompressed = 0;
4381
- let parsedSections = 0;
4382
4642
  for (let si = 0; si < sections.length; si++) {
4383
- if (pageFilter && !pageFilter.has(si + 1)) continue;
4384
4643
  try {
4385
- const sectionData = sections[si];
4386
- const data = !distribution && compressed ? decompressStream(Buffer.from(sectionData)) : Buffer.from(sectionData);
4644
+ const data = !distribution && compressed ? decompressStream(Buffer.from(sections[si])) : Buffer.from(sections[si]);
4387
4645
  totalDecompressed += data.length;
4388
4646
  if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError("\uCD1D \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (decompression bomb \uC758\uC2EC)");
4389
4647
  const records = readRecords(data);
4390
- const sectionBlocks = parseSection(records, docInfo, warnings, si + 1, doc);
4648
+ sectionRecords.push(records);
4649
+ sectionDetects.push(detectHwp5SectionPages(records));
4650
+ } catch (secErr) {
4651
+ if (secErr instanceof KordocError) throw secErr;
4652
+ sectionRecords.push(null);
4653
+ sectionDetects.push(null);
4654
+ warnings.push({ page: si + 1, message: `\uC139\uC158 ${si + 1} \uD30C\uC2F1 \uC2E4\uD328: ${secErr instanceof Error ? secErr.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`, code: "PARTIAL_PARSE" });
4655
+ }
4656
+ }
4657
+ const layoutPages = sectionDetects.length > 0 && sectionDetects.every((d) => d != null && d.usable);
4658
+ metadata.pageMode = layoutPages ? "layout" : "section";
4659
+ metadata.pageCount = layoutPages ? Math.max(sectionDetects.reduce((sum, d) => sum + (d?.pages ?? 0), 0), 1) : sections.length;
4660
+ const pageFilter = options?.pages ? parsePageRange(options.pages, metadata.pageCount) : null;
4661
+ if (pageFilter && !layoutPages) {
4662
+ warnings.push({ code: "PAGE_BOUNDARY_APPROXIMATE", message: "\uC870\uD310 \uCE90\uC2DC\uAC00 \uC5C6\uC5B4 pages \uD544\uD130\uB97C \uC139\uC158 \uB2E8\uC704 \uADFC\uC0AC\uB85C \uC801\uC6A9\uD588\uC2B5\uB2C8\uB2E4" });
4663
+ }
4664
+ let bodyBlocks = [];
4665
+ const doc = createHwp5DocState();
4666
+ doc.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
4667
+ let parsedSections = 0;
4668
+ let pageBase = 0;
4669
+ for (let si = 0; si < sections.length; si++) {
4670
+ const records = sectionRecords[si];
4671
+ if (!records) continue;
4672
+ const detect = sectionDetects[si];
4673
+ if (!layoutPages && pageFilter && !pageFilter.has(si + 1)) continue;
4674
+ try {
4675
+ const sectionBlocks = parseSection(
4676
+ records,
4677
+ docInfo,
4678
+ warnings,
4679
+ si + 1,
4680
+ doc,
4681
+ layoutPages ? { base: pageBase, pageAtPara: detect.pageAtPara } : void 0
4682
+ );
4391
4683
  bodyBlocks.push(...sectionBlocks);
4392
4684
  parsedSections++;
4393
- options?.onProgress?.(parsedSections, totalTarget);
4685
+ options?.onProgress?.(parsedSections, sections.length);
4394
4686
  } catch (secErr) {
4395
4687
  if (secErr instanceof KordocError) throw secErr;
4396
4688
  warnings.push({ page: si + 1, message: `\uC139\uC158 ${si + 1} \uD30C\uC2F1 \uC2E4\uD328: ${secErr instanceof Error ? secErr.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`, code: "PARTIAL_PARSE" });
4689
+ } finally {
4690
+ pageBase += detect.pages;
4397
4691
  }
4398
4692
  }
4693
+ if (pageFilter && layoutPages) {
4694
+ bodyBlocks = bodyBlocks.filter((b) => b.pageNumber != null && pageFilter.has(b.pageNumber));
4695
+ }
4399
4696
  const blocks = [...doc.headerBlocks, ...bodyBlocks, ...doc.footerBlocks];
4400
4697
  const images = cfb ? extractHwp5Images(cfb.FileIndex, blocks, warnings, !pageFilter) : extractHwp5ImagesLenient(lenientCfb, blocks, warnings, !pageFilter);
4401
4698
  let flatBlocks = flattenLayoutTables(blocks);
@@ -4632,8 +4929,17 @@ function createHwp5DocState() {
4632
4929
  footerBlocks: []
4633
4930
  };
4634
4931
  }
4635
- function parseSection(records, docInfo, warnings, sectionNum, doc) {
4636
- const ctx = { docInfo, warnings, sectionNum, doc: doc ?? createHwp5DocState(), depth: 0 };
4932
+ function parseSection(records, docInfo, warnings, sectionNum, doc, pageMap) {
4933
+ const ctx = {
4934
+ docInfo,
4935
+ warnings,
4936
+ sectionNum,
4937
+ doc: doc ?? createHwp5DocState(),
4938
+ depth: 0,
4939
+ page: pageMap ? pageMap.base + 1 : sectionNum,
4940
+ pageMap,
4941
+ topOrdinal: 0
4942
+ };
4637
4943
  return parseParagraphList(records, 0, records.length, ctx);
4638
4944
  }
4639
4945
  function parseParagraphList(records, start, end, ctx) {
@@ -4642,6 +4948,11 @@ function parseParagraphList(records, start, end, ctx) {
4642
4948
  while (i < end) {
4643
4949
  if (records[i].tagId === TAG_PARA_HEADER) {
4644
4950
  const baseLevel = records[i].level;
4951
+ if (baseLevel === 0 && ctx.depth === 0 && ctx.pageMap) {
4952
+ const pg = ctx.pageMap.pageAtPara[ctx.topOrdinal];
4953
+ if (pg !== void 0) ctx.page = ctx.pageMap.base + pg + 1;
4954
+ ctx.topOrdinal++;
4955
+ }
4645
4956
  let j = i + 1;
4646
4957
  while (j < end && records[j].level > baseLevel) j++;
4647
4958
  blocks.push(...parseParagraph(records, i, j, ctx));
@@ -4742,7 +5053,7 @@ function parseParagraph(records, start, end, ctx) {
4742
5053
  const block = {
4743
5054
  type: headingLevel > 0 ? "heading" : "paragraph",
4744
5055
  text: headMarker ? `${headMarker} ${trimmed}` : trimmed,
4745
- pageNumber: ctx.sectionNum
5056
+ pageNumber: ctx.page
4746
5057
  };
4747
5058
  if (headingLevel > 0) block.level = headingLevel;
4748
5059
  if (ctx.docInfo && charShapeIds.length > 0) {
@@ -4759,7 +5070,7 @@ function parseParagraph(records, start, end, ctx) {
4759
5070
  if (footnotes.length > 0) block.footnoteText = footnotes.join("; ");
4760
5071
  blocks.push(block);
4761
5072
  } else if (footnotes.length > 0) {
4762
- blocks.push({ type: "paragraph", text: `(\uC8FC: ${footnotes.join("; ")})`, pageNumber: ctx.sectionNum });
5073
+ blocks.push({ type: "paragraph", text: `(\uC8FC: ${footnotes.join("; ")})`, pageNumber: ctx.page });
4763
5074
  }
4764
5075
  for (const ctrl of ctrls) {
4765
5076
  if (ctrl.afterBlocks) blocks.push(...ctrl.afterBlocks);
@@ -4770,7 +5081,7 @@ function applyCtrlEffect(ctrl, records, ctx) {
4770
5081
  switch (ctrl.id) {
4771
5082
  case CTRL_TBL: {
4772
5083
  const table2 = parseTableControl(ctrl, records, ctx);
4773
- if (table2) ctrl.afterBlocks = [{ type: "table", table: table2, pageNumber: ctx.sectionNum }];
5084
+ if (table2) ctrl.afterBlocks = [{ type: "table", table: table2, pageNumber: ctx.page }];
4774
5085
  return;
4775
5086
  }
4776
5087
  case CTRL_GSO: {
@@ -4822,7 +5133,7 @@ function applyCtrlEffect(ctrl, records, ctx) {
4822
5133
  return;
4823
5134
  }
4824
5135
  case CTRL_OLE: {
4825
- ctx.warnings.push({ page: ctx.sectionNum, message: "\uC2A4\uD0B5\uB41C OLE \uAC1C\uCCB4", code: "SKIPPED_OLE" });
5136
+ ctx.warnings.push({ page: ctx.page, message: "\uC2A4\uD0B5\uB41C OLE \uAC1C\uCCB4", code: "SKIPPED_OLE" });
4826
5137
  return;
4827
5138
  }
4828
5139
  // 숨은 설명/단 정의/쪽번호 위치/감추기/찾아보기/책갈피/글자겹침/덧말 — 본문 텍스트 없음 또는 의도적 스킵
@@ -4899,7 +5210,7 @@ function applyHeaderFooterEffect(ctrl, records, ctx, isHeader) {
4899
5210
  const key = (isHeader ? "h:" : "f:") + text;
4900
5211
  if (ctx.doc.headerTexts.has(key)) return;
4901
5212
  ctx.doc.headerTexts.add(key);
4902
- const block = { type: "paragraph", text, pageNumber: ctx.sectionNum };
5213
+ const block = { type: "paragraph", text, pageNumber: ctx.page };
4903
5214
  if (isHeader) ctx.doc.headerBlocks.push(block);
4904
5215
  else ctx.doc.footerBlocks.push(block);
4905
5216
  }
@@ -5121,11 +5432,11 @@ function pictureToImageBlock(data, ctx) {
5121
5432
  if (binDataId === 0) return null;
5122
5433
  const item = ctx.docInfo?.binData[binDataId - 1];
5123
5434
  if (item?.kind === "link") {
5124
- ctx.warnings.push({ page: ctx.sectionNum, message: `\uC678\uBD80 \uC5F0\uACB0 \uC774\uBBF8\uC9C0 (binDataId ${binDataId})`, code: "SKIPPED_IMAGE" });
5435
+ ctx.warnings.push({ page: ctx.page, message: `\uC678\uBD80 \uC5F0\uACB0 \uC774\uBBF8\uC9C0 (binDataId ${binDataId})`, code: "SKIPPED_IMAGE" });
5125
5436
  return null;
5126
5437
  }
5127
5438
  const storageId = item && item.storageId > 0 ? item.storageId : binDataId;
5128
- return { type: "image", text: String(storageId), pageNumber: ctx.sectionNum };
5439
+ return { type: "image", text: String(storageId), pageNumber: ctx.page };
5129
5440
  }
5130
5441
  function resolveCharStyle(charShapeIds, docInfo) {
5131
5442
  if (charShapeIds.length === 0 || docInfo.charShapes.length === 0) return void 0;
@@ -19016,8 +19327,8 @@ function isDisplayMath(el) {
19016
19327
 
19017
19328
  // src/docx/parser.ts
19018
19329
  var MAX_DECOMPRESS_SIZE5 = 100 * 1024 * 1024;
19019
- function matchesLocal(el, localName2) {
19020
- return el.localName === localName2 || (el.tagName?.endsWith(`:${localName2}`) ?? false);
19330
+ function matchesLocal(el, localName3) {
19331
+ return el.localName === localName3 || (el.tagName?.endsWith(`:${localName3}`) ?? false);
19021
19332
  }
19022
19333
  function effectiveChildElements(parent) {
19023
19334
  const result = [];
@@ -19041,10 +19352,10 @@ function effectiveChildElements(parent) {
19041
19352
  }
19042
19353
  return result;
19043
19354
  }
19044
- function getChildElements(parent, localName2) {
19045
- return effectiveChildElements(parent).filter((el) => matchesLocal(el, localName2));
19355
+ function getChildElements(parent, localName3) {
19356
+ return effectiveChildElements(parent).filter((el) => matchesLocal(el, localName3));
19046
19357
  }
19047
- function findElements(parent, localName2) {
19358
+ function findElements(parent, localName3) {
19048
19359
  const result = [];
19049
19360
  const walk = (node) => {
19050
19361
  const children = node.childNodes;
@@ -19052,7 +19363,7 @@ function findElements(parent, localName2) {
19052
19363
  const child = children[i];
19053
19364
  if (child.nodeType === 1) {
19054
19365
  const el = child;
19055
- if (el.localName === localName2 || el.tagName?.endsWith(`:${localName2}`)) {
19366
+ if (el.localName === localName3 || el.tagName?.endsWith(`:${localName3}`)) {
19056
19367
  result.push(el);
19057
19368
  }
19058
19369
  walk(el);
@@ -19062,11 +19373,11 @@ function findElements(parent, localName2) {
19062
19373
  walk(parent);
19063
19374
  return result;
19064
19375
  }
19065
- function getAttr(el, localName2) {
19376
+ function getAttr(el, localName3) {
19066
19377
  const attrs = el.attributes;
19067
19378
  for (let i = 0; i < attrs.length; i++) {
19068
19379
  const attr = attrs[i];
19069
- if (attr.localName === localName2 || attr.name === localName2) return attr.value;
19380
+ if (attr.localName === localName3 || attr.name === localName3) return attr.value;
19070
19381
  }
19071
19382
  return null;
19072
19383
  }
@@ -19585,8 +19896,8 @@ async function parseDocxDocument(buffer, options) {
19585
19896
  const bodyEl = body[0];
19586
19897
  const topLevel = effectiveChildElements(bodyEl);
19587
19898
  for (const el of topLevel) {
19588
- const localName2 = el.localName ?? el.tagName?.split(":").pop();
19589
- if (localName2 === "p") {
19899
+ const localName3 = el.localName ?? el.tagName?.split(":").pop();
19900
+ if (localName3 === "p") {
19590
19901
  const block = parseParagraph2(el, styles, numbering, footnotes, rels);
19591
19902
  if (block) blocks.push(block);
19592
19903
  if (imageMap.size > 0) emitParagraphImages(el, imageMap, linkedImages, blocks);
@@ -19595,7 +19906,7 @@ async function parseDocxDocument(buffer, options) {
19595
19906
  if (tb) blocks.push(tb);
19596
19907
  if (imageMap.size > 0) emitParagraphImages(tp, imageMap, linkedImages, blocks);
19597
19908
  }
19598
- } else if (localName2 === "tbl") {
19909
+ } else if (localName3 === "tbl") {
19599
19910
  const block = parseTable(el, styles, numbering, footnotes, rels, options?.keepTrailingEmptyCols);
19600
19911
  if (block) blocks.push(block);
19601
19912
  }
@@ -28678,7 +28989,7 @@ var Surgeon = class {
28678
28989
  // src/roundtrip/hwp5-patch.ts
28679
28990
  var require3 = createRequire2(import.meta.url);
28680
28991
  var CFB2 = require3("cfb");
28681
- var TAG_PARA_LINE_SEG = 69;
28992
+ var TAG_PARA_LINE_SEG2 = 69;
28682
28993
  function cid2(s) {
28683
28994
  return (s.charCodeAt(0) << 24 | s.charCodeAt(1) << 16 | s.charCodeAt(2) << 8 | s.charCodeAt(3)) >>> 0;
28684
28995
  }
@@ -28759,7 +29070,7 @@ function scanSection(stream, sectionIndex, compressed) {
28759
29070
  textIdx = textIdx === -1 ? j : -2;
28760
29071
  appendParaText(state, records[j].data);
28761
29072
  } else if (t === TAG_CHAR_SHAPE && charShapeIdx === -1) charShapeIdx = j;
28762
- else if (t === TAG_PARA_LINE_SEG && lineSegIdx === -1) lineSegIdx = j;
29073
+ else if (t === TAG_PARA_LINE_SEG2 && lineSegIdx === -1) lineSegIdx = j;
28763
29074
  }
28764
29075
  let ctrlSeen = false, nonGso = false;
28765
29076
  for (let a = parent[i]; a >= 0; a = parent[a]) {
@@ -31326,7 +31637,7 @@ async function parse(input, options) {
31326
31637
  }
31327
31638
  async function parseImage(buffer, options) {
31328
31639
  try {
31329
- const { parseImageDocument } = await import("./image-ocr-TQ5THN35.js");
31640
+ const { parseImageDocument } = await import("./image-ocr-XHBZONFZ.js");
31330
31641
  const { blocks, warnings } = await parseImageDocument(buffer, options);
31331
31642
  return {
31332
31643
  success: true,
@@ -31385,7 +31696,7 @@ async function parseHwp(buffer, options) {
31385
31696
  async function parsePdf(buffer, options) {
31386
31697
  let parsePdfDocument;
31387
31698
  try {
31388
- const mod = await import("./parser-FXEIK3VJ.js");
31699
+ const mod = await import("./parser-Z36MZUHS.js");
31389
31700
  parsePdfDocument = mod.parsePdfDocument;
31390
31701
  } catch {
31391
31702
  return {