kordoc 4.5.0 → 4.6.0

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 (184) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +176 -176
  3. package/README.md +958 -951
  4. package/THIRD_PARTY/apache-2.0.LICENSE +201 -201
  5. package/THIRD_PARTY/claw-hwp.LICENSE +21 -21
  6. package/THIRD_PARTY/hml-equation-parser.LICENSE +201 -201
  7. package/THIRD_PARTY/hml-equation-parser.txt +35 -35
  8. package/THIRD_PARTY/opendataloader-pdf.txt +32 -32
  9. package/THIRD_PARTY/pix2text.txt +17 -17
  10. package/THIRD_PARTY/rhwp-forms.txt +35 -35
  11. package/dist/{-CWYIC4ES.js → -DEVLQKIT.js} +12 -12
  12. package/dist/{chunk-M4I5F6GK.js → chunk-2CABQYTT.js} +9 -9
  13. package/dist/chunk-2CABQYTT.js.map +1 -0
  14. package/dist/{chunk-NBOVSNSR.js → chunk-2CENWSAW.js} +10 -10
  15. package/dist/chunk-2CENWSAW.js.map +1 -0
  16. package/dist/{chunk-3H5X63BQ.js → chunk-3FFPHTXD.js} +1 -1
  17. package/dist/chunk-3FFPHTXD.js.map +1 -0
  18. package/dist/{chunk-MOL7MDBG.js → chunk-3TBUDJDE.js} +1 -1
  19. package/dist/chunk-3TBUDJDE.js.map +1 -0
  20. package/dist/{chunk-5RPYBC2Q.js → chunk-6GBLFQMA.js} +1 -1
  21. package/dist/chunk-6GBLFQMA.js.map +1 -0
  22. package/dist/{chunk-GZDOWYJM.js → chunk-6XPXIG57.js} +2 -2
  23. package/dist/chunk-6XPXIG57.js.map +1 -0
  24. package/dist/{chunk-45EUJ2P6.js → chunk-7BTO44Q7.js} +37 -21
  25. package/dist/chunk-7BTO44Q7.js.map +1 -0
  26. package/dist/{chunk-GE43BE46.js → chunk-AIQ3ISQU.js} +1 -1
  27. package/dist/chunk-AIQ3ISQU.js.map +1 -0
  28. package/dist/{chunk-GLPLLT4Q.cjs → chunk-CARNWWGI.cjs} +4 -4
  29. package/dist/chunk-CARNWWGI.cjs.map +1 -0
  30. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  31. package/dist/{chunk-TCPO6XAY.cjs → chunk-HPRGVU6A.cjs} +11 -11
  32. package/dist/chunk-HPRGVU6A.cjs.map +1 -0
  33. package/dist/{chunk-U3ZNF4SO.cjs → chunk-I42DND3B.cjs} +17 -17
  34. package/dist/chunk-I42DND3B.cjs.map +1 -0
  35. package/dist/{chunk-ZHNUH2IW.js → chunk-IBVMJIHR.js} +2 -2
  36. package/dist/chunk-IBVMJIHR.js.map +1 -0
  37. package/dist/{chunk-XYSTZIIL.js → chunk-IHK5LCYX.js} +2 -2
  38. package/dist/chunk-IHK5LCYX.js.map +1 -0
  39. package/dist/{chunk-TP4P7M4I.js → chunk-KC63W72C.js} +2 -2
  40. package/dist/chunk-KC63W72C.js.map +1 -0
  41. package/dist/{chunk-GCHH4WHT.js → chunk-KQOTL4WI.js} +2 -2
  42. package/dist/chunk-KQOTL4WI.js.map +1 -0
  43. package/dist/{chunk-ZV5OKQ3Z.cjs → chunk-MGGU3ZRK.cjs} +1 -1
  44. package/dist/chunk-MGGU3ZRK.cjs.map +1 -0
  45. package/dist/{chunk-FT2EXMMP.js → chunk-MSMSHU2F.js} +9 -9
  46. package/dist/chunk-MSMSHU2F.js.map +1 -0
  47. package/dist/{chunk-DZIXKL7E.js → chunk-O5EK52TG.js} +1 -1
  48. package/dist/chunk-O5EK52TG.js.map +1 -0
  49. package/dist/{chunk-CBUC73JM.js → chunk-OPL22SYF.js} +3 -3
  50. package/dist/chunk-OPL22SYF.js.map +1 -0
  51. package/dist/{chunk-2CF2WXBG.js → chunk-PQDNCWAZ.js} +11 -11
  52. package/dist/chunk-PQDNCWAZ.js.map +1 -0
  53. package/dist/{chunk-UEWW3DH2.js → chunk-PSAHY5XW.js} +1 -1
  54. package/dist/chunk-PSAHY5XW.js.map +1 -0
  55. package/dist/{chunk-EYLOFSA5.js → chunk-UGKXJA3W.js} +4 -3
  56. package/dist/chunk-UGKXJA3W.js.map +1 -0
  57. package/dist/{chunk-WDIPTY3Q.js → chunk-UQ4NSK66.js} +3 -3
  58. package/dist/chunk-UQ4NSK66.js.map +1 -0
  59. package/dist/{chunk-7ZQKECQT.js → chunk-VNEV6VKD.js} +2 -2
  60. package/dist/chunk-VNEV6VKD.js.map +1 -0
  61. package/dist/{chunk-7FCNOKOR.js → chunk-W34QSJFF.js} +1 -1
  62. package/dist/chunk-W34QSJFF.js.map +1 -0
  63. package/dist/{chunk-UQ6KDJWE.cjs → chunk-WX7WQTOT.cjs} +4 -3
  64. package/dist/chunk-WX7WQTOT.cjs.map +1 -0
  65. package/dist/{chunk-DCZVOIEO.cjs → chunk-X34YWRL5.cjs} +1 -1
  66. package/dist/chunk-X34YWRL5.cjs.map +1 -0
  67. package/dist/{chunk-UAFSPWU7.js → chunk-YNJDNP44.js} +1 -1
  68. package/dist/chunk-YNJDNP44.js.map +1 -0
  69. package/dist/{chunk-TTEHX4FN.js → chunk-ZYQ5X6PG.js} +4 -3
  70. package/dist/chunk-ZYQ5X6PG.js.map +1 -0
  71. package/dist/chunks-Y2VE2WCJ.js +10 -0
  72. package/dist/cli.js +54 -29
  73. package/dist/cli.js.map +1 -1
  74. package/dist/{formula-JLYS73QL.js → formula-ETB6MESN.js} +2 -2
  75. package/dist/formula-ETB6MESN.js.map +1 -0
  76. package/dist/{formula-LIVQSCMM.js → formula-KW4NI5IO.js} +2 -2
  77. package/dist/formula-KW4NI5IO.js.map +1 -0
  78. package/dist/{formula-CG4S7TWO.cjs → formula-WKGP3D3Y.cjs} +8 -8
  79. package/dist/formula-WKGP3D3Y.cjs.map +1 -0
  80. package/dist/{image-ocr-BWQIUUVC.js → image-ocr-C6U7YHRC.js} +6 -6
  81. package/dist/image-ocr-C6U7YHRC.js.map +1 -0
  82. package/dist/{image-ocr-EDZOUU6Q.js → image-ocr-YIBJCOUQ.js} +8 -8
  83. package/dist/image-ocr-YIBJCOUQ.js.map +1 -0
  84. package/dist/{image-ocr-KZ4MGVJ3.cjs → image-ocr-YID6TDN5.cjs} +11 -11
  85. package/dist/image-ocr-YID6TDN5.cjs.map +1 -0
  86. package/dist/index.cjs +209 -193
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.cts +13 -1
  89. package/dist/index.d.ts +13 -1
  90. package/dist/index.js +31 -15
  91. package/dist/index.js.map +1 -1
  92. package/dist/mcp.js +25 -23
  93. package/dist/mcp.js.map +1 -1
  94. package/dist/{models-6SIKLKRH.js → models-6VMAVZR6.js} +3 -3
  95. package/dist/page-range-CIRRJPXJ.js +7 -0
  96. package/dist/page-range-OF5I4PQY.js +8 -0
  97. package/dist/page-range-TPIRSA3J.cjs +8 -0
  98. package/dist/page-range-TPIRSA3J.cjs.map +1 -0
  99. package/dist/{parser-7G2QFIYZ.cjs → parser-EYCVDUJA.cjs} +33 -33
  100. package/dist/parser-EYCVDUJA.cjs.map +1 -0
  101. package/dist/{parser-JLEKXA2Z.js → parser-GFXZV2VJ.js} +11 -11
  102. package/dist/parser-GFXZV2VJ.js.map +1 -0
  103. package/dist/{parser-6OJOFJ64.js → parser-SHM3JEH6.js} +9 -9
  104. package/dist/parser-SHM3JEH6.js.map +1 -0
  105. package/dist/pdf-ocr-TMAWPOHC.cjs +13 -0
  106. package/dist/pdf-ocr-TMAWPOHC.cjs.map +1 -0
  107. package/dist/pdf-ocr-UKFYODLP.js +15 -0
  108. package/dist/pdf-ocr-ZG4UVUIT.js +12 -0
  109. package/dist/{profile-io-FMPT6VEZ.js → profile-io-FQZNNA3O.js} +3 -3
  110. package/dist/{rasterize-LARCX6CS.js → rasterize-QGKFNTXO.js} +2 -2
  111. package/dist/rasterize-QGKFNTXO.js.map +1 -0
  112. package/dist/{redact-7ILEAUTS.js → redact-W5K3ILQH.js} +2 -2
  113. package/dist/render-YFIWQYVB.js +10 -0
  114. package/dist/seal-GZ7NDWLR.js +10 -0
  115. package/dist/{setup-GUQT5ELJ.js → setup-I4I75QJU.js} +1 -1
  116. package/dist/setup-I4I75QJU.js.map +1 -0
  117. package/dist/{watch-Y2VKD2MN.js → watch-OIVXH2RN.js} +12 -12
  118. package/dist/watch-OIVXH2RN.js.map +1 -0
  119. package/package.json +103 -103
  120. package/templates//352/260/204/354/235/264/352/270/260/354/225/210/353/254/270_/354/230/210/354/213/234/352/260/222.json +9 -9
  121. package/templates//354/235/274/353/260/230/352/270/260/354/225/210/353/254/270_/354/230/210/354/213/234/352/260/222.json +24 -24
  122. package/dist/chunk-2CF2WXBG.js.map +0 -1
  123. package/dist/chunk-3H5X63BQ.js.map +0 -1
  124. package/dist/chunk-45EUJ2P6.js.map +0 -1
  125. package/dist/chunk-5RPYBC2Q.js.map +0 -1
  126. package/dist/chunk-7FCNOKOR.js.map +0 -1
  127. package/dist/chunk-7ZQKECQT.js.map +0 -1
  128. package/dist/chunk-CBUC73JM.js.map +0 -1
  129. package/dist/chunk-DCZVOIEO.cjs.map +0 -1
  130. package/dist/chunk-DZIXKL7E.js.map +0 -1
  131. package/dist/chunk-EYLOFSA5.js.map +0 -1
  132. package/dist/chunk-FT2EXMMP.js.map +0 -1
  133. package/dist/chunk-GCHH4WHT.js.map +0 -1
  134. package/dist/chunk-GE43BE46.js.map +0 -1
  135. package/dist/chunk-GLPLLT4Q.cjs.map +0 -1
  136. package/dist/chunk-GZDOWYJM.js.map +0 -1
  137. package/dist/chunk-M4I5F6GK.js.map +0 -1
  138. package/dist/chunk-MOL7MDBG.js.map +0 -1
  139. package/dist/chunk-NBOVSNSR.js.map +0 -1
  140. package/dist/chunk-TCPO6XAY.cjs.map +0 -1
  141. package/dist/chunk-TP4P7M4I.js.map +0 -1
  142. package/dist/chunk-TTEHX4FN.js.map +0 -1
  143. package/dist/chunk-U3ZNF4SO.cjs.map +0 -1
  144. package/dist/chunk-UAFSPWU7.js.map +0 -1
  145. package/dist/chunk-UEWW3DH2.js.map +0 -1
  146. package/dist/chunk-UQ6KDJWE.cjs.map +0 -1
  147. package/dist/chunk-WDIPTY3Q.js.map +0 -1
  148. package/dist/chunk-XYSTZIIL.js.map +0 -1
  149. package/dist/chunk-ZHNUH2IW.js.map +0 -1
  150. package/dist/chunk-ZV5OKQ3Z.cjs.map +0 -1
  151. package/dist/chunks-EYDY4Y6O.js +0 -10
  152. package/dist/formula-CG4S7TWO.cjs.map +0 -1
  153. package/dist/formula-JLYS73QL.js.map +0 -1
  154. package/dist/formula-LIVQSCMM.js.map +0 -1
  155. package/dist/image-ocr-BWQIUUVC.js.map +0 -1
  156. package/dist/image-ocr-EDZOUU6Q.js.map +0 -1
  157. package/dist/image-ocr-KZ4MGVJ3.cjs.map +0 -1
  158. package/dist/page-range-737B4EZW.js +0 -8
  159. package/dist/page-range-LNMXJU6W.js +0 -7
  160. package/dist/page-range-P7SDW6LR.cjs +0 -8
  161. package/dist/page-range-P7SDW6LR.cjs.map +0 -1
  162. package/dist/parser-6OJOFJ64.js.map +0 -1
  163. package/dist/parser-7G2QFIYZ.cjs.map +0 -1
  164. package/dist/parser-JLEKXA2Z.js.map +0 -1
  165. package/dist/pdf-ocr-364VNE6N.cjs +0 -13
  166. package/dist/pdf-ocr-364VNE6N.cjs.map +0 -1
  167. package/dist/pdf-ocr-OWUZLWQ3.js +0 -15
  168. package/dist/pdf-ocr-YQGMCT53.js +0 -12
  169. package/dist/rasterize-LARCX6CS.js.map +0 -1
  170. package/dist/render-4WHQ7JCX.js +0 -10
  171. package/dist/seal-BX6YA6PI.js +0 -10
  172. package/dist/setup-GUQT5ELJ.js.map +0 -1
  173. package/dist/watch-Y2VKD2MN.js.map +0 -1
  174. /package/dist/{-CWYIC4ES.js.map → -DEVLQKIT.js.map} +0 -0
  175. /package/dist/{chunks-EYDY4Y6O.js.map → chunks-Y2VE2WCJ.js.map} +0 -0
  176. /package/dist/{models-6SIKLKRH.js.map → models-6VMAVZR6.js.map} +0 -0
  177. /package/dist/{page-range-737B4EZW.js.map → page-range-CIRRJPXJ.js.map} +0 -0
  178. /package/dist/{page-range-LNMXJU6W.js.map → page-range-OF5I4PQY.js.map} +0 -0
  179. /package/dist/{pdf-ocr-OWUZLWQ3.js.map → pdf-ocr-UKFYODLP.js.map} +0 -0
  180. /package/dist/{pdf-ocr-YQGMCT53.js.map → pdf-ocr-ZG4UVUIT.js.map} +0 -0
  181. /package/dist/{profile-io-FMPT6VEZ.js.map → profile-io-FQZNNA3O.js.map} +0 -0
  182. /package/dist/{redact-7ILEAUTS.js.map → redact-W5K3ILQH.js.map} +0 -0
  183. /package/dist/{render-4WHQ7JCX.js.map → render-YFIWQYVB.js.map} +0 -0
  184. /package/dist/{seal-BX6YA6PI.js.map → seal-GZ7NDWLR.js.map} +0 -0
