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
@@ -6,7 +6,7 @@ import {
6
6
  HEADING_RATIO_H1,
7
7
  HEADING_RATIO_H2,
8
8
  HEADING_RATIO_H3
9
- } from "./chunk-4VNUQYUE.js";
9
+ } from "./chunk-4KGGVCYX.js";
10
10
  import {
11
11
  parsePageRange
12
12
  } from "./chunk-MOL7MDBG.js";
@@ -22,7 +22,7 @@ import {
22
22
  paraTextPureT,
23
23
  patchZipEntries,
24
24
  scanSectionXml
25
- } from "./chunk-IN2AOMX6.js";
25
+ } from "./chunk-NYLYIN55.js";
26
26
  import {
27
27
  MAX_COLS,
28
28
  MAX_ROWS,
@@ -32,14 +32,14 @@ import {
32
32
  dedupeRunningHeaders,
33
33
  flattenLayoutTables,
34
34
  mapPuaText
35
- } from "./chunk-P7HPLWEP.js";
35
+ } from "./chunk-KEGAEHWO.js";
36
36
  import {
37
37
  SPACE_EM_FIXED,
38
38
  charWidthEm1000,
39
39
  fitRatioForFewerLines,
40
40
  measureTextWidth,
41
41
  simulateWrap
42
- } from "./chunk-FJBOVJAG.js";
42
+ } from "./chunk-XOO6YEZE.js";
43
43
  import {
44
44
  MAX_DECOMPRESS_SIZE,
45
45
  MAX_XML_DEPTH,
@@ -55,7 +55,7 @@ import {
55
55
  findChildByLocalName,
56
56
  localName,
57
57
  rawTextContent
58
- } from "./chunk-LJW5EQXS.js";
58
+ } from "./chunk-VZ3AQVBN.js";
59
59
  import {
60
60
  KordocError,
61
61
  classifyError,
@@ -67,7 +67,7 @@ import {
67
67
  sanitizeHref,
68
68
  stripDtd,
69
69
  toArrayBuffer
70
- } from "./chunk-VXB52EWX.js";
70
+ } from "./chunk-IMFIACHP.js";
71
71
 
72
72
  // src/index.ts
73
73
  import { readFile } from "fs/promises";
@@ -433,7 +433,7 @@ function comResultToParseResult(pages, pageCount, warnings) {
433
433
  }
434
434
  }
435
435
  const markdown = lines.join("\n\n");
