Skip to content

Task #164: HWPX Serializer 구현 — Document IR → HWPX 저장#170

Merged
edwardkim merged 13 commits intoedwardkim:develfrom
seunghan91:feature/task164-hwpx-serializer
Apr 17, 2026
Merged

Task #164: HWPX Serializer 구현 — Document IR → HWPX 저장#170
edwardkim merged 13 commits intoedwardkim:develfrom
seunghan91:feature/task164-hwpx-serializer

Conversation

@seunghan91
Copy link
Copy Markdown
Contributor

Summary

  • Document IR을 한글2020/한컴오피스가 정상 오픈할 수 있는 .hwpx 파일로 직렬화하는 신규 모듈 추가
  • 다문단 + 소프트 라인브레이크(\n) + 탭(\t) + XML escape 지원
  • 한컴 OLE(pyhwpx) 기반 자동 검증 인프라 동봉

closes #164

주요 변경사항

신규 모듈 src/serializer/hwpx/

  • mod.rsserialize_hwpx() 진입점, ZIP 패키징
  • writer.rs — mimetype STORED + 나머지 DEFLATED
  • header.rs — 한컴2020 레퍼런스 템플릿 임베딩 (Stage 2.5에서 IR 기반 동적화 예정)
  • section.rs<hp:p> 다문단, <hp:t>+<hp:tab>+<hp:lineBreak/> 동적 생성
  • content.rsContents/content.hpf OPF manifest
  • static_assets.rs — META-INF/version/Preview 정적 자산
  • templates/*.xml — 한컴 호환 5개 정적 템플릿

IR 매핑

  • section.paragraphs 여러 개 → 하드 문단 경계 (<hp:p> 여러 개)
  • paragraph.text\n<hp:lineBreak/> (소프트 브레이크, 같은 문단 내)
  • paragraph.text\t<hp:tab width="4000" leader="0" type="1"/>

검증 인프라

  • tools/verify_hwpx.py — pyhwpx 기반 한컴 자동 오픈/텍스트 검증
  • tools/verify_all.py — 모든 stage 산출물 일괄 검증
  • examples/hwpx_dump_empty|text|roundtrip.rs — 산출 예제

단계별 진행 (4 stage)

  1. Stage 1: 모듈 스켈레톤 + 한컴 호환 11파일 빈 HWPX
  2. Stage 2.1: section.xml 텍스트 IR 주입 + xml_escape
  3. Stage 2.2: 탭/줄바꿈 인라인 직렬화 (구조)
  4. Stage 2.3: 다문단 + 소프트 브레이크 + 탭 정식 (사용자 제공 ref_mixed.hwpx 역공학 기반)

각 stage별 완료 보고서는 mydocs/working/task_m100_164_stage*.md 참조.

검증 결과

단위 테스트 — 10/10 통과

한글2020 자동 검증 — 4/4 통과

  • stage1_empty / stage2_text / stage2_mixed / rt_ref_mixed

실문서 라운드트립 (참고)

  • "2025년 2분기 해외직접투자 (최종).hwpx" (134KB, 130문단, 2섹션) → 한컴 정상 오픈 + 본문 보존, 3페이지 페이지네이션 동작 (표/이미지/스타일은 본 PR 범위 외)

한계 및 후속 이슈 제안

Test plan

  • cargo test --lib serializer::hwpx (10 passed)
  • cargo run --example hwpx_dump_empty --release → 한글2020 오픈
  • cargo run --example hwpx_dump_text --release → "안녕 Hello 123" + 다문단/탭/소프트 브레이크 시각 확인
  • python tools/verify_all.py (4/4 passed)
  • cargo run --example hwpx_roundtrip --release -- 실문서.hwpx → 한컴 오픈 검증

🤖 Generated with Claude Code

seunghan91 and others added 13 commits April 16, 2026 19:57
HWPX Serializer 구현을 위한 5단계 계획:
1. 모듈 스켈레톤 + 빈 HWPX
2. 본문 문단·텍스트·lineSegArray
3. 표(Table) 직렬화
4. 그림(Picture) + BinData
5. 라운드트립 테스트 + CLI + 보고서

참조 문서: hwp_save_guide.md, hwp_hwpx_ir_differences.md
- src/serializer/hwpx/ 신설 (mod/writer/content/header/section/utils)
- HwpxSerializer + DocumentSerializer 구현체 등록
- SerializeError를 serializer 루트로 이동 (HWP+HWPX 공용)
- examples/hwpx_dump_empty: output/stage1_empty.hwpx 생성용
- 단위 테스트 4개 통과: parse_hwpx 라운드트립, mimetype STORED/최초엔트리
- ref_empty.hwpx — 빈 문서 (111.hwpx)
- ref_text.hwpx — '안녕 Hello 123' 1줄 (222.hwpx)
- ref_table.hwpx — 2x3 표 포함 (333.hwpx)

Stage 1 호환성 검증 + Stage 2~3 구조 역공학 레퍼런스
한컴 레퍼런스(ref_empty.hwpx) 분석 결과 반영:
- static_assets 신설: version.xml, META-INF/container.{xml,rdf}, META-INF/manifest.xml,
  settings.xml, Preview/PrvText.txt(CRLF), Preview/PrvImage.png(1x1 투명)
- content.hpf 확장: 14개 네임스페이스 선언 + metadata(opf:language, creator 등)
- section.rs 확장: 1 문단 + full <hp:secPr>(pagePr/margin/footNotePr/endNotePr/
  pageBorderFill×3) + <hp:ctrl><hp:colPr> + <hp:linesegarray>
- header.rs 확장: fontfaces(7언어) + 1 charPr/paraPr/borderFill/style/tabPr +
  compatibleDocument + docOption

테스트 5개 통과:
- hancom_required_files_present (11개 필수 파일 검증)
- parse_hwpx 라운드트립 2종
- mimetype STORED + 최초 엔트리 검증 2종
빈 문서(1섹션) 출력 시 한컴2020 ref_empty.hwpx의 header.xml(32KB),
section0.xml(3340B), version.xml, settings.xml을 include_str!로 직접
사용하여 한컴 호환성 보장. 11개 파일 중 9개 바이트 일치,
PrvImage.png(1x1 PNG 대체)과 content.hpf(metadata 일반화)만 차이.

Stage 2+에서 IR 기반 동적 생성으로 점진 교체 예정.
한컴2020에서 빈 HWPX 정상 오픈 검증 완료.
- 자동 테스트 5/5 통과
- 핵심 XML 9/11 바이트 동일, 2파일 기능적 대체
- Stage 2(본문 문단·텍스트·lineSegArray) 준비 완료
- utils.rs에 xml_escape() 추가 (&, <, >, ", ')
- section.rs에서 첫 문단 텍스트를 <hp:t/> 자리에 주입
- 제어문자 있는 문단은 Stage 2.2 전까지 빈 템플릿 유지
- examples/hwpx_dump_text.rs로 시각 검증용 산출
- 한글2020에서 "안녕 Hello 123" 정상 표시 확인
- 단위 테스트 2종 추가 (로운드트립 + escape), 7/7 통과

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- section.rs render_text_run(): \t -> <hp:tab/>, \n -> <hp:lineBreak/>
- 한컴 레퍼런스 패턴(<hp:t> 내 혼합 콘텐츠) 채택
- 단위 테스트 tab_and_linebreak_emitted_inline 추가
- stage2_ctrl.hwpx 예제로 시각 검증
- 한계: lineseg 1개뿐이라 시각 줄 나눔 미반영 (Stage 2.3 연기)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
사용자 제공 레퍼런스(ref_mixed.hwpx) 역공학 기반:
- Enter -> <hp:p> 여러 개 (하드 문단 경계)
- Shift+Enter -> <hp:t> 내 <hp:lineBreak/> 혼합 콘텐츠
- Tab -> <hp:tab width="4000" leader="0" type="1"/> 속성 포함
- vertpos 증분 1600 HWPUNIT (vertsize 1000 + spacing 600)

IR 매핑:
- section.paragraphs 개수 = 하드 문단 수
- paragraph.text 내 \n = 소프트 브레이크
- paragraph.text 내 \t = 탭

한글2020에서 4문단 + 소프트 브레이크 + 탭 정상 렌더링 확인.
단위 테스트 10/10 통과.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- tools/verify_hwpx.py: 한컴 OLE(pyhwpx) 기반 오픈/텍스트/문단 수 검증
- 보안 팝업은 pyhwpx 내장 FilePathCheckerModule.dll로 자동 회피
- stage2_mixed.hwpx 검증 통과 (4문단 + 소프트 브레이크 + 탭)
- 사용: PYTHONIOENCODING=utf-8 python tools/verify_hwpx.py <파일> [--expect-text ...] [--expect-paragraphs N]

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tools/verify_all.py: 모든 stage 산출물을 한 번에 한컴 자동 검증
- stage1_empty (빈 문서)
- stage2_text (단일 문단)
- stage2_mixed (다문단 + 소프트 브레이크 + 탭)

3/3 통과 확인.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- examples/hwpx_roundtrip.rs: 입력 HWPX -> 파싱 -> 재직렬화
- tools/verify_all.py: rt_ref_mixed.hwpx 검증 케이스 포함

검증된 라운드트립:
- ref_mixed.hwpx (4문단): 본문/탭/소프트브레이크 완벽 보존
- 2025년 2분기 해외직접투자 (134KB, 130문단): 한컴 오픈 + 본문 보존,
  3페이지 페이지네이션 동작 (표/이미지/스타일은 범위 밖이라 탈락)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- task_m100_164_report.md: 4 stage + 검증 인프라 + 라운드트립 결과 종합
- orders/20260417.md: 오늘할일 등록, Task edwardkim#165/166/167 후속 이슈 제안

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edwardkim
Copy link
Copy Markdown
Owner

고생하셨습니다. 덕분에 오늘 패지버전으로 배포할 수 있겠습니다.

Copy link
Copy Markdown
Owner

@edwardkim edwardkim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로컬 검증 완료 ✅

코드 구조 리뷰:

  • 파서(parser::hwpx)의 거울 구조로 깔끔하게 구성
  • 테스트 10개로 라운드트립, XML escape, mimetype 순서 등 철저 검증
  • OPC 규격 준수 (mimetype STORED, 최초 엔트리)

보안 점검:

  • XML Injection: xml_escape() + quick_xml 자동 이스케이프 ✅
  • ZIP path traversal: 하드코딩 경로만 사용 ✅
  • 메모리 안전: unsafe 없음 ✅
  • BinDataEntry.href: 현재 미사용, 후속 구현 시 경로 검증 필요 (주의)

빌드/테스트:

  • cargo test: 798개 전체 통과
  • cargo clippy: 통과

후속 이슈:

  • #176: WASM API 노출 및 사용자 오류 전달

하이퍼-워터폴을 자발적으로 적용한 훌륭한 기여입니다! 🎉

@edwardkim edwardkim merged commit 0aac213 into edwardkim:devel Apr 17, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants