dochan 0.1.0__tar.gz

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 (60) hide show
  1. dochan-0.1.0/LICENSE +21 -0
  2. dochan-0.1.0/NOTICE +8 -0
  3. dochan-0.1.0/PKG-INFO +263 -0
  4. dochan-0.1.0/README.md +229 -0
  5. dochan-0.1.0/dochan/__init__.py +9 -0
  6. dochan-0.1.0/dochan/batch.py +154 -0
  7. dochan-0.1.0/dochan/cli.py +118 -0
  8. dochan-0.1.0/dochan/constants.py +54 -0
  9. dochan-0.1.0/dochan/control_char.py +51 -0
  10. dochan-0.1.0/dochan/fallback/__init__.py +0 -0
  11. dochan-0.1.0/dochan/fallback/filter_server.py +93 -0
  12. dochan-0.1.0/dochan/hwp/__init__.py +0 -0
  13. dochan-0.1.0/dochan/hwp/bin_data.py +96 -0
  14. dochan-0.1.0/dochan/hwp/doc_info.py +212 -0
  15. dochan-0.1.0/dochan/hwp/header.py +78 -0
  16. dochan-0.1.0/dochan/hwp/records/__init__.py +0 -0
  17. dochan-0.1.0/dochan/hwp/records/char_shape.py +115 -0
  18. dochan-0.1.0/dochan/hwp/records/ctrl_header.py +67 -0
  19. dochan-0.1.0/dochan/hwp/records/para_char_shape.py +35 -0
  20. dochan-0.1.0/dochan/hwp/records/para_header.py +63 -0
  21. dochan-0.1.0/dochan/hwp/records/para_text.py +66 -0
  22. dochan-0.1.0/dochan/hwp/records/style.py +68 -0
  23. dochan-0.1.0/dochan/hwp/records/table.py +62 -0
  24. dochan-0.1.0/dochan/hwp/section.py +472 -0
  25. dochan-0.1.0/dochan/hwpx/__init__.py +0 -0
  26. dochan-0.1.0/dochan/hwpx/parser.py +461 -0
  27. dochan-0.1.0/dochan/model/__init__.py +0 -0
  28. dochan-0.1.0/dochan/model/document.py +97 -0
  29. dochan-0.1.0/dochan/model/equation.py +58 -0
  30. dochan-0.1.0/dochan/model/header_footer.py +25 -0
  31. dochan-0.1.0/dochan/model/image.py +25 -0
  32. dochan-0.1.0/dochan/model/style.py +37 -0
  33. dochan-0.1.0/dochan/model/table.py +37 -0
  34. dochan-0.1.0/dochan/output/__init__.py +0 -0
  35. dochan-0.1.0/dochan/output/json_out.py +84 -0
  36. dochan-0.1.0/dochan/output/markdown.py +159 -0
  37. dochan-0.1.0/dochan/output/plain_text.py +47 -0
  38. dochan-0.1.0/dochan/quality/__init__.py +0 -0
  39. dochan-0.1.0/dochan/quality/batch_validate.py +222 -0
  40. dochan-0.1.0/dochan/quality/checker.py +75 -0
  41. dochan-0.1.0/dochan/quality/comparator.py +66 -0
  42. dochan-0.1.0/dochan/quality/cross_validator.py +410 -0
  43. dochan-0.1.0/dochan/reader.py +174 -0
  44. dochan-0.1.0/dochan/tests/__init__.py +0 -0
  45. dochan-0.1.0/dochan/tests/test_char_shape.py +72 -0
  46. dochan-0.1.0/dochan/tests/test_control_char.py +90 -0
  47. dochan-0.1.0/dochan/tests/test_quality.py +82 -0
  48. dochan-0.1.0/dochan/utils/__init__.py +0 -0
  49. dochan-0.1.0/dochan/utils/error_recovery.py +55 -0
  50. dochan-0.1.0/dochan/utils/logger.py +38 -0
  51. dochan-0.1.0/dochan/utils/ocr.py +117 -0
  52. dochan-0.1.0/dochan/utils/safe_decompress.py +27 -0
  53. dochan-0.1.0/dochan.egg-info/PKG-INFO +263 -0
  54. dochan-0.1.0/dochan.egg-info/SOURCES.txt +58 -0
  55. dochan-0.1.0/dochan.egg-info/dependency_links.txt +1 -0
  56. dochan-0.1.0/dochan.egg-info/entry_points.txt +2 -0
  57. dochan-0.1.0/dochan.egg-info/requires.txt +11 -0
  58. dochan-0.1.0/dochan.egg-info/top_level.txt +1 -0
  59. dochan-0.1.0/pyproject.toml +47 -0
  60. dochan-0.1.0/setup.cfg +4 -0
