kordoc 4.24.8 → 4.24.10

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 (130) hide show
  1. package/README.md +7 -7
  2. package/dist/{-TLCDUU22.js → -H7GN7FIS.js} +11 -11
  3. package/dist/{chunk-IT5XO3UG.js → chunk-37JRATLZ.js} +129 -13
  4. package/dist/chunk-37JRATLZ.js.map +1 -0
  5. package/dist/{chunk-5MQG3GJI.js → chunk-3BRGHU2F.js} +2 -2
  6. package/dist/{chunk-5MQG3GJI.js.map → chunk-3BRGHU2F.js.map} +1 -1
  7. package/dist/{chunk-RCT5MO53.js → chunk-4AKR7LBP.js} +6 -6
  8. package/dist/{chunk-CB57N2MG.js → chunk-4TAOUMLJ.js} +2 -2
  9. package/dist/{chunk-OUJIV2AM.js → chunk-ALCD3FB7.js} +2 -2
  10. package/dist/{chunk-DPB6ZVQ5.js → chunk-CZGAGILH.js} +130 -14
  11. package/dist/chunk-CZGAGILH.js.map +1 -0
  12. package/dist/{chunk-KA6JZZIW.js → chunk-ENQWDN6M.js} +23 -7
  13. package/dist/chunk-ENQWDN6M.js.map +1 -0
  14. package/dist/{chunk-TTUAJYTC.js → chunk-HFNNRSAJ.js} +23 -7
  15. package/dist/chunk-HFNNRSAJ.js.map +1 -0
  16. package/dist/{chunk-ZJYNTA5R.cjs → chunk-IV4ZRFHF.cjs} +83 -67
  17. package/dist/chunk-IV4ZRFHF.cjs.map +1 -0
  18. package/dist/{chunk-7F35EPMM.cjs → chunk-JH3AA5TY.cjs} +3 -3
  19. package/dist/{chunk-7F35EPMM.cjs.map → chunk-JH3AA5TY.cjs.map} +1 -1
  20. package/dist/{chunk-F7UWIMUA.js → chunk-LLKLJ2JF.js} +2 -2
  21. package/dist/{chunk-F7UWIMUA.js.map → chunk-LLKLJ2JF.js.map} +1 -1
  22. package/dist/{chunk-WTSID6AI.js → chunk-LMG7Y3TN.js} +2 -2
  23. package/dist/{chunk-J7MFKS73.js → chunk-NFJN75Y2.js} +2 -2
  24. package/dist/{chunk-D3EHIHYN.js → chunk-OZTG6NCW.js} +2 -2
  25. package/dist/{chunk-FSWJC5RR.js → chunk-PB6EOWE4.js} +3 -3
  26. package/dist/{chunk-N2XN6GAQ.js → chunk-PDPHUGVP.js} +2 -2
  27. package/dist/{chunk-XOUZC7MX.js → chunk-PSQJYUJC.js} +2 -2
  28. package/dist/{chunk-C3BOYTY5.cjs → chunk-QAHWMIBF.cjs} +2 -2
  29. package/dist/{chunk-C3BOYTY5.cjs.map → chunk-QAHWMIBF.cjs.map} +1 -1
  30. package/dist/{chunk-TDB27K2Z.js → chunk-RNDG6RQU.js} +2 -2
  31. package/dist/{chunk-7XPTKW76.js → chunk-S4HE5OH5.js} +2 -2
  32. package/dist/{chunk-BJABLQMB.js → chunk-TT6UHEDY.js} +2 -2
  33. package/dist/{chunk-K2FGQ5D5.js → chunk-UK3CENY6.js} +151 -20
  34. package/dist/chunk-UK3CENY6.js.map +1 -0
  35. package/dist/{chunk-RCDRTQ37.js → chunk-VAYHMKSC.js} +2 -2
  36. package/dist/{chunk-JM7PZM6E.cjs → chunk-WKN5YUMP.cjs} +3 -3
  37. package/dist/{chunk-JM7PZM6E.cjs.map → chunk-WKN5YUMP.cjs.map} +1 -1
  38. package/dist/{chunk-7JUF6G6Z.js → chunk-WZWJCMM3.js} +2 -2
  39. package/dist/{chunk-73MRAQXA.cjs → chunk-X5BFTWCB.cjs} +152 -36
  40. package/dist/chunk-X5BFTWCB.cjs.map +1 -0
  41. package/dist/{chunk-NW2CKOFL.cjs → chunk-XFM4SX74.cjs} +6 -6
  42. package/dist/{chunk-NW2CKOFL.cjs.map → chunk-XFM4SX74.cjs.map} +1 -1
  43. package/dist/{chunk-Z5DT55TW.js → chunk-XMAOBI35.js} +3 -3
  44. package/dist/{chunk-NE6G6LCP.js → chunk-YAKLWOT2.js} +2 -2
  45. package/dist/chunks-CMSL2XXG.js +10 -0
  46. package/dist/cli.js +30 -30
  47. package/dist/{formula-PNMQ5JFJ.js → formula-P5VHY4YS.js} +3 -3
  48. package/dist/{formula-ZLNSVWS6.cjs → formula-TBG6BYE2.cjs} +9 -9
  49. package/dist/{formula-ZLNSVWS6.cjs.map → formula-TBG6BYE2.cjs.map} +1 -1
  50. package/dist/{formula-DZMBNPWT.js → formula-YCZOVUJU.js} +4 -4
  51. package/dist/{image-ocr-P5Q6YXTO.js → image-ocr-2UD6RWE6.js} +6 -6
  52. package/dist/{image-ocr-BPH25SFW.js → image-ocr-3QCUKBTE.js} +9 -9
  53. package/dist/{image-ocr-YUXCVOOP.cjs → image-ocr-J6EWIQ73.cjs} +17 -17
  54. package/dist/{image-ocr-YUXCVOOP.cjs.map → image-ocr-J6EWIQ73.cjs.map} +1 -1
  55. package/dist/index.cjs +826 -695
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.js +146 -15
  58. package/dist/index.js.map +1 -1
  59. package/dist/mcp.js +23 -23
  60. package/dist/{model-bundle-XAM6JFZV.js → model-bundle-EFAOVVYU.js} +5 -5
  61. package/dist/{models-6Q5ULSTP.js → models-SJXAHLSA.js} +5 -5
  62. package/dist/{parse-worker-v2-ONX5QLJZ.js → parse-worker-v2-ZSSOTZVE.js} +12 -12
  63. package/dist/{parser-54R7ENH6.js → parser-3XHPJKL7.js} +12 -10
  64. package/dist/parser-3XHPJKL7.js.map +1 -0
  65. package/dist/{parser-JHXN4S6L.js → parser-4I4MM5AJ.js} +15 -13
  66. package/dist/parser-4I4MM5AJ.js.map +1 -0
  67. package/dist/{parser-Z352BN56.cjs → parser-4MXHN43N.cjs} +162 -160
  68. package/dist/parser-4MXHN43N.cjs.map +1 -0
  69. package/dist/{pdf-ocr-LFVER7IE.js → pdf-ocr-J6GXPLZJ.js} +6 -6
  70. package/dist/pdf-ocr-JPLZOU23.cjs +31 -0
  71. package/dist/{pdf-ocr-SMFFJYTD.cjs.map → pdf-ocr-JPLZOU23.cjs.map} +1 -1
  72. package/dist/{pdf-ocr-FQ4LAXM6.js → pdf-ocr-QXZIMPQI.js} +9 -9
  73. package/dist/{profile-io-YW5QWKBB.js → profile-io-X7ZMPA7T.js} +3 -3
  74. package/dist/{rasterize-T4S3NMQ7.js → rasterize-42GMMS3L.js} +3 -3
  75. package/dist/{redact-doc-CAVBR5XO.js → redact-doc-WCNALJSX.js} +11 -11
  76. package/dist/{render-ZW52ZX6R.js → render-2SSXV7MR.js} +7 -7
  77. package/dist/seal-ENUBZ7ZX.js +10 -0
  78. package/dist/{setup-7UGSJPJP.js → setup-VY5MAK7O.js} +3 -3
  79. package/dist/visual-ON76FHWH.js +21 -0
  80. package/dist/{watch-M4A7B5WS.js → watch-JLCOQOAL.js} +12 -12
  81. package/package.json +1 -1
  82. package/dist/chunk-73MRAQXA.cjs.map +0 -1
  83. package/dist/chunk-DPB6ZVQ5.js.map +0 -1
  84. package/dist/chunk-IT5XO3UG.js.map +0 -1
  85. package/dist/chunk-K2FGQ5D5.js.map +0 -1
  86. package/dist/chunk-KA6JZZIW.js.map +0 -1
  87. package/dist/chunk-TTUAJYTC.js.map +0 -1
  88. package/dist/chunk-ZJYNTA5R.cjs.map +0 -1
  89. package/dist/chunks-S2GGFN6W.js +0 -10
  90. package/dist/parser-54R7ENH6.js.map +0 -1
  91. package/dist/parser-JHXN4S6L.js.map +0 -1
  92. package/dist/parser-Z352BN56.cjs.map +0 -1
  93. package/dist/pdf-ocr-SMFFJYTD.cjs +0 -31
  94. package/dist/seal-TQ3RLVVT.js +0 -10
  95. package/dist/visual-6GXHKPCJ.js +0 -21
  96. /package/dist/{-TLCDUU22.js.map → -H7GN7FIS.js.map} +0 -0
  97. /package/dist/{chunk-RCT5MO53.js.map → chunk-4AKR7LBP.js.map} +0 -0
  98. /package/dist/{chunk-CB57N2MG.js.map → chunk-4TAOUMLJ.js.map} +0 -0
  99. /package/dist/{chunk-OUJIV2AM.js.map → chunk-ALCD3FB7.js.map} +0 -0
  100. /package/dist/{chunk-WTSID6AI.js.map → chunk-LMG7Y3TN.js.map} +0 -0
  101. /package/dist/{chunk-J7MFKS73.js.map → chunk-NFJN75Y2.js.map} +0 -0
  102. /package/dist/{chunk-D3EHIHYN.js.map → chunk-OZTG6NCW.js.map} +0 -0
  103. /package/dist/{chunk-FSWJC5RR.js.map → chunk-PB6EOWE4.js.map} +0 -0
  104. /package/dist/{chunk-N2XN6GAQ.js.map → chunk-PDPHUGVP.js.map} +0 -0
  105. /package/dist/{chunk-XOUZC7MX.js.map → chunk-PSQJYUJC.js.map} +0 -0
  106. /package/dist/{chunk-TDB27K2Z.js.map → chunk-RNDG6RQU.js.map} +0 -0
  107. /package/dist/{chunk-7XPTKW76.js.map → chunk-S4HE5OH5.js.map} +0 -0
  108. /package/dist/{chunk-BJABLQMB.js.map → chunk-TT6UHEDY.js.map} +0 -0
  109. /package/dist/{chunk-RCDRTQ37.js.map → chunk-VAYHMKSC.js.map} +0 -0
  110. /package/dist/{chunk-7JUF6G6Z.js.map → chunk-WZWJCMM3.js.map} +0 -0
  111. /package/dist/{chunk-Z5DT55TW.js.map → chunk-XMAOBI35.js.map} +0 -0
  112. /package/dist/{chunk-NE6G6LCP.js.map → chunk-YAKLWOT2.js.map} +0 -0
  113. /package/dist/{chunks-S2GGFN6W.js.map → chunks-CMSL2XXG.js.map} +0 -0
  114. /package/dist/{formula-PNMQ5JFJ.js.map → formula-P5VHY4YS.js.map} +0 -0
  115. /package/dist/{formula-DZMBNPWT.js.map → formula-YCZOVUJU.js.map} +0 -0
  116. /package/dist/{image-ocr-P5Q6YXTO.js.map → image-ocr-2UD6RWE6.js.map} +0 -0
  117. /package/dist/{image-ocr-BPH25SFW.js.map → image-ocr-3QCUKBTE.js.map} +0 -0
  118. /package/dist/{model-bundle-XAM6JFZV.js.map → model-bundle-EFAOVVYU.js.map} +0 -0
  119. /package/dist/{models-6Q5ULSTP.js.map → models-SJXAHLSA.js.map} +0 -0
  120. /package/dist/{parse-worker-v2-ONX5QLJZ.js.map → parse-worker-v2-ZSSOTZVE.js.map} +0 -0
  121. /package/dist/{pdf-ocr-FQ4LAXM6.js.map → pdf-ocr-J6GXPLZJ.js.map} +0 -0
  122. /package/dist/{pdf-ocr-LFVER7IE.js.map → pdf-ocr-QXZIMPQI.js.map} +0 -0
  123. /package/dist/{profile-io-YW5QWKBB.js.map → profile-io-X7ZMPA7T.js.map} +0 -0
  124. /package/dist/{rasterize-T4S3NMQ7.js.map → rasterize-42GMMS3L.js.map} +0 -0
  125. /package/dist/{redact-doc-CAVBR5XO.js.map → redact-doc-WCNALJSX.js.map} +0 -0
  126. /package/dist/{render-ZW52ZX6R.js.map → render-2SSXV7MR.js.map} +0 -0
  127. /package/dist/{seal-TQ3RLVVT.js.map → seal-ENUBZ7ZX.js.map} +0 -0
  128. /package/dist/{setup-7UGSJPJP.js.map → setup-VY5MAK7O.js.map} +0 -0
  129. /package/dist/{visual-6GXHKPCJ.js.map → visual-ON76FHWH.js.map} +0 -0
  130. /package/dist/{watch-M4A7B5WS.js.map → watch-JLCOQOAL.js.map} +0 -0
