kordoc 4.24.14 → 4.24.16

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 (124) hide show
  1. package/README.md +6 -6
  2. package/dist/{-MG25ST6Y.js → -ZKOPHIGY.js} +11 -11
  3. package/dist/{chunk-NVD24SLL.js → chunk-2UXGDAWQ.js} +2 -2
  4. package/dist/{chunk-MIJYCKJA.js → chunk-3TXAIEXL.js} +246 -14
  5. package/dist/chunk-3TXAIEXL.js.map +1 -0
  6. package/dist/{chunk-FUC3V2FR.js → chunk-5Y7E5VTW.js} +2 -2
  7. package/dist/{chunk-LMQT2JFS.js → chunk-6K2SYTNJ.js} +49 -11
  8. package/dist/chunk-6K2SYTNJ.js.map +1 -0
  9. package/dist/{chunk-MACV3MZF.js → chunk-6KELCESN.js} +50 -12
  10. package/dist/chunk-6KELCESN.js.map +1 -0
  11. package/dist/{chunk-ZIGWQ3IS.js → chunk-6PPVAQ2N.js} +2 -2
  12. package/dist/{chunk-UUNCLJB7.js → chunk-BHY2QAPM.js} +11 -11
  13. package/dist/{chunk-ZBHS7KLW.cjs → chunk-CCH6BUNI.cjs} +6 -6
  14. package/dist/{chunk-ZBHS7KLW.cjs.map → chunk-CCH6BUNI.cjs.map} +1 -1
  15. package/dist/{chunk-AM6TECXD.js → chunk-DH564VPR.js} +2 -2
  16. package/dist/{chunk-UTLHDVC5.js → chunk-F3VIVCIF.js} +2 -2
  17. package/dist/{chunk-LSFVBS2I.js → chunk-FK7KGS5D.js} +2 -2
  18. package/dist/{chunk-BUGXM2H5.cjs → chunk-GPZMU2CM.cjs} +3 -3
  19. package/dist/{chunk-BUGXM2H5.cjs.map → chunk-GPZMU2CM.cjs.map} +1 -1
  20. package/dist/{chunk-J3OJWUON.js → chunk-GYVSGOQW.js} +2 -2
  21. package/dist/{chunk-VBOFP27O.js → chunk-J5M5OW4S.js} +2 -2
  22. package/dist/{chunk-WFOPOJHN.js → chunk-JS5CL2ZI.js} +246 -14
  23. package/dist/chunk-JS5CL2ZI.js.map +1 -0
  24. package/dist/{chunk-SSDHVKLM.js → chunk-K3OLWLXH.js} +2 -2
  25. package/dist/{chunk-FGERVDQZ.js → chunk-LPNWTWOQ.js} +2 -2
  26. package/dist/{chunk-V26UPU2L.cjs → chunk-NS2F4DXC.cjs} +3 -3
  27. package/dist/{chunk-V26UPU2L.cjs.map → chunk-NS2F4DXC.cjs.map} +1 -1
  28. package/dist/{chunk-TPZPICDM.js → chunk-NUQ5B2ZM.js} +3 -3
  29. package/dist/{chunk-HVILFVAT.cjs → chunk-NVOB6OHS.cjs} +2 -2
  30. package/dist/{chunk-HVILFVAT.cjs.map → chunk-NVOB6OHS.cjs.map} +1 -1
  31. package/dist/{chunk-PVAJ4DBW.js → chunk-QJA3KGRP.js} +2 -2
  32. package/dist/{chunk-NIPFEMJJ.js → chunk-QOMLSZLM.js} +2 -2
  33. package/dist/{chunk-DIFJPJGN.js → chunk-S2VWNC2W.js} +3 -3
  34. package/dist/{chunk-GV7ECKOK.cjs → chunk-SR6DJBPM.cjs} +195 -157
  35. package/dist/chunk-SR6DJBPM.cjs.map +1 -0
  36. package/dist/{chunk-ERYRF3EA.js → chunk-TSCQDCAR.js} +2 -2
  37. package/dist/{chunk-3LFNTOL7.js → chunk-UCEGWDWX.js} +6 -6
  38. package/dist/{chunk-AULD3QAB.js → chunk-V5R3PJQH.js} +2 -2
  39. package/dist/{chunk-7VNYQHJV.js → chunk-VAMCM435.js} +2 -2
  40. package/dist/{chunk-ZX66BXB5.cjs → chunk-X2SPQOI7.cjs} +328 -96
  41. package/dist/chunk-X2SPQOI7.cjs.map +1 -0
  42. package/dist/chunks-BHTYFFI2.js +10 -0
  43. package/dist/cli.js +30 -30
  44. package/dist/{formula-4KFXQSEG.js → formula-4C7ADOLE.js} +4 -4
  45. package/dist/{formula-WAYIKS54.js → formula-4Y7UWTEG.js} +3 -3
  46. package/dist/{formula-N6GWFPHK.cjs → formula-KZWH4YUD.cjs} +9 -9
  47. package/dist/{formula-N6GWFPHK.cjs.map → formula-KZWH4YUD.cjs.map} +1 -1
  48. package/dist/{image-ocr-QBCR7ZD6.js → image-ocr-AEX2L326.js} +9 -9
  49. package/dist/{image-ocr-P5S2QDKW.js → image-ocr-ASLUPAQG.js} +6 -6
  50. package/dist/{image-ocr-GY7SZAHN.cjs → image-ocr-GT3NILUJ.cjs} +17 -17
  51. package/dist/{image-ocr-GY7SZAHN.cjs.map → image-ocr-GT3NILUJ.cjs.map} +1 -1
  52. package/dist/index.cjs +360 -360
  53. package/dist/index.js +6 -6
  54. package/dist/mcp.js +23 -23
  55. package/dist/{model-bundle-QAM24SYG.js → model-bundle-2TXD77HN.js} +5 -5
  56. package/dist/{models-YHC236MP.js → models-WRJKLZA6.js} +5 -5
  57. package/dist/{parse-worker-v2-LP7NKQBP.js → parse-worker-v2-C2GR62SO.js} +12 -12
  58. package/dist/{parser-JWU6HYZZ.cjs → parser-5R34V6PS.cjs} +159 -159
  59. package/dist/{parser-JWU6HYZZ.cjs.map → parser-5R34V6PS.cjs.map} +1 -1
  60. package/dist/{parser-26YUZU55.js → parser-GFZILOKI.js} +12 -12
  61. package/dist/{parser-DFJZ3CCE.js → parser-LU7BIHXD.js} +9 -9
  62. package/dist/pdf-ocr-QRBPEPOZ.cjs +35 -0
  63. package/dist/{pdf-ocr-Z4PBLB75.cjs.map → pdf-ocr-QRBPEPOZ.cjs.map} +1 -1
  64. package/dist/{pdf-ocr-EEWQFS7P.js → pdf-ocr-U2ZRNQD4.js} +6 -6
  65. package/dist/{pdf-ocr-BO6ACAGL.js → pdf-ocr-VNK6UIS2.js} +9 -9
  66. package/dist/{profile-io-PQXYWUBL.js → profile-io-J3IWFBCM.js} +3 -3
  67. package/dist/{rasterize-CS7WZUGF.js → rasterize-23MJWOUQ.js} +3 -3
  68. package/dist/{redact-doc-PI4JNHV6.js → redact-doc-GGCEGT74.js} +11 -11
  69. package/dist/{render-M2CIEOF4.js → render-M2UAFQSI.js} +7 -7
  70. package/dist/seal-EHZTTMNE.js +10 -0
  71. package/dist/{setup-HGPUR27M.js → setup-D7JWID23.js} +3 -3
  72. package/dist/visual-2ZLXZ5NN.js +21 -0
  73. package/dist/{watch-B5ISBY3N.js → watch-E2XCUIA5.js} +12 -12
  74. package/package.json +1 -1
  75. package/dist/chunk-GV7ECKOK.cjs.map +0 -1
  76. package/dist/chunk-LMQT2JFS.js.map +0 -1
  77. package/dist/chunk-MACV3MZF.js.map +0 -1
  78. package/dist/chunk-MIJYCKJA.js.map +0 -1
  79. package/dist/chunk-WFOPOJHN.js.map +0 -1
  80. package/dist/chunk-ZX66BXB5.cjs.map +0 -1
  81. package/dist/chunks-JGSWNVLF.js +0 -10
  82. package/dist/pdf-ocr-Z4PBLB75.cjs +0 -35
  83. package/dist/seal-4755WLFB.js +0 -10
  84. package/dist/visual-JWWGGTKQ.js +0 -21
  85. /package/dist/{-MG25ST6Y.js.map → -ZKOPHIGY.js.map} +0 -0
  86. /package/dist/{chunk-NVD24SLL.js.map → chunk-2UXGDAWQ.js.map} +0 -0
  87. /package/dist/{chunk-FUC3V2FR.js.map → chunk-5Y7E5VTW.js.map} +0 -0
  88. /package/dist/{chunk-ZIGWQ3IS.js.map → chunk-6PPVAQ2N.js.map} +0 -0
  89. /package/dist/{chunk-UUNCLJB7.js.map → chunk-BHY2QAPM.js.map} +0 -0
  90. /package/dist/{chunk-AM6TECXD.js.map → chunk-DH564VPR.js.map} +0 -0
  91. /package/dist/{chunk-UTLHDVC5.js.map → chunk-F3VIVCIF.js.map} +0 -0
  92. /package/dist/{chunk-LSFVBS2I.js.map → chunk-FK7KGS5D.js.map} +0 -0
  93. /package/dist/{chunk-J3OJWUON.js.map → chunk-GYVSGOQW.js.map} +0 -0
  94. /package/dist/{chunk-VBOFP27O.js.map → chunk-J5M5OW4S.js.map} +0 -0
  95. /package/dist/{chunk-SSDHVKLM.js.map → chunk-K3OLWLXH.js.map} +0 -0
  96. /package/dist/{chunk-FGERVDQZ.js.map → chunk-LPNWTWOQ.js.map} +0 -0
  97. /package/dist/{chunk-TPZPICDM.js.map → chunk-NUQ5B2ZM.js.map} +0 -0
  98. /package/dist/{chunk-PVAJ4DBW.js.map → chunk-QJA3KGRP.js.map} +0 -0
  99. /package/dist/{chunk-NIPFEMJJ.js.map → chunk-QOMLSZLM.js.map} +0 -0
  100. /package/dist/{chunk-DIFJPJGN.js.map → chunk-S2VWNC2W.js.map} +0 -0
  101. /package/dist/{chunk-ERYRF3EA.js.map → chunk-TSCQDCAR.js.map} +0 -0
  102. /package/dist/{chunk-3LFNTOL7.js.map → chunk-UCEGWDWX.js.map} +0 -0
  103. /package/dist/{chunk-AULD3QAB.js.map → chunk-V5R3PJQH.js.map} +0 -0
  104. /package/dist/{chunk-7VNYQHJV.js.map → chunk-VAMCM435.js.map} +0 -0
  105. /package/dist/{chunks-JGSWNVLF.js.map → chunks-BHTYFFI2.js.map} +0 -0
  106. /package/dist/{formula-4KFXQSEG.js.map → formula-4C7ADOLE.js.map} +0 -0
  107. /package/dist/{formula-WAYIKS54.js.map → formula-4Y7UWTEG.js.map} +0 -0
  108. /package/dist/{image-ocr-QBCR7ZD6.js.map → image-ocr-AEX2L326.js.map} +0 -0
  109. /package/dist/{image-ocr-P5S2QDKW.js.map → image-ocr-ASLUPAQG.js.map} +0 -0
  110. /package/dist/{model-bundle-QAM24SYG.js.map → model-bundle-2TXD77HN.js.map} +0 -0
  111. /package/dist/{models-YHC236MP.js.map → models-WRJKLZA6.js.map} +0 -0
  112. /package/dist/{parse-worker-v2-LP7NKQBP.js.map → parse-worker-v2-C2GR62SO.js.map} +0 -0
  113. /package/dist/{parser-26YUZU55.js.map → parser-GFZILOKI.js.map} +0 -0
  114. /package/dist/{parser-DFJZ3CCE.js.map → parser-LU7BIHXD.js.map} +0 -0
  115. /package/dist/{pdf-ocr-BO6ACAGL.js.map → pdf-ocr-U2ZRNQD4.js.map} +0 -0
  116. /package/dist/{pdf-ocr-EEWQFS7P.js.map → pdf-ocr-VNK6UIS2.js.map} +0 -0
  117. /package/dist/{profile-io-PQXYWUBL.js.map → profile-io-J3IWFBCM.js.map} +0 -0
  118. /package/dist/{rasterize-CS7WZUGF.js.map → rasterize-23MJWOUQ.js.map} +0 -0
  119. /package/dist/{redact-doc-PI4JNHV6.js.map → redact-doc-GGCEGT74.js.map} +0 -0
  120. /package/dist/{render-M2CIEOF4.js.map → render-M2UAFQSI.js.map} +0 -0
  121. /package/dist/{seal-4755WLFB.js.map → seal-EHZTTMNE.js.map} +0 -0
  122. /package/dist/{setup-HGPUR27M.js.map → setup-D7JWID23.js.map} +0 -0
  123. /package/dist/{visual-JWWGGTKQ.js.map → visual-2ZLXZ5NN.js.map} +0 -0
  124. /package/dist/{watch-B5ISBY3N.js.map → watch-E2XCUIA5.js.map} +0 -0