436
- const metadata = { pageCount };
436
+ const metadata = { pageCount, pageMode: "layout" };
437
437
  warnings.push({
438
438
  message: "DRM \uBB38\uC11C: \uD55C\uCEF4 COM API\uB85C \uD14D\uC2A4\uD2B8 \uCD94\uCD9C (\uC11C\uC2DD/\uD45C \uC815\uBCF4 \uC81C\uD55C\uC801)",
439
439
  code: "DRM_COM_FALLBACK"
@@ -1419,11 +1419,11 @@ function buildTableWithCellMeta(state, keepAnchoredEmptyCols, keepEmptyParagraph
1419
1419
  function completeTable(newTable, tableStack, blocks, ctx) {
1420
1420
  const parentTable = tableStack.length > 0 ? tableStack.pop() : null;
1421
1421
  if (newTable.rows.length === 0) {
1422
- if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.sectionNum });
1422
+ if (newTable.caption) blocks.push({ type: "paragraph", text: newTable.caption, pageNumber: ctx.page });
1423
1423
  return parentTable;
1424
1424
  }
1425
1425
  const ir = buildTableWithCellMeta(newTable, ctx.shared.keepTrailingEmptyCols, ctx.shared.keepEmptyParagraphs);
1426
- const block = { type: "table", table: ir, pageNumber: ctx.sectionNum };
1426
+ const block = { type: "table", table: ir, pageNumber: ctx.page };
1427
1427
  if (parentTable?.cell) {
1428
1428
  const cell2 = parentTable.cell;
1429
1429
  (cell2.blocks ??= []).push(block);
@@ -1441,6 +1441,133 @@ function completeTable(newTable, tableStack, blocks, ctx) {
1441
1441
  return parentTable;
1442
1442
  }
1443
1443
 
1444
+ // src/hwpx/page-boundary.ts
1445
+ var MIDPAGE_V = 2e3;
1446
+ var localName2 = (el) => {
1447
+ const n = el.nodeName;
1448
+ const i = n.indexOf(":");
1449
+ return i >= 0 ? n.slice(i + 1) : n;
1450
+ };
1451
+ var childElements = (parent) => {
1452
+ const out = [];
1453
+ for (let c = parent.firstChild; c; c = c.nextSibling) {
1454
+ if (c.nodeType === 1) out.push(c);
1455
+ }
1456
+ return out;
1457
+ };
1458
+ var numAttr = (el, name) => {
1459
+ const v = el.getAttribute(name);
1460
+ const n = v == null ? NaN : Number(v);
1461
+ return Number.isFinite(n) ? n : 0;
1462
+ };
1463
+ var findDescendant = (root, local, maxDepth = 8) => {
1464
+ if (maxDepth < 0) return null;
1465
+ for (const c of childElements(root)) {
1466
+ if (localName2(c) === local) return c;
1467
+ const found = findDescendant(c, local, maxDepth - 1);
1468
+ if (found) return found;
1469
+ }
1470
+ return null;
1471
+ };
1472
+ function linesegsOf(p) {
1473
+ const lsa = childElements(p).find((c) => localName2(c) === "linesegarray");
1474
+ return lsa ? childElements(lsa).filter((s) => localName2(s) === "lineseg") : [];
1475
+ }
1476
+ function cellFlowResets(subList) {
1477
+ let prevV = Number.NEGATIVE_INFINITY;
1478
+ let resets = 0;
1479
+ for (const p of childElements(subList)) {
1480
+ if (localName2(p) !== "p") continue;
1481
+ for (const s of linesegsOf(p)) {
1482
+ const v = numAttr(s, "vertpos");
1483
+ if (v < prevV) resets++;
1484
+ prevV = v;
1485
+ }
1486
+ }
1487
+ return resets;
1488
+ }
1489
+ function tableIntraBreaks(tbl2) {
1490
+ const rowMax = /* @__PURE__ */ new Map();
1491
+ for (const tr of childElements(tbl2)) {
1492
+ if (localName2(tr) !== "tr") continue;
1493
+ for (const tc2 of childElements(tr)) {
1494
+ if (localName2(tc2) !== "tc") continue;
1495
+ const addr = childElements(tc2).find((e) => localName2(e) === "cellAddr");
1496
+ const row = addr ? numAttr(addr, "rowAddr") : 0;
1497
+ const sub = childElements(tc2).find((e) => localName2(e) === "subList");
1498
+ if (!sub) continue;
1499
+ const r = cellFlowResets(sub);
1500
+ rowMax.set(row, Math.max(rowMax.get(row) ?? 0, r));
1501
+ }
1502
+ }
1503
+ let sum = 0;
1504
+ for (const v of rowMax.values()) sum += v;
1505
+ return sum;
1506
+ }
1507
+ function detectHwpxSectionPages(root) {
1508
+ const colPr = findDescendant(root, "colPr");
1509
+ const multiCol = colPr ? numAttr(colPr, "colCount") > 1 : false;
1510
+ const paraPage = /* @__PURE__ */ new Map();
1511
+ let cur = 0;
1512
+ let prevV = Number.NEGATIVE_INFINITY;
1513
+ let prevH = Number.NEGATIVE_INFINITY;
1514
+ let first = true;
1515
+ let suppressMidReset = false;
1516
+ let topParas = 0;
1517
+ let parasWithSegs = 0;
1518
+ for (const p of childElements(root)) {
1519
+ if (localName2(p) !== "p") continue;
1520
+ topParas++;
1521
+ const explicit = p.getAttribute("pageBreak") === "1";
1522
+ let brokeByExplicit = false;
1523
+ if (explicit && !first) {
1524
+ cur++;
1525
+ brokeByExplicit = true;
1526
+ suppressMidReset = false;
1527
+ }
1528
+ const segs = linesegsOf(p);
1529
+ if (segs.length > 0) parasWithSegs++;
1530
+ let paraFirst = true;
1531
+ let startPage = cur;
1532
+ for (const s of segs) {
1533
+ const v = numAttr(s, "vertpos");
1534
+ const h = numAttr(s, "horzpos");
1535
+ const brk = v < prevV ? !multiCol || h <= prevH : paraFirst && v === prevV && h <= prevH;
1536
+ if (brk && !(paraFirst && brokeByExplicit)) {
1537
+ if (paraFirst && suppressMidReset && v >= MIDPAGE_V) {
1538
+ } else {
1539
+ cur++;
1540
+ }
1541
+ }
1542
+ if (paraFirst) {
1543
+ suppressMidReset = false;
1544
+ startPage = cur;
1545
+ }
1546
+ paraFirst = false;
1547
+ prevV = v;
1548
+ prevH = h;
1549
+ }
1550
+ paraPage.set(p, startPage);
1551
+ first = false;
1552
+ let add = 0;
1553
+ for (const r of childElements(p)) {
1554
+ if (localName2(r) !== "run") continue;
1555
+ for (const t of childElements(r)) {
1556
+ if (localName2(t) === "tbl") add += tableIntraBreaks(t);
1557
+ }
1558
+ }
1559
+ if (add > 0) {
1560
+ cur += add;
1561
+ suppressMidReset = true;
1562
+ }
1563
+ }
1564
+ return {
1565
+ paraPage,
1566
+ pages: cur + 1,
1567
+ usable: topParas > 0 && parasWithSegs === topParas
1568
+ };
1569
+ }
1570
+
1444
1571
  // src/hwpx/section-walker.ts
1445
1572
  function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
1446
1573
  const parser = createXmlParser(warnings);
@@ -1448,6 +1575,13 @@ function parseSectionXml(xml, styleMap, warnings, sectionNum, shared) {
1448
1575
  if (!doc.documentElement) return [];
1449
1576
  const ctx = { styleMap, warnings, sectionNum, shared: shared ?? createSectionShared() };
1450
1577
  ctx.shared.track.deleteDepth = 0;
1578
+ const detect = detectHwpxSectionPages(doc.documentElement);
1579
+ const ps = ctx.shared.pageState;
1580
+ if (!detect.usable) ps.allUsable = false;
1581
+ ctx.paraPage = detect.paraPage;
1582
+ ctx.pageBase = ps.base;
1583
+ ctx.page = ps.base + 1;
1584
+ ps.base += detect.pages;
1451
1585
  for (const tagName of ["hp:secPr", "secPr"]) {
1452
1586
  const els = doc.getElementsByTagName(tagName);
1453
1587
  if (els.length > 0) {
@@ -1494,7 +1628,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1494
1628
  const chan = kordocTableChannel(el, ctx);
1495
1629
  if (chan) {
1496
1630
  if (chan.kind === "heading" && chan.text) {
1497
- blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
1631
+ blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.page });
1498
1632
  }
1499
1633
  break;
1500
1634
  }
@@ -1513,7 +1647,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1513
1647
  tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + cap.text;
1514
1648
  if (cap.hasStructure) (tableCtx.captionBlocks ??= []).push(...cap.blocks);
1515
1649
  } else {
1516
- blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.sectionNum });
1650
+ blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.page });
1517
1651
  }
1518
1652
  }
1519
1653
  break;
@@ -1556,6 +1690,8 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1556
1690
  }
1557
1691
  break;