package/README.md CHANGED
@@ -105,19 +105,19 @@ await writeFile("보고서.hwpx", Buffer.from(hwpx))
105
105
 
106
106
  ## 검증 결과
107
107
 
108
- **4.22.14 · 2026-10-19 검증**(렌더 배치·PDF 글·표·OCR 은 4.24.8 · 2026-10-26). 고정 코퍼스와 참조 기준으로 측정한 결과입니다.
108
+ **4.22.14 · 2026-10-19 검증**(렌더 배치·PDF 글·표·OCR 은 4.24.10 · 2026-10-26). 고정 코퍼스와 참조 기준으로 측정한 결과입니다.
109
109
 
110
110
  | 대상 | 규모 | 결과 |
111
111
  | --- | --- | --- |
112
112
  | HWPX | 2,424문서 · 10,342표 | 표 구조 일치 **10,342/10,342 · 100%** |
113
113
  | HWP 5.x ↔ HWPX | 1,130쌍 · 4,315표 | 짝 문서의 표 구조 일치 **100%** |
114
- | PDF 글 | 744쌍 | 글자 재현율 **99.881%** · 정확도 **99.676%** · 읽기 순서 **99.309%** · 어절 F1 **99.064%** |
115
- | PDF 표 | 708쌍 · 2,331표 | 표 탐지 **99.83%** · 구조 일치 **98.20%** · 칸 F1 **0.9919** |
114
+ | PDF 글 | 744쌍 | 글자 재현율 **99.881%** · 정확도 **99.677%** · 읽기 순서 **99.309%** · 어절 F1 **99.066%** |
115
+ | PDF 표 | 708쌍 · 2,331표 | 표 탐지 **99.83%** · 구조 일치 **98.24%** · 칸 F1 **0.9919** |
116
116
  | HWPX 재조판 | 다중줄 25,187문단 (12개 폴더) | 모든 줄 시작 일치 **24,941/25,187 · 99.02%** |