@@ -1,35 +1,35 @@
1
- rhwp — 표준 기안문 서식 자산 (templates/)
2
- Copyright 2025-2026 Edward Kim
3
- License: MIT
4
- Source: https://github.com/edwardkim/rhwp
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in
14
- all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- THE SOFTWARE.
23
-
24
- ---
25
-
26
- Usage in kordoc:
27
- templates/ 의 내장 표준 기안문 서식 2종(일반기안문_서식.hwpx,
28
- 간이기안문_서식.hwpx)과 예시값 JSON은 rhwp 저장소 tools/forms/ 의
29
- 누름틀(CLICK_HERE) 내장 서식 자산을 그대로 번들한 것이다.
30
-
31
- 서식의 원본 배치는 「행정 효율과 협업 촉진에 관한 규정 시행규칙」
32
- 별지 제1호서식(일반기안문)·별지 제2호서식(간이기안문) — 대한민국 법령
33
- 부속 서식으로, 서식 자체는 저작권 보호 대상이 아닌 공공 자료다.
34
- rhwp 는 이를 HWPX 누름틀 서식으로 구현했고, kordoc 은 그 구현물을
35
- MIT 라이선스에 따라 재배포한다.
1
+ rhwp — 표준 기안문 서식 자산 (templates/)
2
+ Copyright 2025-2026 Edward Kim
3
+ License: MIT
4
+ Source: https://github.com/edwardkim/rhwp
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+
24
+ ---
25
+
26
+ Usage in kordoc:
27
+ templates/ 의 내장 표준 기안문 서식 2종(일반기안문_서식.hwpx,
28
+ 간이기안문_서식.hwpx)과 예시값 JSON은 rhwp 저장소 tools/forms/ 의
29
+ 누름틀(CLICK_HERE) 내장 서식 자산을 그대로 번들한 것이다.
30
+
31
+ 서식의 원본 배치는 「행정 효율과 협업 촉진에 관한 규정 시행규칙」
32
+ 별지 제1호서식(일반기안문)·별지 제2호서식(간이기안문) — 대한민국 법령
33
+ 부속 서식으로, 서식 자체는 저작권 보호 대상이 아닌 공공 자료다.
34
+ rhwp 는 이를 HWPX 누름틀 서식으로 구현했고, kordoc 은 그 구현물을
35
+ MIT 라이선스에 따라 재배포한다.
@@ -52,28 +52,28 @@ import {
52
52
  resolveBuiltinTemplate,
53
53
  unknownFontWarnings,
54
54
  validateHwpx
55
- } from "./chunk-45EUJ2P6.js";
56
- import "./chunk-7FCNOKOR.js";
57
- import "./chunk-UEWW3DH2.js";
58
- import "./chunk-MOL7MDBG.js";
55
+ } from "./chunk-7BTO44Q7.js";
56
+ import "./chunk-W34QSJFF.js";
57
+ import "./chunk-PSAHY5XW.js";
58
+ import "./chunk-3TBUDJDE.js";
59
59
  import {
60
60
  applySplices,
61
61
  buildParagraphSplices,
62
62
  buildRangeSplices,
63
63
  placeSealHwpx,
64
64
  scanSectionXml
65
- } from "./chunk-CBUC73JM.js";
65
+ } from "./chunk-OPL22SYF.js";
66
66
  import {
67
67
  DEFAULT_REDACT_RULES,
68
68
  redactMarkdown,
69
69
  redactText
70
- } from "./chunk-DZIXKL7E.js";
70
+ } from "./chunk-O5EK52TG.js";
71
71
  import {
72
72
  blocksToChunks
73
- } from "./chunk-7ZQKECQT.js";
73
+ } from "./chunk-VNEV6VKD.js";
74
74
  import {
75
75
  blocksToMarkdown
76
- } from "./chunk-ZHNUH2IW.js";
76
+ } from "./chunk-IBVMJIHR.js";
77
77
  import {
78
78
  SPACE_EM_FIXED,
79
79
  SPACE_EM_FONT,
@@ -83,11 +83,11 @@ import {
83
83
  renderHwpxToSvg,
84
84
  simulateWrap,
85
85
  simulateWrapKeepWord
86
- } from "./chunk-WDIPTY3Q.js";
87
- import "./chunk-TP4P7M4I.js";
86
+ } from "./chunk-UQ4NSK66.js";
87
+ import "./chunk-KC63W72C.js";
88
88
  import {
89
89
  VERSION
90
- } from "./chunk-TTEHX4FN.js";
90
+ } from "./chunk-ZYQ5X6PG.js";
91
91
  export {
92
92
  BUILTIN_TEMPLATES,
93
93
  DEFAULT_REDACT_RULES,
@@ -161,4 +161,4 @@ export {
161
161
  unknownFontWarnings,
162
162
  validateHwpx
163
163
  };
164
- //# sourceMappingURL=-CWYIC4ES.js.map
164
+ //# sourceMappingURL=-DEVLQKIT.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  extractPageBlocksWithLines
4
- } from "./chunk-2CF2WXBG.js";
4
+ } from "./chunk-PQDNCWAZ.js";
5
5
  import {
6
6
  OCR_DET_MODEL,
7
7
  OCR_REC_DICT,
@@ -9,7 +9,7 @@ import {
9
9
  ensureOcrModels,
10
10
  getOcrModelsDir,
11
11
  parseCharacterDict
12
- } from "./chunk-GZDOWYJM.js";
12
+ } from "./chunk-6XPXIG57.js";
13
13
 
