hwpx-tomd 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.
@@ -0,0 +1,38 @@
1
+ # Byte-compiled / optimized
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ pip-wheel-metadata/
12
+
13
+ # Test / coverage
14
+ .pytest_cache/
15
+ .coverage
16
+ .coverage.*
17
+ htmlcov/
18
+ .tox/
19
+ .nox/
20
+
21
+ # Virtual environments
22
+ .venv/
23
+ venv/
24
+ env/
25
+
26
+ # Editors / OS
27
+ .vscode/
28
+ .idea/
29
+ *.swp
30
+ *~
31
+ .DS_Store
32
+ Thumbs.db
33
+
34
+ # 저작물·민감 자료 보호 안전장치
35
+ # 픽스처는 conftest.py가 동적 생성하므로 실제 HWP/HWPX는 repo에 두지 않는다.
36
+ # 로컬 검증용으로 실제 워크시트·고사 원안을 폴더에 복사해도 실수로 커밋되지 않게 막는다.
37
+ *.hwp
38
+ *.hwpx
@@ -0,0 +1,106 @@
1
+ > 🤖 **이 파일은 자동 생성됩니다. 직접 수정하지 마세요.**
2
+ > 정본은 `CLAUDE.md` 입니다. 내용을 바꾸려면 `CLAUDE.md` 를 수정한 뒤
3
+ > 프로젝트 루트에서 `python sync_agent_docs.py` 를 실행하세요.
4
+ > 이 파일을 직접 고치면 다음 동기화 때 경고와 함께 덮어쓰기 대상이 됩니다.
5
+
6
+ <!-- SYNC-BODY-START: 이 줄 아래 본문은 CLAUDE.md 와 100% 동일하게 자동 생성됨 -->
7
+ # CLAUDE.md
8
+
9
+ 이 파일은 Claude Code(claude.ai/code)가 이 저장소에서 작업할 때 참고하는 가이드다.
10
+
11
+ ## 프로젝트 개요
12
+
13
+ `hwpx-tomd`: HWPX 파일을 외부 API 없이 로컬에서 Markdown/텍스트로 변환하는 가볍고 독립적인 Python 패키지(읽기 전용). 라이브러리 API와 CLI를 함께 제공한다.
14
+
15
+ - 사용자 호칭: "헌용 쌤" (영어: "Hunyong")
16
+ - 작성자: 신명중 김헌용 교사(시각장애 중등 영어 교사, 장교조 위원장). 학교·장교조 업무에서 HWPX를 매우 자주 다룬다.
17
+ - 라이선스(예정): MIT
18
+
19
+ ## 현재 상태: 코어 구현·검증 완료 + 두 스킬 마이그레이션 완료
20
+
21
+ 패키지 코어가 구현·검증되었고(2026-06-05), 첫 git 커밋과 두 소비 스킬 통합까지 마쳤다(2026-06-06). 배경·설계·로드맵 상세는 [`PLAN.md`](PLAN.md) 참조.
22
+
23
+ - **완료**: 이름 확정(`hwpx-tomd`, PyPI 가용 확인), src layout 스캐폴드, `core.py`(엔진 이식·라이브러리화), `cli.py`, 테스트 33개(결함 ①②③ 회귀 가드 + recall + 암호화 + 파싱 오류 + CLI + 이미지 경고 + merge_fill + char_recall + 마커 가드), editable 설치, 검증 샘플 10종이 원본 엔진 출력과 글자 단위 동일(IDENTICAL)·recall 100% 확인. 표가 많은 문서까지 동일하여 python-hwpx 표 로직 차용은 불필요로 결론.
24
+ - **Upstage 교차검증(2026-06-06)**: 추가 5종(고사 원안·교육과정·평가계획·체크리스트)을 Upstage 파싱본과 정밀 대조. 내용 동등(체크리스트 100% 일치), 표·객관식 마커는 패키지 우위, 유일한 격차는 이미지 내 텍스트(OCR 영역). 이를 바탕으로 세 개선 반영: (A) `ConversionResult.image_count` + 이미지 존재 경고(self-recall 맹점 보완), (B) recall이 `<hp:t>` 텍스트 기준이라는 한계 문서화, (C) 병합 칸 채우기 옵션 `merge_fill`(라이브러리·`--merge-fill`).
25
+ - **전수 스윕 + 자가검증 강화(2026-06-06)**: 나머지 미사용 쌍 29종을 전수 대조. 원본 `<hp:t>` 대비 33종 전부 마커 Δ=0·글자 멀티셋 손실 0(=문자·마커 단위 완벽 보존)으로 패키지에 텍스트 결함 없음을 입증. Upstage 대비 낮아 보이는 char 커버리지는 전부 이미지 캡션(그래픽 손실, OCR 영역). 비-pic 시각객체 census 결과 OLE/수식/차트 0건이고 container·polygon은 순수 텍스트 문서에도 흔해 이미지 경고 확장은 오탐 위험으로 기각. 대신 자가검증 메트릭의 맹점을 보강: (D) `ConversionResult.char_recall`(글자 멀티셋 recall; 단어 집합 recall이 못 보는 반복·숫자·짧은 토큰 손실 감지), (E) 객관식 마커 보존 가드(①②③ 등이 출력에서 줄면 임계값 무관 정확 경고). 실문서 33종 char_recall 전부 1.0·마커 경고 0건(오탐 없음).
26
+ - **첫 커밋(2026-06-06)**: `109399e feat: hwpx-tomd 0.1.0 초기 구현`. 13파일/1850줄. `.gitignore`가 `*.hwp`/`*.hwpx` 차단, 저작물 파일 미포함 확인.
27
+ - **hwpx-automation 마이그레이션 완료(2026-06-06, 로드맵 7)**: `hwpx_edit.py`가 to-md 전용 렌더 함수군 8개를 제거하고 `cmd_to_md`를 `from hwpx_tomd import convert` 호출 래퍼로 교체(편집 명령 잔류). 출력 byte-IDENTICAL 검증, `--merge-fill` 추가. 변환 엔진이 이제 이 패키지에만 존재(분기 제거).
28
+ - **docparse 마이그레이션 완료(2026-06-06, 로드맵 8)**: `parsers/hwpx_local_parse.py`(출력 `_hwpxlocal.md`)가 이 패키지를 호출. HWPX 티어가 hwpx_local 우선 + Upstage 폴백으로 전환.
29
+ - **대기**: 공개(9, TestPyPI/PyPI), 멀티 디바이스 배포(10).
30
+ - 의존성은 `lxml`만 요구하며 설치 환경의 lxml 6.x와 정상 동작(`python-hwpx`의 `lxml<6` 핀 회피 확인).
31
+ - 사전 조사 결론: PyPI의 기존 패키지(hwpx2md, python-hwpx, hwp2md, pyhwpxlib 등) 중 세 결함을 동시에 해결하는 것이 없어 자체 엔진 추출이 정답으로 재확인됨.
32
+
33
+ ## 왜 만드는가 (요약)
34
+
35
+ `hwpx_edit.py`(현재 `tools/hwpx-automation/`)의 `--to-md` 파서 엔진이 2026-06-05 디버깅으로 세 결함(① `<hp:t>` tail 손실 ② 글상자 본문 누락 ③ 표 병합 무시)이 모두 수정되어 텍스트 완전성이 유료 Upstage 파서와 동급이 되었다. 이 엔진을 `hwpx-automation`과 `docparse` 두 스킬이 함께 쓰게 되면서, "복제하면 분기, 복제 안 하면 self-contained 위배"라는 충돌이 생겼다. 해법은 **엔진을 독립 패키지로 추출**하는 것이다. 코드가 한 곳에만 존재하므로 분기가 물리적으로 불가능하고, 두 스킬은 이 패키지의 소비자가 된다. 상세 의사결정 기록은 PLAN.md 1절.
36
+
37
+ ## 범위
38
+
39
+ - **In scope(읽기 전용)**: HWPX → Markdown/텍스트, 표 그리드 배치(`cellAddr`/`cellSpan`), 글상자 reading-order 수집, tail 보존, recall 자가검증, 암호화 감지·안내, 라이브러리 + CLI
40
+ - **Out of scope**: HWPX 편집(hwpx-automation 잔류), HWP 바이너리 직접 처리, 암호 해제, 표 구조화 데이터 반환(추후)
41
+
42
+ ## 아키텍처 핵심
43
+
44
+ 원본 `hwpx_edit.py`는 파싱·CLI 출력·`sys.exit`가 한 함수에 엉켜 있다. 라이브러리화의 핵심은 분리다.
45
+
46
+ - `src/hwpx_tomd/core.py`: 부작용 없는 순수 함수. 암호화는 예외(`HwpxEncryptedError`), recall은 반환 dataclass에 담는다(`print`/`sys.exit` 금지).
47
+ - `src/hwpx_tomd/cli.py`: argparse 얇은 계층. core 호출 + 예외→메시지·종료코드 변환 + recall 경고를 stderr 출력.
48
+
49
+ 공개 API 초안:
50
+ ```python
51
+ from hwpx_tomd import to_markdown, convert, HwpxEncryptedError
52
+ md = to_markdown("file.hwpx", cell_br=False) # str
53
+ result = convert("file.hwpx") # .markdown / .recall / .warnings
54
+ ```
55
+
56
+ ## 의존성
57
+
58
+ - **lxml만 필요** (to-md 경로는 표준 라이브러리 + `lxml.etree`만 사용). `python-hwpx`에 의존하지 않으므로 그 패키지의 `lxml<6` 핀 함정을 회피한다.
59
+ - Python 3.10+ (개발 3.12).
60
+
61
+ ## 이식 원본·참조 경로
62
+
63
+ | 자료 | 경로 |
64
+ |------|------|
65
+ | 원본 파서 엔진(이식 대상) | `C:/Users/pc/Windows-Projects/tools/hwpx-automation/hwpx_edit.py` (상단 헬퍼 + 221~556줄) |
66
+ | 디버깅 보고서(결함·검증 상세) | `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/hwpx_edit_디버깅가능성_검토_2026-06-05.md` |
67
+ | 검증 샘플(워크시트·이질 5종) | `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/` |
68
+ | 소비 스킬 1 | `C:/Users/pc/Windows-Projects/tools/hwpx-automation/SKILL.md` |
69
+ | 소비 스킬 2 | `C:/Users/pc/Windows-Projects/tools/docparse/SKILL.md` |
70
+
71
+ 이식할 함수 목록과 줄 위치는 PLAN.md 3절 표를 참조.
72
+
73
+ ## 개발 명령어
74
+
75
+ ```bash
76
+ # editable 설치 (테스트 의존 포함)
77
+ pip install -e ".[test]"
78
+
79
+ # 테스트
80
+ pytest
81
+
82
+ # CLI 실행
83
+ hwpx-tomd file.hwpx
84
+ hwpx-tomd file.hwpx -o out.md --cell-br
85
+ ```
86
+
87
+ ## 테스트 데이터 주의 (중요)
88
+
89
+ 공개 repo가 될 수 있으므로 **출판사 워크시트·학교 고사 원안 등 저작물·민감 문서를 `tests/data/`에 넣지 않는다.** 결함 ①②③을 재현하는 직접 제작한 합성 HWPX만 픽스처로 둔다. 출판사·학교 자료 검증은 로컬에서만.
90
+
91
+ ## 글로벌 작업 규칙 (반드시 준수)
92
+
93
+ 이 컴퓨터의 모든 작업에 적용되는 사용자 전역 규칙(`~/.claude/CLAUDE.md`)이다.
94
+
95
+ - **문서에 em dash(—)·en dash(–) 금지.** 콜론(:), 괄호, 물결표(~), 가운뎃점(·)으로 대체.
96
+ - **날짜에 요일 병기 시 반드시 검증.** 추론 금지. `python -c "import datetime; print(datetime.date(2026,6,5).strftime('%A'))"` 또는 PowerShell `(Get-Date "2026-06-05").DayOfWeek`. 매핑 Mon=월 Tue=화 Wed=수 Thu=목 Fri=금 Sat=토 Sun=일.
97
+ - **작업 완료 시 TTS 요약**을 `C:/Users/pc/.claude/tts-summary.txt`에 Write 도구로 작성(한국어, 직접 서술체).
98
+ - **커밋·push는 사용자가 요청할 때만.** 커밋 메시지 끝에 `Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>`.
99
+ - 코드 작성 시 최신 문서 확인(Context7/chub 등). 학습 데이터에 의존하지 않는다.
100
+
101
+ ## 관련 스킬
102
+
103
+ | 작업 | 스킬 |
104
+ |------|------|
105
+ | HWP→HWPX 변환(입력 준비), HWPX 편집 | `/hwpx-automation` |
106
+ | 문서 파싱(PDF·스캔·교차검증) | `/docparse` |
@@ -0,0 +1,100 @@
1
+ # CLAUDE.md
2
+
3
+ 이 파일은 Claude Code(claude.ai/code)가 이 저장소에서 작업할 때 참고하는 가이드다.
4
+
5
+ ## 프로젝트 개요
6
+
7
+ `hwpx-tomd`: HWPX 파일을 외부 API 없이 로컬에서 Markdown/텍스트로 변환하는 가볍고 독립적인 Python 패키지(읽기 전용). 라이브러리 API와 CLI를 함께 제공한다.
8
+
9
+ - 사용자 호칭: "헌용 쌤" (영어: "Hunyong")
10
+ - 작성자: 신명중 김헌용 교사(시각장애 중등 영어 교사, 장교조 위원장). 학교·장교조 업무에서 HWPX를 매우 자주 다룬다.
11
+ - 라이선스(예정): MIT
12
+
13
+ ## 현재 상태: 코어 구현·검증 완료 + 두 스킬 마이그레이션 완료
14
+
15
+ 패키지 코어가 구현·검증되었고(2026-06-05), 첫 git 커밋과 두 소비 스킬 통합까지 마쳤다(2026-06-06). 배경·설계·로드맵 상세는 [`PLAN.md`](PLAN.md) 참조.
16
+
17
+ - **완료**: 이름 확정(`hwpx-tomd`, PyPI 가용 확인), src layout 스캐폴드, `core.py`(엔진 이식·라이브러리화), `cli.py`, 테스트 33개(결함 ①②③ 회귀 가드 + recall + 암호화 + 파싱 오류 + CLI + 이미지 경고 + merge_fill + char_recall + 마커 가드), editable 설치, 검증 샘플 10종이 원본 엔진 출력과 글자 단위 동일(IDENTICAL)·recall 100% 확인. 표가 많은 문서까지 동일하여 python-hwpx 표 로직 차용은 불필요로 결론.
18
+ - **Upstage 교차검증(2026-06-06)**: 추가 5종(고사 원안·교육과정·평가계획·체크리스트)을 Upstage 파싱본과 정밀 대조. 내용 동등(체크리스트 100% 일치), 표·객관식 마커는 패키지 우위, 유일한 격차는 이미지 내 텍스트(OCR 영역). 이를 바탕으로 세 개선 반영: (A) `ConversionResult.image_count` + 이미지 존재 경고(self-recall 맹점 보완), (B) recall이 `<hp:t>` 텍스트 기준이라는 한계 문서화, (C) 병합 칸 채우기 옵션 `merge_fill`(라이브러리·`--merge-fill`).
19
+ - **전수 스윕 + 자가검증 강화(2026-06-06)**: 나머지 미사용 쌍 29종을 전수 대조. 원본 `<hp:t>` 대비 33종 전부 마커 Δ=0·글자 멀티셋 손실 0(=문자·마커 단위 완벽 보존)으로 패키지에 텍스트 결함 없음을 입증. Upstage 대비 낮아 보이는 char 커버리지는 전부 이미지 캡션(그래픽 손실, OCR 영역). 비-pic 시각객체 census 결과 OLE/수식/차트 0건이고 container·polygon은 순수 텍스트 문서에도 흔해 이미지 경고 확장은 오탐 위험으로 기각. 대신 자가검증 메트릭의 맹점을 보강: (D) `ConversionResult.char_recall`(글자 멀티셋 recall; 단어 집합 recall이 못 보는 반복·숫자·짧은 토큰 손실 감지), (E) 객관식 마커 보존 가드(①②③ 등이 출력에서 줄면 임계값 무관 정확 경고). 실문서 33종 char_recall 전부 1.0·마커 경고 0건(오탐 없음).
20
+ - **첫 커밋(2026-06-06)**: `109399e feat: hwpx-tomd 0.1.0 초기 구현`. 13파일/1850줄. `.gitignore`가 `*.hwp`/`*.hwpx` 차단, 저작물 파일 미포함 확인.
21
+ - **hwpx-automation 마이그레이션 완료(2026-06-06, 로드맵 7)**: `hwpx_edit.py`가 to-md 전용 렌더 함수군 8개를 제거하고 `cmd_to_md`를 `from hwpx_tomd import convert` 호출 래퍼로 교체(편집 명령 잔류). 출력 byte-IDENTICAL 검증, `--merge-fill` 추가. 변환 엔진이 이제 이 패키지에만 존재(분기 제거).
22
+ - **docparse 마이그레이션 완료(2026-06-06, 로드맵 8)**: `parsers/hwpx_local_parse.py`(출력 `_hwpxlocal.md`)가 이 패키지를 호출. HWPX 티어가 hwpx_local 우선 + Upstage 폴백으로 전환.
23
+ - **대기**: 공개(9, TestPyPI/PyPI), 멀티 디바이스 배포(10).
24
+ - 의존성은 `lxml`만 요구하며 설치 환경의 lxml 6.x와 정상 동작(`python-hwpx`의 `lxml<6` 핀 회피 확인).
25
+ - 사전 조사 결론: PyPI의 기존 패키지(hwpx2md, python-hwpx, hwp2md, pyhwpxlib 등) 중 세 결함을 동시에 해결하는 것이 없어 자체 엔진 추출이 정답으로 재확인됨.
26
+
27
+ ## 왜 만드는가 (요약)
28
+
29
+ `hwpx_edit.py`(현재 `tools/hwpx-automation/`)의 `--to-md` 파서 엔진이 2026-06-05 디버깅으로 세 결함(① `<hp:t>` tail 손실 ② 글상자 본문 누락 ③ 표 병합 무시)이 모두 수정되어 텍스트 완전성이 유료 Upstage 파서와 동급이 되었다. 이 엔진을 `hwpx-automation`과 `docparse` 두 스킬이 함께 쓰게 되면서, "복제하면 분기, 복제 안 하면 self-contained 위배"라는 충돌이 생겼다. 해법은 **엔진을 독립 패키지로 추출**하는 것이다. 코드가 한 곳에만 존재하므로 분기가 물리적으로 불가능하고, 두 스킬은 이 패키지의 소비자가 된다. 상세 의사결정 기록은 PLAN.md 1절.
30
+
31
+ ## 범위
32
+
33
+ - **In scope(읽기 전용)**: HWPX → Markdown/텍스트, 표 그리드 배치(`cellAddr`/`cellSpan`), 글상자 reading-order 수집, tail 보존, recall 자가검증, 암호화 감지·안내, 라이브러리 + CLI
34
+ - **Out of scope**: HWPX 편집(hwpx-automation 잔류), HWP 바이너리 직접 처리, 암호 해제, 표 구조화 데이터 반환(추후)
35
+
36
+ ## 아키텍처 핵심
37
+
38
+ 원본 `hwpx_edit.py`는 파싱·CLI 출력·`sys.exit`가 한 함수에 엉켜 있다. 라이브러리화의 핵심은 분리다.
39
+
40
+ - `src/hwpx_tomd/core.py`: 부작용 없는 순수 함수. 암호화는 예외(`HwpxEncryptedError`), recall은 반환 dataclass에 담는다(`print`/`sys.exit` 금지).
41
+ - `src/hwpx_tomd/cli.py`: argparse 얇은 계층. core 호출 + 예외→메시지·종료코드 변환 + recall 경고를 stderr 출력.
42
+
43
+ 공개 API 초안:
44
+ ```python
45
+ from hwpx_tomd import to_markdown, convert, HwpxEncryptedError
46
+ md = to_markdown("file.hwpx", cell_br=False) # str
47
+ result = convert("file.hwpx") # .markdown / .recall / .warnings
48
+ ```
49
+
50
+ ## 의존성
51
+
52
+ - **lxml만 필요** (to-md 경로는 표준 라이브러리 + `lxml.etree`만 사용). `python-hwpx`에 의존하지 않으므로 그 패키지의 `lxml<6` 핀 함정을 회피한다.
53
+ - Python 3.10+ (개발 3.12).
54
+
55
+ ## 이식 원본·참조 경로
56
+
57
+ | 자료 | 경로 |
58
+ |------|------|
59
+ | 원본 파서 엔진(이식 대상) | `C:/Users/pc/Windows-Projects/tools/hwpx-automation/hwpx_edit.py` (상단 헬퍼 + 221~556줄) |
60
+ | 디버깅 보고서(결함·검증 상세) | `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/hwpx_edit_디버깅가능성_검토_2026-06-05.md` |
61
+ | 검증 샘플(워크시트·이질 5종) | `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/` |
62
+ | 소비 스킬 1 | `C:/Users/pc/Windows-Projects/tools/hwpx-automation/SKILL.md` |
63
+ | 소비 스킬 2 | `C:/Users/pc/Windows-Projects/tools/docparse/SKILL.md` |
64
+
65
+ 이식할 함수 목록과 줄 위치는 PLAN.md 3절 표를 참조.
66
+
67
+ ## 개발 명령어
68
+
69
+ ```bash
70
+ # editable 설치 (테스트 의존 포함)
71
+ pip install -e ".[test]"
72
+
73
+ # 테스트
74
+ pytest
75
+
76
+ # CLI 실행
77
+ hwpx-tomd file.hwpx
78
+ hwpx-tomd file.hwpx -o out.md --cell-br
79
+ ```
80
+
81
+ ## 테스트 데이터 주의 (중요)
82
+
83
+ 공개 repo가 될 수 있으므로 **출판사 워크시트·학교 고사 원안 등 저작물·민감 문서를 `tests/data/`에 넣지 않는다.** 결함 ①②③을 재현하는 직접 제작한 합성 HWPX만 픽스처로 둔다. 출판사·학교 자료 검증은 로컬에서만.
84
+
85
+ ## 글로벌 작업 규칙 (반드시 준수)
86
+
87
+ 이 컴퓨터의 모든 작업에 적용되는 사용자 전역 규칙(`~/.claude/CLAUDE.md`)이다.
88
+
89
+ - **문서에 em dash(—)·en dash(–) 금지.** 콜론(:), 괄호, 물결표(~), 가운뎃점(·)으로 대체.
90
+ - **날짜에 요일 병기 시 반드시 검증.** 추론 금지. `python -c "import datetime; print(datetime.date(2026,6,5).strftime('%A'))"` 또는 PowerShell `(Get-Date "2026-06-05").DayOfWeek`. 매핑 Mon=월 Tue=화 Wed=수 Thu=목 Fri=금 Sat=토 Sun=일.
91
+ - **작업 완료 시 TTS 요약**을 `C:/Users/pc/.claude/tts-summary.txt`에 Write 도구로 작성(한국어, 직접 서술체).
92
+ - **커밋·push는 사용자가 요청할 때만.** 커밋 메시지 끝에 `Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>`.
93
+ - 코드 작성 시 최신 문서 확인(Context7/chub 등). 학습 데이터에 의존하지 않는다.
94
+
95
+ ## 관련 스킬
96
+
97
+ | 작업 | 스킬 |
98
+ |------|------|
99
+ | HWP→HWPX 변환(입력 준비), HWPX 편집 | `/hwpx-automation` |
100
+ | 문서 파싱(PDF·스캔·교차검증) | `/docparse` |
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hunyong Kim (김헌용)
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.
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: hwpx-tomd
3
+ Version: 0.1.0
4
+ Summary: Convert HWPX (Hancom Office) documents to Markdown locally, with no external API. Read-only.
5
+ Project-URL: Homepage, https://github.com/Engccer/hwpx-tomd
6
+ Project-URL: Repository, https://github.com/Engccer/hwpx-tomd
7
+ Project-URL: Issues, https://github.com/Engccer/hwpx-tomd/issues
8
+ Author-email: Hunyong Kim <engccer@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: converter,document,hancom,hwp,hwpx,korean,markdown,ooxml
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Natural Language :: Korean
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Office/Business
22
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: lxml>=4.9
25
+ Provides-Extra: test
26
+ Requires-Dist: pytest>=7.0; extra == 'test'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # hwpx-tomd
30
+
31
+ HWPX(한컴오피스 한글) 문서를 외부 API 없이 **로컬에서 Markdown/텍스트로 변환**하는 가볍고 독립적인 Python 패키지입니다. 읽기 전용이며 `lxml`에만 의존합니다.
32
+
33
+ 문서 파싱 서비스(Upstage 등)에 보내지 않고, 인터넷 연결 없이, 무료로 HWPX 본문을 추출합니다. 텍스트 완전성은 유료 파서와 동급 수준으로 검증되었습니다.
34
+
35
+ ## 특징
36
+
37
+ - **본문·표·글상자**를 모두 추출: 글상자(drawText) 내부 본문을 reading order로 수집합니다.
38
+ - **표 병합 보존**: `cellAddr`/`cellSpan` 그리드 기반으로 가로·세로 병합을 정확히 배치합니다.
39
+ - **tail 텍스트 보존**: `<hp:t>` 내부 `<hp:tab>`/`<hp:lineBreak>` 뒤에 오는 텍스트(객관식 선택지 ②③⑤ 등)를 잃지 않습니다.
40
+ - **자가검증 recall**: 변환 후 원본 대비 **단어 recall**과 **글자 멀티셋 recall**을 함께 계산해 조용한 누락을 경고합니다(글자 recall은 반복·숫자·짧은 토큰 손실까지 잡습니다).
41
+ - **객관식 마커 보존 가드**: ①②③ 같은 선택지 마커가 렌더링 중 하나라도 빠지면 임계값과 무관하게 경고합니다(시험 문항 무결성).
42
+ - **이미지 존재 경고**: 본문에 그림이 있으면 그 개수를 알리고, 그래픽에 박힌 텍스트가 누락될 수 있음을 경고합니다.
43
+ - **병합 칸 채우기 옵션**: `merge_fill=True`로 병합 셀 값을 모든 칸에 채워 행 단위 파싱·LLM 입력에 맞춥니다.
44
+ - **암호화 감지**: AES 암호화 HWPX를 자동 감지하고 복호화 방법을 안내합니다.
45
+ - **가벼운 의존성**: `lxml`만 필요합니다(`python-hwpx`에 의존하지 않아 `lxml<6` 핀 충돌을 피합니다).
46
+
47
+ ## 설치
48
+
49
+ ```bash
50
+ pip install hwpx-tomd
51
+ ```
52
+
53
+ 로컬 개발:
54
+
55
+ ```bash
56
+ pip install -e .
57
+ ```
58
+
59
+ ## 라이브러리 사용법
60
+
61
+ ```python
62
+ from hwpx_tomd import to_markdown, convert, HwpxEncryptedError
63
+
64
+ # 1) 가장 간단: Markdown 문자열
65
+ md = to_markdown("file.hwpx")
66
+ md = to_markdown("file.hwpx", cell_br=True) # 셀 내부 문단을 <br>로 구분
67
+ md = to_markdown("file.hwpx", merge_fill=True) # 병합 칸을 같은 값으로 채움
68
+
69
+ # 2) 자가검증 결과까지: ConversionResult
70
+ result = convert("file.hwpx")
71
+ print(result.markdown) # str
72
+ print(result.recall) # float (단어 집합 recall, 0.0~1.0)
73
+ print(result.char_recall) # float (글자 멀티셋 recall, 0.0~1.0; 더 엄격)
74
+ print(result.warnings) # list[str]
75
+ print(result.image_count) # int (본문 이미지 개수)
76
+
77
+ # 3) 암호화 파일: 예외
78
+ try:
79
+ to_markdown("encrypted.hwpx")
80
+ except HwpxEncryptedError as e:
81
+ print(e) # 복호화 안내 포함
82
+ ```
83
+
84
+ ### recall의 한계 (꼭 읽어 주세요)
85
+
86
+ `recall`은 **section XML의 텍스트(`<hp:t>`) 기준**입니다. 그림(`<hp:pic>`) 안에 그래픽으로 박힌 텍스트(출판사 제목 이미지, 도표 캡션 등)는 애초에 분모에 없으므로, **recall이 1.0이어도 이미지 속 글자는 누락될 수 있습니다.** 이미지 내 텍스트는 OCR 영역이라 본 패키지(텍스트 추출)의 범위를 벗어납니다.
87
+
88
+ 이 맹점을 보완하기 위해 `convert()`는 본문 이미지 개수를 `result.image_count`로 반환하고, 이미지가 있으면 `result.warnings`에 안내를 추가합니다. 이미지 속 텍스트까지 필요하면 OCR 기반 파서(Upstage 등)를 함께 쓰세요.
89
+
90
+ ### 단어 recall vs 글자 recall vs 마커 가드
91
+
92
+ `recall`(단어 집합)은 같은 단어의 **반복 손실**이나 **숫자·1~2글자 토큰 손실**을 구조적으로 보지 못합니다(집합이라 한 번만 세고, 짧은 토큰은 단어 정규식이 거릅니다). 이를 `char_recall`(한글·영문·숫자 **글자 멀티셋** recall)이 보완합니다. 출력이 원본 글자를 모두 포함하면 1.0이고, 반복·숫자가 빠지면 1.0 미만으로 떨어집니다.
93
+
94
+ 객관식 선택지 마커(①②③ 등 둘러싸인 영숫자)는 시험 문항에서 치명적이라 별도 **마커 보존 가드**가 있습니다. 원본 `<hp:t>`에 있던 마커가 출력에서 하나라도 줄면, recall 임계값과 무관하게 어떤 마커가 몇 개 빠졌는지 경고합니다. 큰 문서에서 마커 한두 개 손실은 recall 임계값에 안 걸려 묻히기 쉬운데, 이 가드는 정확 비교라 그런 누락도 잡습니다. (실측: 실제 시험·교육과정 문서 33종에서 두 recall 모두 1.0, 마커 손실 0건으로 오탐이 없음을 확인했습니다.)
95
+
96
+ ### `merge_fill` 옵션
97
+
98
+ 기본값(`merge_fill=False`)은 표 병합으로 덮인 칸을 빈 칸으로 두어 GFM 열 정렬을 보존합니다. `merge_fill=True`이면 병합 시작 칸의 값을 덮인 칸에도 채워, 모든 행이 자족적이 됩니다(정보량은 동일). 교육과정·평가계획처럼 머리 셀이 세로로 길게 병합된 표를 **행 단위로 읽거나 LLM에 입력**할 때 유용합니다.
99
+
100
+ ## CLI 사용법
101
+
102
+ ```bash
103
+ hwpx-tomd file.hwpx # file.md 생성
104
+ hwpx-tomd file.hwpx -o out.md # 출력 경로 지정
105
+ hwpx-tomd file.hwpx --stdout # 표준출력 (파이프용)
106
+ hwpx-tomd file.hwpx --cell-br # 표 셀 내부 문단을 <br>로 구분
107
+ hwpx-tomd file.hwpx --merge-fill # 표 병합 칸을 같은 값으로 채움
108
+ ```
109
+
110
+ 종료 코드: `0` 성공, `1` 일반 오류, `2` 잘못된 인자/파일 없음, `3` 암호화된 HWPX.
111
+
112
+ ## 범위
113
+
114
+ - **In scope (읽기 전용)**: HWPX → Markdown/텍스트, 표 그리드 배치, 병합 칸 채우기 옵션, 글상자 수집, tail 보존, 자가검증(단어 recall · 글자 멀티셋 recall · 마커 보존 가드), 이미지 존재 경고, 암호화 감지·안내.
115
+ - **Out of scope**: HWPX 편집, HWP(구형 바이너리) 직접 처리, 암호 해제, **이미지 내 텍스트 OCR**, 표의 구조화 데이터 반환(추후 검토).
116
+
117
+ HWP(구형 바이너리)는 먼저 HWPX로 변환한 뒤 입력하세요. HWPX 편집이 필요하면 별도 도구를 사용하세요.
118
+
119
+ ## 라이선스
120
+
121
+ [MIT](LICENSE) © 2026 Hunyong Kim (김헌용)
@@ -0,0 +1,220 @@
1
+ # hwpx-tomd 패키지 개발·배포 계획
2
+
3
+ 작성일: 2026-06-05(금)
4
+ 상태: 계획 수립 완료, 구현 착수 전
5
+
6
+ ## 0. 한 줄 정의
7
+
8
+ HWPX 파일을 외부 API 없이 로컬에서 Markdown/텍스트로 변환하는 가볍고 독립적인 Python 패키지(읽기 전용). 라이브러리 API와 CLI를 함께 제공한다.
9
+
10
+ ## 1. 배경: 왜 이 패키지를 만드는가
11
+
12
+ ### 출발점
13
+ `hwpx_edit.py`(현재 `tools/hwpx-automation/` 안, 976줄)의 `--to-md` 파서 엔진은 2026-06-05 실측 디버깅으로 세 가지 결함이 모두 수정되어, HWPX 텍스트 추출 완전성이 유료 Upstage 파서와 동급 또는 그 이상으로 검증되었다. 그 결과 이 엔진을 두 스킬이 함께 쓰게 되었다.
14
+
15
+ - `hwpx-automation` 스킬: HWPX 읽기·편집
16
+ - `docparse` 스킬: 문서 파싱(HWPX는 그동안 Upstage API로 처리했으나, 이제 무료·오프라인 로컬 추출 경로가 생김)
17
+
18
+ ### 문제: self-contained 원칙과 DRY의 충돌
19
+ 한 코드를 두 스킬이 쓰게 되면 두 가지 원칙이 충돌한다.
20
+
21
+ - self-contained: 각 스킬은 자기 폴더만으로 동작해야 한다(복제본이 있어야 함)
22
+ - DRY/단일 소스: 같은 코드가 여러 곳에 있으면 분기·관리 부담이 생긴다(복제본이 없어야 함)
23
+
24
+ ### 선택한 해법: 독립 패키지(분기를 관리하는 게 아니라 제거)
25
+ 파서 엔진을 별도 설치형 패키지로 추출하면 코드가 한 곳(패키지)에만 존재한다. 복제본 자체가 없으므로 분기가 물리적으로 불가능하고, 동기화 스크립트조차 필요 없다. 두 스킬은 이 패키지를 바라보는 두 소비자가 될 뿐이다.
26
+
27
+ 추가로, 김헌용 교사는 학교·장교조 업무에서 HWPX를 매우 자주 다루므로 "스킬에 묶이지 않은 독립 변환 도구"의 실수요가 크고, 성능 고도화 후 대중 공개(PyPI, MIT) 가능성도 있다. `hwpx-automation`은 이미 공개 git repo(`github.com/Engccer/hwpx-automation`, MIT)이고 npm 패키지 배포 경험(`@dodo-planet/cli`, `@tobilu/qmd`)도 있어 배포 인프라 부담이 작다.
28
+
29
+ ### 의사결정 기록 (검토했던 대안)
30
+ | 안 | 내용 | 채택 여부 | 사유 |
31
+ |----|------|----------|------|
32
+ | A. 벤더링 + sync 자동화 | 캐노니컬에서 docparse로 코드를 복제하고 sync 스크립트로 동기화 | 미채택 | 복제본이 존재해 sync가 필요. 분기 부담을 줄이지만 0은 아님 |
33
+ | B. 런타임 의존 + 폴백 | docparse가 실행 시 hwpx-automation 경로를 탐색해 호출, 없으면 Upstage | 미채택 | self-contained 약화(미설치 디바이스에서 로컬 파싱 불가) |
34
+ | **C. 독립 패키지** | 파서 엔진을 pip 설치형 패키지로 추출, 두 스킬이 의존 | **채택** | 분기를 완전 제거. 독립 활용·대중 공개 경로 확보 |
35
+
36
+ 범위는 **(i) 읽기 전용**으로 확정했다. 편집 기능은 docparse가 쓰지 않고, 가벼운 단일 책임 도구로서 대중 공개 시 이해가 쉽기 때문이다.
37
+
38
+ ## 2. 범위
39
+
40
+ ### In scope (읽기 전용)
41
+ - HWPX → Markdown 변환 (본문 문단, 표, 글상자)
42
+ - HWPX → 평문 텍스트 추출
43
+ - 표를 `cellAddr`/`cellSpan` 기반 그리드로 정확히 배치(가로·세로 병합 보존)
44
+ - 병합 칸 채우기 옵션(`merge_fill`): 병합으로 덮인 칸을 시작 칸 값으로 채워 행 단위 파싱·LLM 입력에 적합(기본 off=GFM 정렬 보존)
45
+ - 글상자(drawText) 내부 본문을 reading-order로 수집
46
+ - `<hp:t>` tail 텍스트 보존(객관식 선택지 누락 방지)
47
+ - 변환 후 자가검증 3종: 단어 집합 recall + 글자 멀티셋 recall(`char_recall`, 반복·숫자·짧은 토큰 손실 감지) + 객관식 마커 보존 가드(①②③ 등이 줄면 임계값 무관 경고)
48
+ - 본문 이미지 개수 집계(`image_count`) + 이미지 존재 경고(이미지 내 텍스트 누락 가능성 고지, recall 맹점 보완)
49
+ - 암호화(AES) HWPX 자동 감지 후 명확한 예외/안내
50
+ - 라이브러리 API + CLI
51
+
52
+ ### Out of scope (이번 패키지에서 제외)
53
+ - HWPX 편집(set-cell, find-replace, split-cell, delete-rows 등): `hwpx-automation` 스킬에 잔류
54
+ - HWP(구형 바이너리) 직접 처리: `hwpx-automation`의 `hwp2hwpx.bat`로 변환 후 입력
55
+ - 암호 해제(복호화): 한컴 COM 필요, 범위 밖(감지·안내까지만)
56
+ - 이미지 내 텍스트 OCR: 텍스트 추출 범위 밖(존재 경고까지만). 필요 시 Upstage 등 OCR 파서 병용
57
+ - 표의 구조화 데이터 반환(리스트/딕셔너리): 추후 검토(YAGNI)
58
+
59
+ ## 3. 이식할 코드: hwpx_edit.py 파서 엔진
60
+
61
+ 원본 경로: `C:/Users/pc/Windows-Projects/tools/hwpx-automation/hwpx_edit.py`
62
+
63
+ ### 추출 대상 함수·상수 (읽기 전용 코어)
64
+ | 구분 | 항목 | 원본 위치(줄) |
65
+ |------|------|--------------|
66
+ | 상수 | `NS`(네임스페이스), `ENCRYPTION_HINT` | 24~32, 67~81 |
67
+ | 헬퍼 | `localname`, `t_full_text`, `is_encrypted_hwpx`, `get_output_path` | 221~243, 51~64, 35~48 |
68
+ | 표 접근 | `get_table_rows`, `get_row_cells`, `get_cell_addr`, `get_cell_span` | 211~218, 288~293, 274~285 |
69
+ | 셀 텍스트 | `get_cell_text`, `get_cell_paragraph_texts` | 246~271 |
70
+ | 렌더 | `render_cell_lines`, `render_table_md`, `render_block_lines`, `table_to_markdown` | 357~481, 328~354 |
71
+ | 엔트리 | `cmd_to_md` (→ 라이브러리 `to_markdown`/`convert`로 리네임·재설계) | 484~556 |
72
+
73
+ `cmd_info`, `cmd_set_cell` 등 편집 명령군(558~872줄)과 `main()` CLI(884줄~)는 이식하지 않는다.
74
+
75
+ ### 이미 수정된 세 결함 (반드시 회귀 테스트로 보존)
76
+ | 결함 | 증상 | 수정 | 효과 |
77
+ |------|------|------|------|
78
+ | ① `<hp:t>` tail 손실 | `t_elem.text`만 읽어 내부 `<hp:tab>`/`<hp:lineBreak>`의 tail에 든 텍스트(객관식 선택지 ②③⑤ 등)를 잃음 | `t_full_text`가 `itertext`로 전체 수집, tab/lineBreak는 공백 치환 | 선택지 전부 보존 |
79
+ | ② 글상자(drawText) 본문 누락 | `cmd_to_md`가 최상위 p의 `run>t` 직속과 `.//tbl`만 수집해 글상자 내부 본문을 통째로 누락 | `render_block_lines`의 reading-order 재귀 순회 | Workbook류 recall 17~33% → 100% |
80
+ | ③ rowSpan/colSpan 미처리 | span을 `cellAddr`에서 잘못 읽어 항상 (1,1) 반환 → 모든 병합 무시, 표 정렬 붕괴 | `get_cell_span`이 별도 `<hp:cellSpan>`에서 읽고, `render_table_md`가 `cellAddr`/`cellSpan` 그리드 배치 | 표 정렬 복원 |
81
+
82
+ 자가검증: 변환 후 원본 `<hp:t>` 단어 집합 대비 출력 recall을 계산하고 0.95 미만이면 경고(조용한 누락 방지). 단어 추출 정규식 `[A-Za-z]{3,}|[가-힣]{2,}`.
83
+
84
+ ### 검증 데이터 (회귀 기준)
85
+ - 워크시트 5종(동아출판 윤정미 22개정, 2026 1학년 1학기 기말 워크시트) + 이질 5종(고사 원안·평가계획·체크리스트·채점기준표·읽기 안내문) 단어 recall 100%, 회귀 없음
86
+ - 암호화 배포본(고사 원안 일부, AES-256-CBC)은 파싱 불가가 정상이며 자동 감지로 명확히 안내됨
87
+ - 상세 보고서: `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/hwpx_edit_디버깅가능성_검토_2026-06-05.md`
88
+
89
+ ## 4. 아키텍처: 라이브러리 + CLI 분리
90
+
91
+ 현재 `hwpx_edit.py`는 파싱·CLI 출력·`sys.exit`가 한 함수에 엉켜 있다. 라이브러리화의 핵심 리팩토링은 다음 분리다.
92
+
93
+ - **core.py(순수 함수)**: 부작용 없이 값만 반환. 암호화는 `print`+`sys.exit`이 아니라 예외(`HwpxEncryptedError`)로, recall은 `print`가 아니라 반환 결과(dataclass)에 담는다.
94
+ - **cli.py(얇은 계층)**: argparse로 입력을 받아 core를 호출하고, 예외를 잡아 사용자 메시지·종료 코드로 변환하며, recall 경고를 stderr로 출력한다.
95
+
96
+ 이 분리로 라이브러리 사용자는 깔끔한 반환값을 얻고, CLI 사용자는 기존과 같은 동작을 얻는다.
97
+
98
+ ## 5. 공개 API 설계 (초안)
99
+
100
+ ```python
101
+ from hwpx_tomd import to_markdown, convert, HwpxEncryptedError
102
+
103
+ # 1) 가장 간단: Markdown 문자열 반환
104
+ md: str = to_markdown("file.hwpx") # cell_br=False 기본
105
+ md: str = to_markdown("file.hwpx", cell_br=True) # 셀 내부 문단을 <br>로 구분
106
+
107
+ # 2) 자가검증 결과까지 필요할 때: dataclass 반환
108
+ result = convert("file.hwpx")
109
+ # result.markdown : str
110
+ # result.recall : float (원본 대비 단어 recall, 0.0~1.0)
111
+ # result.warnings : list[str] (recall<0.95 등)
112
+
113
+ # 3) 암호화 파일: 예외
114
+ try:
115
+ to_markdown("encrypted.hwpx")
116
+ except HwpxEncryptedError as e:
117
+ print(e) # 복호화 안내 메시지 포함
118
+ ```
119
+
120
+ CLI:
121
+ ```bash
122
+ hwpx-tomd file.hwpx # file.md 생성 (또는 stdout 옵션)
123
+ hwpx-tomd file.hwpx -o out.md
124
+ hwpx-tomd file.hwpx --cell-br
125
+ hwpx-tomd file.hwpx --stdout # 파이프용
126
+ ```
127
+
128
+ ## 6. 패키지 구조 (src layout)
129
+
130
+ ```
131
+ hwpx-tomd/
132
+ ├── pyproject.toml # 빌드·메타데이터·[project.scripts] 엔트리
133
+ ├── README.md # 사용법·설치·예제 (공개 대비)
134
+ ├── LICENSE # MIT
135
+ ├── CLAUDE.md # 작업 가이드 (이미 작성됨)
136
+ ├── PLAN.md # 이 문서
137
+ ├── src/
138
+ │ └── hwpx_tomd/
139
+ │ ├── __init__.py # 공개 API export (to_markdown, convert, 예외, __version__)
140
+ │ ├── core.py # 파서 엔진 (이식·라이브러리화)
141
+ │ ├── cli.py # argparse CLI
142
+ │ └── _version.py # 단일 버전 출처
143
+ └── tests/
144
+ ├── conftest.py
145
+ ├── data/ # 합성 HWPX 픽스처 + 기대 Markdown (골든)
146
+ └── test_to_markdown.py
147
+ ```
148
+
149
+ CLI 엔트리포인트는 `pyproject.toml`의 `[project.scripts]`에 `hwpx-tomd = "hwpx_tomd.cli:main"`으로 등록한다.
150
+
151
+ ## 7. 의존성
152
+
153
+ - **lxml만 필요**: to-md 경로는 표준 라이브러리(`zipfile`, `os`, `sys`, `re`) + `lxml.etree`만 사용한다. `python-hwpx`에는 의존하지 않는다.
154
+ - 이점: `python-hwpx`가 도입한 `lxml<6` 핀 함정(과거 실측 사례)을 원천 회피한다. `pyproject.toml`에서 `lxml>=4.9`처럼 넉넉히 잡는다.
155
+ - Python 3.10+ 권장(dataclass·타입힌트). 개발 환경은 3.12.
156
+
157
+ ## 8. 테스트 전략
158
+
159
+ - **골든 파일 테스트**: 입력 HWPX → 기대 Markdown 비교. 결함 ①②③ 각각을 재현하는 최소 픽스처를 포함(선택지 tail, 글상자, 병합 표).
160
+ - **recall 회귀 가드**: 각 픽스처 변환 결과의 recall ≥ 0.95 단언.
161
+ - **암호화 경로**: 암호화 HWPX 픽스처에서 `HwpxEncryptedError` 발생 단언.
162
+ - **테스트 데이터 주의(중요)**: 공개 repo가 될 수 있으므로 출판사 워크시트·학교 고사 원안 등 저작물·민감 문서는 테스트 픽스처에 넣지 않는다. 결함을 재현하는 **직접 제작한 합성 HWPX**만 `tests/data/`에 둔다. 출판사·학교 자료를 이용한 검증은 로컬에서만 수행한다.
163
+
164
+ ## 9. 두 소비 스킬 마이그레이션
165
+
166
+ ### hwpx-automation 스킬
167
+ - `hwpx_edit.py`의 to-md 함수군을 제거하고 `from hwpx_tomd import to_markdown` 호출로 교체.
168
+ - `--to-md` CLI 동작·출력 위치는 하위호환 유지(기존 사용자·다른 스킬이 의존).
169
+ - 편집 명령군은 그대로 잔류.
170
+ - 의존성에 `hwpx-tomd` 추가. 의존 방향은 단방향(hwpx-tomd는 hwpx-automation을 모름).
171
+
172
+ ### docparse 스킬
173
+ - `parsers/hwpx_local_parse.py` 신규: 다른 `*_parse.py`와 동일한 인터페이스(`python hwpx_local_parse.py "<파일>"` → `_hwpxlocal.md`). 내부에서 `hwpx_tomd` 호출.
174
+ - `SKILL.md` HWPX 티어 갱신: "단순 텍스트 추출은 무료·오프라인 `hwpx_tomd` 우선, 시각적 배치 재현이 중요하거나 recall<95% 경고가 나는 문서는 Upstage." (현재도 안내 문구는 이 방향으로 정리되어 있으니 실제 파서 편입으로 확장.)
175
+
176
+ ## 10. 배포 단계
177
+
178
+ 1. **로컬 개발 설치**: `pip install -e .` (editable). 두 스킬이 이 패키지를 import.
179
+ 2. **자체 검증**: 골든 테스트 통과 + 이질 5종 로컬 재현 + 두 스킬 회귀 없음 확인.
180
+ 3. **공개 결정 시(고도화 후)**: README·LICENSE 정비 → TestPyPI 업로드·검증 → PyPI 정식 공개.
181
+ 4. **멀티 디바이스 배포**: Mac·다른 Windows에 `pip install`. (스킬 파일은 junction/복제로 전파되지만, 패키지는 디바이스별 pip 설치가 필요함을 문서화.)
182
+
183
+ ## 11. 버전 정책
184
+
185
+ - semver. `0.1.0`에서 시작.
186
+ - 버전 단일 출처는 `src/hwpx_tomd/_version.py`(또는 `pyproject.toml` dynamic).
187
+ - 패키지 버전을 올릴 때 의존하는 두 스킬을 점검(특히 공개 API 시그니처 변경 시).
188
+
189
+ ## 12. 로드맵 (체크리스트)
190
+
191
+ - [x] 1. 패키지 이름 확정 + PyPI 가용성 확인 (`hwpx-tomd` 확정, PyPI 404=가용 확인. `hwpx2md`는 타인 점유)
192
+ - [x] 2. 스캐폴드: `pyproject.toml`(hatchling, PEP 639 license), src layout, `LICENSE`(MIT), `README.md`
193
+ - [x] 3. `core.py`: 함수 이식 + 라이브러리화(예외 `HwpxError`/`HwpxEncryptedError`/`HwpxParseError`, `ConversionResult` dataclass, print/sys.exit 제거)
194
+ - [x] 4. `cli.py`: argparse, 암호화·recall을 CLI 계층에서 출력(종료코드 0/1/2/3, `--stdout`/`--cell-br`/`-o`)
195
+ - [x] 5. 테스트: 합성 HWPX 픽스처 + 결함 ①②③ 회귀 가드 + recall 단언 + 암호화·파싱오류 예외 + CLI (현재 33개 통과; 6c·6d에서 8+8 증설)
196
+ - [x] 6. 로컬 editable 설치 + 검증 샘플 10종 재현 (원본 엔진 대비 전부 IDENTICAL·recall 100%, 암호화 1종 정상 실패)
197
+ - [x] 6b. Upstage 파싱본 대비 추가 5종 교차검증 (2026-06-06): 내용 동등(체크리스트 100% 일치), 표·객관식 마커는 패키지 우위(3학년 원안 마커 145 vs 131, Upstage가 ①③ 일부 누락), Upstage의 낮아 보이던 recall은 (a)HTML 태그 노이즈 (b)단어 분절 차이 (c)병합셀 반복 때문이며 실제 내용 손실 아님으로 규명. 유일한 격차는 이미지 내 텍스트(L5 BMP 제목)=OCR 영역. 암호화 1종(2학년 원안) 정상 차단.
198
+ - [x] 6c. 6b에서 도출한 개선 반영 (2026-06-06): (A)본문 이미지 개수 `ConversionResult.image_count` + 이미지 존재 경고로 self-recall 맹점 보완 (B)recall이 `<hp:t>` 텍스트 기준이라 이미지 내 텍스트를 못 잡는 한계를 README·docstring에 명시 (C)병합 칸 채우기 옵션 `merge_fill`(라이브러리·`--merge-fill` CLI) 추가. 회귀 테스트 8개 추가(16→25). 기본 출력은 무손상(byte-stable).
199
+ - [x] 6d. 나머지 미사용 쌍 전수 스윕 + 자가검증 강화 (2026-06-06): 미사용 HWPX-Upstage 쌍 29종을 전수 대조. **핵심 발견**: 원본 `<hp:t>` 대비 패키지 출력이 33종 전부 마커 Δ=0·글자 멀티셋 손실 0(=문자·마커 단위 완벽 보존). char_cov 하위권은 전부 Workbook이고 Upstage-only 단어가 죄다 이미지 캡션 어휘(image/placeholder/illustration…)→그래픽 손실이지 패키지 결함 아님. L5_Grammar Plus 마커 pkg 63<up 69도 원본이 정확히 63이라 Upstage 측 이미지 OCR+행 중복. 비-pic 시각객체 census 결과 OLE/수식/차트 0건, container·polygon은 순수 텍스트 문서에도 흔해 경고 확장 부적합으로 판정. **도출 개선**: (D)글자 멀티셋 recall `ConversionResult.char_recall`(단어 집합 recall이 못 보는 반복·숫자·짧은 토큰 손실 감지) (E)객관식 마커 보존 가드(①②③ 등이 출력에서 줄면 임계값 무관 정확 경고, 시험 무결성). 회귀 테스트 8개 추가(25→33). 실문서 33종에서 char_recall 전부 1.0·마커 경고 0건(오탐 없음). 기본 출력 무손상.
200
+ - [x] 7. hwpx-automation 마이그레이션 (2026-06-06): `hwpx_edit.py`의 to-md 전용 렌더 함수군 8개(get_cell_paragraph_texts·get_cell_addr·get_para_direct_text·parse_table_to_rows·table_to_markdown·render_cell_lines·render_table_md·render_block_lines, 약 244줄) 제거하고 `cmd_to_md`를 `from hwpx_tomd import convert` 호출 래퍼로 교체. 편집 명령군·공유 헬퍼(open_hwpx·save_hwpx·get_cell_text·t_full_text 등)는 잔류. `--merge-fill` CLI 추가, requirements.txt에 hwpx-tomd 추가, SKILL.md·CLAUDE.md·AGENTS.md 갱신. 검증: 출력 MD 2종 byte-IDENTICAL, --info(편집 경로) IDENTICAL, 암호화 정상 차단, py_compile 통과. 미설치 시 ImportError를 명확한 설치 안내로 변환(exit 3). 어떤 코드도 hwpx_edit를 모듈 import하지 않음을 전수 확인 후 제거.
201
+ - [x] 8. docparse 마이그레이션 (2026-06-06): `parsers/hwpx_local_parse.py` 신규(다른 *_parse.py와 동일 인터페이스, 출력 `_hwpxlocal.md`, hwpx_tomd 호출, 자가검증·이미지 경고·암호화 처리·stdin isatty 가드). **라우팅까지 일치시킴**(문서만 고치면 불일치): `assess_document.py`의 recommended_parsers를 `.hwpx`→`["hwpx_local"]`로, tier를 `"single"`→`"hwpx"`로 수정; `references/tier-rules.md`의 HWPX 1순위를 hwpx_local로; SKILL.md HWPX 티어를 "Upstage 단독"→"hwpx_local 우선 + Upstage 폴백(이미지·레이아웃·경고 시)"으로 정합화, 파서 표·특성 비교표·실행 예시·Step 8 정리 갱신, changelog v3.9 기록. 검증: `assess <x>.hwpx`→tier/format=hwpx·recommended=["hwpx_local"], clean recall 100%, 워크시트 이미지 경고+교차검증 안내, 암호화 차단. hwpx-automation 편집 명령(--set-cell/--find/--split-cell) 회귀 없음 실행 확인.
202
+ - [ ] 9. (공개 결정 시) README·문서 완성 → TestPyPI → PyPI
203
+ - [ ] 10. 멀티 디바이스 배포 + 문서 동기화
204
+
205
+ ## 13. 미결정 사항 (새 세션 초반에 확정)
206
+
207
+ - **패키지 이름**: `hwpx-tomd`(잠정, import명 `hwpx_tomd`). 후보: `hwpx2md`, `hwpx-reader`. PyPI 충돌 여부 확인 후 확정. `python-hwpx`(범용 읽기/쓰기 라이브러리)와 이름·기능이 구분되도록 "to markdown" 정체성을 살린다.
208
+ - **공개 시점**: 성능 고도화 후 대중 공개(현 단계는 로컬 개발·검증 우선).
209
+ - **표 구조화 API**: 표를 리스트/딕셔너리로 반환하는 API를 추가할지는 실수요 확인 후 결정.
210
+
211
+ ## 14. 참조 경로
212
+
213
+ | 자료 | 경로 |
214
+ |------|------|
215
+ | 원본 파서 엔진 | `C:/Users/pc/Windows-Projects/tools/hwpx-automation/hwpx_edit.py` (상단 헬퍼 + 221~556줄) |
216
+ | 디버깅 보고서 | `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/hwpx_edit_디버깅가능성_검토_2026-06-05.md` |
217
+ | 변경 이력 | `C:/Users/pc/Windows-Projects/docs/DocumentParse/CHANGELOG.md` |
218
+ | 검증 샘플 | `C:/Users/pc/Windows-Projects/docs/DocumentParse/HWPX_파서비교_2026-06-05/` (워크시트·이질 5종) |
219
+ | 소비 스킬 1 | `C:/Users/pc/Windows-Projects/tools/hwpx-automation/SKILL.md` |
220
+ | 소비 스킬 2 | `C:/Users/pc/Windows-Projects/tools/docparse/SKILL.md` |