117
117
  | PDF 중첩표 (텍스트층) | 29문서 · 101표 | 구조·칸 내용 **101/101 · 100%** |
118
118
  | 법령 별표 | 272건 · 346표 | HWP **346/346**, PDF **342/346 · 98.84%** |
119
119
  | PDF 전체 | 1,911문서 중 1,729건 채점 | 글 커버리지 **99.80%** |
120
- | OCR | 53문서 · 102쪽 · 67표 | CER **0.0314** · 글자 재현율 **99.05%** · 표 구조 일치 **25/67** |
120
+ | OCR | 53문서 · 102쪽 · 67표 | CER **0.0310** · 글자 재현율 **99.10%** · 표 구조 일치 **25/67** |
121
121
  | opendataloader-bench | PDF 200문서 | 종합 기본값 **0.964** · OCR 끔 **0.939** |
122
122
  | HWPX 렌더 배치 (4.24.8) | 한컴 PDF 짝 · 글 조각 90,906 · 그림 4,107 | 글 위치(±1.5pt) **87,620 · 96.4%** · 그림 위치(±2pt) **4,019 · 97.9%** |
123
123
 
@@ -125,15 +125,15 @@ await writeFile("보고서.hwpx", Buffer.from(hwpx))
125
125
 
126
126
  ## 최근 업데이트