package/README.md CHANGED
@@ -108,19 +108,19 @@ await writeFile("보고서.hwpx", Buffer.from(hwpx))
108
108
 
109
109
  ## 검증 결과
110
110
 
111
- **4.22.14 · 2026-10-19 검증**(렌더 배치·PDF 글·표·OCR 은 4.24.14 · 2026-10-28). 고정 코퍼스와 참조 기준으로 측정한 결과입니다.
111
+ **4.22.14 · 2026-10-19 검증**(렌더 배치는 4.24.14, PDF 글·표·OCR 은 4.24.16 · 2026-10-28). 고정 코퍼스와 참조 기준으로 측정한 결과입니다.
112
112
 
113
113
  | 대상 | 규모 | 결과 |
114
114
  | --- | --- | --- |
115
115
  | HWPX | 2,424문서 · 10,342표 | 표 구조 일치 **10,342/10,342 · 100%** |
116
116
  | HWP 5.x ↔ HWPX | 1,130쌍 · 4,315표 | 짝 문서의 표 구조 일치 **100%** |
117
- | PDF 글 | 744쌍 | 글자 재현율 **99.883%** · 정확도 **99.678%** · 읽기 순서 **99.312%** · 어절 F1 **99.066%** |
117
+ | PDF 글 | 744쌍 | 글자 재현율 **99.883%** · 정확도 **99.678%** · 읽기 순서 **99.312%** · 어절 F1 **99.067%** |
118
118
  | PDF 표 | 708쌍 · 2,331표 | 표 탐지 **99.83%** · 구조 일치 **98.24%** · 칸 F1 **0.9919** |