14
14
  // src/ocr/ruling-lines.ts
15
15
  var INK_LUMA_MAX = 190;
@@ -371,7 +371,7 @@ function getOcrEngine() {
371
371
  // src/ocr/pdf-ocr.ts
372
372
  var OCR_RENDER_SCALE = 3;
373
373
  var PAGE_TIMEOUT_MS = 12e4;
374
- async function runPdfOcr(buffer, targets, mode, warnings, onProgress) {
374
+ async function runPdfOcr(buffer, targets, mode, warnings, onProgress, detectTables = true) {
375
375
  const result = /* @__PURE__ */ new Map();
376
376
  if (targets.size === 0) return result;
377
377
  const pdfiumMod = await tryImport2(
@@ -397,7 +397,7 @@ async function runPdfOcr(buffer, targets, mode, warnings, onProgress) {
397
397
  onProgress?.(++done, targets.size);
398
398
  try {
399
399
  const blocks = await withTimeout(
400
- ocrOnePage(page, pageNo, mode, engine, warnings),
400
+ ocrOnePage(page, pageNo, mode, engine, warnings, detectTables),
401
401
  PAGE_TIMEOUT_MS,
402
402
  `OCR \uD398\uC774\uC9C0 ${pageNo} \uD0C0\uC784\uC544\uC6C3 (${PAGE_TIMEOUT_MS / 1e3}\uCD08)`
403
403
  );
@@ -416,7 +416,7 @@ async function runPdfOcr(buffer, targets, mode, warnings, onProgress) {
416
416
  }
417
417
  return result;
418
418
  }
419
- async function ocrOnePage(page, pageNo, mode, engine, warnings) {
419
+ async function ocrOnePage(page, pageNo, mode, engine, warnings, detectTables) {
420
420
  const { originalWidth: pdfW, originalHeight: pdfH } = page.getOriginalSize();
421
421
  const rendered = await page.render({
422
422
  scale: OCR_RENDER_SCALE,
@@ -437,7 +437,7 @@ async function ocrOnePage(page, pageNo, mode, engine, warnings) {
437
437
  const scale = rh / pdfH;
438
438
  const ruling = detectRulingLines(rgba, rw, rh, scale);
439
439
  const extraLines = rulingToPdfLines(ruling, scale, pdfH);
440
- return ocrItemsToBlocks(items, pageNo, pdfW, pdfH, scale, extraLines);
440
+ return ocrItemsToBlocks(items, pageNo, pdfW, pdfH, scale, extraLines, detectTables);
441
441
  }
442
442
  const sharpModRaw = await tryImport2(
443
443
  "sharp",
@@ -453,7 +453,7 @@ async function ocrOnePage(page, pageNo, mode, engine, warnings) {
453
453
  }
454
454
  return [{ type: "paragraph", text: text.trim(), pageNumber: pageNo }];
455
455
  }
456
- function ocrItemsToBlocks(items, pageNumber, pdfW, pdfH, scale, extraLines) {
456
+ function ocrItemsToBlocks(items, pageNumber, pdfW, pdfH, scale, extraLines, detectTables = true) {
457
457
  const norm = items.map((it) => {
458
458
  const h = it.h / scale;
459
459
  return {
@@ -469,7 +469,7 @@ function ocrItemsToBlocks(items, pageNumber, pdfW, pdfH, scale, extraLines) {
469
469
  isHidden: false
470
470
  };
471
471
  });
472
- return extractPageBlocksWithLines(norm, pageNumber, { fnArray: [], argsArray: [] }, pdfW, pdfH, extraLines);
472
+ return extractPageBlocksWithLines(norm, pageNumber, { fnArray: [], argsArray: [] }, pdfW, pdfH, extraLines, detectTables);
473
473
  }
474
474
  async function tryImport2(name, loader) {
475
475
  try {
@@ -513,4 +513,4 @@ export {
513
513
  runPdfOcr,
514
514
  ocrItemsToBlocks
515
515
  };
516
- //# sourceMappingURL=chunk-M4I5F6GK.js.map
516
+ //# sourceMappingURL=chunk-2CABQYTT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ocr/ruling-lines.ts","../src/ocr/engine.ts","../src/ocr/pdf-ocr.ts"],"sourcesContent":["/**\r\n * 래스터 괘선 감지 — 스캔/이미지 페이지의 픽셀에서 표 괘선(수평/수직 선)을\r\n * 찾아 선 기반 표 파이프라인(line-detector → table-grid)에 공급한다.\r\n *\r\n * PDF 그래픽 ops 가 없는 OCR 경로는 종전에 클러스터 감지기만 탔는데,\r\n * 병합 라벨 셀 + 다중줄 서술형 서식(정부 제출 서식류)은 라인 클러스터로\r\n * 행 경계를 잡을 수 없다. 괘선은 이미지에 실존하므로 이진화 + 런렝스로\r\n * 직접 감지한다 — ML 없음, 축 정렬 전제(스캔 스큐 보정은 스코프 밖).\r\n *\r\n * 오탐 방어 3겹 (실측: 우수사례 제출 서식 스캔 — 진짜 괘선은 길고 얇음):\r\n * - 최소 길이 20pt: 글리프 획(가로 ≤한 글자폭·세로 ≤글자높이)은 걸러짐\r\n * - 두께 상한 2.5pt: 굵은 제목 획(6~13px)·텍스트 줄 밴드·채운 색상바 탈락\r\n * - 양측 잉크 포위 제외: 색상바(붙임 박스 등) 안 흰 글자 틈새 슬리버 탈락\r\n */\r\n\r\nimport type { LineSegment } from \"../pdf/line-types.js\"\r\n\r\n/** 잉크 판정 휘도 상한 — 셀 배경 음영(회색 ~220)은 통과, 실선(진회~검정·남색)만 잉크 */\r\nconst INK_LUMA_MAX = 190\r\n/** 최소 선 길이 (pt) — 최소 셀 변(≈7mm)과 대형 글리프 획(≤1em) 사이 */\r\nconst MIN_LINE_LENGTH_PT = 20\r\n/** 선 두께 상한 (pt) — 진짜 괘선(0.12~0.7mm ≈ 0.3~2pt)과 굵은 글리프 획(2pt+) 분리 */\r\nconst MAX_LINE_WIDTH_PT = 2.5\r\n/** 런 내 허용 끊김 (px) — 스캔 노이즈/JPEG 아티팩트 */\r\nconst GAP_TOL_PX = 2\r\n/** 밴드 병합 시 요구하는 x(또는 y) 범위 겹침 비율 (짧은 쪽 기준) */\r\nconst BAND_OVERLAP_RATIO = 0.5\r\n/** 밴드 양측(±2px)이 이 비율 이상 잉크면 채운 영역 내부 슬리버로 판정 */\r\nconst SURROUND_INK_RATIO = 0.35\r\n\r\n/** 픽셀 공간(top-left origin) 선분 — 두께는 밴드 픽셀 수 */\r\nexport interface PxSegment {\r\n x1: number; y1: number\r\n x2: number; y2: number\r\n thicknessPx: number\r\n}\r\n\r\nexport interface RulingLines {\r\n horizontals: PxSegment[]\r\n verticals: PxSegment[]\r\n}\r\n\r\n/**\r\n * RGBA 래스터에서 수평/수직 괘선 감지.\r\n * @param scale 렌더 스케일 (px/pt) — 길이·두께 임계를 pt 기준으로 환산\r\n */\r\nexport function detectRulingLines(\r\n rgba: Uint8Array,\r\n width: number,\r\n height: number,\r\n scale: number,\r\n): RulingLines {\r\n const minLenPx = Math.round(MIN_LINE_LENGTH_PT * scale)\r\n const maxThickPx = Math.max(1, Math.floor(MAX_LINE_WIDTH_PT * scale))\r\n\r\n // 잉크 마스크 1회 구축 — 수평/수직 두 패스가 공유\r\n const ink = new Uint8Array(width * height)\r\n for (let p = 0, i = 0; p < ink.length; p++, i += 4) {\r\n // ITU-R BT.601 휘도 근사 (정수 시프트)\r\n const luma = (rgba[i] * 77 + rgba[i + 1] * 150 + rgba[i + 2] * 29) >> 8\r\n if (luma <= INK_LUMA_MAX && rgba[i + 3] >= 128) ink[p] = 1\r\n }\r\n\r\n const horizontals = detectBands(ink, width, height, minLenPx, maxThickPx, false)\r\n const verticals = detectBands(ink, width, height, minLenPx, maxThickPx, true)\r\n return { horizontals, verticals }\r\n}\r\n\r\n/** 열린 밴드 — 인접 스캔라인의 런을 누적 */\r\ninterface Band {\r\n lo: number // 런 진행축 시작 (h: x1, v: y1)\r\n hi: number // 런 진행축 끝\r\n first: number // 직교축 시작 스캔라인 (h: yTop, v: xLeft)\r\n last: number // 직교축 마지막 스캔라인\r\n}\r\n\r\n/**\r\n * 스캔라인 런렝스 → 인접 라인 밴드 병합 → 두께 필터 → 선분 방출.\r\n * transpose=false 면 수평선(행 스캔), true 면 수직선(열 스캔).\r\n */\r\nfunction detectBands(\r\n ink: Uint8Array,\r\n width: number,\r\n height: number,\r\n minLenPx: number,\r\n maxThickPx: number,\r\n transpose: boolean,\r\n): PxSegment[] {\r\n const lines = transpose ? width : height // 스캔라인 개수 (직교축)\r\n const span = transpose ? height : width // 런 진행축 길이\r\n const at = transpose\r\n ? (line: number, pos: number) => ink[pos * width + line]\r\n : (line: number, pos: number) => ink[line * width + pos]\r\n\r\n const out: PxSegment[] = []\r\n let open: Band[] = []\r\n\r\n /** 밴드 양측(±2px 스캔라인)의 잉크 비율 — 채운 색상바 안 흰 글자 틈새 슬리버 판별 */\r\n const surroundInkRatio = (b: Band, side: number): number => {\r\n if (side < 0 || side >= lines) return 0\r\n let dark = 0, total = 0\r\n for (let pos = b.lo; pos <= b.hi; pos += 3) {\r\n total++\r\n if (at(side, pos) === 1) dark++\r\n }\r\n return total > 0 ? dark / total : 0\r\n }\r\n\r\n const emit = (b: Band) => {\r\n const thick = b.last - b.first + 1\r\n if (thick > maxThickPx) return // 색상바·텍스트 밴드\r\n if (\r\n surroundInkRatio(b, b.first - 2) >= SURROUND_INK_RATIO &&\r\n surroundInkRatio(b, b.last + 2) >= SURROUND_INK_RATIO\r\n ) return // 채운 영역 내부 슬리버\r\n const center = (b.first + b.last) / 2\r\n out.push(\r\n transpose\r\n ? { x1: center, y1: b.lo, x2: center, y2: b.hi, thicknessPx: thick }\r\n : { x1: b.lo, y1: center, x2: b.hi, y2: center, thicknessPx: thick },\r\n )\r\n }\r\n\r\n for (let line = 0; line < lines; line++) {\r\n // 1) 이 스캔라인의 잉크 런 수집 (GAP_TOL_PX 이하 끊김 허용)\r\n const runs: Array<{ lo: number; hi: number }> = []\r\n let runStart = -1\r\n let gap = 0\r\n for (let pos = 0; pos <= span; pos++) {\r\n const on = pos < span && at(line, pos) === 1\r\n if (on) {\r\n if (runStart < 0) runStart = pos\r\n gap = 0\r\n } else if (runStart >= 0) {\r\n if (++gap > GAP_TOL_PX || pos >= span) {\r\n const hi = pos - gap\r\n if (hi - runStart + 1 >= minLenPx) runs.push({ lo: runStart, hi })\r\n runStart = -1\r\n gap = 0\r\n }\r\n }\r\n }\r\n\r\n // 2) 열린 밴드와 병합 — 직전 스캔라인까지 이어졌고 범위가 겹치면 확장\r\n const next: Band[] = []\r\n const used = new Set<number>()\r\n for (const band of open) {\r\n if (band.last !== line - 1) { emit(band); continue } // 연속 끊김 → 확정\r\n let merged = false\r\n for (let r = 0; r < runs.length; r++) {\r\n if (used.has(r)) continue\r\n const run = runs[r]\r\n const overlap = Math.min(band.hi, run.hi) - Math.max(band.lo, run.lo) + 1\r\n const shorter = Math.min(band.hi - band.lo, run.hi - run.lo) + 1\r\n if (overlap >= shorter * BAND_OVERLAP_RATIO) {\r\n band.lo = Math.min(band.lo, run.lo)\r\n band.hi = Math.max(band.hi, run.hi)\r\n band.last = line\r\n next.push(band)\r\n used.add(r)\r\n merged = true\r\n break\r\n }\r\n }\r\n if (!merged) emit(band)\r\n }\r\n for (let r = 0; r < runs.length; r++) {\r\n if (!used.has(r)) next.push({ lo: runs[r].lo, hi: runs[r].hi, first: line, last: line })\r\n }\r\n open = next\r\n }\r\n for (const band of open) emit(band)\r\n return out\r\n}\r\n\r\n/**\r\n * 픽셀 선분 → PDF pt LineSegment (bottom-up, classifyAndAdd 규약:\r\n * 수평선 y1==y2·x1<x2, 수직선 x1==x2·y1<y2).\r\n */\r\nexport function rulingToPdfLines(\r\n ruling: RulingLines,\r\n scale: number,\r\n pdfHeight: number,\r\n): { horizontals: LineSegment[]; verticals: LineSegment[] } {\r\n const horizontals: LineSegment[] = ruling.horizontals.map(s => {\r\n const y = pdfHeight - (s.y1 + s.y2) / 2 / scale\r\n return {\r\n x1: Math.min(s.x1, s.x2) / scale,\r\n y1: y,\r\n x2: Math.max(s.x1, s.x2) / scale,\r\n y2: y,\r\n lineWidth: s.thicknessPx / scale,\r\n }\r\n })\r\n const verticals: LineSegment[] = ruling.verticals.map(s => {\r\n const x = (s.x1 + s.x2) / 2 / scale\r\n const yA = pdfHeight - Math.max(s.y1, s.y2) / scale\r\n const yB = pdfHeight - Math.min(s.y1, s.y2) / scale\r\n return { x1: x, y1: yA, x2: x, y2: yB, lineWidth: s.thicknessPx / scale }\r\n })\r\n return { horizontals, verticals }\r\n}\r\n","/**\r\n * 내장 텍스트 OCR 엔진 — PP-OCRv5 korean (det DBNet + rec SVTR/CTC) ONNX 추론.\r\n *\r\n * 파이프라인: 페이지 RGBA → det(선 검출) → 라인 crop → rec(CTC 인식) → OcrItem[]\r\n * (좌표는 입력 픽셀 기준 top-left origin — 호출자가 PDF 좌표계로 변환).\r\n *\r\n * 전·후처리는 공식 inference.yml 스펙 그대로:\r\n * - det: BGR, 긴 변 960 리사이즈(32 배수), mean/std [0.485,0.456,0.406]/[0.229,0.224,0.225],\r\n * DBPostProcess thresh 0.3 / box_thresh 0.6 / unclip_ratio 1.5\r\n * - rec: BGR, 높이 48 고정 비율 리사이즈 + 우측 zero-pad, (x/255-0.5)/0.5,\r\n * CTC 디코드 (blank=0, 1..N=사전, N+1=공백), text_score 0.5\r\n * DB 후처리의 contour+minAreaRect 는 축정렬 connected-component bbox 로 근사\r\n * (공문서 스캔은 수평 텍스트가 지배적 — 회전 텍스트는 v1 범위 밖).\r\n *\r\n * 의존성(onnxruntime-node, sharp)은 optional — 미설치 시 create()가 명확한 에러.\r\n * 모델 미다운로드 시에도 즉시 실패 — 호출자가 ensureOcrModels() 먼저.\r\n */\r\n\r\nimport type { InferenceSession } from \"onnxruntime-node\"\r\nimport { readFile } from \"fs/promises\"\r\nimport { join } from \"path\"\r\nimport {\r\n OCR_DET_MODEL,\r\n OCR_REC_MODEL,\r\n OCR_REC_DICT,\r\n getOcrModelsDir,\r\n parseCharacterDict,\r\n} from \"./models.js\"\r\n\r\n/** OCR 인식 결과 한 줄 — 좌표는 입력 이미지 픽셀 (top-left origin, y down) */\r\nexport interface OcrItem {\r\n text: string\r\n x: number\r\n y: number\r\n w: number\r\n h: number\r\n /** CTC 평균 신뢰도 0~1 */\r\n confidence: number\r\n}\r\n\r\n/** det 입력 긴 변 (공식 resize_long) — 1920 보존 실험은 recall Δ median +0.04pp에\r\n * 속도 -25%로 기각 (bench/ocr-accuracy.mjs 실측, 2026-08-02) */\r\nconst DET_LONG_SIDE = 960\r\nconst DET_THRESH = 0.3\r\nconst DET_BOX_THRESH = 0.6\r\nconst DET_UNCLIP_RATIO = 1.5\r\nconst DET_MIN_SIZE = 3\r\nconst DET_MAX_BOXES = 1000\r\nconst REC_HEIGHT = 48\r\nconst REC_MIN_WIDTH = 320\r\nconst REC_MAX_WIDTH = 3200\r\nconst TEXT_SCORE = 0.5\r\n\r\n// det: BGR 채널 순서에 yml 기재 순서 그대로 적용 (mean[0]→B)\r\nconst DET_MEAN = [0.485, 0.456, 0.406]\r\nconst DET_STD = [0.229, 0.224, 0.225]\r\n\r\ntype SharpFactory = (\r\n input: Uint8Array | Buffer,\r\n options?: { raw?: { width: number; height: number; channels: number } },\r\n) => SharpChain\r\ninterface SharpChain {\r\n extract(region: { left: number; top: number; width: number; height: number }): SharpChain\r\n resize(w: number, h: number, opts?: { fit?: string }): SharpChain\r\n removeAlpha(): SharpChain\r\n raw(): { toBuffer(): Promise<Buffer> }\r\n}\r\n\r\nexport class OcrEngine {\r\n private det: InferenceSession\r\n private rec: InferenceSession\r\n private dict: string[]\r\n private ort: typeof import(\"onnxruntime-node\")\r\n private sharp: SharpFactory\r\n\r\n private constructor(parts: {\r\n det: InferenceSession\r\n rec: InferenceSession\r\n dict: string[]\r\n ort: typeof import(\"onnxruntime-node\")\r\n sharp: SharpFactory\r\n }) {\r\n this.det = parts.det\r\n this.rec = parts.rec\r\n this.dict = parts.dict\r\n this.ort = parts.ort\r\n this.sharp = parts.sharp\r\n }\r\n\r\n static async create(): Promise<OcrEngine> {\r\n const [ortMod, sharpModRaw] = await Promise.all([\r\n tryImport<typeof import(\"onnxruntime-node\")>(\"onnxruntime-node\", () => import(\"onnxruntime-node\")),\r\n tryImport<{ default?: SharpFactory } & SharpFactory>(\r\n \"sharp\",\r\n () => import(\"sharp\") as unknown as Promise<{ default?: SharpFactory } & SharpFactory>,\r\n ),\r\n ])\r\n const sharpAny = sharpModRaw as { default?: SharpFactory } | SharpFactory\r\n const sharpMod: SharpFactory =\r\n typeof sharpAny === \"function\" ? sharpAny : (sharpAny.default ?? (sharpAny as unknown as SharpFactory))\r\n\r\n const dir = getOcrModelsDir()\r\n const sessionOpts: import(\"onnxruntime-node\").InferenceSession.SessionOptions = {\r\n graphOptimizationLevel: \"all\",\r\n executionProviders: [\"cpu\"],\r\n logSeverityLevel: 3, // paddle2onnx 변환 잔여물 W 로그 폭주 억제\r\n }\r\n const [det, rec, dictYml] = await Promise.all([\r\n ortMod.InferenceSession.create(join(dir, OCR_DET_MODEL.filename), sessionOpts),\r\n ortMod.InferenceSession.create(join(dir, OCR_REC_MODEL.filename), sessionOpts),\r\n readFile(join(dir, OCR_REC_DICT.filename), \"utf-8\"),\r\n ])\r\n const dict = parseCharacterDict(dictYml)\r\n if (dict.length === 0) throw new Error(\"OCR 사전 파싱 실패 — 모델 캐시를 삭제 후 재다운로드하세요\")\r\n\r\n return new OcrEngine({ det, rec, dict, ort: ortMod, sharp: sharpMod })\r\n }\r\n\r\n /** onnxruntime-node 1.14+ InferenceSession.release() — 구버전은 무시 */\r\n async destroy(): Promise<void> {\r\n for (const s of [this.det, this.rec]) {\r\n const rel = (s as unknown as { release?: () => Promise<void> }).release\r\n if (typeof rel === \"function\") {\r\n try { await rel.call(s) } catch { /* ignore */ }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 페이지 RGBA 픽셀 → 텍스트 라인 인식.\r\n * 반환 좌표는 입력 픽셀 기준. 라인은 위→아래, 좌→우 정렬.\r\n * @param stats 저신뢰(conf<0.5) 폐기 라인 카운트 출력 — 종전엔 무음 폐기라 관측 불가\r\n */\r\n async recognizePage(\r\n rgba: Uint8Array,\r\n width: number,\r\n height: number,\r\n stats?: { droppedLowConf: number },\r\n ): Promise<OcrItem[]> {\r\n if (width < DET_MIN_SIZE || height < DET_MIN_SIZE) return []\r\n const boxes = await this.detect(rgba, width, height)\r\n const items: OcrItem[] = []\r\n for (const b of boxes) {\r\n const r = await this.recognizeLine(rgba, width, height, b)\r\n if (!r || !r.text.trim()) continue\r\n if (r.confidence >= TEXT_SCORE) items.push(r)\r\n else if (stats) stats.droppedLowConf++\r\n }\r\n items.sort((a, b) => (a.y - b.y) || (a.x - b.x))\r\n return items\r\n }\r\n\r\n // ─── det ─────────────────────────────────────────────\r\n\r\n private async detect(\r\n rgba: Uint8Array,\r\n width: number,\r\n height: number,\r\n ): Promise<Array<{ x: number; y: number; w: number; h: number }>> {\r\n const ratio = DET_LONG_SIDE / Math.max(width, height)\r\n const dw = Math.max(32, Math.round((width * ratio) / 32) * 32)\r\n const dh = Math.max(32, Math.round((height * ratio) / 32) * 32)\r\n\r\n const rgb = await this.sharp(rgba, { raw: { width, height, channels: 4 } })\r\n .resize(dw, dh, { fit: \"fill\" })\r\n .removeAlpha()\r\n .raw()\r\n .toBuffer()\r\n\r\n // HWC RGB → CHW BGR float32 정규화\r\n const plane = dw * dh\r\n const input = new Float32Array(3 * plane)\r\n for (let i = 0; i < plane; i++) {\r\n const r = rgb[i * 3] / 255\r\n const g = rgb[i * 3 + 1] / 255\r\n const b = rgb[i * 3 + 2] / 255\r\n input[i] = (b - DET_MEAN[0]) / DET_STD[0]\r\n input[plane + i] = (g - DET_MEAN[1]) / DET_STD[1]\r\n input[2 * plane + i] = (r - DET_MEAN[2]) / DET_STD[2]\r\n }\r\n\r\n const tensor = new this.ort.Tensor(\"float32\", input, [1, 3, dh, dw])\r\n const out = await this.det.run({ [this.det.inputNames[0]]: tensor })\r\n const probMap = out[this.det.outputNames[0]].data as Float32Array\r\n\r\n const rawBoxes = componentBoxes(probMap, dw, dh)\r\n const sx = width / dw\r\n const sy = height / dh\r\n const boxes: Array<{ x: number; y: number; w: number; h: number }> = []\r\n for (const rb of rawBoxes.slice(0, DET_MAX_BOXES)) {\r\n // unclip: DB 는 학습 시 텍스트 영역을 수축시키므로 검출 박스를 되팽창\r\n const bw = rb.x2 - rb.x1 + 1\r\n const bh = rb.y2 - rb.y1 + 1\r\n const delta = (bw * bh * DET_UNCLIP_RATIO) / (2 * (bw + bh))\r\n const x1 = Math.max(0, Math.floor((rb.x1 - delta) * sx))\r\n const y1 = Math.max(0, Math.floor((rb.y1 - delta) * sy))\r\n const x2 = Math.min(width, Math.ceil((rb.x2 + 1 + delta) * sx))\r\n const y2 = Math.min(height, Math.ceil((rb.y2 + 1 + delta) * sy))\r\n if (x2 - x1 < DET_MIN_SIZE || y2 - y1 < DET_MIN_SIZE) continue\r\n boxes.push({ x: x1, y: y1, w: x2 - x1, h: y2 - y1 })\r\n }\r\n return boxes\r\n }\r\n\r\n // ─── rec ─────────────────────────────────────────────\r\n\r\n private async recognizeLine(\r\n rgba: Uint8Array,\r\n width: number,\r\n height: number,\r\n box: { x: number; y: number; w: number; h: number },\r\n ): Promise<OcrItem | null> {\r\n const rw = Math.min(REC_MAX_WIDTH, Math.max(16, Math.round((box.w * REC_HEIGHT) / box.h)))\r\n const padded = Math.max(REC_MIN_WIDTH, rw)\r\n\r\n const rgb = await this.sharp(rgba, { raw: { width, height, channels: 4 } })\r\n .extract({ left: box.x, top: box.y, width: box.w, height: box.h })\r\n .resize(rw, REC_HEIGHT, { fit: \"fill\" })\r\n .removeAlpha()\r\n .raw()\r\n .toBuffer()\r\n\r\n // HWC RGB → CHW BGR, (x/255-0.5)/0.5, 우측 zero-pad\r\n const plane = padded * REC_HEIGHT\r\n const input = new Float32Array(3 * plane) // pad 영역은 0 (=정규화 후 회색 아님 주의: PP 도 0 pad)\r\n for (let y = 0; y < REC_HEIGHT; y++) {\r\n for (let x = 0; x < rw; x++) {\r\n const src = (y * rw + x) * 3\r\n const dst = y * padded + x\r\n input[dst] = rgb[src + 2] / 255 / 0.5 - 1 // B\r\n input[plane + dst] = rgb[src + 1] / 255 / 0.5 - 1 // G\r\n input[2 * plane + dst] = rgb[src] / 255 / 0.5 - 1 // R\r\n }\r\n }\r\n\r\n const tensor = new this.ort.Tensor(\"float32\", input, [1, 3, REC_HEIGHT, padded])\r\n const out = await this.rec.run({ [this.rec.inputNames[0]]: tensor })\r\n const logits = out[this.rec.outputNames[0]]\r\n const [, T, C] = logits.dims as number[]\r\n const data = logits.data as Float32Array\r\n\r\n const decoded = ctcDecode(data, T, C, this.dict)\r\n if (!decoded) return null\r\n return { text: decoded.text, x: box.x, y: box.y, w: box.w, h: box.h, confidence: decoded.confidence }\r\n }\r\n}\r\n\r\n/** CTC greedy 디코드 — 연속 중복 붕괴 → blank(0) 제거 → 사전 매핑 (테스트용 export) */\r\nexport function ctcDecode(\r\n data: Float32Array,\r\n T: number,\r\n C: number,\r\n dict: string[],\r\n): { text: string; confidence: number } | null {\r\n let text = \"\"\r\n let confSum = 0\r\n let confCount = 0\r\n let prev = -1\r\n for (let t = 0; t < T; t++) {\r\n const off = t * C\r\n let best = 0\r\n let bestV = data[off]\r\n for (let c = 1; c < C; c++) {\r\n const v = data[off + c]\r\n if (v > bestV) { bestV = v; best = c }\r\n }\r\n const repeat = best === prev\r\n prev = best\r\n if (best === 0 || repeat) continue\r\n // 모델 출력이 softmax 확률이 아니면 (>1) 해당 스텝만 정규화\r\n let p = bestV\r\n if (p > 1.0001 || p < 0) {\r\n let denom = 0\r\n for (let c = 0; c < C; c++) denom += Math.exp(data[off + c] - bestV)\r\n p = 1 / denom\r\n }\r\n confSum += p\r\n confCount++\r\n if (best >= 1 && best <= dict.length) text += dict[best - 1]\r\n else if (best === dict.length + 1) text += \" \"\r\n }\r\n if (!text) return null\r\n return { text, confidence: confCount > 0 ? confSum / confCount : 0 }\r\n}\r\n\r\n/** 이진화 확률맵의 4-연결 성분 bbox (score = 성분 평균 확률, 테스트용 export) */\r\nexport function componentBoxes(\r\n prob: Float32Array,\r\n w: number,\r\n h: number,\r\n): Array<{ x1: number; y1: number; x2: number; y2: number }> {\r\n const visited = new Uint8Array(w * h)\r\n const boxes: Array<{ x1: number; y1: number; x2: number; y2: number; score: number }> = []\r\n const stack: number[] = []\r\n\r\n for (let start = 0; start < w * h; start++) {\r\n if (visited[start] || prob[start] <= DET_THRESH) continue\r\n let x1 = start % w, x2 = x1, y1 = (start / w) | 0, y2 = y1\r\n let sum = 0\r\n let count = 0\r\n stack.length = 0\r\n stack.push(start)\r\n visited[start] = 1\r\n while (stack.length) {\r\n const p = stack.pop()!\r\n const px = p % w\r\n const py = (p / w) | 0\r\n sum += prob[p]\r\n count++\r\n if (px < x1) x1 = px\r\n if (px > x2) x2 = px\r\n if (py < y1) y1 = py\r\n if (py > y2) y2 = py\r\n // 4-이웃\r\n if (px > 0 && !visited[p - 1] && prob[p - 1] > DET_THRESH) { visited[p - 1] = 1; stack.push(p - 1) }\r\n if (px < w - 1 && !visited[p + 1] && prob[p + 1] > DET_THRESH) { visited[p + 1] = 1; stack.push(p + 1) }\r\n if (py > 0 && !visited[p - w] && prob[p - w] > DET_THRESH) { visited[p - w] = 1; stack.push(p - w) }\r\n if (py < h - 1 && !visited[p + w] && prob[p + w] > DET_THRESH) { visited[p + w] = 1; stack.push(p + w) }\r\n }\r\n if (x2 - x1 + 1 < DET_MIN_SIZE && y2 - y1 + 1 < DET_MIN_SIZE) continue\r\n boxes.push({ x1, y1, x2, y2, score: sum / count })\r\n }\r\n\r\n return boxes\r\n .filter(b => b.score >= DET_BOX_THRESH)\r\n .sort((a, b) => (a.y1 - b.y1) || (a.x1 - b.x1))\r\n}\r\n\r\nasync function tryImport<T>(name: string, loader: () => Promise<T>): Promise<T> {\r\n try {\r\n return await loader()\r\n } catch (e) {\r\n throw new Error(\r\n `내장 OCR 을 사용하려면 optional dependency '${name}' 이 필요합니다. ` +\r\n `\\`npm install ${name}\\` 후 다시 실행하세요. 원인: ${(e as Error).message}`,\r\n )\r\n }\r\n}\r\n\r\n// ─── 엔진 싱글턴 (watch/서버 장기 실행에서 세션 재사용) ───\r\nlet enginePromise: Promise<OcrEngine> | null = null\r\n\r\nexport function getOcrEngine(): Promise<OcrEngine> {\r\n if (!enginePromise) {\r\n enginePromise = OcrEngine.create().catch(err => {\r\n enginePromise = null // 실패는 캐시하지 않음 — 모델 설치 후 재시도 가능\r\n throw err\r\n })\r\n }\r\n return enginePromise\r\n}\r\n","/**\r\n * PDF OCR 브릿지 — 대상 페이지를 pdfium 으로 래스터해 내장 엔진 또는\r\n * 사용자 OcrProvider 로 인식하고, 페이지별 IRBlock[] 을 돌려준다.\r\n *\r\n * - 내장 엔진: OcrItem 좌표를 PDF 포인트로 환산해 기존 블록 파이프라인\r\n * (extractPageBlocksWithLines — xy-cut 읽기순서·클러스터 표 감지)에 태운다.\r\n * 스캔 문서도 표 구조 복원이 가능한 이유.\r\n * - 사용자 프로바이더: 페이지 PNG → 텍스트 → 페이지당 paragraph 블록 (종전 계약).\r\n *\r\n * 에러 계약 (v4.1.0 리뷰 F6/F7):\r\n * - 환경 오류(의존성·모델 미설치, PDF 오픈 실패)는 throw — 호출자가 NEEDS_OCR 폴백.\r\n * - 페이지 단위 실패는 본문 오염 대신 warnings(OCR_FAILED) 로 기록하고 계속.\r\n */\r\n\r\nimport type { IRBlock, OcrProvider, ParseWarning } from \"../types.js\"\r\nimport type { NormItem } from \"../pdf/text-line.js\"\r\nimport type { LineSegment } from \"../pdf/line-types.js\"\r\nimport { extractPageBlocksWithLines } from \"../pdf/page-blocks.js\"\r\nimport { detectRulingLines, rulingToPdfLines } from \"./ruling-lines.js\"\r\nimport { getOcrEngine, type OcrItem } from \"./engine.js\"\r\nimport { ensureOcrModels } from \"./models.js\"\r\n\r\n/** OCR 렌더 스케일 (72dpi × 3 = 216dpi) — 10pt 본문이 rec 입력 높이(48px)에 근접 */\r\nconst OCR_RENDER_SCALE = 3\r\n/** 페이지 하나당 OCR 타임아웃 — det+rec 수십 라인 기준 넉넉히 */\r\nconst PAGE_TIMEOUT_MS = 120_000\r\n\r\nexport type OcrMode = \"builtin\" | OcrProvider\r\n\r\n/**\r\n * 대상 페이지들을 OCR 해 페이지별 블록 맵 반환.\r\n * @param buffer 원본 PDF (pdfjs 가 detach 하기 전에 clone 해 둔 것)\r\n * @param targets 1-based 페이지 번호 집합\r\n */\r\nexport async function runPdfOcr(\r\n buffer: ArrayBuffer,\r\n targets: Set<number>,\r\n mode: OcrMode,\r\n warnings: ParseWarning[],\r\n onProgress?: (current: number, total: number) => void,\r\n detectTables = true,\r\n): Promise<Map<number, IRBlock[]>> {\r\n const result = new Map<number, IRBlock[]>()\r\n if (targets.size === 0) return result\r\n\r\n // 환경 준비 — 실패는 그대로 throw (호출자가 NEEDS_OCR 폴백)\r\n const pdfiumMod = await tryImport<typeof import(\"@hyzyla/pdfium\")>(\r\n \"@hyzyla/pdfium\",\r\n () => import(\"@hyzyla/pdfium\"),\r\n )\r\n if (mode === \"builtin\") {\r\n await ensureOcrModels(p => {\r\n if (p.phase === \"download\" && p.downloaded === 0) {\r\n process.stderr.write(`[kordoc-ocr] ${p.spec.name} 다운로드 중 (~${p.spec.sizeMb}MB)...\\n`)\r\n }\r\n })\r\n }\r\n const engine = mode === \"builtin\" ? await getOcrEngine() : null\r\n\r\n const pdfium = await pdfiumMod.PDFiumLibrary.init()\r\n const doc = await pdfium.loadDocument(new Uint8Array(buffer))\r\n try {\r\n let done = 0\r\n for (const page of doc.pages()) {\r\n // pdfium page.number 는 0-based pageIndex — 대외 계약(1-based)으로 환산\r\n const pageNo = page.number + 1\r\n if (!targets.has(pageNo)) continue\r\n onProgress?.(++done, targets.size)\r\n try {\r\n const blocks = await withTimeout(\r\n ocrOnePage(page, pageNo, mode, engine, warnings, detectTables),\r\n PAGE_TIMEOUT_MS,\r\n `OCR 페이지 ${pageNo} 타임아웃 (${PAGE_TIMEOUT_MS / 1000}초)`,\r\n )\r\n result.set(pageNo, blocks)\r\n } catch (e) {\r\n warnings.push({\r\n page: pageNo,\r\n message: `페이지 ${pageNo} OCR 실패: ${e instanceof Error ? e.message : String(e)}`,\r\n code: \"OCR_FAILED\",\r\n })\r\n }\r\n }\r\n } finally {\r\n doc.destroy()\r\n pdfium.destroy()\r\n }\r\n return result\r\n}\r\n\r\nasync function ocrOnePage(\r\n page: import(\"@hyzyla/pdfium\").PDFiumPage,\r\n pageNo: number,\r\n mode: OcrMode,\r\n engine: Awaited<ReturnType<typeof getOcrEngine>> | null,\r\n warnings: ParseWarning[],\r\n detectTables: boolean,\r\n): Promise<IRBlock[]> {\r\n const { originalWidth: pdfW, originalHeight: pdfH } = page.getOriginalSize()\r\n const rendered = await page.render({\r\n scale: OCR_RENDER_SCALE,\r\n render: async ({ data }) => data,\r\n })\r\n const { data: bgra, width: rw, height: rh } = rendered\r\n const rgba = bgraToRgba(bgra)\r\n\r\n if (mode === \"builtin\") {\r\n const stats = { droppedLowConf: 0 }\r\n const items = await engine!.recognizePage(rgba, rw, rh, stats)\r\n if (stats.droppedLowConf > 0) {\r\n warnings.push({\r\n page: pageNo,\r\n message: `페이지 ${pageNo}: 저신뢰 OCR 라인 ${stats.droppedLowConf}개 폐기 (인식 결손 가능)`,\r\n code: \"OCR_LOW_CONF\",\r\n })\r\n }\r\n // 래스터에서 표 괘선 감지 — 스캔본 병합셀 서식도 선 기반 표 파이프라인을 탄다\r\n const scale = rh / pdfH\r\n const ruling = detectRulingLines(rgba, rw, rh, scale)\r\n const extraLines = rulingToPdfLines(ruling, scale, pdfH)\r\n return ocrItemsToBlocks(items, pageNo, pdfW, pdfH, scale, extraLines, detectTables)\r\n }\r\n\r\n // 사용자 프로바이더 — PNG 인코딩 후 호출, 페이지당 paragraph (종전 계약)\r\n type SharpPngFactory = (\r\n input: Uint8Array,\r\n opts: { raw: { width: number; height: number; channels: number } },\r\n ) => { png(): { toBuffer(): Promise<Buffer> } }\r\n const sharpModRaw = await tryImport<{ default?: SharpPngFactory } & SharpPngFactory>(\r\n \"sharp\",\r\n () => import(\"sharp\") as unknown as Promise<{ default?: SharpPngFactory } & SharpPngFactory>,\r\n )\r\n const sharpAny = sharpModRaw as { default?: SharpPngFactory } | SharpPngFactory\r\n const sharp: SharpPngFactory =\r\n typeof sharpAny === \"function\" ? sharpAny : (sharpAny.default ?? (sharpAny as unknown as SharpPngFactory))\r\n const png = await sharp(rgba, { raw: { width: rw, height: rh, channels: 4 } })\r\n .png()\r\n .toBuffer()\r\n\r\n const text = await mode(new Uint8Array(png), pageNo, \"image/png\")\r\n if (!text.trim()) {\r\n warnings.push({ page: pageNo, message: `페이지 ${pageNo} OCR 결과 없음`, code: \"OCR_FAILED\" })\r\n return []\r\n }\r\n return [{ type: \"paragraph\", text: text.trim(), pageNumber: pageNo }]\r\n}\r\n\r\n/**\r\n * OcrItem(렌더 픽셀, top-left origin) → NormItem(PDF pt, bottom-up baseline)\r\n * 으로 환산해 기존 블록 파이프라인에 태운다. 괘선은 그래픽 ops 대신\r\n * 래스터 감지 결과(extraLines)로 공급 — 없으면 클러스터 감지기 몫이다.\r\n * (이미지 직접 입력 경로 image-ocr.ts 와 공유)\r\n */\r\nexport function ocrItemsToBlocks(\r\n items: OcrItem[],\r\n pageNumber: number,\r\n pdfW: number,\r\n pdfH: number,\r\n scale: number,\r\n extraLines?: { horizontals: LineSegment[]; verticals: LineSegment[] },\r\n detectTables = true,\r\n): IRBlock[] {\r\n const norm: NormItem[] = items.map(it => {\r\n const h = it.h / scale\r\n return {\r\n text: it.text,\r\n x: Math.round(it.x / scale),\r\n // NormItem.y 는 pdfjs transform[5] = 베이스라인 (bottom-up) — 박스 하단으로 근사\r\n y: Math.round(pdfH - (it.y + it.h) / scale),\r\n w: Math.round(it.w / scale),\r\n h: Math.round(h),\r\n fontSize: Math.max(1, Math.round(h * 0.8)), // 박스높이는 어센더+디센더 포함 — 글자크기 근사\r\n fontName: \"ocr\",\r\n isHidden: false,\r\n }\r\n })\r\n return extractPageBlocksWithLines(norm, pageNumber, { fnArray: [], argsArray: [] }, pdfW, pdfH, extraLines, detectTables)\r\n}\r\n\r\nasync function tryImport<T>(name: string, loader: () => Promise<T>): Promise<T> {\r\n try {\r\n return await loader()\r\n } catch (e) {\r\n throw new Error(\r\n `OCR 을 사용하려면 optional dependency '${name}' 이 필요합니다. ` +\r\n `\\`npm install ${name}\\` 후 다시 실행하세요. 원인: ${(e as Error).message}`,\r\n )\r\n }\r\n}\r\n\r\nasync function withTimeout<T>(promise: Promise<T>, ms: number, msg: string): Promise<T> {\r\n // 타임아웃 패배 후 원 promise 의 사후 reject 가 unhandled rejection 이 되지 않게 흡수\r\n promise.catch(() => {})\r\n let timer: NodeJS.Timeout | undefined\r\n try {\r\n return await Promise.race([\r\n promise,\r\n new Promise<never>((_, reject) => {\r\n timer = setTimeout(() => reject(new Error(msg)), ms)\r\n }),\r\n ])\r\n } finally {\r\n if (timer) clearTimeout(timer)\r\n }\r\n}\r\n\r\n/** pdfium BGRA → RGBA */\r\nfunction bgraToRgba(bgra: Uint8Array): Uint8Array {\r\n const out = new Uint8Array(bgra.length)\r\n for (let i = 0; i < bgra.length; i += 4) {\r\n out[i] = bgra[i + 2]\r\n out[i + 1] = bgra[i + 1]\r\n out[i + 2] = bgra[i]\r\n out[i + 3] = bgra[i + 3]\r\n }\r\n return out\r\n}\r\n"],"mappings":"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","names":["tryImport"]}
@@ -4,7 +4,7 @@ import {
4
4
  HEADING_RATIO_H3,
5
5
  safeMax,
6
6
  safeMin
7
- } from "./chunk-EYLOFSA5.js";
7
+ } from "./chunk-UGKXJA3W.js";
8
8
 
9
9
  // src/pdf/image-regions.ts
10
10
  import { OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
@@ -2898,7 +2898,7 @@ function buildGridTable(lines, columns) {
2898
2898
  }
2899
2899
 
2900
2900
  // src/pdf/page-blocks.ts
2901
- function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines) {
2901
+ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeight, extraLines, detectTables = true) {
2902
2902
  if (items.length === 0) return [];
2903
2903
  let { horizontals, verticals } = extractLines(opList.fnArray, opList.argsArray);
2904
2904
  if (extraLines) {
@@ -2912,11 +2912,11 @@ function extractPageBlocksWithLines(items, pageNum, opList, pageWidth, pageHeigh
2912
2912
  verticals = bridgeSplitColumnVerticals(horizontals, verticals);
2913
2913
  markStrikethroughItems(items, horizontals);
2914
2914
  wrapStrikethroughRuns(items);
2915
- const grids = buildTableGrids(horizontals, verticals);
2915
+ const grids = detectTables ? buildTableGrids(horizontals, verticals) : [];
2916
2916
  if (grids.length > 0) {
2917
2917
  return extractBlocksWithGrids(items, pageNum, grids, horizontals, verticals);
2918
2918
  }
2919
- return extractPageBlocksFallback(items, pageNum, true);
2919
+ return extractPageBlocksFallback(items, pageNum, true, detectTables);
2920
2920
  }
2921
2921
  var STRIKE_MAX_THICKNESS = 2;
2922
2922
  var STRIKE_MAX_THICKNESS_RATIO = 0.25;
@@ -3282,7 +3282,7 @@ function splitTwoColumnProse(items, cutX) {
3282
3282
  }
3283
3283
  return groups;
3284
3284
  }
3285
- function extractPageBlocksFallback(items, pageNum, fullPage = false) {
3285
+ function extractPageBlocksFallback(items, pageNum, fullPage = false, detectTables = true) {
3286
3286
  if (items.length === 0) return [];
3287
3287
  const blocks = [];
3288
3288
  const clusterItems = items.map((i) => ({
@@ -3295,7 +3295,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false) {
3295
3295
  fontName: i.fontName,
3296
3296
  hasSpaceBefore: i.hasSpaceBefore
3297
3297
  }));
3298
- const clusterResults = detectClusterTables(clusterItems, pageNum);
3298
+ const clusterResults = detectTables ? detectClusterTables(clusterItems, pageNum) : [];
3299
3299
  if (clusterResults.length > 0) {
3300
3300
  const ciToIdx = /* @__PURE__ */ new Map();
3301
3301
  for (let ci = 0; ci < clusterItems.length; ci++) ciToIdx.set(clusterItems[ci], ci);
@@ -3325,7 +3325,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false) {
3325
3325
  } else {
3326
3326
  const proseCutX = fullPage ? findTwoColumnProseCutX(items) : null;
3327
3327
  const allYLines = mergeSuperscriptLines(groupByY(items));
3328
- const columns = proseCutX !== null ? null : detectColumns(allYLines);
3328
+ const columns = proseCutX !== null || !detectTables ? null : detectColumns(allYLines);
3329
3329
  if (columns && columns.length >= 3) {
3330
3330
  const tableText = extractWithColumns(allYLines, columns);
3331
3331
  const bbox = computeBBox(items, pageNum);
@@ -3338,7 +3338,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false) {
3338
3338
  for (const group of orderedGroups) {
3339
3339
  if (group.length === 0) continue;
3340
3340
  const yLines = mergeSuperscriptLines(groupByY(group));
3341
- const groupColumns = detectColumns(yLines);
3341
+ const groupColumns = detectTables ? detectColumns(yLines) : null;
3342
3342
  if (groupColumns && groupColumns.length >= 3) {
3343
3343
  const tableText = extractWithColumns(yLines, groupColumns);
3344
3344
  const bbox = computeBBox(group, pageNum);
@@ -3354,7 +3354,7 @@ function extractPageBlocksFallback(items, pageNum, fullPage = false) {
3354
3354
  }
3355
3355
  }
3356
3356
  }
3357
- return detectSpecialKoreanTables(blocks);
3357
+ return detectTables ? detectSpecialKoreanTables(blocks) : blocks;
3358
3358
  }
3359
3359
 
3360
3360
  export {
@@ -3371,4 +3371,4 @@ export {
3371
3371
  extractPageBlocksWithLines,
3372
3372
  mergeCrossPageTables
3373
3373
  };
3374
- //# sourceMappingURL=chunk-NBOVSNSR.js.map
3374
+ //# sourceMappingURL=chunk-2CENWSAW.js.map