dochan-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 dochan contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
dochan-0.1.0/NOTICE ADDED
@@ -0,0 +1,8 @@
1
+ NOTICE
2
+
3
+ 본 소프트웨어는 한글과컴퓨터의 HWP 문서 파일(.hwp) 공개 문서를 참고하여 개발되었습니다.
4
+ 참조 스펙: 한글문서파일형식_5.0_revision1.3.pdf (2018.11.08)
5
+
6
+ This software was developed with reference to the publicly available HWP document
7
+ file format specification published by Hancom Inc.
8
+ Reference: HWP Document File Format 5.0 revision 1.3 (2018.11.08)
dochan-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,263 @@
1
+ Metadata-Version: 2.4
2
+ Name: dochan
3
+ Version: 0.1.0
4
+ Summary: dochan — HWP/HWPX 문서 파서, AI/LLM 최적 Markdown 변환
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/illuwa/dochan
7
+ Project-URL: Repository, https://github.com/illuwa/dochan
8
+ Project-URL: Issues, https://github.com/illuwa/dochan/issues
9
+ Keywords: hwp,hwpx,parser,korean,document,markdown,ai,llm,hancom
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Text Processing :: Markup
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ License-File: NOTICE
24
+ Requires-Dist: olefile>=0.47
25
+ Requires-Dist: lxml>=4.9
26
+ Requires-Dist: pyyaml>=6.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Requires-Dist: pytest-cov; extra == "dev"
30
+ Provides-Extra: ocr
31
+ Requires-Dist: pytesseract>=0.3; extra == "ocr"
32
+ Requires-Dist: Pillow>=9.0; extra == "ocr"
33
+ Dynamic: license-file
34
+
35
+ <p align="center">
36
+ <h1 align="center">dochan (독한)</h1>
37
+ <p align="center">
38
+ <strong>독한 HWP/HWPX 파서 — AI/LLM 최적 Markdown 변환</strong>
39
+ </p>
40
+ <p align="center">
41
+ The toughest Korean document parser. HWP/HWPX → Markdown, JSON, Plain Text.
42
+ </p>
43
+ <p align="center">
44
+ <a href="https://pypi.org/project/dochan/"><img src="https://img.shields.io/pypi/v/dochan?color=blue" alt="PyPI"></a>
45
+ <a href="https://pypi.org/project/dochan/"><img src="https://img.shields.io/pypi/pyversions/dochan" alt="Python"></a>
46
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
47
+ </p>
48
+ </p>
49
+
50
+ ---
51
+
52
+ ## What is dochan?
53
+
54
+ **dochan**(독한)은 한글(HWP/HWPX) 문서를 파싱하여 AI/LLM이 바로 사용할 수 있는 Markdown으로 변환하는 Python 파서입니다.
55
+
56
+ - `doc` (문서) + `한` (韓, 한국) = **dochan** — "독한 파서"라는 더블 미닝
57
+ - HWP 5.0 바이너리 + HWPX(OWPML) XML 이중 지원
58
+ - 155개 실문서로 검증, 평균 96.5점, 공개가능한 문서로 계속 학습시켜 개선할 예정
59
+
60
+ ```python
61
+ from dochan import Dochan
62
+
63
+ doc = Dochan("공문서.hwp")
64
+ print(doc.to_markdown())
65
+ ```
66
+
67
+ ## Features
68
+
69
+ | 기능 | 설명 |
70
+ |------|------|
71
+ | **HWP + HWPX** | 바이너리(.hwp)와 XML(.hwpx) 모두 자동 감지 파싱 |
72
+ | **Markdown 출력** | 제목, 표, 서식(bold/italic), 수식까지 AI가 바로 쓸 수 있는 Markdown |
73
+ | **표 파싱** | 셀 병합, 중첩 표, 좌표 배치 지원 |
74
+ | **서식 보존** | CharShape 기반 bold/italic/글자크기 → TextRun 연결 |
75
+ | **제목 자동 감지** | Style 이름 + 글자 크기 기반 heading 레벨 판별 |
76
+ | **수식 LaTeX** | HWP 수식 스크립트 → LaTeX 기본 변환 |
77
+ | **JSON / Plain Text** | Markdown 외 구조화 JSON, 플레인 텍스트 출력 |
78
+ | **OCR (선택)** | Tesseract 연동, 이미지 속 텍스트 추출 |
79
+ | **CLI** | `dochan convert 문서.hwp` 한 줄로 변환 |
80
+ | **배치 처리** | 디렉토리 단위 병렬 변환 |
81
+ | **보안** | Zip Bomb, XXE, Path Traversal, 메모리 폭발 방어 |
82
+
83
+ ## Installation
84
+
85
+ ```bash
86
+ pip install dochan
87
+ ```
88
+
89
+ OCR 기능이 필요한 경우:
90
+ ```bash
91
+ pip install dochan[ocr]
92
+ brew install tesseract tesseract-lang # macOS
93
+ ```
94
+
95
+ ## Quick Start
96
+
97
+ ### Python API
98
+
99
+ ```python
100
+ from dochan import Dochan
101
+
102
+ # HWP 또는 HWPX — 자동 감지
103
+ doc = Dochan("보고서.hwp")
104
+
105
+ # AI/LLM용 Markdown
106
+ markdown = doc.to_markdown()
107
+
108
+ # 구조화 JSON
109
+ json_str = doc.to_json()
110
+
111
+ # 플레인 텍스트
112
+ text = doc.to_plain_text()
113
+
114
+ # 요소별 접근
115
+ for table in doc.find_all('table'):
116
+ print(f"표: {table.row_count}행 x {table.col_count}열")
117
+
118
+ for eq in doc.find_all('equation'):
119
+ print(f"수식: {eq.latex}")
120
+
121
+ # 메타데이터
122
+ print(doc.metadata)
123
+ ```
124
+
125
+ ### CLI
126
+
127
+ ```bash
128
+ # Markdown 변환 (stdout)
129
+ dochan convert 문서.hwp
130
+
131
+ # 파일로 저장
132
+ dochan convert 문서.hwp -o output.md
133
+
134
+ # JSON 출력
135
+ dochan convert 문서.hwpx --format json
136
+
137
+ # 디렉토리 일괄 변환
138
+ dochan batch input_dir/ output_dir/ --format markdown --workers 4
139
+
140
+ # 문서 정보
141
+ dochan info 문서.hwp
142
+ ```
143
+
144
+ ### OCR (이미지 속 텍스트 추출)
145
+
146
+ ```python
147
+ doc = Dochan("이미지포함문서.hwpx", ocr=True)
148
+ print(doc.to_markdown()) # 이미지 속 텍스트도 포함
149
+ ```
150
+
151
+ ## Output Examples
152
+
153
+ ### Input: 한글 공문서 (.hwp)
154
+
155
+ ### Output: Markdown
156
+
157
+ ```markdown
158
+ # **사내 규정집**
159
+
160
+ | 연번 | 내용 | 일자 |
161
+ | --- | --- | --- |
162
+ | 1 | 제정 | 2020. 3. 1. |
163
+ | 2 | 개정 | 2024. 9.15. |
164
+
165
+ ### **제1장 총칙**
166
+
167
+ **제1조(목적)** 이 규정은 회사 직원의 복무에 관한 사항을 정함을
168
+ 목적으로 한다.
169
+ ```
170
+
171
+ ## Supported Elements
172
+
173
+ | 요소 | HWP (바이너리) | HWPX (XML) |
174
+ |------|:-:|:-:|
175
+ | 텍스트 | ✅ | ✅ |
176
+ | 표 (단순) | ✅ | ✅ |
177
+ | 표 (셀 병합) | ✅ | ✅ |
178
+ | 표 (중첩) | ✅ | ✅ |
179
+ | 서식 (bold/italic) | ✅ | ✅ |
180
+ | 제목 감지 | ✅ | ✅ |
181
+ | 수식 | ✅ | ✅ |
182
+ | 이미지 참조 | ✅ | ✅ |
183
+ | 이미지 OCR | ✅ | ✅ |
184
+ | 머리글/바닥글 | ✅ | ⬜ |
185
+ | 각주/미주 | ✅ | ⬜ |
186
+ | 암호화 문서 | ⬜ | — |
187
+
188
+ ✅ 지원 &nbsp; ⬜ 미지원 &nbsp; — 해당 없음
189
+
190
+ ## Architecture
191
+
192
+ ```
193
+ dochan/
194
+ ├── reader.py # 통합 진입점 (Dochan 클래스)
195
+ ├── cli.py # CLI 도구
196
+ ├── hwp/ # HWP 5.0 바이너리 파서
197
+ │ ├── header.py # FileHeader (256바이트)
198
+ │ ├── doc_info.py # DocInfo (서식/스타일)
199
+ │ ├── section.py # 섹션 (레코드 트리 → 모델)
200
+ │ └── records/ # 개별 레코드 파서
201
+ ├── hwpx/ # HWPX (OWPML) XML 파서
202
+ │ └── parser.py
203
+ ├── model/ # Document 모델
204
+ │ ├── document.py # Document, Section, Paragraph
205
+ │ ├── table.py # Table, Cell
206
+ │ └── equation.py # Equation (LaTeX 변환)
207
+ ├── output/ # 출력 포맷
208
+ │ ├── markdown.py # Markdown (AI/LLM 최적)
209
+ │ ├── json_out.py # 구조화 JSON
210
+ │ └── plain_text.py # 플레인 텍스트
211
+ └── utils/ # 유틸리티
212
+ ├── ocr.py # Tesseract OCR
213
+ └── safe_decompress.py # Zip Bomb 방어
214
+ ```
215
+
216
+ ## Security
217
+
218
+ dochan은 신뢰할 수 없는 문서도 안전하게 처리합니다:
219
+
220
+ - **Zip Bomb 방어**: zlib/ZIP 해제 크기 제한 (200MB)
221
+ - **XXE 차단**: XML 외부 엔티티 해석 비활성화
222
+ - **Path Traversal 방지**: 배치 처리 시 경로 탈출 차단
223
+ - **메모리 제한**: 표 크기 1M셀, 재귀 깊이 100 제한
224
+ - **입력 검증**: FileHeader/스트림명/바이너리 바운드 체크
225
+
226
+ ## Contributing
227
+
228
+ 기여를 환영합니다! Issues, Pull Requests 모두 열려 있습니다.
229
+
230
+ ```bash
231
+ # 개발 환경 설정
232
+ git clone https://github.com/illuwa/dochan.git
233
+ cd dochan
234
+ pip install -e ".[dev]"
235
+ python -m pytest dochan/tests/
236
+ ```
237
+
238
+ ## Acknowledgments
239
+
240
+ > [kordoc](https://github.com/chrisryugj/kordoc)를 보고 자극받아 만들었습니다.
241
+
242
+ dochan은 다음 프로젝트와 자료를 기반으로 개발되었습니다:
243
+
244
+ **스펙 참조**
245
+ - [한글과컴퓨터](https://www.hancom.com/) — HWP 문서 파일 형식 5.0 공개 스펙 (revision 1.3, 2018)
246
+ - [OWPML (KS X 6101:2011)](https://www.kssn.net/) — HWPX 국가 표준
247
+
248
+ **오픈소스**
249
+ - [olefile](https://github.com/decalage2/olefile) — OLE2 파일 파싱 (Philippe Lagadec, BSD)
250
+ - [lxml](https://lxml.de/) — XML 파싱 (BSD)
251
+ - [pdfplumber](https://github.com/jsvine/pdfplumber) — 품질 검증용 PDF 추출 (MIT)
252
+ - [Tesseract OCR](https://github.com/tesseract-ocr/tesseract) — 이미지 텍스트 추출 (Apache 2.0)
253
+
254
+ **선행 연구**
255
+ - [hwplib](https://github.com/neolord0/hwplib) (Java) — HWP 레코드 구조 참조
256
+ - [hwp.js](https://github.com/niceeee/hwp.js) — 레코드 트리 구축 참조
257
+ - [pyhwp](https://github.com/mete0r/pyhwp) — Python HWP 파서 선구자
258
+
259
+ ## License
260
+
261
+ [MIT License](LICENSE)
262
+
263
+ 본 소프트웨어는 한글과컴퓨터의 HWP 문서 파일(.hwp) 공개 문서를 참고하여 개발되었습니다. 자세한 내용은 [NOTICE](NOTICE) 파일을 참조하세요.
dochan-0.1.0/README.md ADDED
@@ -0,0 +1,229 @@
1
+ <p align="center">
2
+ <h1 align="center">dochan (독한)</h1>
3
+ <p align="center">
4
+ <strong>독한 HWP/HWPX 파서 — AI/LLM 최적 Markdown 변환</strong>
5
+ </p>
6
+ <p align="center">
7
+ The toughest Korean document parser. HWP/HWPX → Markdown, JSON, Plain Text.
8
+ </p>
9
+ <p align="center">
10
+ <a href="https://pypi.org/project/dochan/"><img src="https://img.shields.io/pypi/v/dochan?color=blue" alt="PyPI"></a>
11
+ <a href="https://pypi.org/project/dochan/"><img src="https://img.shields.io/pypi/pyversions/dochan" alt="Python"></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
13
+ </p>
14
+ </p>
15
+
16
+ ---
17
+
18
+ ## What is dochan?
19
+
20
+ **dochan**(독한)은 한글(HWP/HWPX) 문서를 파싱하여 AI/LLM이 바로 사용할 수 있는 Markdown으로 변환하는 Python 파서입니다.
21
+
22
+ - `doc` (문서) + `한` (韓, 한국) = **dochan** — "독한 파서"라는 더블 미닝
23
+ - HWP 5.0 바이너리 + HWPX(OWPML) XML 이중 지원
24
+ - 155개 실문서로 검증, 평균 96.5점, 공개가능한 문서로 계속 학습시켜 개선할 예정
25
+
26
+ ```python
27
+ from dochan import Dochan
28
+
29
+ doc = Dochan("공문서.hwp")
30
+ print(doc.to_markdown())
31
+ ```
32
+
33
+ ## Features
34
+
35
+ | 기능 | 설명 |
36
+ |------|------|
37
+ | **HWP + HWPX** | 바이너리(.hwp)와 XML(.hwpx) 모두 자동 감지 파싱 |
38
+ | **Markdown 출력** | 제목, 표, 서식(bold/italic), 수식까지 AI가 바로 쓸 수 있는 Markdown |
39
+ | **표 파싱** | 셀 병합, 중첩 표, 좌표 배치 지원 |
40
+ | **서식 보존** | CharShape 기반 bold/italic/글자크기 → TextRun 연결 |
41
+ | **제목 자동 감지** | Style 이름 + 글자 크기 기반 heading 레벨 판별 |
42
+ | **수식 LaTeX** | HWP 수식 스크립트 → LaTeX 기본 변환 |
43
+ | **JSON / Plain Text** | Markdown 외 구조화 JSON, 플레인 텍스트 출력 |
44
+ | **OCR (선택)** | Tesseract 연동, 이미지 속 텍스트 추출 |
45
+ | **CLI** | `dochan convert 문서.hwp` 한 줄로 변환 |
46
+ | **배치 처리** | 디렉토리 단위 병렬 변환 |
47
+ | **보안** | Zip Bomb, XXE, Path Traversal, 메모리 폭발 방어 |
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pip install dochan
53
+ ```
54
+
55
+ OCR 기능이 필요한 경우:
56
+ ```bash
57
+ pip install dochan[ocr]
58
+ brew install tesseract tesseract-lang # macOS
59
+ ```
60
+
61
+ ## Quick Start
62
+
63
+ ### Python API
64
+
65
+ ```python
66
+ from dochan import Dochan
67
+
68
+ # HWP 또는 HWPX — 자동 감지
69
+ doc = Dochan("보고서.hwp")
70
+
71
+ # AI/LLM용 Markdown
72
+ markdown = doc.to_markdown()
73
+
74
+ # 구조화 JSON
75
+ json_str = doc.to_json()
76
+
77
+ # 플레인 텍스트
78
+ text = doc.to_plain_text()
79
+
80
+ # 요소별 접근
81
+ for table in doc.find_all('table'):
82
+ print(f"표: {table.row_count}행 x {table.col_count}열")
83
+
84
+ for eq in doc.find_all('equation'):
85
+ print(f"수식: {eq.latex}")
86
+
87
+ # 메타데이터
88
+ print(doc.metadata)
89
+ ```
90
+
91
+ ### CLI
92
+
93
+ ```bash
94
+ # Markdown 변환 (stdout)
95
+ dochan convert 문서.hwp
96
+
97
+ # 파일로 저장
98
+ dochan convert 문서.hwp -o output.md
99
+
100
+ # JSON 출력
101
+ dochan convert 문서.hwpx --format json
102
+
103
+ # 디렉토리 일괄 변환
104
+ dochan batch input_dir/ output_dir/ --format markdown --workers 4
105
+
106
+ # 문서 정보
107
+ dochan info 문서.hwp
108
+ ```
109
+
110
+ ### OCR (이미지 속 텍스트 추출)
111
+
112
+ ```python
113
+ doc = Dochan("이미지포함문서.hwpx", ocr=True)
114
+ print(doc.to_markdown()) # 이미지 속 텍스트도 포함
115
+ ```
116
+
117
+ ## Output Examples
118
+
119
+ ### Input: 한글 공문서 (.hwp)
120
+
121
+ ### Output: Markdown
122
+
123
+ ```markdown
124
+ # **사내 규정집**
125
+
126
+ | 연번 | 내용 | 일자 |
127
+ | --- | --- | --- |
128
+ | 1 | 제정 | 2020. 3. 1. |
129
+ | 2 | 개정 | 2024. 9.15. |
130
+
131
+ ### **제1장 총칙**
132
+
133
+ **제1조(목적)** 이 규정은 회사 직원의 복무에 관한 사항을 정함을
134
+ 목적으로 한다.
135
+ ```
136
+
137
+ ## Supported Elements
138
+
139
+ | 요소 | HWP (바이너리) | HWPX (XML) |
140
+ |------|:-:|:-:|
141
+ | 텍스트 | ✅ | ✅ |
142
+ | 표 (단순) | ✅ | ✅ |
143
+ | 표 (셀 병합) | ✅ | ✅ |
144
+ | 표 (중첩) | ✅ | ✅ |
145
+ | 서식 (bold/italic) | ✅ | ✅ |
146
+ | 제목 감지 | ✅ | ✅ |
147
+ | 수식 | ✅ | ✅ |
148
+ | 이미지 참조 | ✅ | ✅ |
149
+ | 이미지 OCR | ✅ | ✅ |
150
+ | 머리글/바닥글 | ✅ | ⬜ |
151
+ | 각주/미주 | ✅ | ⬜ |
152
+ | 암호화 문서 | ⬜ | — |
153
+
154
+ ✅ 지원 &nbsp; ⬜ 미지원 &nbsp; — 해당 없음
155
+
156
+ ## Architecture
157
+
158
+ ```
159
+ dochan/
160
+ ├── reader.py # 통합 진입점 (Dochan 클래스)
161
+ ├── cli.py # CLI 도구
162
+ ├── hwp/ # HWP 5.0 바이너리 파서
163
+ │ ├── header.py # FileHeader (256바이트)
164
+ │ ├── doc_info.py # DocInfo (서식/스타일)
165
+ │ ├── section.py # 섹션 (레코드 트리 → 모델)
166
+ │ └── records/ # 개별 레코드 파서
167
+ ├── hwpx/ # HWPX (OWPML) XML 파서
168
+ │ └── parser.py
169
+ ├── model/ # Document 모델
170
+ │ ├── document.py # Document, Section, Paragraph
171
+ │ ├── table.py # Table, Cell
172
+ │ └── equation.py # Equation (LaTeX 변환)
173
+ ├── output/ # 출력 포맷
174
+ │ ├── markdown.py # Markdown (AI/LLM 최적)
175
+ │ ├── json_out.py # 구조화 JSON
176
+ │ └── plain_text.py # 플레인 텍스트
177
+ └── utils/ # 유틸리티
178
+ ├── ocr.py # Tesseract OCR
179
+ └── safe_decompress.py # Zip Bomb 방어
180
+ ```
181
+
182
+ ## Security
183
+
184
+ dochan은 신뢰할 수 없는 문서도 안전하게 처리합니다:
185
+
186
+ - **Zip Bomb 방어**: zlib/ZIP 해제 크기 제한 (200MB)
187
+ - **XXE 차단**: XML 외부 엔티티 해석 비활성화
188
+ - **Path Traversal 방지**: 배치 처리 시 경로 탈출 차단
189
+ - **메모리 제한**: 표 크기 1M셀, 재귀 깊이 100 제한
190
+ - **입력 검증**: FileHeader/스트림명/바이너리 바운드 체크
191
+
192
+ ## Contributing
193
+
194
+ 기여를 환영합니다! Issues, Pull Requests 모두 열려 있습니다.
195
+
196
+ ```bash
197
+ # 개발 환경 설정
198
+ git clone https://github.com/illuwa/dochan.git
199
+ cd dochan
200
+ pip install -e ".[dev]"
201
+ python -m pytest dochan/tests/
202
+ ```
203
+
204
+ ## Acknowledgments
205
+
206
+ > [kordoc](https://github.com/chrisryugj/kordoc)를 보고 자극받아 만들었습니다.
207
+
208
+ dochan은 다음 프로젝트와 자료를 기반으로 개발되었습니다:
209
+
210
+ **스펙 참조**
211
+ - [한글과컴퓨터](https://www.hancom.com/) — HWP 문서 파일 형식 5.0 공개 스펙 (revision 1.3, 2018)
212
+ - [OWPML (KS X 6101:2011)](https://www.kssn.net/) — HWPX 국가 표준
213
+
214
+ **오픈소스**
215
+ - [olefile](https://github.com/decalage2/olefile) — OLE2 파일 파싱 (Philippe Lagadec, BSD)
216
+ - [lxml](https://lxml.de/) — XML 파싱 (BSD)
217
+ - [pdfplumber](https://github.com/jsvine/pdfplumber) — 품질 검증용 PDF 추출 (MIT)
218
+ - [Tesseract OCR](https://github.com/tesseract-ocr/tesseract) — 이미지 텍스트 추출 (Apache 2.0)
219
+
220
+ **선행 연구**
221
+ - [hwplib](https://github.com/neolord0/hwplib) (Java) — HWP 레코드 구조 참조
222
+ - [hwp.js](https://github.com/niceeee/hwp.js) — 레코드 트리 구축 참조
223
+ - [pyhwp](https://github.com/mete0r/pyhwp) — Python HWP 파서 선구자
224
+
225
+ ## License
226
+
227
+ [MIT License](LICENSE)
228
+
229
+ 본 소프트웨어는 한글과컴퓨터의 HWP 문서 파일(.hwp) 공개 문서를 참고하여 개발되었습니다. 자세한 내용은 [NOTICE](NOTICE) 파일을 참조하세요.
@@ -0,0 +1,9 @@
1
+ """dochan — HWP/HWPX 문서 파서, AI/LLM 최적 Markdown 변환"""
2
+ __version__ = "0.1.0"
3
+
4
+ from .reader import Dochan
5
+
6
+ # 하위 호환 별칭
7
+ HWPReader = Dochan
8
+
9
+ __all__ = ['Dochan', 'HWPReader']
@@ -0,0 +1,154 @@
1
+ """
2
+ batch.py — 배치 처리
3
+ 다수 HWP 파일의 병렬 변환
4
+ """
5
+
6
+ import os
7
+ import logging
8
+ from concurrent.futures import ProcessPoolExecutor, as_completed
9
+ from dataclasses import dataclass, field
10
+ from pathlib import Path
11
+ from typing import List
12
+
13
+ logger = logging.getLogger('dochan')
14
+
15
+
16
+ @dataclass
17
+ class BatchResult:
18
+ """단일 파일 처리 결과"""
19
+ file_path: str = ""
20
+ success: bool = False
21
+ output_path: str = ""
22
+ error_count: int = 0
23
+ errors: List[str] = field(default_factory=list)
24
+
25
+
26
+ @dataclass
27
+ class BatchSummary:
28
+ """배치 처리 전체 요약"""
29
+ total: int = 0
30
+ success: int = 0
31
+ failed: int = 0
32
+ results: List[BatchResult] = field(default_factory=list)
33
+
34
+ @property
35
+ def success_rate(self) -> float:
36
+ return self.success / max(self.total, 1) * 100
37
+
38
+
39
+ def _process_single(file_path: str, output_dir: str, output_format: str) -> BatchResult:
40
+ """단일 파일 처리 (별도 프로세스에서 실행)"""
41
+ from .reader import Dochan
42
+
43
+ result = BatchResult(file_path=file_path)
44
+
45
+ try:
46
+ reader = Dochan(file_path)
47
+
48
+ # 출력 파일명 생성
49
+ base_name = os.path.splitext(os.path.basename(file_path))[0]
50
+ ext_map = {'markdown': '.md', 'json': '.json', 'text': '.txt'}
51
+ ext = ext_map.get(output_format, '.md')
52
+ output_path = os.path.join(output_dir, base_name + ext)
53
+
54
+ # 변환
55
+ if output_format == 'json':
56
+ content = reader.to_json()
57
+ elif output_format == 'text':
58
+ content = reader.to_plain_text()
59
+ else:
60
+ content = reader.to_markdown()
61
+
62
+ # 저장
63
+ with open(output_path, 'w', encoding='utf-8') as f:
64
+ f.write(content)
65
+
66
+ result.success = True
67
+ result.output_path = output_path
68
+ result.errors = reader.errors
69
+ result.error_count = len(reader.errors)
70
+
71
+ except Exception as e:
72
+ result.success = False
73
+ result.errors = [str(e)]
74
+ result.error_count = 1
75
+
76
+ return result
77
+
78
+
79
+ def batch_convert(
80
+ input_dir: str,
81
+ output_dir: str,
82
+ output_format: str = 'markdown',
83
+ max_workers: int = 4,
84
+ extensions: tuple = ('.hwp', '.hwpx'),
85
+ ) -> BatchSummary:
86
+ """
87
+ 디렉토리 내 HWP 파일 일괄 변환
88
+
89
+ Args:
90
+ input_dir: 입력 디렉토리
91
+ output_dir: 출력 디렉토리
92
+ output_format: 'markdown', 'json', 'text'
93
+ max_workers: 병렬 워커 수
94
+ extensions: 처리할 확장자
95
+
96
+ Returns:
97
+ BatchSummary
98
+ """
99
+ # output_dir 경로 검증
100
+ try:
101
+ Path(output_dir).resolve().relative_to(Path(os.getcwd()).resolve())
102
+ except ValueError:
103
+ pass # output_dir가 cwd 밖이어도 허용하되, 아래에서 symlink 공격 방지
104
+
105
+ os.makedirs(output_dir, exist_ok=True)
106
+
107
+ # 파일 수집
108
+ files = []
109
+ resolved_input = Path(input_dir).resolve()
110
+ for root, _, filenames in os.walk(input_dir):
111
+ for fn in filenames:
112
+ if any(fn.lower().endswith(ext) for ext in extensions):
113
+ full_path = os.path.join(root, fn)
114
+ # Path traversal 방지: 실제 경로가 input_dir 내부인지 검증
115
+ try:
116
+ Path(full_path).resolve().relative_to(resolved_input)
117
+ except ValueError:
118
+ logger.warning(f"경로 이탈 감지, 건너뜀: {full_path}")
119
+ continue
120
+ files.append(full_path)
121
+
122
+ summary = BatchSummary(total=len(files))
123
+ logger.info(f"배치 시작: {len(files)}개 파일, {max_workers} 워커")
124
+
125
+ # 병렬 처리
126
+ with ProcessPoolExecutor(max_workers=max_workers) as executor:
127
+ futures = {
128
+ executor.submit(_process_single, f, output_dir, output_format): f
129
+ for f in files
130
+ }
131
+
132
+ for future in as_completed(futures):
133
+ file_path = futures[future]
134
+ try:
135
+ result = future.result()
136
+ summary.results.append(result)
137
+ if result.success:
138
+ summary.success += 1
139
+ logger.info(f"✓ {os.path.basename(file_path)}")
140
+ else:
141
+ summary.failed += 1
142
+ logger.error(f"✗ {os.path.basename(file_path)}: {result.errors}")
143
+ except Exception as e:
144
+ summary.failed += 1
145
+ summary.results.append(BatchResult(
146
+ file_path=file_path,
147
+ success=False,
148
+ errors=[str(e)],
149
+ error_count=1,
150
+ ))
151
+ logger.error(f"✗ {os.path.basename(file_path)}: {e}")
152
+
153
+ logger.info(f"배치 완료: {summary.success}/{summary.total} 성공 ({summary.success_rate:.1f}%)")
154
+ return summary