119
119
  | HWPX 재조판 | 다중줄 25,187문단 (12개 폴더) | 모든 줄 시작 일치 **24,941/25,187 · 99.02%** |
120
120
  | PDF 중첩표 (텍스트층) | 29문서 · 101표 | 구조·칸 내용 **101/101 · 100%** |
121
121
  | 법령 별표 | 272건 · 346표 | HWP **346/346**, PDF **342/346 · 98.84%** |
122
122
  | PDF 전체 | 1,911문서 중 1,729건 채점 | 글 커버리지 **99.80%** |
123
- | OCR | 53문서 · 102쪽 · 67표 | CER **0.0260** · 글자 재현율 **99.19%** · 표 구조 일치 **26/67** |
123
+ | OCR | 53문서 · 102쪽 · 67표 | CER **0.0250** · 글자 재현율 **99.27%** · 표 구조 일치 **26/67** |
124
124
  | opendataloader-bench | PDF 200문서 | 종합 기본값 **0.964** · OCR 끔 **0.939** |
125
125
  | HWPX 렌더 배치 (4.24.14) | 한컴 PDF 짝 · 글 조각 90,840 · 그림 4,107 | 글 위치(±1.5pt) **87,643 · 96.5%** · 그림 위치(±2pt) **4,019 · 97.9%** |
126
126
 
@@ -128,15 +128,15 @@ await writeFile("보고서.hwpx", Buffer.from(hwpx))
128
128
 
129
129
  ## 최근 업데이트
130
130
 