127
127
 
128
- 최신 배포: **[4.24.8](https://github.com/chrisryugj/kordoc/releases/tag/v4.24.8)** · 2026-10-26
128
+ 최신 배포: **[4.24.10](https://github.com/chrisryugj/kordoc/releases/tag/v4.24.10)** · 2026-10-26
129
129
 
130
130
  | 버전 | 주요 변경 | 수치 변화 |
131
131
  | --- | --- | --- |
132
+ | 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%** |
133
+ | 4.24.9 | 견본 서식·patch 행 추가 표의 행마다 이중선 · OCR: 통째로 빠진 줄 머리 ○ · PDF: 쪽을 넘는 표가 다음 쪽 첫 행을 삼키던 것 | OCR CER 0.0314 → **0.0313** · 흐린 입력 OCR CER 0.0638 → **0.0635** · PDF 표 구조 98.20% → **98.24%** |
132
134
  | 4.24.8 | 보안: 알려진 의존성 취약점 6건 → 0건(zod 4, MCP SDK 하위 의존성), 출시·CI 에 `npm audit` 게이트 · OCR: 숫자로 읽은 줄 머리 고리 글머리, 다음 글자와 맞닿은 「 · PDF: 머리 칸에만 클립을 건 통계표 | `npm audit` 6 → **0** · OCR CER 0.0327 → **0.0314** · 흐린 입력 OCR CER 0.0663 → **0.0638** · PDF 글 순서 99.307% → **99.309%** |
133
135
  | 4.24.7 | 견본 생성: 목차 상자를 요약 상자로 쓰지 않음, 부호 문단 아래 들여 쓴 목록 단계 · HWPML: 묶음·고정폭 빈칸과 강제 줄바꿈 · OCR: 줄 끝 배경 무늬, 머리행에 붙은 숫자 열, 붙임표·줄표 길이 | OCR CER 0.0332 → **0.0327** · 글자 재현율 99.02% → **99.04%** |
134
136
  | 4.24.6 | OCR: 예산 사업명세서의 목·내역 하위 행, 칸마다 갈린 색 머리 띠, 칸 안 위아래 두 줄 뒤집힘 · PDF: 쪽 바깥 넉 자 세로 탭 · 렌더: 쪽 번호 새로 시작 · HWPX: 들여 써 저장한 XML 의 공백 | OCR CER 0.0409 → **0.0332** · OCR 표 구조 24 → **25/67** · 렌더 글 위치 87,509 → **87,620** · PDF 글 순서 99.302% → **99.307%** |
135
- | 4.24.5 | PDF: 분수 막대 수식 복원, 고정폭 글줄의 가짜 무괘선 표, 덧찍은 목차 쪽 번호 · OCR: 선 없이 쌓은 하위 행, 색 머리 띠 안 흰 구분선, 글의 밑줄 표지 · HWPX·HWP: 표 아래 캡션 차례 | PDF 글 순서 99.247% → **99.302%** · 별표 수식 7 → **11/11** · OCR 표 구조 21 → **24/67** |
136
- | 4.24.4 | 렌더: 넘친 줄 줄이기(빈칸 20%·나머지 글자마다), 칸 안 개체가 키운 칸 높이, KoPub 대체 글꼴로 작성된 문서, 쪽 바닥 행 판정 · PDF: 옆 칸 선이 걸친 칸 변 | 렌더 글 위치 87,289 → **87,509**(새 측정 기준) · 그림 4,017 → **4,019** · PDF 표 구조 98.16% → **98.20%** |
137
137
 
138
138
  이전 버전과 상세 내용은 [변경 이력](CHANGELOG.md)에 있습니다.
139
139
 
@@ -58,10 +58,10 @@ import {
58
58
  usesGaejosikMunche,
59
59
  usesLogicStructure,
60
60
  validateHwpx
61
- } from "./chunk-K2FGQ5D5.js";
61
+ } from "./chunk-UK3CENY6.js";
62
62
  import {
63
63
  blocksToChunks
64
- } from "./chunk-RCDRTQ37.js";
64
+ } from "./chunk-VAYHMKSC.js";
65
65
  import {
66
66
  SPACE_EM_FIXED,
67
67
  SPACE_EM_FONT,
@@ -86,20 +86,20 @@ import {
86
86
  renderSceneToHtml,
87
87
  simulateWrap,
88
88
  simulateWrapKeepWord
89
- } from "./chunk-RCT5MO53.js";
90
- import "./chunk-J7MFKS73.js";
89
+ } from "./chunk-4AKR7LBP.js";
90
+ import "./chunk-NFJN75Y2.js";
91
91
  import {
92
92
  blocksToPages
93
- } from "./chunk-7JUF6G6Z.js";
93
+ } from "./chunk-WZWJCMM3.js";
94
94
  import "./chunk-VKVDMH7X.js";
95
95
  import "./chunk-VQFA55IC.js";
96
96
  import "./chunk-BYMPPVT3.js";
97
- import "./chunk-TDB27K2Z.js";
97
+ import "./chunk-RNDG6RQU.js";
98
98
  import {
99
99
  blocksToMarkdown,
100
100
  flattenLayoutTables,
101
101
  hasStructuredCellContent
102
- } from "./chunk-BJABLQMB.js";
102
+ } from "./chunk-TT6UHEDY.js";
103
103
  import {
104
104
  ValueCursor,
105
105
  applySplices,
@@ -113,11 +113,11 @@ import {
113
113
  isLabelCell,
114
114
  placeSealHwpx,
115
115
  scanSectionXml
116
- } from "./chunk-FSWJC5RR.js";
117
- import "./chunk-7XPTKW76.js";
116
+ } from "./chunk-PB6EOWE4.js";
117
+ import "./chunk-S4HE5OH5.js";
118
118
  import {
119
119
  VERSION
120
- } from "./chunk-5MQG3GJI.js";
120
+ } from "./chunk-3BRGHU2F.js";
121
121
  import {
122
122
  DEFAULT_REDACT_RULES,
123
123
  redactMarkdown,
@@ -227,4 +227,4 @@ export {
227
227
  usesLogicStructure,
228
228
  validateHwpx
229
229
  };
230
- //# sourceMappingURL=-TLCDUU22.js.map
230
+ //# sourceMappingURL=-H7GN7FIS.js.map
@@ -6,11 +6,11 @@ import {
6
6
  HEADING_RATIO_H3,
7
7
  tidyScriptTags,
8
8
  toTex
9
- } from "./chunk-TDB27K2Z.js";
9
+ } from "./chunk-RNDG6RQU.js";
10
10
  import {
11
11
  safeMax,
12
12
  safeMin
13
- } from "./chunk-5MQG3GJI.js";
13
+ } from "./chunk-3BRGHU2F.js";
14
14
 
15
15
  // src/ocr/line-split.ts
16
16
  function grayCrop(rgba, pageW, box) {
@@ -507,7 +507,8 @@ function gapGlyphs(gray, w, h, ink) {
507
507
  const speck = Math.max(3, bh * 0.08);
508
508
  const dot = ch <= bh * 0.25 && cw <= bh * 0.25 && ch >= speck && cw >= speck && cw >= ch * 0.6 && cw <= ch * 1.6;
509
509
  const small = ch <= bh * 0.25 && cw <= bh * 0.25 && ch >= 2 && cw >= 2 && cw >= ch * 0.6 && cw <= ch * 1.6 && fill >= 0.6 && cy >= 0.3 && cy <= 0.7;
510
- if (!apart && !abut && !(nearlyApart && ch >= bh * 0.6)) {
510
+ const flushDot = !apart && dot && fill >= 0.6 && cy >= 0.75 && cw >= ch * 0.8 && !others.some((o) => o.x0 < c.x1 && o.x1 > c.x0);
511
+ if (!apart && !abut && !flushDot && !(nearlyApart && ch >= bh * 0.6)) {
511
512
  if (small) out.push({ x0: c.x0, x1: c.x1, mark: "\xB7" });
512
513
  continue;
513
514
  }
@@ -524,7 +525,8 @@ function gapGlyphs(gray, w, h, ink) {
524
525
  if (c.y1 >= bandB - bh * 0.15 && bottom >= 0.7 && top <= 0.5 && stem(c.y0, c.y1 - band) >= 0.65) return "\u300D";
525
526
  return null;
526
527
  };
527
- if (!apart && !abut) {
528
+ if (flushDot) mark = ".";
529
+ else if (!apart && !abut) {
528
530
  } else if (abut) {
529
531
  if (fill < 0.5) mark = bracket();
530
532
  } else if (dot && fill >= 0.6 && cy >= 0.3 && cy <= 0.7) mark = "\xB7";
@@ -604,6 +606,34 @@ function dashRatio(gray, w, h, ink, cx) {
604
606
  const dash = comps.filter((c) => c.y1 - c.y0 <= bh * 0.25 && c.x1 - c.x0 >= (c.y1 - c.y0) * 2 && Math.abs((c.x0 + c.x1) / 2 - cx) <= bh * 0.6).sort((a, b) => Math.abs((a.x0 + a.x1) / 2 - cx) - Math.abs((b.x0 + b.x1) / 2 - cx))[0];
605
607
  return dash ? (dash.x1 - dash.x0) / bh : null;
606
608
  }
609
+ function leadingRing(gray, w, h, ink, firstX) {
610
+ if (firstX < h * 0.8) return false;
611
+ const isInk = (v) => ink.darkInk ? v <= ink.threshold : v > ink.threshold;
612
+ let left = -1;
613
+ for (let x = 0; x < w && left < 0; x++) for (let y = 0; y < h; y++) if (isInk(gray[y * w + x])) {
614
+ left = x;
615
+ break;
616
+ }
617
+ if (left < 0 || ringBullet(gray, w, h, ink, left + h * 0.25) !== "\u25CB") return false;
618
+ const colInk = (x) => {
619
+ for (let y = 0; y < h; y++) if (isInk(gray[y * w + x])) return true;
620
+ return false;
621
+ };
622
+ let right = left;
623
+ while (right + 1 < w && colInk(right + 1)) right++;
624
+ let top = h, bottom = -1;
625
+ for (let y = 0; y < h; y++) for (let x = left; x <= right; x++) if (isInk(gray[y * w + x])) {
626
+ top = Math.min(top, y);
627
+ bottom = Math.max(bottom, y);
628
+ break;
629
+ }
630
+ const cw = right - left + 1, ch = bottom - top + 1, kx = Math.max(1, Math.round(cw * 0.1)), ky = Math.max(1, Math.round(ch * 0.1));
631
+ const corner = (x0, y0) => {
632
+ for (let y = y0; y < y0 + ky; y++) for (let x = x0; x < x0 + kx; x++) if (isInk(gray[y * w + x])) return true;
633
+ return false;
634
+ };
635
+ return !corner(left, top) && !corner(right + 1 - kx, top) && !corner(left, bottom + 1 - ky) && !corner(right + 1 - kx, bottom + 1 - ky);
636
+ }
607
637
  function discAt(gray, w, h, ink, cx) {
608
638
  const { comps, label } = components(gray, w, h, ink);
609
639
  let charH = 0;
@@ -688,6 +718,29 @@ function leadingBullet(gray, w, h, ink, firstX) {
688
718
  const cl = cand.filter((c) => c.x0 < first.x1 && c.x1 > first.x0);
689
719
  const x0 = Math.min(...cl.map((c) => c.x0)), x1 = Math.max(...cl.map((c) => c.x1)), y0 = Math.min(...cl.map((c) => c.y0)), y1 = Math.max(...cl.map((c) => c.y1));
690
720
  const cw = x1 - x0, ch = y1 - y0;
721
+ if (cl.length === 1 && cw >= ch * 0.55 && cw <= ch * 1.1 && ch >= charH * 0.3 && ch <= charH * 0.7) {
722
+ const runs = [];
723
+ let aligned = true;
724
+ for (let y = y0; y < y1 && aligned; y++) {
725
+ let first2 = -1, last = -1, segs = 0, prev = false;
726
+ for (let x = x0; x < x1; x++) {
727
+ const on = label[y * w + x] === cl[0].id;
728
+ if (on) {
729
+ if (!prev) segs++;
730
+ if (first2 < 0) first2 = x;
731
+ last = x;
732
+ }
733
+ prev = on;
734
+ }
735
+ aligned = segs === 1 && first2 - x0 <= Math.max(1, cw * 0.15);
736
+ runs.push(last - first2 + 1);
737
+ }
738
+ const peak = runs.indexOf(Math.max(...runs));
739
+ if (aligned && Math.abs(peak - ch / 2) <= ch * 0.2 && runs[0] <= cw * 0.45 && runs[runs.length - 1] <= cw * 0.45 && Math.max(...runs) >= cw * 0.85) {
740
+ const next2 = cand.find((c) => c.x0 >= x1);
741
+ if (!next2 || next2.x0 - x1 >= ch * 0.3) return { mark: "\u2023", covers: firstX <= x1 + ch * 0.3 };
742
+ }
743
+ }
691
744
  if (cw < ch * 0.8 || cw > ch * 1.25) return null;
692
745
  const next = cand.find((c) => c.x0 >= x1);
693
746
  if (next && next.x0 - x1 < ch * 0.3) return null;
@@ -722,8 +775,32 @@ function leadingBullet(gray, w, h, ink, firstX) {
722
775
  }
723
776
  }
724
777
  const hollowSquare = cl.length === 1 && fill < 0.5 && midRuns === 2 && edgeFull(y0, 1) && edgeFull(y1 - 1, -1);
778
+ const core = cl.length === 2 ? cl.find((b) => cl.some((a) => a !== b && b.x0 > a.x0 && b.x1 < a.x1 && b.y0 > a.y0 && b.y1 < a.y1)) : void 0;
779
+ const coreFill = (() => {
780
+ if (!core) return 0;
781
+ let k = 0;
782
+ for (let y = core.y0; y < core.y1; y++) for (let x = core.x0; x < core.x1; x++) if (label[y * w + x] === core.id) k++;
783
+ return k / ((core.x1 - core.x0) * (core.y1 - core.y0));
784
+ })();
785
+ const filledSquare = !!core && coreFill >= 0.85 && ring(core) && Math.abs(core.x0 + core.x1 - (x0 + x1)) <= cw * 0.2 && Math.abs(core.y0 + core.y1 - (y0 + y1)) <= ch * 0.2 && edgeFull(y0, 1) && edgeFull(y1 - 1, -1);
786
+ const run = (x, y, dx, dy) => {
787
+ let n = 0;
788
+ while (x >= x0 && x < x1 && y >= y0 && y < y1 && cl.some((c) => label[y * w + x] === c.id)) {
789
+ n++;
790
+ x += dx;
791
+ y += dy;
792
+ }
793
+ return n;
794
+ };
795
+ const my0 = y0 + (ch >> 1), mx0 = x0 + (cw >> 1);
796
+ const thin = (a, b) => a > 0 && b >= a * 1.8 && b - a >= Math.max(3, ch * 0.08);
797
+ const boxed = cl.length === 1 && midRuns === 2 && edgeFull(y0, 1) && edgeFull(y1 - 1, -1) && thin(run(x0, my0, 1, 0), run(x1 - 1, my0, -1, 0));
798
+ const top = run(mx0, y0, 0, 1), bottom = run(mx0, y1 - 1, 0, -1);
799
+ const shadow = !boxed ? null : thin(bottom, top) ? "\u2750" : thin(top, bottom) ? "\u274F" : null;
725
800
  let mark = null;
726
- if (hollowSquare && rel >= 0.6 && rel <= 0.92) mark = "\u25A1";
801
+ if (shadow && rel >= 0.6 && rel <= 0.92) mark = shadow;
802
+ else if (hollowSquare && rel >= 0.6 && rel <= 0.92) mark = "\u25A1";
803
+ else if (filledSquare && rel >= 0.6 && rel <= 0.92) mark = "\u25A3";
727
804
  else if (nested && fill < 0.45 && rel >= 0.6) mark = "\u25CE";
728
805
  else if (cl.length === 1 && fill >= 0.9 && rel >= 0.28 && rel <= 0.6) mark = "\u25AA";
729
806
  else if (cl.length === 1 && fill >= 0.65 && fill <= 0.88 && rel >= 0.5 && rel <= 0.8) mark = "\u25CF";
@@ -1068,9 +1145,10 @@ function extractLines(fnArray, argsArray) {
1068
1145
  break;
1069
1146
  }
1070
1147
  }
1148
+ const gradient = gradientBands(horizontals, verticals);
1071
1149
  return {
1072
- horizontals: chainShortSegments(horizontals, thinShortH, "h", (l) => thinFill.has(l)),
1073
- verticals: chainShortSegments(verticals, thinShortV, "v", (l) => thinFill.has(l)),
1150
+ horizontals: chainShortSegments(horizontals.filter((l) => !gradient.has(l)), thinShortH, "h", (l) => thinFill.has(l)),
1151
+ verticals: chainShortSegments(verticals.filter((l) => !gradient.has(l)), thinShortV, "v", (l) => thinFill.has(l)),
1074
1152
  clipRects,
1075
1153
  fillRects,
1076
1154
  hiddenBoxes,
@@ -1079,6 +1157,39 @@ function extractLines(fnArray, argsArray) {
1079
1157
  nonRules
1080
1158
  };
1081
1159
  }
1160
+ var GRADIENT_MIN_EDGES = 8;
1161
+ var GRADIENT_PITCH = 1;
1162
+ function gradientBands(horizontals, verticals) {
1163
+ const out = /* @__PURE__ */ new Set();
1164
+ for (const [lines, dir] of [[horizontals, "h"], [verticals, "v"]]) {
1165
+ const pos = (l) => dir === "h" ? l.y1 : l.x1;
1166
+ const span2 = (l) => dir === "h" ? `${Math.round(l.x1)}|${Math.round(l.x2)}` : `${Math.round(l.y1)}|${Math.round(l.y2)}`;
1167
+ const groups = /* @__PURE__ */ new Map();
1168
+ for (const l of lines) if (l.fromFill) {
1169
+ const k = span2(l);
1170
+ const g = groups.get(k);
1171
+ if (g) g.push(l);
1172
+ else groups.set(k, [l]);
1173
+ }
1174
+ for (const g of groups.values()) {
1175
+ if (g.length < GRADIENT_MIN_EDGES) continue;
1176
+ g.sort((a, b) => pos(a) - pos(b));
1177
+ let run = [g[0]];
1178
+ const flush = () => {
1179
+ if (run.length >= GRADIENT_MIN_EDGES) for (const l of run) out.add(l);
1180
+ };
1181
+ for (let i = 1; i < g.length; i++) {
1182
+ if (pos(g[i]) - pos(g[i - 1]) <= GRADIENT_PITCH) run.push(g[i]);
1183
+ else {
1184
+ flush();
1185
+ run = [g[i]];
1186
+ }
1187
+ }
1188
+ flush();
1189
+ }
1190
+ }
1191
+ return out;
1192
+ }
1082
1193
  var SHORT_CHAIN_POS_TOL = 0.5;
1083
1194
  var SHORT_CHAIN_GAP = 0.2;
1084
1195
  function chainShortSegments(longs, shorts, dir, chainable = (l) => !l.fromFill) {
@@ -3445,6 +3556,7 @@ function cellTextToString(items, wrap) {
3445
3556
  if (s.length === 1) return s[0].text;
3446
3557
  if (s.length === 2 && /^[가-힣]$/.test(s[0].text) && /^[가-힣]$/.test(s[1].text) && !!wrap?.lex && (wrap.lex.isWord(s[0].text + s[1].text) || wrap.lex.evidence(s[0].text, s[1].text) === "" || REGION_LABELS.has(s[0].text + s[1].text)) && s[1].x - (s[0].x + s[0].w) >= Math.max(s[0].fontSize, s[1].fontSize) * 1.5 && s[1].x + s[1].w - s[0].x >= wrap.box.x2 - wrap.box.x1 - Math.max(s[0].fontSize, s[1].fontSize) * 2) return s[0].text + s[1].text;
3447
3558
  const evenSpaced = detectEvenSpacedItems(s, true);
3559
+ const hangulLine = s.some((it) => /[가-힣]/.test(it.text));
3448
3560
  let result = s[0].text;
3449
3561
  for (let j = 1; j < s.length; j++) {
3450
3562
  if (evenSpaced[j]) {
@@ -3453,7 +3565,7 @@ function cellTextToString(items, wrap) {
3453
3565
  }
3454
3566
  const gap = s[j].x - (s[j - 1].x + s[j - 1].w);
3455
3567
  const avgFs = (s[j].fontSize + s[j - 1].fontSize) / 2;
3456
- if (isCjkLatinAutospace(s[j - 1].text, s[j].text, gap, avgFs)) {
3568
+ if (isCjkLatinAutospace(s[j - 1].text, s[j].text, gap, avgFs, hangulLine)) {
3457
3569
  result += s[j].text;
3458
3570
  } else if (s[j].hasSpaceBefore && gap >= avgFs * 0.05) {
3459
3571
  result += " " + s[j].text;
@@ -3792,8 +3904,9 @@ function sortLineByX(items) {
3792
3904
  }
3793
3905
  return items;
3794
3906
  }
3795
- function isCjkLatinAutospace(prevText, nextText, gap, fontSize) {
3796
- if (!(gap < fontSize * 0.3)) return false;
3907
+ function isCjkLatinAutospace(prevText, nextText, gap, fontSize, hangulLine = false) {
3908
+ if (!(gap < fontSize * 0.35)) return false;
3909
+ if (hangulLine) return false;
3797
3910
  const a = prevText.slice(-1), b = nextText[0] ?? "";
3798
3911
  const cjk = /[\u3040-\u30ff\u3400-\u9fff\uf900-\ufaff]/, latin = /[A-Za-z0-9]/;
3799
3912
  return cjk.test(a) && latin.test(b) || latin.test(a) && cjk.test(b);
@@ -4133,6 +4246,7 @@ function mergeLineSimple(items) {
4133
4246
  if (items.length <= 1) return items[0]?.text || "";
4134
4247
  const sorted = sortLineByX([...items]);
4135
4248
  const isEvenSpaced = detectEvenSpacedItems(sorted);
4249
+ const hangulLine = sorted.some((it) => /[가-힣]/.test(it.text));
4136
4250
  let result = sorted[0].text;
4137
4251
  for (let i = 1; i < sorted.length; i++) {
4138
4252
  const gap = sorted[i].x - (sorted[i - 1].x + sorted[i - 1].w);
@@ -4147,7 +4261,7 @@ function mergeLineSimple(items) {
4147
4261
  result += sorted[i].text;
4148
4262
  continue;
4149
4263
  }
4150
- if (isCjkLatinAutospace(sorted[i - 1].text, sorted[i].text, gap, avgFs)) {
4264
+ if (isCjkLatinAutospace(sorted[i - 1].text, sorted[i].text, gap, avgFs, hangulLine)) {
4151
4265
  result += sorted[i].text;
4152
4266
  continue;
4153
4267
  }
@@ -4313,11 +4427,12 @@ function clusterCellLines(items) {
4313
4427
  }
4314
4428
  return lines.sort((a, b) => b.top - a.top).map(({ items: line }) => {
4315
4429
  line.sort((a, b) => a.x - b.x);
4430
+ const hangulLine = line.some((it) => /[가-힣]/.test(it.text));
4316
4431
  let s = line[0].text;
4317
4432
  for (let i = 1; i < line.length; i++) {
4318
4433
  const gap = line[i].x - (line[i - 1].x + line[i - 1].w);
4319
4434
  const fs = (line[i].fontSize + line[i - 1].fontSize) / 2;
4320
- s += (!isCjkLatinAutospace(line[i - 1].text, line[i].text, gap, fs) && (line[i].hasSpaceBefore && gap >= fs * 0.05 || gap > spaceGapThreshold(fs)) ? " " : "") + line[i].text;
4435
+ s += (!isCjkLatinAutospace(line[i - 1].text, line[i].text, gap, fs, hangulLine) && (line[i].hasSpaceBefore && gap >= fs * 0.05 || gap > spaceGapThreshold(fs)) ? " " : "") + line[i].text;
4321
4436
  }
4322
4437
  const last = line.reduce((a, b) => b.x + b.w > a.x + a.w ? b : a);
4323
4438
  return {
@@ -10004,6 +10119,7 @@ export {
10004
10119
  gapGlyphs,
10005
10120
  ringBullet,
10006
10121
  dashRatio,
10122
+ leadingRing,
10007
10123
  discAt,
10008
10124
  serifOne,
10009
10125
  starRun,
@@ -10066,4 +10182,4 @@ export {
10066
10182
  UNRULED_REGION_TABLES,
10067
10183
  mergeOcrImageRegions
10068
10184
  };
10069
- //# sourceMappingURL=chunk-IT5XO3UG.js.map
10185
+ //# sourceMappingURL=chunk-37JRATLZ.js.map