1558
1692
  case "p": {
1693
+ const paraPg = ctx.paraPage?.get(el);
1694
+ if (paraPg !== void 0 && ctx.pageBase !== void 0) ctx.page = ctx.pageBase + paraPg + 1;
1559
1695
  const { text: rawText, href, footnote, style, segments } = extractParagraphInfo(el, ctx.styleMap, ctx);
1560
1696
  let text = rawText;
1561
1697
  let headingLevel;
@@ -1577,7 +1713,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1577
1713
  const flush = () => {
1578
1714
  const s = segs[segIdx++];
1579
1715
  if (!s) return;
1580
- const block = { type: "paragraph", text: s, pageNumber: ctx.sectionNum };
1716
+ const block = { type: "paragraph", text: s, pageNumber: ctx.page };
1581
1717
  if (first && !cell2) {
1582
1718
  first = false;
1583
1719
  if (style) block.style = style;
@@ -1601,7 +1737,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1601
1737
  if (footnote) text += ` (\uC8FC: ${footnote})`;
1602
1738
  cell2.text += (cell2.text || cell2.paraSeen ? "\n" : "") + text;
1603
1739
  cell2.paraSeen = true;
1604
- const cellBlock = { type: "paragraph", text, pageNumber: ctx.sectionNum };
1740
+ const cellBlock = { type: "paragraph", text, pageNumber: ctx.page };
1605
1741
  const cellSpanMode = spanModeOf(ctx.shared.kordocLayout);
1606
1742
  if (cellSpanMode && !ph?.prefix) {
1607
1743
  const spans = extractRunSpans(el, ctx, cellSpanMode, cellSpanMode !== "kordoc");
@@ -1613,11 +1749,11 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1613
1749
  (cell2.blocks ??= []).push(cellBlock);
1614
1750
  } else if (!tableCtx) {
1615
1751
  if (ctx.shared.kordocLayout && /^─{10,}$/.test(text)) {
1616
- blocks.push({ type: "separator", pageNumber: ctx.sectionNum });
1752
+ blocks.push({ type: "separator", pageNumber: ctx.page });
1617
1753
  tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
1618
1754
  break;
1619
1755
  }
1620
- const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.sectionNum };
1756
+ const block = { type: headingLevel ? "heading" : "paragraph", text, pageNumber: ctx.page };
1621
1757
  if (headingLevel) block.level = headingLevel;
1622
1758
  if (style) block.style = style;
1623
1759
  if (href) block.href = href;
@@ -1643,7 +1779,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1643
1779
  }
1644
1780
  blocks.push(block);
1645
1781
  } else {
1646
- blocks.push({ type: "paragraph", text, pageNumber: ctx.sectionNum });
1782
+ blocks.push({ type: "paragraph", text, pageNumber: ctx.page });
1647
1783
  }
1648
1784
  } else if (ctx.shared.keepEmptyParagraphs && !hasObjectDescendant(el)) {
1649
1785
  if (tableCtx?.cell) {
@@ -1651,9 +1787,9 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1651
1787
  if (cell2.text || cell2.paraSeen) cell2.text += "\n";
1652
1788
  cell2.paraSeen = true;
1653
1789
  cell2.lineOpen = false;
1654
- (cell2.blocks ??= []).push({ type: "paragraph", text: "", pageNumber: ctx.sectionNum });
1790
+ (cell2.blocks ??= []).push({ type: "paragraph", text: "", pageNumber: ctx.page });
1655
1791
  } else if (!tableCtx) {
1656
- blocks.push({ type: "paragraph", text: "", pageNumber: ctx.sectionNum });
1792
+ blocks.push({ type: "paragraph", text: "", pageNumber: ctx.page });
1657
1793
  }
1658
1794
  }
1659
1795
  tableCtx = walkParagraphChildren(el, blocks, tableCtx, tableStack, ctx, depth + 1);
@@ -1676,7 +1812,7 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1676
1812
  case "memogroup":
1677
1813
  case "memo": {
1678
1814
  if (ctx.warnings && extractTextFromNode(el)) {
1679
- ctx.warnings.push({ page: ctx.sectionNum, message: "\uBA54\uBAA8 \uD14D\uC2A4\uD2B8 \uBCF8\uBB38 \uC81C\uC678: memogroup", code: "HIDDEN_TEXT_FILTERED" });
1815
+ ctx.warnings.push({ page: ctx.page, message: "\uBA54\uBAA8 \uD14D\uC2A4\uD2B8 \uBCF8\uBB38 \uC81C\uC678: memogroup", code: "HIDDEN_TEXT_FILTERED" });
1680
1816
  }
1681
1817
  break;
1682
1818
  }
@@ -1688,9 +1824,9 @@ function walkSection(node, blocks, tableCtx, tableStack, ctx, depth = 0) {
1688
1824
  }
1689
1825
  function handleShape(el, sink, ctx) {
1690
1826
  const imgRef = extractImageRef(el);
1691
- const drawTextChild = findDescendant(el, "drawText");
1827
+ const drawTextChild = findDescendant2(el, "drawText");
1692
1828
  if (imgRef) {
1693
- const block = { type: "image", text: imgRef, pageNumber: ctx.sectionNum };
1829
+ const block = { type: "image", text: imgRef, pageNumber: ctx.page };
1694
1830
  const alt = userShapeComment(el);
1695
1831
  if (alt) block.footnoteText = alt;
1696
1832
  sink.push(block);
@@ -1701,11 +1837,11 @@ function handleShape(el, sink, ctx) {
1701
1837
  const capEl = findChildByLocalName(el, "caption");
1702
1838
  if (capEl) {
1703
1839
  const capText = collectSubListText(capEl, ctx);
1704
- if (capText) sink.push({ type: "paragraph", text: capText, pageNumber: ctx.sectionNum });
1840
+ if (capText) sink.push({ type: "paragraph", text: capText, pageNumber: ctx.page });
1705
1841
  }
1706
1842
  if (!imgRef && !drawTextChild && ctx.warnings && ctx.sectionNum) {
1707
1843
  const localTag = (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
1708
- ctx.warnings.push({ page: ctx.sectionNum, message: `\uC2A4\uD0B5\uB41C \uC694\uC18C: ${localTag}`, code: "SKIPPED_IMAGE" });
1844
+ ctx.warnings.push({ page: ctx.page, message: `\uC2A4\uD0B5\uB41C \uC694\uC18C: ${localTag}`, code: "SKIPPED_IMAGE" });
1709
1845
  }
1710
1846
  }
1711
1847
  function userShapeComment(el) {
@@ -1790,7 +1926,7 @@ function collectSubListContent(el, ctx, depth = 0) {
1790
1926
  const t = extractParagraphInfo(ch, ctx.styleMap, ctx).text;
1791
1927
  if (t) {
1792
1928
  parts.push(t);
1793
- out.blocks.push({ type: "paragraph", text: t, pageNumber: ctx.sectionNum });
1929
+ out.blocks.push({ type: "paragraph", text: t, pageNumber: ctx.page });
1794
1930
  }
1795
1931
  const tbls = [];
1796
1932
  findTopLevelTbls(ch, tbls);
@@ -1863,7 +1999,7 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1863
1999
  const chan = kordocTableChannel(el, ctx);
1864
2000
  if (chan) {
1865
2001
  if (chan.kind === "heading" && chan.text) {
1866
- blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.sectionNum });
2002
+ blocks.push({ type: "heading", level: chan.level, text: chan.text, pageNumber: ctx.page });
1867
2003
  }
1868
2004
  continue;
1869
2005
  }
@@ -1875,11 +2011,11 @@ function walkParagraphChildren(node, blocks, tableCtx, tableStack, ctx, depth =
1875
2011
  } else if (localTag === "caption" && !inShape) {
1876
2012
  const cap = collectSubListContent(el, ctx);
1877
2013
  if (cap.text) {
1878
- if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: cap.text, pageNumber: ctx.sectionNum }]);
2014
+ if (tableCtx?.cell) mergeBlocksIntoCell(tableCtx.cell, [{ type: "paragraph", text: cap.text, pageNumber: ctx.page }]);
1879
2015
  else if (tableCtx) {
1880
2016
  tableCtx.caption = (tableCtx.caption ? tableCtx.caption + "\n" : "") + cap.text;
1881
2017
  if (cap.hasStructure) (tableCtx.captionBlocks ??= []).push(...cap.blocks);
1882
- } else blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.sectionNum });
2018
+ } else blocks.push({ type: "paragraph", text: cap.text, pageNumber: ctx.page });
1883
2019
  }