131
- 최신 배포: **[4.24.14](https://github.com/chrisryugj/kordoc/releases/tag/v4.24.14)** · 2026-10-28
131
+ 최신 배포: **[4.24.16](https://github.com/chrisryugj/kordoc/releases/tag/v4.24.16)** · 2026-10-28
132
132
 
133
133
  | 버전 | 주요 변경 | 수치 변화 |
134
134
  | --- | --- | --- |
135
+ | 4.24.16 | OCR: 짧은 목차 리더 ··, 과제 번호의 Ⅲ(파·피로 읽음)·네모 번호 앞 [ ·붙임표가 붙은 원문자 ①, 한글 뒤 로마 숫자 획 수(Ⅰ↔Ⅱ), 칸 테두리 앞·빈칸 긴 서식 칸 끝 닫는 괄호 | OCR CER 0.0255 → **0.0250** · 글자 재현율 99.23% → **99.27%** · 정밀도 99.53% → **99.54%** · 열화 입력 8종 개선(원본 0.0397 → **0.0394**, 스캔만 0.0692 → 0.0693) |
136
+ | 4.24.15 | OCR: 검출기가 놓친 서식 빈칸 네모 □□-□□□·목차 줄 머리 대시·줄 끝 홑 쪽 번호, 큰 글머리 ■, 리터 ℓ, 작은 고리 。, 윗줄에 묻힌 줄 머리 가운뎃점, 빗금 칸 환각 · PDF: 줄 간격 넓은 중·일문 두 줄 묶음 문단 잇기 | OCR CER 0.0260 → **0.0255** · 글자 재현율 99.19% → **99.23%** · 열화 입력 8종 개선(원본 0.0405 → **0.0397**, 잡음만 0.0485 → 0.0486) · PDF 어절 F1 99.066% → **99.067%** |
135
137
  | 4.24.14 | 렌더: 칸 간격(`cellSpacing`) 표의 칸 자리 · OCR: 공백·따옴표로 읽은 홑낫표 「, 검출기가 놓친 쪽 번호, 라틴 약어 앞·C 로 읽은 줄 머리 ○, 고리만 든 칸 ○, 마름모 글머리 ◆·◇·◈, 서식 칸 끝 닫는 괄호 | 렌더 글 위치 87,577 → **87,643** · OCR CER 0.0263 → **0.0260** · 열화 입력 9종 모두 개선(원본 0.0409 → **0.0405**) |
136
138
  | 4.24.13 | 기관팩(`.kpack`): 기관 문서 몇 건으로 서식 묶음을 만들어 그 서식대로 새 문서 · OCR: 선 없는 표 칸 순서·넓게 벌린 균등배분, 글자 상자 안 획 조각, 줄 머리 □, 기간 날짜 끝점, 전화 기호 ☎ · PDF·HWP: 한컴 소프트 하이픈 붙임표(증감 칸 음수 부호) · patch 추가 문단 강조 서식·렌더 자란 표 겹침(#175) | 기관팩 처음 보는 문서 재현 서울 68.3% → **74.3%** · 광양 77.1% → **94.5%** · OCR CER 0.0281 → **0.0263** · 열화 입력 9종 모두 개선(원본 0.0429 → **0.0409**) |
137
139
  | 4.24.12 | OCR: 조직도 상자 연결선을 괘선으로(과·팀 상자 열 단위 읽기), 요일 앞 날짜 마침표, 목차 리더 잡티, 화살표 모양 ⇨·➔·▶, 쉼표로 읽은 」 | OCR CER 0.0306 → **0.0281** · 글자 재현율 99.12% → **99.14%** · 열화 입력 9종 모두 개선(원본 0.0475 → **0.0429**) |
138
- | 4.24.11 | OCR: 표 칸·행 경계를 가로지른 검출 박스 가르기, 날짜 쉼표, 직함 앞 전(前)·현(現) · 렌더: 쪽 번호 글자 크기 · 생성: 서울 방침서 제목 줄간격·요약 정렬 | OCR CER 0.0310 → **0.0306** · 글자 재현율 99.10% → **99.12%** · 렌더 글 위치 87,546 → **87,577** · 생성 재현 0.806 → **0.807** |
139
- | 4.24.10 | 보안: 데모 lockfile 의 zod·fast-uri, 정부 GitLab 미러 복구(Java SDK `kordoc-pom.xml`) · OCR: 줄 머리 ▣·❐·‣, `\|` 로 읽은 로마 숫자, 날짜 끝 마침표 · PDF: 세로쓰기 、。, 그러데이션 배경 괘선, 중·일문 자동 간격 | OCR CER 0.0313 → **0.0310** · 글자 재현율 99.06% → **99.10%** · 열화 입력 9종 모두 개선(스캔 0.0734 → **0.0730**) · PDF 어절 F1 99.064% → **99.066%** |
140
140
 
141
141
  이전 버전과 상세 내용은 [변경 이력](CHANGELOG.md)에 있습니다.
142
142
 
@@ -64,10 +64,10 @@ import {
64
64
  usesGaejosikMunche,
65
65
  usesLogicStructure,
66
66
  validateHwpx
67
- } from "./chunk-UUNCLJB7.js";
67
+ } from "./chunk-BHY2QAPM.js";
68
68
  import {
69
69
  blocksToChunks
70
- } from "./chunk-NVD24SLL.js";
70
+ } from "./chunk-2UXGDAWQ.js";
71
71
  import {
72
72
  SPACE_EM_FIXED,
73
73
  SPACE_EM_FONT,
@@ -92,20 +92,20 @@ import {
92
92
  renderSceneToHtml,
93
93
  simulateWrap,
94
94
  simulateWrapKeepWord
95
- } from "./chunk-3LFNTOL7.js";
96
- import "./chunk-UTLHDVC5.js";
95
+ } from "./chunk-UCEGWDWX.js";
96
+ import "./chunk-F3VIVCIF.js";
97
97
  import {
98
98
  blocksToPages
99
- } from "./chunk-LSFVBS2I.js";
99
+ } from "./chunk-FK7KGS5D.js";
100
100
  import "./chunk-VKVDMH7X.js";
101
101
  import "./chunk-VQFA55IC.js";
102
102
  import "./chunk-BYMPPVT3.js";
103
- import "./chunk-AM6TECXD.js";
103
+ import "./chunk-DH564VPR.js";
104
104
  import {
105
105
  blocksToMarkdown,
106
106
  flattenLayoutTables,
107
107
  hasStructuredCellContent
108
- } from "./chunk-NIPFEMJJ.js";
108
+ } from "./chunk-QOMLSZLM.js";
109
109
  import {
110
110
  ValueCursor,
111
111
  applySplices,
@@ -119,11 +119,11 @@ import {
119
119
  isLabelCell,
120
120
  placeSealHwpx,
121
121
  scanSectionXml
122
- } from "./chunk-DIFJPJGN.js";
123
- import "./chunk-ERYRF3EA.js";
122
+ } from "./chunk-S2VWNC2W.js";
123
+ import "./chunk-TSCQDCAR.js";
124
124
  import {
125
125
  VERSION
126
- } from "./chunk-ZIGWQ3IS.js";
126
+ } from "./chunk-6PPVAQ2N.js";
127
127
  import {
128
128
  DEFAULT_REDACT_RULES,
129
129
  redactMarkdown,
@@ -239,4 +239,4 @@ export {
239
239
  usesLogicStructure,
240
240
  validateHwpx
241
241
  };
242
- //# sourceMappingURL=-MG25ST6Y.js.map
242
+ //# sourceMappingURL=-ZKOPHIGY.js.map
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  assignTableIds,
4
4
  blocksToMarkdown
5
- } from "./chunk-NIPFEMJJ.js";
5
+ } from "./chunk-QOMLSZLM.js";
6
6
 
7
7
  // src/chunks.ts
8
8
  var LIST_MARKER_RE = /^(?:[□■◇◆○◎●◦ㅇ•▪▸▶※-]|\d{1,3}[.)]|[가나다라마바사아자차카타파하][.)]|\([가나다라마바사아자차카타파하0-9]{1,3}\)|[①-⑳㉮-㉻㈎-㈛])\s/;
@@ -82,4 +82,4 @@ function blocksToChunks(blocks, options) {
82
82
  export {
83
83
  blocksToChunks
84
84
  };
85
- //# sourceMappingURL=chunk-NVD24SLL.js.map
85
+ //# sourceMappingURL=chunk-2UXGDAWQ.js.map
@@ -30,7 +30,7 @@ import {
30
30
  splitRowBands,
31
31
  starRun,
32
32
  tallInkCount
33
- } from "./chunk-LMQT2JFS.js";
33
+ } from "./chunk-6K2SYTNJ.js";
34
34
  import {
35
35
  validateOcrOptions
36
36
  } from "./chunk-BYMPPVT3.js";
@@ -41,11 +41,11 @@ import {
41
41
  ocrSourcePath,
42
42
  parseCharacterDict,
43
43
  resolveOcrModels
44
- } from "./chunk-VBOFP27O.js";
44
+ } from "./chunk-J5M5OW4S.js";
45
45
  import {
46
46
  KordocError,
47
47
  OPTIONAL_DEP_INSTALL_HINT
48
- } from "./chunk-ZIGWQ3IS.js";
48
+ } from "./chunk-6PPVAQ2N.js";
49
49
 
50
50
  // src/ocr/ruling-lines.ts
51
51
  var INK_LUMA_MAX = 205;
@@ -535,6 +535,14 @@ function gaps({ chars, cx, gray, w, h, ink }) {
535
535
  const pitch = d.length >= 2 ? d.sort((a, b) => a - b)[d.length >> 1] : bandH;
536
536
  const nearTo = (g) => (i) => Math.abs(cx(i) - (g.x0 + g.x1) / 2) <= Math.max(pitch * 0.45, (g.x1 - g.x0) / 2 + 1);
537
537
  const loose = glyphs.filter((g) => /^[\u00b7.]/.test(g.mark) && !live.some(nearTo(g)));
538
+ const tail = [...live].reverse().findIndex((i) => !/^\d$/.test(chars[i]));
539
+ if (tail >= 1 && tail <= 3 && tail < live.length) {
540
+ const prev = live[live.length - 1 - tail], num = live[live.length - tail];
541
+ const run = loose.filter((g) => (g.x0 + g.x1) / 2 > cx(prev) && (g.x0 + g.x1) / 2 < cx(num));
542
+ const mark = run[0]?.mark[0];
543
+ if (run.length >= 2 && run.length <= 3 && run.every((g) => g.mark[0] === mark) && /^[가-힣?)]$/.test(chars[prev]) && !glyphs.some((g) => !run.includes(g) && (g.x0 + g.x1) / 2 > cx(prev) && (g.x0 + g.x1) / 2 < cx(num)))
544
+ chars[prev] += " " + mark.repeat(run.length) + " ";
545
+ }
538
546
  for (const g of glyphs.sort((a, b) => a.x0 - b.x0)) {
539
547
  const mid = (g.x0 + g.x1) / 2;
540
548
  const near = nearTo(g);
@@ -547,6 +555,7 @@ function gaps({ chars, cx, gray, w, h, ink }) {
547
555
  }
548
556
  if (cover.length) {
549
557
  if (g.mark.startsWith("\u25A1") && cover.length === 1 && /^[0Oo\u25cb]$/.test(chars[cover[0]])) chars[cover[0]] = "\u25A1";
558
+ if (g.mark === "\u3002" && cover.length === 1 && /^[\u00b7\u2022\u318d]$/.test(chars[cover[0]])) chars[cover[0]] = "\u3002";
550
559
  continue;
551
560
  }
552
561
  if (g.mark === "\u25A1\0") continue;
@@ -611,13 +620,14 @@ function roman({ chars, cx, gray, w, h, ink }) {
611
620
  if (serifOne(gray, w, h, ink, cx(i))) chars[i] = "\u2160";
612
621
  }
613
622
  for (let i = 0; i < chars.length; i++) {
614
- if (chars[i] !== "|" || chars[i - 1] === "|" || chars[i + 1] === "|") continue;
623
+ const hangul = /^[파피]$/.test(chars[i]);
624
+ if (chars[i] !== "|" && !hangul || chars[i - 1] === "|" || chars[i + 1] === "|") continue;
615
625
  let p = i - 1, q = i + 1;
616
626
  while (p >= 0 && !chars[p].trim()) p--;
617
627
  while (q < chars.length && !chars[q].trim()) q++;
618
- const glued = /[가-힣]$/.test(chars[i - 1] ?? "") || (chars[i + 1] ?? "") === "." || chars[p] === "-" && chars[q] === "-";
628
+ const glued = hangul ? chars[p] === "(" && chars[q] === "-" : /[가-힣]$/.test(chars[i - 1] ?? "") || (chars[i + 1] ?? "") === "." || chars[p] === "-" && chars[q] === "-";
619
629
  if (!glued) continue;
620
- const n = romanStems(gray, w, h, ink, p >= 0 ? cx(p) : -Infinity, q < chars.length ? cx(q) : Infinity);
630
+ const n = romanStems(gray, w, h, ink, p >= 0 ? cx(p) : -Infinity, q < chars.length ? cx(q) : Infinity, /[가-힣]$/.test(chars[p] ?? ""));
621
631
  if (n) chars[i] = String.fromCharCode(8543 + n);
622
632
  }
623
633
  const inRoman = (i) => ROMAN.test(chars[i]) || chars[i] === "1" && (ROMAN.test(chars[i - 1] ?? "") || ROMAN.test(chars[i + 1] ?? ""));
@@ -632,7 +642,7 @@ function roman({ chars, cx, gray, w, h, ink }) {
632
642
  continue;
633
643
  }
634
644
  let e = s;
635
- while (e < chars.length && inRoman(e)) e++;
645
+ while (e < chars.length && (inRoman(e) || e > s && chars[e] === "|" && chars[e + 1] !== "|")) e++;
636
646
  const tok = chars.slice(s, e).join("");
637
647
  if (e < chars.length && /^[A-Za-z]/.test(chars[e])) {
638
648
  s = e;
@@ -641,7 +651,7 @@ function roman({ chars, cx, gray, w, h, ink }) {
641
651
  let p = s - 1, q = e;
642
652
  while (p >= 0 && !chars[p].trim()) p--;
643
653
  while (q < chars.length && !chars[q].trim()) q++;
644
- const n = romanStems(gray, w, h, ink, p >= 0 ? cx(p) : -Infinity, q < chars.length ? cx(q) : Infinity);
654
+ const n = romanStems(gray, w, h, ink, p >= 0 ? cx(p) : -Infinity, q < chars.length ? cx(q) : Infinity, /[가-힣]$/.test(chars[p] ?? ""));
645
655
  const canon = n ? [["\u2160", "I"], ["\u2161", "II"], ["\u2162", "III"]][n - 1] : null;
646
656
  if (canon && !canon.includes(tok)) {
647
657
  chars[s] = canon[0];
@@ -660,7 +670,9 @@ function restoreSymbols(text, ringDecided = false) {
660
670
  s = s.replace(/[\u2206\u0394]/g, "\u25B3");
661
671
  s = s.replace(/\(cid:\d*\)/g, "");
662
672
  s = s.replace(/(^|[^\uac00-\ud7a3])(전|현)\(\s?\)(?=\S)/g, (_, a, b) => `${a}${b}(${b === "\uC804" ? "\u524D" : "\u73FE"})`);
673
+ if (/[가-힣]/.test(s)) s = s.replace(/(\d)l(?![A-Za-z])/g, "$1\u2113");
663
674
  s = s.replace(/([((])\s?[A-Za-z@&]\s?(?=(?:0\d{1,2}-)?\d{3,4}-\d{4}\b)/g, "$1\u260E");
675
+ if (!s.includes("]")) s = s.replace(/^\[(?=[\u2460-\u2473])/, "");
664
676
  return smartQuotes(dateDots(dateCommas(joinDigitGroups(s))));
665
677
  }
666
678
  function dateCommas(s) {
@@ -907,6 +919,182 @@ var MIN_INK_RATIO = 0.01;
907
919
  var MAX_LOCAL_MODEL_BYTES = 1024 * 1024 * 1024;
908
920
  var DET_MEAN = [0.485, 0.456, 0.406];
909
921
  var DET_STD = [0.229, 0.224, 0.225];
922
+ function rowHeadDashes(rgba, width, items) {
923
+ const out = [];
924
+ const dark = (x, y) => {
925
+ const p = (y * width + x) * 4;
926
+ return rgba[p] * 77 + rgba[p + 1] * 150 + rgba[p + 2] * 29 >> 8 < 160;
927
+ };
928
+ const inBox = (x, y) => items.some((b) => x >= b.x && x < b.x + b.w && y >= b.y && y < b.y + b.h);
929
+ const done = /* @__PURE__ */ new Set();
930
+ for (const it of [...items]) {
931
+ if (done.has(it) || it.h < 12) continue;
932
+ const row = items.filter((o) => Math.min(o.y + o.h, it.y + it.h) - Math.max(o.y, it.y) >= Math.min(o.h, it.h) * 0.6);
933
+ for (const o of row) done.add(o);
934
+ if (row.reduce((k, o) => k + (o.text.match(/[가-힣]/g)?.length ?? 0), 0) < 2) continue;
935
+ const first = row.reduce((m, o) => o.x < m.x ? o : m);
936
+ if (!/^[가-힣]/.test(first.text)) continue;
937
+ if (row.filter((o) => /\d/.test(o.text)).length >= 2) continue;
938
+ const y0 = Math.min(...row.map((o) => o.y)), y1 = Math.max(...row.map((o) => o.y + o.h)), h = y1 - y0;
939
+ const x0 = Math.max(0, Math.round(first.x - 2.5 * h)), x1 = first.x - 2;
940
+ let minX = -1, maxX = -1, minY = Infinity, maxY = -1, groups = 0;
941
+ for (let x = x0; x < x1; x++) {
942
+ let on = false;
943
+ for (let y = y0; y < y1; y++) if (dark(x, y) && !inBox(x, y)) {
944
+ on = true;
945
+ if (y < minY) minY = y;
946
+ if (y > maxY) maxY = y;
947
+ }
948
+ if (!on) continue;
949
+ if (maxX < 0 || x - maxX > 2) {
950
+ groups++;
951
+ if (groups === 1) minX = x;
952
+ }
953
+ maxX = x;
954
+ }
955
+ const bw = maxX - minX + 1, bh = maxY - minY + 1, cy = ((minY + maxY) / 2 - y0) / h;
956
+ if (groups !== 1 || bh > h * 0.2 || bw < h * 0.3 || bw > h * 1.2 || cy < 0.3 || cy > 0.7) continue;
957
+ const faint = (x, y) => {
958
+ const p = (y * width + x) * 4;
959
+ return rgba[p] * 77 + rgba[p + 1] * 150 + rgba[p + 2] * 29 >> 8 < 230;
960
+ };
961
+ let touch = minX <= x0;
962
+ for (let y = minY; y <= maxY && !touch; y++) for (let d = 2; d <= 4 && !touch; d++) touch = minX - d >= 0 && faint(minX - d, y) || faint(maxX + d, y);
963
+ if (touch) continue;
964
+ out.push({ text: "-", x: minX, y: y0, w: bw, h, confidence: first.confidence });
965
+ }
966
+ return out;
967
+ }
968
+ function rowTailBoxes(rgba, width, items) {
969
+ const out = [];
970
+ const dark = (x, y) => {
971
+ const p = (y * width + x) * 4;
972
+ return rgba[p] * 77 + rgba[p + 1] * 150 + rgba[p + 2] * 29 >> 8 < 160;
973
+ };
974
+ const done = /* @__PURE__ */ new Set();
975
+ for (const it of items) {
976
+ if (done.has(it) || it.h < 12) continue;
977
+ const row = items.filter((o) => Math.min(o.y + o.h, it.y + it.h) - Math.max(o.y, it.y) >= Math.min(o.h, it.h) * 0.6);
978
+ for (const o of row) done.add(o);
979
+ if (row.reduce((k, o) => k + (o.text.match(/[가-힣]/g)?.length ?? 0), 0) < 2) continue;
980
+ const last = row.reduce((m, o) => o.x + o.w > m.x + m.w ? o : m);
981
+ if (/\d$/.test(last.text.trim())) continue;
982
+ const y0 = Math.min(...row.map((o) => o.y)), y1 = Math.max(...row.map((o) => o.y + o.h)), h = y1 - y0;
983
+ const x0 = last.x + last.w + 4, x1 = Math.floor(width * 0.95);
984
+ if (x1 - x0 < h) continue;
985
+ let groups = 0, minX = -1, maxX = -1, minY = Infinity, maxY = -1;
986
+ for (let x = x0; x < x1; x++) {
987
+ let on = false;
988
+ for (let y = y0; y < y1; y++) if (dark(x, y)) {
989
+ on = true;
990
+ if (y < minY) minY = y;
991
+ if (y > maxY) maxY = y;
992
+ }
993
+ if (!on) continue;
994
+ if (maxX < 0 || x - maxX > h) {
995
+ groups++;
996
+ if (groups === 1) minX = x;
997
+ }
998
+ maxX = x;
999
+ }
1000
+ if (groups !== 1 || maxY - minY + 1 < h * 0.5 || maxX - minX + 1 > h * 4) continue;
1001
+ const height = Math.floor(rgba.length / 4 / width);
1002
+ let rule = false;
1003
+ for (let x = minX; x <= maxX && !rule; x++) rule = minY > 1 && dark(x, minY - 2) || maxY + 2 < height && dark(x, maxY + 2);
1004
+ if (rule) continue;
1005
+ const pad = 4;
1006
+ out.push({ x: Math.max(0, minX - pad), y: Math.max(0, minY - pad), w: maxX - minX + 1 + 2 * pad, h: maxY - minY + 1 + 2 * pad });
1007
+ }
1008
+ return out;
1009
+ }
1010
+ function placeholderSquares(rgba, width, height, items) {
1011
+ const dark = new Uint8Array(width * height);
1012
+ for (let p = 0, i = 0; p < dark.length; p++, i += 4) dark[p] = rgba[i] * 77 + rgba[i + 1] * 150 + rgba[i + 2] * 29 >> 8 < 128 ? 1 : 0;
1013
+ const covered = (x, y) => items.some((b) => x >= b.x - 2 && x < b.x + b.w + 2 && y >= b.y - 2 && y < b.y + b.h + 2);
1014
+ const seen = new Uint8Array(width * height);
1015
+ const squares = [], bars = [];
1016
+ const stack = [];
1017
+ for (let start = 0; start < dark.length; start++) {
1018
+ if (!dark[start] || seen[start]) continue;
1019
+ const c = { x0: width, y0: height, x1: -1, y1: -1, n: 0, px: [] };
1020
+ stack.push(start);
1021
+ seen[start] = 1;
1022
+ while (stack.length) {
1023
+ const p = stack.pop(), x = p % width, y = (p - x) / width;
1024
+ c.n++;
1025
+ if (c.px.length < 4e3) c.px.push(p);
1026
+ if (x < c.x0) c.x0 = x;
1027
+ if (x > c.x1) c.x1 = x;
1028
+ if (y < c.y0) c.y0 = y;
1029
+ if (y > c.y1) c.y1 = y;
1030
+ for (const q of [p - 1, p + 1, p - width, p + width]) if (q >= 0 && q < dark.length && !seen[q] && dark[q] && Math.abs(q % width - x) <= 1) {
1031
+ seen[q] = 1;
1032
+ stack.push(q);
1033
+ }
1034
+ if (c.n > 4e3) break;
1035
+ }
1036
+ while (stack.length) {
1037
+ const p = stack.pop();
1038
+ seen[p] = 1;
1039
+ }
1040
+ const w = c.x1 - c.x0 + 1, h = c.y1 - c.y0 + 1;
1041
+ if (c.n > 4e3 || covered(c.x0 + c.x1 >> 1, c.y0 + c.y1 >> 1)) continue;
1042
+ if (h >= 2 && h <= 6 && w >= 4 && w <= 30 && c.n >= w * h * 0.7) {
1043
+ bars.push(c);
1044
+ continue;
1045
+ }
1046
+ if (w < 12 || h < 12 || w > 60 || h > 60 || w > h * 1.33 || h > w * 1.33) continue;
1047
+ const at = (x, y) => dark[y * width + x] === 1;
1048
+ const row = (y) => {
1049
+ let k = 0;
1050
+ for (let x = c.x0; x <= c.x1; x++) if (at(x, y)) k++;
1051
+ return k / w;
1052
+ };
1053
+ const col = (x) => {
1054
+ let k = 0;
1055
+ for (let y = c.y0; y <= c.y1; y++) if (at(x, y)) k++;
1056
+ return k / h;
1057
+ };
1058
+ const edge = (vals) => Math.max(...vals) >= 0.8;
1059
+ const top = edge([row(c.y0), row(c.y0 + 1)]), bottom = edge([row(c.y1), row(c.y1 - 1)]), left = edge([col(c.x0), col(c.x0 + 1)]), right = edge([col(c.x1), col(c.x1 - 1)]);
1060
+ let inner = 0;
1061
+ for (let y = c.y0 + Math.ceil(h * 0.25); y <= c.y1 - Math.ceil(h * 0.25); y++) for (let x = c.x0 + Math.ceil(w * 0.25); x <= c.x1 - Math.ceil(w * 0.25); x++) if (at(x, y)) inner++;
1062
+ if (top && bottom && left && right && inner === 0) squares.push(c);
1063
+ }
1064
+ squares.sort((a, b) => a.y0 - b.y0 || a.x0 - b.x0);
1065
+ const out = [];
1066
+ const used = /* @__PURE__ */ new Set();
1067
+ for (const s0 of squares) {
1068
+ if (used.has(s0)) continue;
1069
+ const run = [s0];
1070
+ used.add(s0);
1071
+ const side = s0.y1 - s0.y0 + 1, cy = (s0.y0 + s0.y1) / 2;
1072
+ let text = "\u25A1";
1073
+ for (; ; ) {
1074
+ const last = run[run.length - 1];
1075
+ const next = squares.find((q) => !used.has(q) && Math.abs((q.y0 + q.y1) / 2 - cy) <= side * 0.3 && q.x0 > last.x1 && q.x0 - last.x1 <= side * 2);
1076
+ if (!next) break;
1077
+ const gap = next.x0 - last.x1;
1078
+ const bar = bars.find((b) => b.x0 > last.x1 && b.x1 < next.x0 && Math.abs((b.y0 + b.y1) / 2 - cy) <= side * 0.3);
1079
+ let stroke = 0;
1080
+ if (!bar) for (let y = Math.round(cy - side * 0.15); y <= Math.round(cy + side * 0.15); y++) {
1081
+ let k = 0;
1082
+ for (let x = last.x1 + 1; x < next.x0; x++) {
1083
+ k = dark[y * width + x] ? k + 1 : 0;
1084
+ if (k > stroke) stroke = k;
1085
+ }
1086
+ }
1087
+ if (bar || stroke >= Math.max(4, side * 0.25)) text += "-";
1088
+ else if (gap > side) break;
1089
+ text += "\u25A1";
1090
+ used.add(next);
1091
+ run.push(next);
1092
+ }
1093
+ const x0 = run[0].x0, x1 = run[run.length - 1].x1;
1094
+ out.push({ text, x: x0, y: s0.y0, w: x1 - x0 + 1, h: side, confidence: 0.9 });
1095
+ }
1096
+ return out;
1097
+ }
910
1098
  function footerInkBox(rgba, width, height, boxes) {
911
1099
  const y0 = Math.floor(height * 0.88), x0 = Math.floor(width * 0.3), x1 = Math.ceil(width * 0.7);
912
1100
  let minX = Infinity, maxX = -1, minY = Infinity, maxY = -1;
@@ -936,11 +1124,28 @@ function closingParens(rgba, width, items) {
936
1124
  if ((t.match(/\(/g)?.length ?? 0) <= (t.match(/\)/g)?.length ?? 0) || !/\([^()]*$/.test(t)) continue;
937
1125
  const x0 = it.x + it.w + 2;
938
1126
  const next = items.filter((o) => o !== it && o.x >= x0 && o.y < it.y + it.h && o.y + o.h > it.y).reduce((m, o) => Math.min(m, o.x), Infinity);
939
- const x1 = Math.min(width, next, x0 + it.h * 8);
1127
+ let x1 = Math.min(width, next, x0 + it.h * 16);
1128
+ const pageH = Math.floor(rgba.length / 4 / width);
1129
+ const colInk = (x) => {
1130
+ let k = 0;
1131
+ for (let y = it.y; y < it.y + it.h; y++) if (dark(x, y)) k++;
1132
+ return k;
1133
+ };
1134
+ for (let x = x0; x < x1; x++) if (colInk(x) >= it.h * 0.9) {
1135
+ x1 = x;
1136
+ while (x1 > x0 && colInk(x1 - 1) >= it.h * 0.5) x1--;
1137
+ break;
1138
+ }
1139
+ const ruleRow = /* @__PURE__ */ new Set();
1140
+ for (let y = it.y; y < it.y + it.h; y++) {
1141
+ let k = 0;
1142
+ for (let x = x0; x < x1; x++) if (dark(x, y)) k++;
1143
+ if (k > (x1 - x0) * 0.8) for (const d of [-1, 0, 1]) ruleRow.add(y + d);
1144
+ }
940
1145
  let minX = Infinity, maxX = -1, minY = Infinity, maxY = -1, cols = 0, prevOn = false, runs = 0;
941
1146
  for (let x = x0; x < x1; x++) {
942
1147
  let on = false;
943
- for (let y = it.y; y < it.y + it.h; y++) if (dark(x, y)) {
1148
+ for (let y = it.y; y < it.y + it.h; y++) if (!ruleRow.has(y) && dark(x, y)) {
944
1149
  on = true;
945
1150
  if (y < minY) minY = y;
946
1151
  if (y > maxY) maxY = y;
@@ -954,9 +1159,14 @@ function closingParens(rgba, width, items) {
954
1159
  prevOn = on;
955
1160
  }
956
1161
  if (runs !== 1 || maxX - minX + 1 > it.h * 0.5 || maxY - minY + 1 < it.h * 0.6) continue;
957
- let rule = false;
958
- for (let x = minX; x <= maxX && !rule; x++) rule = minY > 1 && dark(x, minY - 2) || dark(x, Math.min(maxY + 2, Math.floor(rgba.length / 4 / width) - 1));
959
- if (rule) continue;
1162
+ const inkRow = (y) => {
1163
+ for (let x = minX; x <= maxX; x++) if (dark(x, y)) return true;
1164
+ return false;
1165
+ };
1166
+ let over = 0;
1167
+ for (let y = minY - 1; y >= 0 && !ruleRow.has(y) && inkRow(y) && over <= it.h; y--) over++;
1168
+ for (let y = maxY + 1; y < pageH && !ruleRow.has(y) && inkRow(y) && over <= it.h; y++) over++;
1169
+ if (over > it.h * 0.6) continue;
960
1170
  const meanX = (ya, yb) => {
961
1171
  let sx = 0, k = 0;
962
1172
  for (let y = ya; y < yb; y++) for (let x = minX; x <= maxX; x++) if (dark(x, y)) {
@@ -1115,6 +1325,12 @@ var OcrEngine = class _OcrEngine {
1115
1325
  const cur = best.get(job.group);
1116
1326
  if (!cur || score(job, r.confidence) > score(cur.job, cur.confidence)) best.set(job.group, { job, ...r });
1117
1327
  });
1328
+ jobs.forEach((job, i) => {
1329
+ const r = results[i], cur = best.get(job.group);
1330
+ if (!job.keep || !r || !cur || cur.job === job) return;
1331
+ const b = cur.text.replace(/\s/g, "");
1332
+ if (r.text.replace(/\s/g, "") === "\xB7" + b && /^[가-힣]/.test(b)) best.set(job.group, { ...cur, text: "\xB7" + cur.text, steps: [r.steps[0], ...cur.steps] });
1333
+ });
1118
1334
  let items = [];
1119
1335
  for (const r of best.values()) {
1120
1336
  const { job, confidence, stepPx } = r;
@@ -1123,6 +1339,10 @@ var OcrEngine = class _OcrEngine {
1123
1339
  const raw = tuning.postprocess && job.rot === 0 ? restoreGlyphs(rgba, width, job.box, read, steps, stepPx, note) : read;
1124
1340
  let text = tuning.postprocess ? restoreSymbols(raw.trim(), note.ringLead) : raw;
1125
1341
  if (!text.trim()) continue;
1342
+ if (tuning.postprocess && job.rot === 0 && text !== "/" && /^[(:;/)]+$/.test(text) && text.includes("/")) {
1343
+ const g = grayCrop(rgba, width, job.box);
1344
+ if (tallInkCount(g, job.box.w, job.box.h, inkStats(g)) === 1) text = "/";
1345
+ }
1126
1346
  if (tuning.postprocess && job.rot === 0 && !/^[◎●▪□■▣❐❏○ㅇ◆◇◈]/.test(text)) {
1127
1347
  const chars = [...read], k = chars.findIndex((c) => c.trim());
1128
1348
  if (k >= 0 && steps.length === chars.length) {
@@ -1195,6 +1415,18 @@ var OcrEngine = class _OcrEngine {
1195
1415
  if (tuning.postprocess) items = dropNestedFragments(items);
1196
1416
  if (tuning.postprocess) for (const it of items) this.outerSquare(rgba, width, it, items);
1197
1417
  if (tuning.postprocess) items.push(...closingParens(rgba, width, items));
1418
+ if (tuning.postprocess) items.push(...placeholderSquares(rgba, width, height, items));
1419
+ if (tuning.postprocess) items.push(...rowHeadDashes(rgba, width, items));
1420
+ if (tuning.postprocess) {
1421
+ const tails = rowTailBoxes(rgba, width, items);
1422
+ if (tails.length) {
1423
+ const res = await this.recognizeJobs(rgba, width, tails.map((box, k) => ({ box, rot: 0, group: k })), tuning.recBatch);
1424
+ tails.forEach((box, k) => {
1425
+ const t = res[k]?.text.trim() ?? "";
1426
+ if (/^\d{1,4}$/.test(t) && (res[k]?.confidence ?? 0) >= tuning.textScore) items.push({ text: t, ...this.itemBox(rgba, width, box, tuning), confidence: res[k].confidence });
1427
+ });
1428
+ }
1429
+ }
1198
1430
  if (tuning.postprocess) restoreBulletItems(items);
1199
1431
  items.sort((a, b) => a.y - b.y || a.x - b.x);
1200
1432
  return items;
@@ -1864,4 +2096,4 @@ export {
1864
2096
  groupOcrLines,
1865
2097
  ocrItemsToBlocks
1866
2098
  };
1867
- //# sourceMappingURL=chunk-MIJYCKJA.js.map
2099
+ //# sourceMappingURL=chunk-3TXAIEXL.js.map