kordoc 4.18.3 → 4.18.4

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 (145) hide show
  1. package/dist/{-WOKB25R2.js → -DDAVBMHD.js} +11 -11
  2. package/dist/{chunk-BGOR5GCF.js → chunk-274ZPMHK.js} +2 -2
  3. package/dist/{chunk-2ZBLQVAS.cjs → chunk-3WLZOJIE.cjs} +6 -6
  4. package/dist/chunk-3WLZOJIE.cjs.map +1 -0
  5. package/dist/{chunk-SHRRD2TD.cjs → chunk-3XSP7V5P.cjs} +2 -2
  6. package/dist/chunk-3XSP7V5P.cjs.map +1 -0
  7. package/dist/{chunk-6GLTGOP6.js → chunk-4IBY5LTJ.js} +2 -2
  8. package/dist/chunk-6H25SXDI.js +11 -0
  9. package/dist/chunk-6H25SXDI.js.map +1 -0
  10. package/dist/{chunk-CAN5P6N6.js → chunk-7GR2GCJF.js} +2 -2
  11. package/dist/{chunk-HZV52V2X.cjs → chunk-AVIZ55NQ.cjs} +3 -3
  12. package/dist/chunk-AVIZ55NQ.cjs.map +1 -0
  13. package/dist/chunk-DC6CTQTK.js +9 -0
  14. package/dist/chunk-DC6CTQTK.js.map +1 -0
  15. package/dist/{chunk-NES5U4JK.js → chunk-DUV7Y6IQ.js} +3 -3
  16. package/dist/{chunk-WN5YLP7H.cjs → chunk-E2644XCO.cjs} +693 -187
  17. package/dist/chunk-E2644XCO.cjs.map +1 -0
  18. package/dist/{chunk-3FZGL3IL.js → chunk-ELOPDQES.js} +2 -2
  19. package/dist/{chunk-2I2RTJJ5.js → chunk-F2GDLA45.js} +25 -35
  20. package/dist/chunk-F2GDLA45.js.map +1 -0
  21. package/dist/{chunk-VGDTONTB.js → chunk-F5YVCJVF.js} +2 -2
  22. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  23. package/dist/{chunk-RDBTOE5X.js → chunk-HRJJDNRD.js} +2 -2
  24. package/dist/{chunk-KSJ7GKNQ.js → chunk-IAXGGQGD.js} +2 -2
  25. package/dist/{chunk-IXX3WHIE.js → chunk-IBQCK4F3.js} +7 -3
  26. package/dist/chunk-IBQCK4F3.js.map +1 -0
  27. package/dist/chunk-J55UQC5H.cjs.map +1 -1
  28. package/dist/{chunk-J2A46VCA.js → chunk-KUUSP6OS.js} +576 -70
  29. package/dist/chunk-KUUSP6OS.js.map +1 -0
  30. package/dist/{chunk-RSDUH4XF.js → chunk-LGF2IO66.js} +2 -2
  31. package/dist/{chunk-BJ6RYYXV.js → chunk-MXA6YW2W.js} +577 -71
  32. package/dist/chunk-MXA6YW2W.js.map +1 -0
  33. package/dist/{chunk-ZD5WNQNC.cjs → chunk-OIUSF2VA.cjs} +16 -12
  34. package/dist/chunk-OIUSF2VA.cjs.map +1 -0
  35. package/dist/{chunk-AZQWESRH.js → chunk-QVCHFCT2.js} +2 -2
  36. package/dist/{chunk-W7P4GPOV.js → chunk-RCI7LY4N.js} +8 -4
  37. package/dist/chunk-RCI7LY4N.js.map +1 -0
  38. package/dist/{chunk-V4BKT65D.cjs → chunk-SVNBZEL6.cjs} +3 -3
  39. package/dist/chunk-SVNBZEL6.cjs.map +1 -0
  40. package/dist/{chunk-OOX2GZZ4.js → chunk-T2BJPT6D.js} +3 -3
  41. package/dist/{chunk-OOX2GZZ4.js.map → chunk-T2BJPT6D.js.map} +1 -1
  42. package/dist/chunk-TK3FDAEE.cjs +9 -0
  43. package/dist/chunk-TK3FDAEE.cjs.map +1 -0
  44. package/dist/{chunk-UVUKT3IQ.js → chunk-TMJVD6FW.js} +2 -2
  45. package/dist/{chunk-ZN3LHNJG.js → chunk-V5X4LLVM.js} +28 -27
  46. package/dist/chunk-V5X4LLVM.js.map +1 -0
  47. package/dist/{chunk-W3RADOLO.js → chunk-VTVK2JMI.js} +2 -2
  48. package/dist/{chunk-PSJLB3SZ.js → chunk-XG4SYOZL.js} +2 -2
  49. package/dist/{chunk-RRURZDEK.js → chunk-YCY2R4FM.js} +2 -2
  50. package/dist/{chunk-5X7XWYR2.js → chunk-Z2PPDQK2.js} +2 -2
  51. package/dist/chunks-5QTTAT5T.js +10 -0
  52. package/dist/cli.js +154 -115
  53. package/dist/cli.js.map +1 -1
  54. package/dist/{formula-QZAOKPBX.js → formula-7ZOZ5QJ6.js} +8 -4
  55. package/dist/{formula-QZAOKPBX.js.map → formula-7ZOZ5QJ6.js.map} +1 -1
  56. package/dist/{formula-GPCOJJXN.js → formula-HQFZEXFF.js} +7 -3
  57. package/dist/{formula-GPCOJJXN.js.map → formula-HQFZEXFF.js.map} +1 -1
  58. package/dist/{formula-BQ5RW5XD.cjs → formula-YYJQBKEN.cjs} +13 -9
  59. package/dist/formula-YYJQBKEN.cjs.map +1 -0
  60. package/dist/{image-ocr-POCME5I4.js → image-ocr-4BGX2EJJ.js} +7 -6
  61. package/dist/{image-ocr-POCME5I4.js.map → image-ocr-4BGX2EJJ.js.map} +1 -1
  62. package/dist/{image-ocr-VKIUBLBX.cjs → image-ocr-CC26Q5KG.cjs} +16 -15
  63. package/dist/image-ocr-CC26Q5KG.cjs.map +1 -0
  64. package/dist/{image-ocr-TWNNVTZS.js → image-ocr-RK3HJVCI.js} +10 -9
  65. package/dist/{image-ocr-TWNNVTZS.js.map → image-ocr-RK3HJVCI.js.map} +1 -1
  66. package/dist/index.cjs +338 -347
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.js +42 -51
  69. package/dist/index.js.map +1 -1
  70. package/dist/mcp.js +22 -22
  71. package/dist/{model-bundle-THYUL7GG.js → model-bundle-VHRXG63R.js} +5 -5
  72. package/dist/{models-U22N4RXX.js → models-G3WA24SY.js} +5 -5
  73. package/dist/page-range-QHHFTL7X.cjs.map +1 -1
  74. package/dist/{parser-JOMFOZLH.cjs → parser-BGORCLIU.cjs} +449 -244
  75. package/dist/parser-BGORCLIU.cjs.map +1 -0
  76. package/dist/{parser-SCXRLOW7.js → parser-GFDGAVLV.js} +242 -37
  77. package/dist/parser-GFDGAVLV.js.map +1 -0
  78. package/dist/{parser-NKP2NCSV.js → parser-SOWTUOMK.js} +245 -40
  79. package/dist/parser-SOWTUOMK.js.map +1 -0
  80. package/dist/pdf-ocr-7IOOVR2I.js +20 -0
  81. package/dist/pdf-ocr-EJ2HNZN6.cjs +17 -0
  82. package/dist/pdf-ocr-EJ2HNZN6.cjs.map +1 -0
  83. package/dist/pdf-ocr-RACAZU3N.js +16 -0
  84. package/dist/{profile-io-UI3TZBYH.js → profile-io-Q2T47RVH.js} +3 -3
  85. package/dist/{rasterize-XMBV5H2I.js → rasterize-OAVMUTE6.js} +3 -3
  86. package/dist/{redact-doc-7ZZMHDBO.js → redact-doc-ECFOVR4X.js} +11 -11
  87. package/dist/{render-SWRTUDAV.js → render-NSKWZVTD.js} +7 -7
  88. package/dist/seal-A4MG7MPQ.js +10 -0
  89. package/dist/{setup-43JJEEU7.js → setup-REH2KZP2.js} +3 -3
  90. package/dist/visual-B56WQAGW.js +20 -0
  91. package/dist/{watch-NRATQ472.js → watch-JXDUZYBY.js} +12 -12
  92. package/package.json +1 -1
  93. package/dist/chunk-2I2RTJJ5.js.map +0 -1
  94. package/dist/chunk-2ZBLQVAS.cjs.map +0 -1
  95. package/dist/chunk-BJ6RYYXV.js.map +0 -1
  96. package/dist/chunk-HZV52V2X.cjs.map +0 -1
  97. package/dist/chunk-IXX3WHIE.js.map +0 -1
  98. package/dist/chunk-J2A46VCA.js.map +0 -1
  99. package/dist/chunk-SHRRD2TD.cjs.map +0 -1
  100. package/dist/chunk-V4BKT65D.cjs.map +0 -1
  101. package/dist/chunk-W7P4GPOV.js.map +0 -1
  102. package/dist/chunk-WN5YLP7H.cjs.map +0 -1
  103. package/dist/chunk-ZD5WNQNC.cjs.map +0 -1
  104. package/dist/chunk-ZN3LHNJG.js.map +0 -1
  105. package/dist/chunks-OQDRDICD.js +0 -10
  106. package/dist/formula-BQ5RW5XD.cjs.map +0 -1
  107. package/dist/image-ocr-VKIUBLBX.cjs.map +0 -1
  108. package/dist/parser-JOMFOZLH.cjs.map +0 -1
  109. package/dist/parser-NKP2NCSV.js.map +0 -1
  110. package/dist/parser-SCXRLOW7.js.map +0 -1
  111. package/dist/pdf-ocr-NF4AY5MI.cjs +0 -16
  112. package/dist/pdf-ocr-NF4AY5MI.cjs.map +0 -1
  113. package/dist/pdf-ocr-OLII3WAG.js +0 -15
  114. package/dist/pdf-ocr-VJL7ON2Q.js +0 -19
  115. package/dist/seal-UKKJZHKE.js +0 -10
  116. package/dist/visual-D2563HCJ.js +0 -20
  117. /package/dist/{-WOKB25R2.js.map → -DDAVBMHD.js.map} +0 -0
  118. /package/dist/{chunk-BGOR5GCF.js.map → chunk-274ZPMHK.js.map} +0 -0
  119. /package/dist/{chunk-6GLTGOP6.js.map → chunk-4IBY5LTJ.js.map} +0 -0
  120. /package/dist/{chunk-CAN5P6N6.js.map → chunk-7GR2GCJF.js.map} +0 -0
  121. /package/dist/{chunk-NES5U4JK.js.map → chunk-DUV7Y6IQ.js.map} +0 -0
  122. /package/dist/{chunk-3FZGL3IL.js.map → chunk-ELOPDQES.js.map} +0 -0
  123. /package/dist/{chunk-VGDTONTB.js.map → chunk-F5YVCJVF.js.map} +0 -0
  124. /package/dist/{chunk-RDBTOE5X.js.map → chunk-HRJJDNRD.js.map} +0 -0
  125. /package/dist/{chunk-KSJ7GKNQ.js.map → chunk-IAXGGQGD.js.map} +0 -0
  126. /package/dist/{chunk-RSDUH4XF.js.map → chunk-LGF2IO66.js.map} +0 -0
  127. /package/dist/{chunk-AZQWESRH.js.map → chunk-QVCHFCT2.js.map} +0 -0
  128. /package/dist/{chunk-UVUKT3IQ.js.map → chunk-TMJVD6FW.js.map} +0 -0
  129. /package/dist/{chunk-W3RADOLO.js.map → chunk-VTVK2JMI.js.map} +0 -0
  130. /package/dist/{chunk-PSJLB3SZ.js.map → chunk-XG4SYOZL.js.map} +0 -0
  131. /package/dist/{chunk-RRURZDEK.js.map → chunk-YCY2R4FM.js.map} +0 -0
  132. /package/dist/{chunk-5X7XWYR2.js.map → chunk-Z2PPDQK2.js.map} +0 -0
  133. /package/dist/{chunks-OQDRDICD.js.map → chunks-5QTTAT5T.js.map} +0 -0
  134. /package/dist/{model-bundle-THYUL7GG.js.map → model-bundle-VHRXG63R.js.map} +0 -0
  135. /package/dist/{models-U22N4RXX.js.map → models-G3WA24SY.js.map} +0 -0
  136. /package/dist/{pdf-ocr-OLII3WAG.js.map → pdf-ocr-7IOOVR2I.js.map} +0 -0
  137. /package/dist/{pdf-ocr-VJL7ON2Q.js.map → pdf-ocr-RACAZU3N.js.map} +0 -0
  138. /package/dist/{profile-io-UI3TZBYH.js.map → profile-io-Q2T47RVH.js.map} +0 -0
  139. /package/dist/{rasterize-XMBV5H2I.js.map → rasterize-OAVMUTE6.js.map} +0 -0
  140. /package/dist/{redact-doc-7ZZMHDBO.js.map → redact-doc-ECFOVR4X.js.map} +0 -0
  141. /package/dist/{render-SWRTUDAV.js.map → render-NSKWZVTD.js.map} +0 -0
  142. /package/dist/{seal-UKKJZHKE.js.map → seal-A4MG7MPQ.js.map} +0 -0
  143. /package/dist/{setup-43JJEEU7.js.map → setup-REH2KZP2.js.map} +0 -0
  144. /package/dist/{visual-D2563HCJ.js.map → visual-B56WQAGW.js.map} +0 -0
  145. /package/dist/{watch-NRATQ472.js.map → watch-JXDUZYBY.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  blocksToPages,
3
3
  encodePng
4
- } from "./chunk-BGOR5GCF.js";
4
+ } from "./chunk-274ZPMHK.js";
5
5
  import {
6
6
  hasRequestedPagesAfter,
7
7
  parsePageRange
@@ -12,6 +12,8 @@ import {
12
12
  CONT_PARTS,
13
13
  EMPTY_PARTS,
14
14
  FILLER_CELLS,
15
+ FRAME_READING_UNITS,
16
+ FRAME_TITLE_BLOCKS,
15
17
  IMAGE_CELLS,
16
18
  NO_EDGES,
17
19
  PART_COLXS,
@@ -22,6 +24,7 @@ import {
22
24
  TOC_BLOCKS,
23
25
  WrapLexicon,
24
26
  collapseEvenSpacing,
27
+ computeBBox,
25
28
  computeMedianFontSizeFromFreq,
26
29
  detectDocumentStyleHeadings,
27
30
  detectHeadings,
@@ -29,26 +32,32 @@ import {
29
32
  detectMarkerHeadings,
30
33
  detectPageLeadHeadings,
31
34
  detectRepeatedPageLabels,
35
+ detectRightArrowRegions,
32
36
  detectSiblingStyleHeadings,
33
37
  detectTableCaptions,
34
38
  detectTypographyHeadings,
39
+ dominantStyle,
35
40
  extractImageRegions,
36
41
  extractLines,
37
42
  extractPageBlocksWithLines,
38
43
  filterHiddenText,
44
+ groupByY,
39
45
  joinCellEdges,
40
46
  joinPageBreakWraps,
41
47
  latinSoftWrap,
48
+ mergeLineSimple,
42
49
  mergeStackedHeadingLines,
43
50
  normalizeItems,
44
51
  ocrModelsCached,
52
+ recordFrameReadingUnit,
45
53
  refineDocumentStyleHeadings,
46
54
  removeHeaderFooterBlocks,
47
55
  removeSideTabs,
56
+ restoreImageBullets,
48
57
  startsNewItem,
49
58
  trimTrailingEmptyTableCols,
50
59
  wrapJoiner
51
- } from "./chunk-BJ6RYYXV.js";
60
+ } from "./chunk-MXA6YW2W.js";
52
61
  import {
53
62
  CELL_EDGES,
54
63
  CONTENT_CELLS,
@@ -58,12 +67,12 @@ import {
58
67
  escapeLiteralTags,
59
68
  stripScriptTags,
60
69
  unframeLayoutTables
61
- } from "./chunk-PSJLB3SZ.js";
62
- import "./chunk-3FZGL3IL.js";
70
+ } from "./chunk-XG4SYOZL.js";
71
+ import "./chunk-ELOPDQES.js";
63
72
  import {
64
73
  KordocError,
65
74
  sanitizeHref
66
- } from "./chunk-CAN5P6N6.js";
75
+ } from "./chunk-7GR2GCJF.js";
67
76
 
68
77
  // src/pdf/ocr-region-merge.ts
69
78
  function mergeOcrImageRegions(blocks, page, regions, ocrBlocks) {
@@ -76,8 +85,10 @@ function mergeOcrImageRegions(blocks, page, regions, ocrBlocks) {
76
85
  const overlapH = Math.max(0, Math.min(b.y + b.height, region.y2) - Math.max(b.y, region.y1));
77
86
  return overlapW * overlapH >= b.width * b.height * 0.8;
78
87
  });
88
+ const labels = candidates.filter((b) => b.type === "paragraph" && b.bbox.height <= 24 && (b.text?.match(/[\p{L}\p{N}]/gu)?.length ?? 0) >= 2);
89
+ const axisFragments = rotatedAxisFragments(candidates);
79
90
  const accepted = candidates.filter((b) => {
80
- if (b.type === "paragraph") return (b.text?.match(/[\p{L}\p{N}]/gu)?.length ?? 0) >= 2;
91
+ if (b.type === "paragraph") return (b.text?.match(/[\p{L}\p{N}]/gu)?.length ?? 0) >= 2 || !axisFragments.has(b) && supportedDiagramLabel(b, labels, region);
81
92
  const t = b.table;
82
93
  if (b.type !== "table" || !t) return false;
83
94
  if (t.rows === 1 && t.cols === 1) {
@@ -88,29 +99,89 @@ function mergeOcrImageRegions(blocks, page, regions, ocrBlocks) {
88
99
  return t.rows >= 2 && t.cols >= 2 && headerLabels >= t.cols * 0.75 && t.cells.slice(1).some((row) => row.filter((c) => c.text.trim()).length >= 2);
89
100
  });
90
101
  const selected = candidates.flatMap((b) => accepted.includes(b) ? [b] : b.type === "table" && b.table ? rowParagraphs(b).filter((p) => /[\p{L}\p{N}]{2}/u.test(p.text ?? "")) : []);
91
- for (const block of selected) {
102
+ const hasOriginal = (block) => blocks.some((existing) => {
92
103
  const b = block.bbox;
93
- const hasOriginal = blocks.some((existing) => {
94
- if (existing.pageNumber !== page || !existing.bbox || existing.type === "image") return false;
95
- const e = existing.bbox;
96
- const x = Math.max(0, Math.min(e.x + e.width, b.x + b.width) - Math.max(e.x, b.x));
97
- const y = Math.max(0, Math.min(e.y + e.height, b.y + b.height) - Math.max(e.y, b.y));
98
- return x * y > b.width * b.height * 0.2;
99
- });
100
- if (hasOriginal) continue;
101
- const index = blocks.findIndex((existing) => {
102
- const e = existing.bbox;
103
- if (existing.pageNumber !== page || !e) return false;
104
- const beside = e.y < region.y2 && e.y + e.height > region.y1 && e.x >= region.x2 - 1;
105
- return e.y < b.y || beside;
106
- });
107
- const placed = block.type === "paragraph" ? { ...block, style: void 0 } : block;
108
- blocks.splice(index < 0 ? blocks.length : index, 0, placed);
109
- added++;
104
+ if (existing.pageNumber !== page || !existing.bbox || existing.type === "image") return false;
105
+ const e = existing.bbox;
106
+ const x = Math.max(0, Math.min(e.x + e.width, b.x + b.width) - Math.max(e.x, b.x));
107
+ const y = Math.max(0, Math.min(e.y + e.height, b.y + b.height) - Math.max(e.y, b.y));
108
+ return x * y > b.width * b.height * 0.2;
109
+ });
110
+ const selectedSet = new Set(selected), seen = /* @__PURE__ */ new Set();
111
+ for (const block of selected) {
112
+ if (seen.has(block)) continue;
113
+ const frame = FRAME_READING_UNITS.get(block);
114
+ const source = frame?.blocks.every((member) => selectedSet.has(member)) ? frame.blocks : [block];
115
+ source.forEach((member) => seen.add(member));
116
+ const available = source.filter((member) => !hasOriginal(member));
117
+ const units = frame && available.length === source.length && source === frame.blocks ? [{ blocks: available, bbox: frame.bbox, atomic: true }] : available.map((member) => ({ blocks: [member], bbox: member.bbox, atomic: false }));
118
+ for (const unit of units) {
119
+ const b = unit.bbox;
120
+ const index = blocks.findIndex((existing) => {
121
+ const e = FRAME_READING_UNITS.get(existing)?.bbox ?? existing.bbox;
122
+ if (existing.pageNumber !== page || !e) return false;
123
+ const beside = e.y < region.y2 && e.y + e.height > region.y1 && e.x >= region.x2 - 1;
124
+ return e.y < b.y || beside;
125
+ });
126
+ const placed = unit.blocks.map((member) => member.type === "paragraph" ? { ...member, style: void 0 } : member);
127
+ if (unit.atomic) recordFrameReadingUnit(placed, unit.bbox);
128
+ blocks.splice(index < 0 ? blocks.length : index, 0, ...placed);
129
+ added += placed.length;
130
+ }
110
131
  }
111
132
  }
112
133
  return added;
113
134
  }
135
+ function supportedDiagramLabel(block, labels, region) {
136
+ const b = block.bbox, text = block.text?.trim() ?? "";
137
+ if (!b || !/^[\p{L}\p{N}]$/u.test(text) || b.height < 4 || b.width < 1 || b.width > b.height * 2 || b.x < region.x1 || b.y < region.y1 || b.x + b.width > region.x2 || b.y + b.height > region.y2 || b.width * b.height > (region.x2 - region.x1) * (region.y2 - region.y1) * 5e-3 || labels.length < 3 || !labels.some((l) => /\p{L}{3}/u.test(l.text ?? "")) || labels.filter((l) => !/\p{L}/u.test(l.text ?? "") && (l.text?.match(/\p{N}/gu)?.length ?? 0) >= 2).length < 2) return false;
138
+ let above = false, below = false;
139
+ for (const label of labels) {
140
+ const a = label.bbox, em = Math.max(b.height, a.height);
141
+ if (b.x + b.width < a.x - em * 2 || b.x > a.x + a.width + em * 2) continue;
142
+ const upperGap = a.y - (b.y + b.height), lowerGap = b.y - (a.y + a.height);
143
+ if (upperGap >= 0 && upperGap <= em * 4) above = true;
144
+ if (lowerGap >= 0 && lowerGap <= em * 4) below = true;
145
+ }
146
+ return above && below;
147
+ }
148
+ function rotatedAxisFragments(candidates) {
149
+ const paragraphs = candidates.filter((b) => b.type === "paragraph" && b.bbox.height <= 24);
150
+ const axes = [];
151
+ for (const block of paragraphs) {
152
+ const b = block.bbox, text = block.text?.trim() ?? "";
153
+ if (!/^[\d\s.,%+−-]+$/u.test(text) || !/\d/u.test(text) || b.width > b.height * 6) continue;
154
+ const axis = axes.find((a) => Math.abs(a[0].bbox.x + a[0].bbox.width - b.x - b.width) <= Math.max(a[0].bbox.height, b.height) * 0.5);
155
+ if (axis) axis.push(block);
156
+ else axes.push([block]);
157
+ }
158
+ const fragments = /* @__PURE__ */ new Set();
159
+ const aligned = (a, b) => {
160
+ const x = a.bbox, y = b.bbox, tolerance = Math.max(x.height, y.height) * 0.5;
161
+ return Math.abs(x.x - y.x) <= tolerance || Math.abs(x.x + x.width - y.x - y.width) <= tolerance;
162
+ };
163
+ for (const axis of axes) {
164
+ if (axis.length < 3) continue;
165
+ const bottom = Math.min(...axis.map((a) => a.bbox.y)), top = Math.max(...axis.map((a) => a.bbox.y + a.bbox.height));
166
+ const em = Math.max(...axis.map((a) => a.bbox.height)), right = axis[0].bbox.x + axis[0].bbox.width;
167
+ if (top - bottom < em * 6) continue;
168
+ const left = paragraphs.filter((block) => {
169
+ const b = block.bbox, text = block.text?.trim() ?? "";
170
+ return /\p{Script=Latin}/u.test(text) && (text.match(/[\p{L}\p{N}]/gu)?.length ?? 0) <= 3 && b.width <= b.height * 3 && b.x + b.width >= right - em * 6 && b.x + b.width <= right - Math.max(em, b.height) && b.y >= bottom && b.y + b.height <= top;
171
+ });
172
+ const stack = left.filter((a) => left.some((b) => {
173
+ if (a === b || !aligned(a, b)) return false;
174
+ const x = a.bbox, y = b.bbox, gap = Math.max(x.y, y.y) - Math.min(x.y + x.height, y.y + y.height);
175
+ return gap >= -Math.min(x.height, y.height) * 0.5 && gap <= Math.max(x.height, y.height);
176
+ }));
177
+ if (stack.length < 2) continue;
178
+ for (const block of paragraphs) {
179
+ const b = block.bbox;
180
+ if (b.y >= bottom && b.y + b.height <= top && b.x + b.width >= right - em * 6 && b.x + b.width <= right - Math.max(em, b.height) && /^[\p{L}\p{N}]$/u.test(block.text?.trim() ?? "") && stack.some((s) => aligned(s, block))) fragments.add(block);
181
+ }
182
+ }
183
+ return fragments;
184
+ }
114
185
  function rowParagraphs(block) {
115
186
  const t = block.table, b = block.bbox;
116
187
  const h = b.height / t.rows;
@@ -1120,7 +1191,7 @@ function sameHeadShape(a, b) {
1120
1191
  let tall = false;
1121
1192
  for (let c = 0; c < a.length; c++) {
1122
1193
  if (a[c].colSpan !== b[c].colSpan || a[c].rowSpan !== b[c].rowSpan) return false;
1123
- if (a[c].rowSpan >= 2 && a[c].text.trim()) tall = true;
1194
+ if (a[c].rowSpan >= 2 && a[c].text.trim() && b[c].text.trim()) tall = true;
1124
1195
  }
1125
1196
  return tall;
1126
1197
  }
@@ -1157,9 +1228,10 @@ function mergeCrossPageTables(blocks, pageHeights, lex) {
1157
1228
  if (px && cx && !shiftedSame(px, cx, !CLIP_TABLES.has(prev.table) && !CLIP_TABLES.has(curr.table))) continue;
1158
1229
  if (!rowTextsEqual(prev.table.cells[0], curr.table.cells[0]) && sameHeadShape(prev.table.cells[0], curr.table.cells[0])) continue;
1159
1230
  let currCells = curr.table.cells;
1160
- if (currCells.length > 1 && prev.table.cells.length > 0 && rowTextsEqual(prev.table.cells[0], currCells[0])) {
1161
- currCells = currCells.slice(1);
1162
- }
1231
+ const headRows = repeatedHeaderRows(prev.table, curr.table);
1232
+ if (headRows < 0) continue;
1233
+ if (headRows) currCells = currCells.slice(headRows);
1234
+ if (currCells.length) continueRowSpans(prev.table.cells, currCells);
1163
1235
  if (currCells.length === 0) {
1164
1236
  blocks.splice(j, 1);
1165
1237
  continue;
@@ -1361,6 +1433,45 @@ function chainHead(blocks, i, pageHeights) {
1361
1433
  }
1362
1434
  return k;
1363
1435
  }
1436
+ var ROW_CONTINUATIONS = /* @__PURE__ */ new WeakMap();
1437
+ function continueRowSpans(prevCells, currCells) {
1438
+ const first = currCells[0];
1439
+ const last = prevCells.length;
1440
+ for (let c = 0; c < first.length; c++) {
1441
+ const cell = first[c];
1442
+ if (hasContent(cell) || (cell.rowSpan || 1) < 2) continue;
1443
+ let anchor;
1444
+ for (let r = last - 1; r >= 0 && !anchor; r--) {
1445
+ const a = prevCells[r]?.[c];
1446
+ if (a && r + (a.rowSpan || 1) === last && (a.colSpan || 1) === (cell.colSpan || 1) && (hasContent(a) || a.rowSpan >= 2)) anchor = a;
1447
+ }
1448
+ if (!anchor) continue;
1449
+ if (!hasContent(anchor)) {
1450
+ ROW_CONTINUATIONS.set(anchor, cell);
1451
+ continue;
1452
+ }
1453
+ for (let part = cell; part; ) {
1454
+ const next = ROW_CONTINUATIONS.get(part);
1455
+ anchor.rowSpan = (anchor.rowSpan || 1) + part.rowSpan;
1456
+ part.rowSpan = 1;
1457
+ part.colSpan = 1;
1458
+ ROW_CONTINUATIONS.delete(part);
1459
+ part = next;
1460
+ }
1461
+ }
1462
+ }
1463
+ function repeatedHeaderRows(prev, curr) {
1464
+ if (!rowTextsEqual(prev.cells[0], curr.cells[0])) return 0;
1465
+ const pa = anchorsOf(prev), ca = anchorsOf(curr);
1466
+ let depth = 1;
1467
+ for (let r = 0; r < depth && r < prev.rows; r++) {
1468
+ for (const a of pa) if (a.r === r) depth = Math.max(depth, r + a.rs);
1469
+ }
1470
+ if (depth >= curr.rows || depth > prev.rows) return -1;
1471
+ const p = pa.filter((a) => a.r < depth), d = ca.filter((a) => a.r < depth);
1472
+ const norm = (text) => flowText(text).replace(/\s+/g, "");
1473
+ return p.length === d.length && p.every((a, i) => a.r === d[i].r && a.c === d[i].c && a.rs === d[i].rs && a.cs === d[i].cs && norm(a.cell.text) === norm(d[i].cell.text)) ? depth : -1;
1474
+ }
1364
1475
  function rowTextsEqual(a, b) {
1365
1476
  if (a.length !== b.length) return false;
1366
1477
  const norm = (t) => flowText(t).replace(/\s+/g, "");
@@ -1455,6 +1566,34 @@ function fillNestedContinuation(blocks, at) {
1455
1566
  blocks.splice(at, 1);
1456
1567
  }
1457
1568
 
1569
+ // src/pdf/selection-context.ts
1570
+ function selectionContextPages(selected, pageCount) {
1571
+ const out = /* @__PURE__ */ new Set();
1572
+ for (const page of selected) {
1573
+ for (let distance = 1; distance <= 4; distance++) {
1574
+ for (const neighbor of [page - distance, page + distance]) {
1575
+ if (neighbor < 1 || neighbor > pageCount || selected.has(neighbor)) continue;
1576
+ out.add(neighbor);
1577
+ }
1578
+ }
1579
+ }
1580
+ return [...out].sort((a, b) => a - b);
1581
+ }
1582
+ function marginContextBlocks(raw, page, view) {
1583
+ const [x1, y1, x2, y2] = view;
1584
+ const width = x2 - x1, height = y2 - y1;
1585
+ const items = filterHiddenText(normalizeItems(raw), width, height, x1, y1).visible;
1586
+ for (const item of items) {
1587
+ item.x -= x1;
1588
+ item.y -= y1;
1589
+ }
1590
+ return groupByY(items).flatMap((line) => {
1591
+ const bbox = computeBBox(line, page);
1592
+ if (bbox.y + bbox.height < height * 0.88 && bbox.y > height * 0.12) return [];
1593
+ return [{ type: "paragraph", text: mergeLineSimple(line), pageNumber: page, bbox, style: dominantStyle(line) }];
1594
+ });
1595
+ }
1596
+
1458
1597
  // src/pdf/symbol-fonts.ts
1459
1598
  var WINANSI_REVERSE = {
1460
1599
  8364: 128,
@@ -1762,6 +1901,9 @@ var NAMED = {
1762
1901
  quotedblright: "\u201D",
1763
1902
  quotesingle: "'",
1764
1903
  quotedbl: '"',
1904
+ bullet: "\u2022",
1905
+ openbullet: "\u25E6",
1906
+ whitebullet: "\u25E6",
1765
1907
  question: "?",
1766
1908
  exclam: "!",
1767
1909
  percent: "%",
@@ -1778,7 +1920,9 @@ function glyphNameText(name) {
1778
1920
  let out = "";
1779
1921
  for (const part of base.split("_")) {
1780
1922
  const uni = /^uni([0-9A-F]{4})$/.exec(part) ?? /^u([0-9A-F]{4,6})$/.exec(part);
1781
- const ch = /^[A-Za-z]$/.test(part) ? part : NAMED[part] ?? (uni ? String.fromCodePoint(parseInt(uni[1], 16)) : void 0);
1923
+ const code = uni ? parseInt(uni[1], 16) : void 0;
1924
+ if (code !== void 0 && (code > 1114111 || code >= 55296 && code <= 57343)) return void 0;
1925
+ const ch = /^[A-Za-z]$/.test(part) ? part : NAMED[part] ?? (code !== void 0 ? String.fromCodePoint(code) : void 0);
1782
1926
  if (ch === void 0) return void 0;
1783
1927
  out += ch;
1784
1928
  }
@@ -2464,6 +2608,35 @@ function demoteNonHeadingRoles(blocks, pageHeights, faceNames) {
2464
2608
  }
2465
2609
  }
2466
2610
  }
2611
+ const citedProse = /* @__PURE__ */ new Set();
2612
+ const numberedProse = /* @__PURE__ */ new Set();
2613
+ for (const page of byPage.values()) {
2614
+ const numbered = page.filter((b) => (b.type === "heading" || b.type === "paragraph") && /^[①-⑮]\s*/.test(b.text?.trim() ?? "") && b.bbox && b.style?.fontName && b.style.fontSize && !/Bold|Black|Heavy|Semibold/i.test(faceNames?.get(b.style.fontName) ?? ""));
2615
+ let chain = [];
2616
+ const finish = () => {
2617
+ if (chain.length < 3 || !chain.some((b) => (b.text?.length ?? 0) >= 40 && b.bbox.height >= b.style.fontSize * 2.4)) return;
2618
+ for (const b of chain) numberedProse.add(b);
2619
+ const first = chain[0], size = first.style.fontSize;
2620
+ for (const b of page) if (/^[○●•◦]\s/.test(b.text?.trim() ?? "") && b.style?.fontName === first.style?.fontName && b.style?.fontSize === size && b.bbox && Math.abs(b.bbox.x - first.bbox.x) <= size * 0.5 && (b.text?.length ?? 0) >= 15) numberedProse.add(b);
2621
+ };
2622
+ for (const b of numbered) {
2623
+ const prev = chain.at(-1), size = b.style.fontSize;
2624
+ if (!prev || b.text.trim().charCodeAt(0) !== prev.text.trim().charCodeAt(0) + 1 || b.style.fontName !== prev.style.fontName || size !== prev.style.fontSize || Math.abs(b.bbox.x - prev.bbox.x) > size * 0.5 || prev.bbox.y - (b.bbox.y + b.bbox.height) > size * 8) {
2625
+ finish();
2626
+ chain = [];
2627
+ }
2628
+ chain.push(b);
2629
+ }
2630
+ finish();
2631
+ }
2632
+ for (let i = 0; i + 1 < blocks.length; i++) {
2633
+ const prose = blocks[i], reference = blocks[i + 1];
2634
+ const a = prose.bbox, b = reference.bbox, size = prose.style?.fontSize ?? 0;
2635
+ const source = reference.text?.replace(/<[^>]+>/g, "").trim() ?? "";
2636
+ if (prose.type !== "heading" && prose.type !== "paragraph" && prose.type !== "list" || reference.type !== "heading" && reference.type !== "paragraph" || (prose.text?.length ?? 0) < 40 || !a || !b || size <= 0 || a.height < size * 2.4 || prose.pageNumber !== reference.pageNumber || prose.style?.fontName !== reference.style?.fontName || prose.style?.fontSize !== reference.style?.fontSize || Math.abs(a.x - b.x) > size && !(/^[○●•◦]\s/.test(prose.text ?? "") && b.x >= a.x && b.x - a.x <= size * 2) || a.y - (b.y + b.height) < 0 || a.y - (b.y + b.height) > size || !/^[((].*(?:제\s*\d+\s*조|(?:Article|Section)\s+\d+).*[))]$/i.test(source)) continue;
2637
+ citedProse.add(prose);
2638
+ citedProse.add(reference);
2639
+ }
2467
2640
  for (let i = 0; i < blocks.length; i++) {
2468
2641
  const block = blocks[i];
2469
2642
  if (block.type !== "heading" || !block.text) continue;
@@ -2511,7 +2684,7 @@ function demoteNonHeadingRoles(blocks, pageHeights, faceNames) {
2511
2684
  const byline = /Italic|Oblique/i.test(face) && !/Bold|Black|Heavy|Semibold/i.test(face) && !!bodyFace && size <= Number(bodySize) + 0.5 && blocks[i - 1]?.type === "heading" && blocks[i - 1].pageNumber === block.pageNumber;
2512
2685
  const ocrFragment = block.style?.fontName === "ocr" && (/^[.,;:…·•-]/.test(text) || text.includes("\u2026") || (text.match(/\p{L}/gu)?.length ?? 0) < 2 || text.split(/\s+/).length > 7);
2513
2686
  const crowdedOcr = block.style?.fontName === "ocr" && !!box && [prev, next].every((o) => o?.type === "paragraph" && o.pageNumber === block.pageNumber && o.style?.fontName === "ocr" && !!o.bbox && (o.text?.length ?? 0) >= 15 && Math.abs(o.bbox.x - box.x) <= size * 0.25 && Math.min(o.bbox.y + o.bbox.height, box.y + box.height) - Math.max(o.bbox.y, box.y) >= o.bbox.height * 0.2) && prev.bbox.y > box.y && next.bbox.y < box.y;
2514
- if (ocrFragment || crowdedOcr || tocEntry || proseStyle || kicker || byline || tiny || !/\p{L}/u.test(text) && !chapterNumber || /^[a-z]/.test(text) || CAPTION.test(text) || EQUATION_NUMBER.test(block.text) || DISPLAY_MATH.test(text) || // 닫는 괄호가 여는 괄호보다 많으면 앞 줄에서 이어진 문장 조각이다 ("Fact-checking) and is used …") — "1)"·"가)" 앞머리 번호는 빼고 센다
2687
+ if (numberedProse.has(block) || citedProse.has(block) || ocrFragment || crowdedOcr || tocEntry || proseStyle || kicker || byline || tiny || !/\p{L}/u.test(text) && !chapterNumber || /^[a-z]/.test(text) || CAPTION.test(text) || EQUATION_NUMBER.test(block.text) || DISPLAY_MATH.test(text) || // 닫는 괄호가 여는 괄호보다 많으면 앞 줄에서 이어진 문장 조각이다 ("Fact-checking) and is used …") — "1)"·"가)" 앞머리 번호는 빼고 센다
2515
2688
  unbalancedClose(text.replace(/^\s*[\dA-Za-z가-힣ⅰ-ⅹ]{1,3}\)\s*/, "")) || isRunningHead(block, page, pageHeights.get(block.pageNumber ?? 0))) {
2516
2689
  block.type = "paragraph";
2517
2690
  block.level = void 0;
@@ -2552,6 +2725,11 @@ function splitSingleCellTables(blocks) {
2552
2725
  const out = [];
2553
2726
  for (const b of blocks) {
2554
2727
  const t = b.type === "table" ? b.table : void 0;
2728
+ const title = t && FRAME_TITLE_BLOCKS.get(t);
2729
+ if (title) {
2730
+ out.push({ ...title, text: `${t.cells[0][0].text.trim()} ${t.cells[0][2].text.trim()}` });
2731
+ continue;
2732
+ }
2555
2733
  const only = t && t.rows === 1 && t.cols === 1 && t.cells[0]?.[0]?.blocks?.length === 1 ? t.cells[0][0].blocks[0] : void 0;
2556
2734
  const squash = (s) => s.replace(/\s/g, "");
2557
2735
  const inner = only && (!squash(t.cells[0][0].text) || only.type === "table" && squash(t.cells[0][0].text) === squash(only.table.cells.flat().map((c) => c.text).join(""))) ? only : void 0;
@@ -2561,12 +2739,16 @@ function splitSingleCellTables(blocks) {
2561
2739
  continue;
2562
2740
  }
2563
2741
  const cell = t && t.rows === 1 && t.cols === 1 ? t.cells[0]?.[0] : void 0;
2564
- if (!cell || cell.blocks?.some((x) => x.type === "table")) {
2742
+ if (!cell) {
2565
2743
  out.push(b);
2566
2744
  continue;
2567
2745
  }
2568
2746
  const lines = (cell.text ?? "").split(/\n/).map((l) => l.trim()).filter(Boolean);
2569
2747
  if (t?.caption?.trim()) out.push({ type: "paragraph", text: t.caption.trim(), pageNumber: b.pageNumber, bbox: b.bbox });
2748
+ if (cell.blocks?.length) {
2749
+ out.push(...splitSingleCellTables(cell.blocks));
2750
+ continue;
2751
+ }
2570
2752
  for (const text of lines) out.push({ type: "paragraph", text, pageNumber: b.pageNumber, bbox: b.bbox });
2571
2753
  }
2572
2754
  return out;
@@ -2691,7 +2873,7 @@ function mergeLinkRuns(markdown) {
2691
2873
 
2692
2874
  // src/pdf/formula-ocr.ts
2693
2875
  async function applyFormulaOcr(buffer, blocks, pageFilter, effectivePageCount, warnings, _onProgress) {
2694
- const formulaMod = await import("./formula-GPCOJJXN.js");
2876
+ const formulaMod = await import("./formula-HQFZEXFF.js");
2695
2877
  const { FormulaPipeline, ensureFormulaModels } = formulaMod;
2696
2878
  await ensureFormulaModels((p) => {
2697
2879
  if (p.phase === "download" && p.total) {
@@ -2914,12 +3096,29 @@ async function parsePdfDocument(buffer, options) {
2914
3096
  const uncoveredImageRegions = /* @__PURE__ */ new Map();
2915
3097
  const largeImageRegions = /* @__PURE__ */ new Set();
2916
3098
  const imageState = createPdfImageState();
3099
+ const imageBulletShapes = /* @__PURE__ */ new Map();
3100
+ const imageArrowDirections = /* @__PURE__ */ new Map();
2917
3101
  const extractedImages = [];
2918
3102
  const pageImageBlocks = /* @__PURE__ */ new Map();
2919
3103
  const carry = {};
2920
3104
  const vectorPageOps = /* @__PURE__ */ new Map();
2921
3105
  const wrapLexicon = new WrapLexicon();
2922
3106
  const noteMarks = /* @__PURE__ */ new Map();
3107
+ const headerContext = [];
3108
+ if (pageFilter && options?.removeHeaderFooter !== false && pageFilter.size < effectivePageCount) {
3109
+ for (const number of selectionContextPages(pageFilter, effectivePageCount)) {
3110
+ let contextPage;
3111
+ try {
3112
+ contextPage = await doc.getPage(number);
3113
+ const content = await contextPage.getTextContent();
3114
+ pageHeights.set(number, contextPage.view[3] - contextPage.view[1]);
3115
+ headerContext.push(...marginContextBlocks(content.items, number, contextPage.view));
3116
+ } catch {
3117
+ } finally {
3118
+ contextPage?.cleanup();
3119
+ }
3120
+ }
3121
+ }
2923
3122
  let parsedPages = 0;
2924
3123
  for (let i = 1; i <= effectivePageCount; i++) {
2925
3124
  if (pageFilter && !pageFilter.has(i)) continue;
@@ -2973,6 +3172,7 @@ async function parsePdfDocument(buffer, options) {
2973
3172
  } : rawOps;
2974
3173
  remapSymbolFontItems(visible, (loadedName) => fontObj(loadedName)?.name);
2975
3174
  remapControlGlyphs(visible, differencesOf);
3175
+ await restoreImageBullets(visible, page, opList.fnArray, opList.argsArray, imageBulletShapes);
2976
3176
  const faces = /* @__PURE__ */ new Map();
2977
3177
  for (const it of visible) {
2978
3178
  if (!it.fontName) continue;
@@ -3019,7 +3219,8 @@ async function parsePdfDocument(buffer, options) {
3019
3219
  if (marks.length) noteMarks.set(i, { marks, seps: footnoteSeparators(extractLines(opList.fnArray, opList.argsArray).horizontals, pageW, pageH) });
3020
3220
  const stamp = marginStamp(visible);
3021
3221
  const flow = stamp.length ? visible.filter((it) => !stamp.includes(it)) : visible;
3022
- const pageBlocks = orderTwoUpPage(extractPageBlocksWithLines(flow, i, opList, pageW, pageH, void 0, options?.tables !== false, carry, wrapLexicon), pageW, pageH);
3222
+ const rightArrows = options?.tables !== false ? await detectRightArrowRegions(page, opList.fnArray, opList.argsArray, imageArrowDirections) : [];
3223
+ const pageBlocks = orderTwoUpPage(extractPageBlocksWithLines(flow, i, opList, pageW, pageH, void 0, options?.tables !== false, carry, wrapLexicon, rightArrows), pageW, pageH);
3023
3224
  if (stamp.length) pageBlocks.unshift({ type: "paragraph", text: [...stamp].sort((a, b) => a.y - b.y).map((it) => it.text).join(" "), pageNumber: i });
3024
3225
  for (const b of pageBlocks) blocks.push(b);
3025
3226
  try {
@@ -3075,7 +3276,7 @@ async function parsePdfDocument(buffer, options) {
3075
3276
  }
3076
3277
  if (targets.size > 0) {
3077
3278
  try {
3078
- const { runPdfOcr } = await import("./pdf-ocr-OLII3WAG.js");
3279
+ const { runPdfOcr } = await import("./pdf-ocr-RACAZU3N.js");
3079
3280
  const mode = typeof options?.ocr === "function" ? options.ocr : "builtin";
3080
3281
  const regionPages = new Map([...ocrRegions].filter(([p]) => options?.ocr !== "force" && !isImageBased && !pageQuality.find((q) => q.page === p)?.needsOcr));
3081
3282
  const ocrPageBlocks = await runPdfOcr(ocrBuffer, targets, mode, warnings, options?.onProgress, options?.tables !== false, vectorPageOps, regionPages);
@@ -3142,8 +3343,8 @@ async function parsePdfDocument(buffer, options) {
3142
3343
  warnings.push({ page, message: `${count}\uAC1C \uC774\uBBF8\uC9C0 \uC601\uC5ED\uC5D0 \uCD94\uCD9C \uAC00\uB2A5\uD55C \uD14D\uC2A4\uD2B8 \uC5C6\uC74C (\uADF8\uB9BC/\uCC28\uD2B8/\uB3C4\uC7A5 \uB0B4\uC6A9 \uB204\uB77D \uAC00\uB2A5 \u2014 \uADF8\uB9BC \uC18D \uAE00\uC740 ocr: true / CLI --ocr \uB85C \uC77D\uC2B5\uB2C8\uB2E4)`, code: "SKIPPED_IMAGE" });
3143
3344
  }
3144
3345
  }
3145
- if (options?.removeHeaderFooter !== false && parsedPageCount >= 3) {
3146
- const removed = removeHeaderFooterBlocks(blocks, pageHeights, warnings, noteMarks);
3346
+ if (options?.removeHeaderFooter !== false && (parsedPageCount >= 3 || headerContext.length)) {
3347
+ const removed = removeHeaderFooterBlocks(blocks, pageHeights, warnings, noteMarks, false, headerContext);
3147
3348
  for (let ri = removed.length - 1; ri >= 0; ri--) {
3148
3349
  blocks.splice(removed[ri], 1);
3149
3350
  }
@@ -3191,6 +3392,10 @@ async function parsePdfDocument(buffer, options) {
3191
3392
  const outline = blocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
3192
3393
  sanitizeBlockControlChars(blocks);
3193
3394
  let outBlocks = splitSingleCellTables(blocks);
3395
+ if (options?.layoutTables !== "keep") {
3396
+ blocks.length = 0;
3397
+ for (const block of outBlocks) blocks.push(block);
3398
+ }
3194
3399
  outBlocks = inlineFootnotes(outBlocks, noteMarks);
3195
3400
  outBlocks = relocateEndnotes(outBlocks);
3196
3401
  if (options?.removeHeaderFooter !== false) outBlocks = outBlocks.filter((b) => {
@@ -3307,4 +3512,4 @@ export {
3307
3512
  parsePdfDocument,
3308
3513
  removeHeaderFooterBlocks
3309
3514
  };
3310
- //# sourceMappingURL=parser-SCXRLOW7.js.map
3515
+ //# sourceMappingURL=parser-GFDGAVLV.js.map