1884
2020
  } else if (localTag === "pic" || localTag === "shape" || localTag === "drawingObject") {
1885
2021
  if (tableCtx?.cell) {
@@ -1924,7 +2060,7 @@ function hasObjectDescendant(node, depth = 0) {
1924
2060
  function isInlineTbl(tbl2) {
1925
2061
  return findChildByLocalName(tbl2, "pos")?.getAttribute("treatAsChar") === "1";
1926
2062
  }
1927
- function findDescendant(node, targetTag, depth = 0) {
2063
+ function findDescendant2(node, targetTag, depth = 0) {
1928
2064
  if (depth > 5) return null;
1929
2065
  const children = node.childNodes;
1930
2066
  if (!children) return null;
@@ -1933,7 +2069,7 @@ function findDescendant(node, targetTag, depth = 0) {
1933
2069
  if (child.nodeType !== 1) continue;
1934
2070
  const tag = (child.tagName || child.localName || "").replace(/^[^:]+:/, "");
1935
2071
  if (tag === targetTag) return child;
1936
- const found = findDescendant(child, targetTag, depth + 1);
2072
+ const found = findDescendant2(child, targetTag, depth + 1);
1937
2073
  if (found) return found;
1938
2074
  }
1939
2075
  return null;
@@ -1954,7 +2090,7 @@ function extractDrawTextBlocks(drawTextNode, blocks, ctx) {
1954
2090
  const ph = resolveParaHeading(child, ctx);
1955
2091
  if (text) {
1956
2092
  if (ph?.prefix) text = ph.prefix + " " + text;
1957
- const block = { type: "paragraph", text, style: info.style ?? void 0, pageNumber: ctx.sectionNum };
2093
+ const block = { type: "paragraph", text, style: info.style ?? void 0, pageNumber: ctx.page };
1958
2094
  if (info.href) block.href = info.href;
1959
2095
  if (info.footnote) block.footnoteText = info.footnote;
1960
2096
  blocks.push(block);
@@ -2057,7 +2193,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2057
2193
  // 숨은 설명 — 본문 혼입 차단
2058
2194
  case "hiddenComment": {
2059
2195
  if (ctx?.warnings && extractTextFromNode(k)) {
2060
- ctx.warnings.push({ page: ctx.sectionNum, message: "\uC228\uC740 \uC124\uBA85 \uD14D\uC2A4\uD2B8 \uC81C\uC678: hiddenComment", code: "HIDDEN_TEXT_FILTERED" });
2196
+ ctx.warnings.push({ page: ctx.page, message: "\uC228\uC740 \uC124\uBA85 \uD14D\uC2A4\uD2B8 \uC81C\uC678: hiddenComment", code: "HIDDEN_TEXT_FILTERED" });
2061
2197
  }
2062
2198
  break;
2063
2199
  }
@@ -2077,7 +2213,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2077
2213
  // 미지원 요소 — 텍스트를 가졌으면 무음 손실 대신 경고
2078
2214
  default: {
2079
2215
  if (ctx?.warnings && extractTextFromNode(k)) {
2080
- ctx.warnings.push({ page: ctx.sectionNum, message: `\uBBF8\uC9C0\uC6D0 \uC81C\uC5B4 \uC694\uC18C\uC758 \uD14D\uC2A4\uD2B8 \uC190\uC2E4: ${ktag}`, code: "UNSUPPORTED_ELEMENT" });
2216
+ ctx.warnings.push({ page: ctx.page, message: `\uBBF8\uC9C0\uC6D0 \uC81C\uC5B4 \uC694\uC18C\uC758 \uD14D\uC2A4\uD2B8 \uC190\uC2E4: ${ktag}`, code: "UNSUPPORTED_ELEMENT" });
2081
2217
  }
2082
2218
  }
2083
2219
  }
@@ -2094,7 +2230,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2094
2230
  if (isInDeletedRange(ctx)) {
2095
2231
  if (t && ctx && !ctx.shared.track.warned) {
2096
2232
  ctx.shared.track.warned = true;
2097
- ctx.warnings?.push({ page: ctx.sectionNum, message: "\uBCC0\uACBD\uCD94\uC801 \uC0AD\uC81C \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uC81C\uC678", code: "HIDDEN_TEXT_FILTERED" });
2233
+ ctx.warnings?.push({ page: ctx.page, message: "\uBCC0\uACBD\uCD94\uC801 \uC0AD\uC81C \uD14D\uC2A4\uD2B8 \uCD9C\uB825 \uC81C\uC678", code: "HIDDEN_TEXT_FILTERED" });
2098
2234
  }
2099
2235
  } else {
2100
2236
  text += t.replace(/\x1E/g, "");
@@ -2206,7 +2342,7 @@ function extractParagraphInfo(para2, styleMap, ctx) {
2206
2342
  } catch {
2207
2343
  if (ctx?.warnings) {
2208
2344
  ctx.warnings.push({
2209
- page: ctx.sectionNum,
2345
+ page: ctx.page,
2210
2346
  message: `\uC218\uC2DD LaTeX \uBCC0\uD658 \uC2E4\uD328 \u2014 \uC218\uC2DD \uD14D\uC2A4\uD2B8 \uC81C\uC678: ${raw.trim().slice(0, 40)}`,
2211
2347
  code: "PARTIAL_PARSE"
2212
2348
  });
@@ -2363,6 +2499,7 @@ import { inflateRawSync as inflateRawSync2, inflateSync } from "zlib";
2363
2499
  var TAG_PARA_HEADER = 66;
2364
2500
  var TAG_PARA_TEXT = 67;
2365
2501
  var TAG_CHAR_SHAPE = 68;
2502
+ var TAG_PARA_LINE_SEG = 69;
2366
2503
  var TAG_CTRL_HEADER = 71;
2367
2504
  var TAG_LIST_HEADER = 72;
2368
2505
  var TAG_TABLE = 77;
@@ -2997,7 +3134,9 @@ function extractFromBrokenZip(buffer) {
2997
3134
  totalDecompressed += content.length * 2;
2998
3135
  if (totalDecompressed > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC");
2999
3136
  sectionNum++;
3000
- blocks.push(...parseSectionXml(content, void 0, warnings, sectionNum, shared));
3137
+ const secBlocks = parseSectionXml(content, void 0, warnings, sectionNum, shared);
3138
+ for (const b of secBlocks) b.pageNumber = sectionNum;
3139
+ blocks.push(...secBlocks);
3001
3140
  } catch {
3002
3141
  continue;
3003
3142
  }
@@ -3149,33 +3288,48 @@ async function parseHwpxDocument(buffer, options) {
3149
3288
  const warnings = [];
3150
3289
  const sectionPaths = await resolveSectionPaths(zip);
3151
3290
  if (sectionPaths.length === 0) throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
3152
- metadata.pageCount = sectionPaths.length;
3153
- const pageFilter = options?.pages ? parsePageRange(options.pages, sectionPaths.length) : null;
3154
- const totalTarget = pageFilter ? pageFilter.size : sectionPaths.length;
3155
- const blocks = [];
3291
+ let allBlocks = [];
3156
3292
  const shared = createSectionShared();
3157
3293
  shared.kordocLayout = await readKordocLayout(zip);
3158
3294
  shared.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
3159
3295
  shared.keepEmptyParagraphs = options?.keepEmptyParagraphs;
3296
+ const sectionRanges = [];
3160
3297
  let parsedSections = 0;
3161
3298
  for (let si = 0; si < sectionPaths.length; si++) {
3162
- if (pageFilter && !pageFilter.has(si + 1)) continue;
3163
3299
  const file = zip.file(sectionPaths[si]);
3164
3300
  if (!file) continue;
3165
3301
  try {
3166
3302
  const xml = await file.async("text");
3167
3303
  decompressed.total += xml.length * 2;
3168
3304
  if (decompressed.total > MAX_DECOMPRESS_SIZE) throw new ZipBombError("ZIP \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (ZIP bomb \uC758\uC2EC)");
3169
- blocks.push(...parseSectionXml(xml, styleMap, warnings, si + 1, shared));
3305
+ const start = allBlocks.length;
3306
+ allBlocks.push(...parseSectionXml(xml, styleMap, warnings, si + 1, shared));
3307
+ sectionRanges.push({ sectionNum: si + 1, start, end: allBlocks.length });
3170
3308
  parsedSections++;
3171
- options?.onProgress?.(parsedSections, totalTarget);
3309
+ options?.onProgress?.(parsedSections, sectionPaths.length);
3172
3310
  } catch (secErr) {
3173
3311
  if (secErr instanceof ZipBombError) throw secErr;
3174
3312
  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" });
3175
3313
  }
3176
3314
  }
3315
+ const layoutPages = shared.pageState.allUsable && parsedSections > 0;
3316
+ metadata.pageMode = layoutPages ? "layout" : "section";
3317
+ metadata.pageCount = layoutPages ? Math.max(shared.pageState.base, 1) : sectionPaths.length;
3318
+ if (!layoutPages) {
3319
+ for (const r of sectionRanges) {
3320
+ for (let b = r.start; b < r.end; b++) allBlocks[b].pageNumber = r.sectionNum;
3321
+ }
3322
+ }
3323
+ if (options?.pages) {
3324
+ const pageFilter = parsePageRange(options.pages, metadata.pageCount);
3325
+ allBlocks = allBlocks.filter((b) => b.pageNumber != null && pageFilter.has(b.pageNumber));
3326
+ if (!layoutPages) {
3327
+ 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" });
3328
+ }
3329
+ }
3330
+ const blocks = allBlocks;
3177
3331
  applyPageText(blocks, shared);
3178
- const images = await extractImagesFromZip(zip, blocks, decompressed, warnings, !pageFilter);
3332
+ const images = await extractImagesFromZip(zip, blocks, decompressed, warnings, !options?.pages);
3179
3333
  detectHwpxHeadings(blocks, styleMap);
3180
3334
  const outline = blocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
3181
3335
  const markdown = blocksToMarkdown(blocks);
@@ -4196,6 +4350,112 @@ function assertSupportedEncryptVersion(version) {
4196
4350
  }
4197
4351
  }
4198
4352
 
4353
+ // src/hwp5/page-boundary.ts
4354
+ var MIDPAGE_V2 = 2e3;
4355
+ var CTRL_ID_TBL = 1952607264;
4356
+ function tableIntraBreaks2(records, start, end) {
4357
+ const rowMax = /* @__PURE__ */ new Map();
4358
+ let curRow = -1;
4359
+ let prevV = Number.NEGATIVE_INFINITY;
4360
+ let resets = 0;
4361
+ const flush = () => {
4362
+ if (curRow >= 0) rowMax.set(curRow, Math.max(rowMax.get(curRow) ?? 0, resets));
4363
+ resets = 0;
4364
+ prevV = Number.NEGATIVE_INFINITY;
4365
+ };
4366
+ for (let i = start; i < end; i++) {
4367
+ const rec = records[i];
4368
+ if (rec.tagId === TAG_LIST_HEADER && rec.level === 2 && rec.data.length >= 12) {
4369
+ flush();
4370
+ curRow = rec.data.readUInt16LE(10);
4371
+ } else if (rec.tagId === TAG_PARA_LINE_SEG && rec.level === 3 && curRow >= 0) {
4372
+ for (let off = 0; off + 36 <= rec.data.length; off += 36) {
4373
+ const v = rec.data.readInt32LE(off + 4);
4374
+ if (v < prevV) resets++;
4375
+ prevV = v;
4376
+ }
4377
+ }
4378
+ }
4379
+ flush();
4380
+ let sum = 0;
4381
+ for (const v of rowMax.values()) sum += v;
4382
+ return sum;
4383
+ }
4384
+ function detectHwp5SectionPages(records) {
4385
+ const pageAtPara = [];
4386
+ let cur = 0;
4387
+ let prevV = Number.NEGATIVE_INFINITY;
4388
+ let prevH = Number.NEGATIVE_INFINITY;
4389
+ let first = true;
4390
+ let suppressMidReset = false;
4391
+ let parasWithSegs = 0;
4392
+ let i = 0;
4393
+ while (i < records.length) {
4394
+ const rec = records[i];
4395
+ if (rec.tagId !== TAG_PARA_HEADER || rec.level !== 0) {
4396
+ i++;
4397
+ continue;
4398
+ }
4399
+ let j = i + 1;
4400
+ while (j < records.length && records[j].level > 0) j++;
4401
+ const divideSort = rec.data.length >= 12 ? rec.data.readUInt8(11) : 0;
4402
+ const explicit = (divideSort & 4) !== 0;
4403
+ let brokeByExplicit = false;
4404
+ if (explicit && !first) {
4405
+ cur++;
4406
+ brokeByExplicit = true;
4407
+ suppressMidReset = false;
4408
+ }
4409
+ let paraFirst = true;
4410
+ let startPage = cur;
4411
+ let hadSegs = false;
4412
+ let intraAdd = 0;
4413
+ for (let k = i + 1; k < j; k++) {
4414
+ const r = records[k];
4415
+ if (r.tagId === TAG_PARA_LINE_SEG && r.level === 1) {
4416
+ hadSegs = true;
4417
+ for (let off = 0; off + 36 <= r.data.length; off += 36) {
4418
+ const v = r.data.readInt32LE(off + 4);
4419
+ const h = r.data.readInt32LE(off + 24);
4420
+ const brk = v < prevV || paraFirst && v === prevV && h <= prevH;
4421
+ if (brk && !(paraFirst && brokeByExplicit)) {
4422
+ if (paraFirst && suppressMidReset && v >= MIDPAGE_V2) {
4423
+ } else {
4424
+ cur++;
4425
+ }
4426
+ }
4427
+ if (paraFirst) {
4428
+ suppressMidReset = false;
4429
+ startPage = cur;
4430
+ }
4431
+ paraFirst = false;
4432
+ prevV = v;
4433
+ prevH = h;
4434
+ }
4435
+ }
4436
+ if (r.tagId === TAG_CTRL_HEADER && r.level === 1 && r.data.length >= 4 && r.data.readUInt32LE(0) === CTRL_ID_TBL) {
4437
+ let ce = k + 1;
4438
+ while (ce < j && records[ce].level > 1) ce++;
4439
+ intraAdd += tableIntraBreaks2(records, k + 1, ce);
4440
+ k = ce - 1;
4441
+ }
4442
+ }
4443
+ if (hadSegs) parasWithSegs++;
4444
+ pageAtPara.push(startPage);
4445
+ first = false;
4446
+ if (intraAdd > 0) {
4447
+ cur += intraAdd;
4448
+ suppressMidReset = true;
4449
+ }
4450
+ i = j;
4451
+ }
4452
+ return {
4453
+ pageAtPara,
4454
+ pages: cur + 1,
4455
+ usable: pageAtPara.length > 0 && parasWithSegs === pageAtPara.length
4456
+ };
4457
+ }
4458
+
4199
4459
  // src/hwp5/parser.ts
4200
4460
  import { createRequire } from "module";
4201
4461
  var require2 = createRequire(import.meta.url);
@@ -4321,31 +4581,63 @@ function parseHwp5Document(buffer, options) {
4321
4581
  const docInfo = cfb && !encrypted ? parseDocInfoStream(cfb, compressed) : parseDocInfoFromStream(findStream("/DocInfo"), compressed);
4322
4582
  const sections = distribution ? cfb ? findViewTextSections(cfb, compressed) : findViewTextSectionsLenient(lenientCfb, compressed) : encrypted ? findSectionsVia(findStream) : cfb ? findSections(cfb) : findSectionsLenient(lenientCfb, compressed);
4323
4583
  if (sections.length === 0) throw new KordocError("\uC139\uC158 \uC2A4\uD2B8\uB9BC\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
4324
- metadata.pageCount = sections.length;
4325
- const pageFilter = options?.pages ? parsePageRange(options.pages, sections.length) : null;
4326
- const totalTarget = pageFilter ? pageFilter.size : sections.length;
4327
- const bodyBlocks = [];
4328
- const doc = createHwp5DocState();
4329
- doc.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
4584
+ const sectionRecords = [];
4585
+ const sectionDetects = [];
4330
4586
  let totalDecompressed = 0;
4331
- let parsedSections = 0;
4332
4587
  for (let si = 0; si < sections.length; si++) {
4333
- if (pageFilter && !pageFilter.has(si + 1)) continue;
4334
4588
  try {
4335
- const sectionData = sections[si];
4336
- const data = !distribution && compressed ? decompressStream(Buffer.from(sectionData)) : Buffer.from(sectionData);
4589
+ const data = !distribution && compressed ? decompressStream(Buffer.from(sections[si])) : Buffer.from(sections[si]);
4337
4590
  totalDecompressed += data.length;
4338
4591
  if (totalDecompressed > MAX_TOTAL_DECOMPRESS) throw new KordocError("\uCD1D \uC555\uCD95 \uD574\uC81C \uD06C\uAE30 \uCD08\uACFC (decompression bomb \uC758\uC2EC)");
4339
4592
  const records = readRecords(data);
4340
- const sectionBlocks = parseSection(records, docInfo, warnings, si + 1, doc);
4593
+ sectionRecords.push(records);
4594
+ sectionDetects.push(detectHwp5SectionPages(records));
4595
+ } catch (secErr) {
4596
+ if (secErr instanceof KordocError) throw secErr;
4597
+ sectionRecords.push(null);
4598
+ sectionDetects.push(null);
4599
+ 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" });
4600
+ }
4601
+ }
4602
+ const layoutPages = sectionDetects.length > 0 && sectionDetects.every((d) => d != null && d.usable);
4603
+ metadata.pageMode = layoutPages ? "layout" : "section";
4604
+ metadata.pageCount = layoutPages ? Math.max(sectionDetects.reduce((sum, d) => sum + (d?.pages ?? 0), 0), 1) : sections.length;
4605
+ const pageFilter = options?.pages ? parsePageRange(options.pages, metadata.pageCount) : null;
4606
+ if (pageFilter && !layoutPages) {
4607
+ 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" });
4608
+ }
4609
+ let bodyBlocks = [];
4610
+ const doc = createHwp5DocState();
4611
+ doc.keepTrailingEmptyCols = options?.keepTrailingEmptyCols;
4612
+ let parsedSections = 0;
4613
+ let pageBase = 0;
4614
+ for (let si = 0; si < sections.length; si++) {
4615
+ const records = sectionRecords[si];
4616
+ if (!records) continue;
4617
+ const detect = sectionDetects[si];
4618
+ if (!layoutPages && pageFilter && !pageFilter.has(si + 1)) continue;
4619
+ try {
4620
+ const sectionBlocks = parseSection(
4621
+ records,
4622
+ docInfo,
4623
+ warnings,
4624
+ si + 1,
4625
+ doc,
4626
+ layoutPages ? { base: pageBase, pageAtPara: detect.pageAtPara } : void 0
4627
+ );
4341
4628
  bodyBlocks.push(...sectionBlocks);
4342
4629
  parsedSections++;
4343
- options?.onProgress?.(parsedSections, totalTarget);
4630
+ options?.onProgress?.(parsedSections, sections.length);
4344
4631
  } catch (secErr) {
4345
4632
  if (secErr instanceof KordocError) throw secErr;
4346
4633
  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" });
4634
+ } finally {
4635
+ pageBase += detect.pages;
4347
4636
  }
4348
4637
  }
4638
+ if (pageFilter && layoutPages) {
4639
+ bodyBlocks = bodyBlocks.filter((b) => b.pageNumber != null && pageFilter.has(b.pageNumber));
4640
+ }
4349
4641
  const blocks = [...doc.headerBlocks, ...bodyBlocks, ...doc.footerBlocks];
4350
4642
  const images = cfb ? extractHwp5Images(cfb.FileIndex, blocks, warnings, !pageFilter) : extractHwp5ImagesLenient(lenientCfb, blocks, warnings, !pageFilter);
4351
4643
  let flatBlocks = flattenLayoutTables(blocks);
@@ -4596,8 +4888,17 @@ function createHwp5DocState() {
4596
4888
  footerBlocks: []
4597
4889
  };
4598
4890
  }
4599
- function parseSection(records, docInfo, warnings, sectionNum, doc) {
4600
- const ctx = { docInfo, warnings, sectionNum, doc: doc ?? createHwp5DocState(), depth: 0 };
4891
+ function parseSection(records, docInfo, warnings, sectionNum, doc, pageMap) {
4892
+ const ctx = {
4893
+ docInfo,
4894
+ warnings,
4895
+ sectionNum,
4896
+ doc: doc ?? createHwp5DocState(),
4897
+ depth: 0,
4898
+ page: pageMap ? pageMap.base + 1 : sectionNum,
4899
+ pageMap,
4900
+ topOrdinal: 0
4901
+ };
4601
4902
  return parseParagraphList(records, 0, records.length, ctx);
4602
4903
  }
4603
4904
  function parseParagraphList(records, start, end, ctx) {
@@ -4606,6 +4907,11 @@ function parseParagraphList(records, start, end, ctx) {
4606
4907
  while (i < end) {
4607
4908
  if (records[i].tagId === TAG_PARA_HEADER) {
4608
4909
  const baseLevel = records[i].level;
4910
+ if (baseLevel === 0 && ctx.depth === 0 && ctx.pageMap) {
4911
+ const pg = ctx.pageMap.pageAtPara[ctx.topOrdinal];
4912
+ if (pg !== void 0) ctx.page = ctx.pageMap.base + pg + 1;
4913
+ ctx.topOrdinal++;
4914
+ }
4609
4915
  let j = i + 1;
4610
4916
  while (j < end && records[j].level > baseLevel) j++;
4611
4917
  blocks.push(...parseParagraph(records, i, j, ctx));
@@ -4706,7 +5012,7 @@ function parseParagraph(records, start, end, ctx) {
4706
5012
  const block = {
4707
5013
  type: headingLevel > 0 ? "heading" : "paragraph",
4708
5014
  text: headMarker ? `${headMarker} ${trimmed}` : trimmed,
4709
- pageNumber: ctx.sectionNum
5015
+ pageNumber: ctx.page
4710
5016
  };
4711
5017
  if (headingLevel > 0) block.level = headingLevel;
4712
5018
  if (ctx.docInfo && charShapeIds.length > 0) {
@@ -4723,7 +5029,7 @@ function parseParagraph(records, start, end, ctx) {
4723
5029
  if (footnotes.length > 0) block.footnoteText = footnotes.join("; ");
4724
5030
  blocks.push(block);
4725
5031
  } else if (footnotes.length > 0) {
4726
- blocks.push({ type: "paragraph", text: `(\uC8FC: ${footnotes.join("; ")})`, pageNumber: ctx.sectionNum });
5032
+ blocks.push({ type: "paragraph", text: `(\uC8FC: ${footnotes.join("; ")})`, pageNumber: ctx.page });
4727
5033
  }
4728
5034
  for (const ctrl of ctrls) {
4729
5035
  if (ctrl.afterBlocks) blocks.push(...ctrl.afterBlocks);
@@ -4734,7 +5040,7 @@ function applyCtrlEffect(ctrl, records, ctx) {
4734
5040
  switch (ctrl.id) {
4735
5041
  case CTRL_TBL: {
4736
5042
  const table2 = parseTableControl(ctrl, records, ctx);
4737
- if (table2) ctrl.afterBlocks = [{ type: "table", table: table2, pageNumber: ctx.sectionNum }];
5043
+ if (table2) ctrl.afterBlocks = [{ type: "table", table: table2, pageNumber: ctx.page }];
4738
5044
  return;
4739
5045
  }
4740
5046
  case CTRL_GSO: {
@@ -4786,7 +5092,7 @@ function applyCtrlEffect(ctrl, records, ctx) {
4786
5092
  return;
4787
5093
  }
4788
5094
  case CTRL_OLE: {
4789
- ctx.warnings.push({ page: ctx.sectionNum, message: "\uC2A4\uD0B5\uB41C OLE \uAC1C\uCCB4", code: "SKIPPED_OLE" });
5095
+ ctx.warnings.push({ page: ctx.page, message: "\uC2A4\uD0B5\uB41C OLE \uAC1C\uCCB4", code: "SKIPPED_OLE" });
4790
5096
  return;
4791
5097
  }
4792
5098
  // 숨은 설명/단 정의/쪽번호 위치/감추기/찾아보기/책갈피/글자겹침/덧말 — 본문 텍스트 없음 또는 의도적 스킵
@@ -4863,7 +5169,7 @@ function applyHeaderFooterEffect(ctrl, records, ctx, isHeader) {
4863
5169
  const key = (isHeader ? "h:" : "f:") + text;
4864
5170
  if (ctx.doc.headerTexts.has(key)) return;
4865
5171
  ctx.doc.headerTexts.add(key);
4866
- const block = { type: "paragraph", text, pageNumber: ctx.sectionNum };
5172
+ const block = { type: "paragraph", text, pageNumber: ctx.page };
4867
5173
  if (isHeader) ctx.doc.headerBlocks.push(block);
4868
5174
  else ctx.doc.footerBlocks.push(block);
4869
5175
  }
@@ -5085,11 +5391,11 @@ function pictureToImageBlock(data, ctx) {
5085
5391
  if (binDataId === 0) return null;
5086
5392
  const item = ctx.docInfo?.binData[binDataId - 1];
5087
5393
  if (item?.kind === "link") {
5088
- ctx.warnings.push({ page: ctx.sectionNum, message: `\uC678\uBD80 \uC5F0\uACB0 \uC774\uBBF8\uC9C0 (binDataId ${binDataId})`, code: "SKIPPED_IMAGE" });
5394
+ ctx.warnings.push({ page: ctx.page, message: `\uC678\uBD80 \uC5F0\uACB0 \uC774\uBBF8\uC9C0 (binDataId ${binDataId})`, code: "SKIPPED_IMAGE" });
5089
5395
  return null;
5090
5396
  }
5091
5397
  const storageId = item && item.storageId > 0 ? item.storageId : binDataId;
5092
- return { type: "image", text: String(storageId), pageNumber: ctx.sectionNum };
5398
+ return { type: "image", text: String(storageId), pageNumber: ctx.page };
5093
5399
  }
5094
5400
  function resolveCharStyle(charShapeIds, docInfo) {
5095
5401
  if (charShapeIds.length === 0 || docInfo.charShapes.length === 0) return void 0;
@@ -18980,8 +19286,8 @@ function isDisplayMath(el) {
18980
19286
 
18981
19287
  // src/docx/parser.ts
18982
19288
  var MAX_DECOMPRESS_SIZE5 = 100 * 1024 * 1024;
18983
- function matchesLocal(el, localName2) {
18984
- return el.localName === localName2 || (el.tagName?.endsWith(`:${localName2}`) ?? false);
19289
+ function matchesLocal(el, localName3) {
19290
+ return el.localName === localName3 || (el.tagName?.endsWith(`:${localName3}`) ?? false);
18985
19291
  }
18986
19292
  function effectiveChildElements(parent) {
18987
19293
  const result = [];
@@ -19005,10 +19311,10 @@ function effectiveChildElements(parent) {
19005
19311
  }
19006
19312
  return result;
19007
19313
  }
19008
- function getChildElements(parent, localName2) {
19009
- return effectiveChildElements(parent).filter((el) => matchesLocal(el, localName2));
19314
+ function getChildElements(parent, localName3) {
19315
+ return effectiveChildElements(parent).filter((el) => matchesLocal(el, localName3));
19010
19316
  }
19011
- function findElements(parent, localName2) {
19317
+ function findElements(parent, localName3) {
19012
19318
  const result = [];
19013
19319
  const walk = (node) => {
19014
19320
  const children = node.childNodes;
@@ -19016,7 +19322,7 @@ function findElements(parent, localName2) {
19016
19322
  const child = children[i];
19017
19323
  if (child.nodeType === 1) {
19018
19324
  const el = child;
19019
- if (el.localName === localName2 || el.tagName?.endsWith(`:${localName2}`)) {
19325
+ if (el.localName === localName3 || el.tagName?.endsWith(`:${localName3}`)) {
19020
19326
  result.push(el);
19021
19327
  }
19022
19328
  walk(el);
@@ -19026,11 +19332,11 @@ function findElements(parent, localName2) {
19026
19332
  walk(parent);
19027
19333
  return result;
19028
19334
  }
19029
- function getAttr(el, localName2) {
19335
+ function getAttr(el, localName3) {
19030
19336
  const attrs = el.attributes;
19031
19337
  for (let i = 0; i < attrs.length; i++) {
19032
19338
  const attr = attrs[i];
19033
- if (attr.localName === localName2 || attr.name === localName2) return attr.value;
19339
+ if (attr.localName === localName3 || attr.name === localName3) return attr.value;
19034
19340
  }
19035
19341
  return null;
19036
19342
  }
@@ -19549,8 +19855,8 @@ async function parseDocxDocument(buffer, options) {
19549
19855
  const bodyEl = body[0];
19550
19856
  const topLevel = effectiveChildElements(bodyEl);
19551
19857
  for (const el of topLevel) {
19552
- const localName2 = el.localName ?? el.tagName?.split(":").pop();
19553
- if (localName2 === "p") {
19858
+ const localName3 = el.localName ?? el.tagName?.split(":").pop();
19859
+ if (localName3 === "p") {
19554
19860
  const block = parseParagraph2(el, styles, numbering, footnotes, rels);
19555
19861
  if (block) blocks.push(block);
19556
19862
  if (imageMap.size > 0) emitParagraphImages(el, imageMap, linkedImages, blocks);
@@ -19559,7 +19865,7 @@ async function parseDocxDocument(buffer, options) {
19559
19865
  if (tb) blocks.push(tb);
19560
19866
  if (imageMap.size > 0) emitParagraphImages(tp, imageMap, linkedImages, blocks);
19561
19867
  }
19562
- } else if (localName2 === "tbl") {
19868
+ } else if (localName3 === "tbl") {
19563
19869
  const block = parseTable(el, styles, numbering, footnotes, rels, options?.keepTrailingEmptyCols);
19564
19870
  if (block) blocks.push(block);
19565
19871
  }
@@ -27165,7 +27471,7 @@ var Surgeon = class {
27165
27471
  // src/roundtrip/hwp5-patch.ts
27166
27472
  var require3 = createRequire2(import.meta.url);
27167
27473
  var CFB2 = require3("cfb");
27168
- var TAG_PARA_LINE_SEG = 69;
27474
+ var TAG_PARA_LINE_SEG2 = 69;
27169
27475
  function cid2(s) {
27170
27476
  return (s.charCodeAt(0) << 24 | s.charCodeAt(1) << 16 | s.charCodeAt(2) << 8 | s.charCodeAt(3)) >>> 0;
27171
27477
  }
@@ -27246,7 +27552,7 @@ function scanSection(stream, sectionIndex, compressed) {
27246
27552
  textIdx = textIdx === -1 ? j : -2;
27247
27553
  appendParaText(state, records[j].data);
27248
27554
  } else if (t === TAG_CHAR_SHAPE && charShapeIdx === -1) charShapeIdx = j;
27249
- else if (t === TAG_PARA_LINE_SEG && lineSegIdx === -1) lineSegIdx = j;
27555
+ else if (t === TAG_PARA_LINE_SEG2 && lineSegIdx === -1) lineSegIdx = j;
27250
27556
  }
27251
27557
  let ctrlSeen = false, nonGso = false;
27252
27558
  for (let a = parent[i]; a >= 0; a = parent[a]) {
@@ -28589,7 +28895,7 @@ async function parse(input, options) {
28589
28895
  }
28590
28896
  async function parseImage(buffer, options) {
28591
28897
  try {
28592
- const { parseImageDocument } = await import("./image-ocr-2UMWFGRS.js");
28898
+ const { parseImageDocument } = await import("./image-ocr-B7TMIG3F.js");
28593
28899
  const { blocks, warnings } = await parseImageDocument(buffer, options);
28594
28900
  return {
28595
28901
  success: true,
@@ -28648,7 +28954,7 @@ async function parseHwp(buffer, options) {
28648
28954
  async function parsePdf(buffer, options) {
28649
28955
  let parsePdfDocument;
28650
28956
  try {
28651
- const mod = await import("./parser-QPMTNRPU.js");
28957
+ const mod = await import("./parser-HT7GZOF2.js");
28652
28958
  parsePdfDocument = mod.parsePdfDocument;
28653
28959
  } catch {
28654
28960
  return {
@@ -28794,4 +29100,4 @@ export {
28794
29100
  parseHwpml,
28795
29101
  fillForm
28796
29102
  };
28797
- //# sourceMappingURL=chunk-QXC5NPO2.js.map
29103
+ //# sourceMappingURL=chunk-W5NZYPRQ.js.map