|
| 1 | +# HWP 5.x 파일 포맷 조사 결과 |
| 2 | + |
| 3 | +## 개요 |
| 4 | + |
| 5 | +HWP 5.x는 한글과컴퓨터의 한글 워드프로세서에서 사용하는 바이너리 문서 포맷이다. 한글 2002부터 한글 2022까지 사용되었으며, 2014년부터는 XML 기반의 HWPX 포맷이 병행 지원된다. |
| 6 | + |
| 7 | +### 구현 상태 |
| 8 | + |
| 9 | +| 기능 | 상태 | 설명 | |
| 10 | +|------|------|------| |
| 11 | +| OLE2 파싱 | ✅ 완료 | mscfb 라이브러리 사용 | |
| 12 | +| FileHeader | ✅ 완료 | 버전, 압축, 암호화 플래그 파싱 | |
| 13 | +| DocInfo | ✅ 완료 | ID 매핑, 글꼴, 스타일 정보 | |
| 14 | +| BodyText/Section | ✅ 완료 | 문단, 텍스트 추출 | |
| 15 | +| 테이블 | ✅ 완료 | 행/열, 셀 병합, 셀 내용 | |
| 16 | +| 이미지 | ⚠️ 부분 | BinData 참조만 지원 | |
| 17 | +| 압축 해제 | ✅ 완료 | zlib/deflate 지원 | |
| 18 | +| 암호화 | ❌ 미지원 | 암호화 문서 차단 | |
| 19 | +| DRM | ❌ 미지원 | DRM 문서 차단 | |
| 20 | + |
| 21 | +## 공식 문서 |
| 22 | + |
| 23 | +### 한글 문서 파일 형식 5.0 명세서 |
| 24 | +- **URL**: https://cdn.hancom.com/link/docs/한글문서파일형식_5.0_revision1.3.pdf |
| 25 | +- **버전**: Revision 1.3 |
| 26 | +- **발행**: 한글과컴퓨터 |
| 27 | + |
| 28 | +### 한컴 기술 블로그 |
| 29 | +- https://tech.hancom.com/python-hwp-parsing-1/ |
| 30 | +- https://tech.hancom.com/한-글-문서-파일-형식-hwp-포맷-구조-살펴보기/ |
| 31 | + |
| 32 | +## 파일 구조 |
| 33 | + |
| 34 | +### OLE2 Compound File Binary (CFB) 컨테이너 |
| 35 | + |
| 36 | +HWP 5.x 파일은 Microsoft의 OLE2 Compound File 형식을 기반으로 한다. 이는 파일 내부에 여러 스트림(Stream)과 저장소(Storage)를 포함하는 계층적 구조이다. |
| 37 | + |
| 38 | +``` |
| 39 | +HWP 파일 |
| 40 | +├── FileHeader # 파일 인식 정보 (고정 256바이트) |
| 41 | +├── DocInfo # 문서 정보 (압축/암호화 가능) |
| 42 | +├── BodyText/ # 본문 저장소 |
| 43 | +│ ├── Section0 # 첫 번째 섹션 |
| 44 | +│ ├── Section1 # 두 번째 섹션 |
| 45 | +│ └── ... |
| 46 | +├── BinData/ # 바이너리 데이터 저장소 |
| 47 | +│ ├── BIN0001.jpg # 이미지 등 |
| 48 | +│ └── ... |
| 49 | +├── PrvText # 미리보기 텍스트 |
| 50 | +├── PrvImage # 미리보기 이미지 |
| 51 | +└── Scripts/ # 스크립트 저장소 (선택) |
| 52 | +``` |
| 53 | + |
| 54 | +### FileHeader 구조 |
| 55 | + |
| 56 | +| 오프셋 | 크기 | 설명 | |
| 57 | +|--------|------|------| |
| 58 | +| 0 | 32 | 시그니처: "HWP Document File" | |
| 59 | +| 32 | 4 | 파일 버전 (예: 5.0.3.0) | |
| 60 | +| 36 | 4 | 속성 플래그 | |
| 61 | +| ... | ... | 예약 영역 | |
| 62 | + |
| 63 | +**속성 플래그**: |
| 64 | +- 비트 0: 압축 여부 |
| 65 | +- 비트 1: 암호화 여부 |
| 66 | +- 비트 2: 배포용 문서 |
| 67 | +- 비트 3: 스크립트 저장 |
| 68 | +- 비트 4: DRM 보안 |
| 69 | +- 비트 5: XMLTemplate 저장 |
| 70 | +- 비트 6: 문서 이력 관리 |
| 71 | +- 비트 7: 전자 서명 |
| 72 | +- 비트 8: 공인 인증서 암호화 |
| 73 | +- 비트 9: 전자 서명 예비 |
| 74 | +- 비트 10: 공인 인증서 DRM |
| 75 | +- 비트 11: CCL 문서 |
| 76 | + |
| 77 | +### 레코드 구조 |
| 78 | + |
| 79 | +DocInfo와 BodyText/Section 스트림은 레코드(Record) 단위로 데이터가 저장된다. |
| 80 | + |
| 81 | +**레코드 헤더 (4바이트)**: |
| 82 | +``` |
| 83 | +┌─────────────────────────────────────────────────────────────┐ |
| 84 | +│ Tag ID (10비트) │ Level (10비트) │ Size (12비트) │ |
| 85 | +└─────────────────────────────────────────────────────────────┘ |
| 86 | +``` |
| 87 | + |
| 88 | +- **Tag ID**: 레코드 종류 식별자 |
| 89 | +- **Level**: 논리적 계층 표현 (0부터 시작) |
| 90 | +- **Size**: 데이터 길이 (바이트), 4095 이상이면 0xFFF로 설정하고 다음 4바이트에 실제 크기 |
| 91 | + |
| 92 | +**주요 레코드 태그**: |
| 93 | +| 태그 | 값 | 설명 | |
| 94 | +|------|-----|------| |
| 95 | +| HWPTAG_DOCUMENT_PROPERTIES | 0x0010 | 문서 속성 | |
| 96 | +| HWPTAG_ID_MAPPINGS | 0x0011 | ID 매핑 테이블 | |
| 97 | +| HWPTAG_BIN_DATA | 0x0012 | 바이너리 데이터 정보 | |
| 98 | +| HWPTAG_FACE_NAME | 0x0013 | 글꼴 이름 | |
| 99 | +| HWPTAG_BORDER_FILL | 0x0014 | 테두리/배경 | |
| 100 | +| HWPTAG_CHAR_SHAPE | 0x0015 | 글자 모양 | |
| 101 | +| HWPTAG_TAB_DEF | 0x0016 | 탭 정의 | |
| 102 | +| HWPTAG_NUMBERING | 0x0017 | 문단 번호 | |
| 103 | +| HWPTAG_BULLET | 0x0018 | 글머리표 | |
| 104 | +| HWPTAG_PARA_SHAPE | 0x0019 | 문단 모양 | |
| 105 | +| HWPTAG_STYLE | 0x001A | 스타일 | |
| 106 | +| HWPTAG_PARA_HEADER | 0x0042 | 문단 헤더 | |
| 107 | +| HWPTAG_PARA_TEXT | 0x0043 | 문단 텍스트 | |
| 108 | +| HWPTAG_PARA_CHAR_SHAPE | 0x0044 | 문단 글자 모양 | |
| 109 | +| HWPTAG_PARA_LINE_SEG | 0x0045 | 문단 레이아웃 | |
| 110 | +| HWPTAG_CTRL_HEADER | 0x0046 | 컨트롤 헤더 | |
| 111 | +| HWPTAG_TABLE | 0x0050 | 표 | |
| 112 | +| HWPTAG_LIST_HEADER | 0x0051 | 리스트 헤더 | |
| 113 | + |
| 114 | +### 압축 및 암호화 |
| 115 | + |
| 116 | +- **압축**: zlib (DEFLATE) 알고리즘 사용 |
| 117 | +- **암호화**: 다양한 암호화 방식 지원 |
| 118 | + - 일반 비밀번호 암호화 |
| 119 | + - 공인인증서 기반 암호화 |
| 120 | + - DRM |
| 121 | + |
| 122 | +## 오픈소스 구현체 |
| 123 | + |
| 124 | +### Go 언어 |
| 125 | + |
| 126 | +현재 Go로 작성된 완전한 HWP 5.x 파서는 찾지 못함. 구현 시 참고할 수 있는 프로젝트들: |
| 127 | + |
| 128 | +### Rust |
| 129 | + |
| 130 | +| 프로젝트 | URL | 특징 | |
| 131 | +|----------|-----|------| |
| 132 | +| **OpenHWP** | https://github.com/openhwp/openhwp | HWP 5.0 읽기, HWPX 읽기/쓰기, IR 변환 지원 | |
| 133 | +| **unhwp** | https://lib.rs/crates/unhwp | HWP 5.0+, HWPX, HWP 3.x 지원, Markdown 출력 | |
| 134 | + |
| 135 | +### Python |
| 136 | + |
| 137 | +| 프로젝트 | URL | 특징 | |
| 138 | +|----------|-----|------| |
| 139 | +| **pyhwp** | https://github.com/mete0r/pyhwp | 가장 성숙한 구현체, ODT/TXT 변환, HWP Binary Specification 1.1 기반 | |
| 140 | +| **hwpers** | https://github.com/Indosaram/hwpers | HWP 5.0 완전 지원 | |
| 141 | +| **hwp-extract** | https://github.com/volexity/hwp-extract | 암호화된 HWP 지원, 메타데이터 추출 | |
| 142 | + |
| 143 | +### JavaScript/TypeScript |
| 144 | + |
| 145 | +| 프로젝트 | URL | 특징 | |
| 146 | +|----------|-----|------| |
| 147 | +| **hwp.js** | https://github.com/hahnlee/hwp.js | TypeScript 기반, 웹 뷰어, cfb-js로 OLE 파싱 | |
| 148 | +| **hwp-parser** | https://github.com/BOB-APT-Solution/hwp-parser | JS 코드 추출 등 보안 분석용 | |
| 149 | + |
| 150 | +## 구현 전략 권장사항 |
| 151 | + |
| 152 | +### 1. OLE2 파싱 |
| 153 | + |
| 154 | +Go에서 OLE2 Compound File을 파싱하기 위한 라이브러리: |
| 155 | +- 직접 구현 또는 기존 라이브러리 활용 필요 |
| 156 | +- 참고: https://github.com/richardlehane/mscfb (Go OLE2 라이브러리) |
| 157 | + |
| 158 | +### 2. 레코드 파싱 |
| 159 | + |
| 160 | +1. FileHeader 읽기 → 버전/속성 확인 |
| 161 | +2. DocInfo 스트림 압축 해제 → 레코드 순회 → ID 매핑 테이블 구축 |
| 162 | +3. BodyText/SectionN 스트림 압축 해제 → 레코드 순회 → IR 변환 |
| 163 | + |
| 164 | +### 3. IR 변환 |
| 165 | + |
| 166 | +현재 HWPX 파서와 동일한 IR 구조(`internal/ir/`)로 변환: |
| 167 | +- `HWPTAG_PARA_HEADER` + `HWPTAG_PARA_TEXT` → `ir.Paragraph` |
| 168 | +- `HWPTAG_TABLE` + 셀 데이터 → `ir.Table` |
| 169 | +- BinData의 이미지 → `ir.Image` |
| 170 | + |
| 171 | +### 4. 참고 구현체 |
| 172 | + |
| 173 | +가장 참고하기 좋은 구현체: |
| 174 | +1. **pyhwp** (Python): 가장 완성도 높고 문서화 잘 됨 |
| 175 | +2. **OpenHWP** (Rust): 최신 구현, IR 변환 아키텍처 참고 |
| 176 | +3. **hwp.js** (TypeScript): 웹 기반 구현 참고 |
| 177 | + |
| 178 | +## 구현 완료 항목 |
| 179 | + |
| 180 | +1. [x] Go OLE2 라이브러리 선정 → `github.com/richardlehane/mscfb` 사용 |
| 181 | +2. [x] FileHeader 파싱 구현 → `internal/parser/hwp5/header.go` |
| 182 | +3. [x] DocInfo 스트림 파싱 (레코드 구조) → `internal/parser/hwp5/docinfo.go` |
| 183 | +4. [x] BodyText/Section 파싱 → `internal/parser/hwp5/section.go` |
| 184 | +5. [x] IR 변환 로직 구현 → `internal/parser/hwp5/parser.go` |
| 185 | +6. [x] 테이블 파싱 구현 → 셀 병합, 셀 내용 포함 |
| 186 | +7. [x] 테스트 케이스 작성 → `internal/parser/hwp5/*_test.go`, `tests/e2e_test.go` |
| 187 | + |
| 188 | +## 향후 개선 사항 |
| 189 | + |
| 190 | +1. [ ] BinData 이미지 추출 개선 |
| 191 | +2. [ ] 다양한 HWP 버전 테스트 (5.0.0.0 ~ 5.1.1.0) |
| 192 | +3. [ ] 복잡한 테이블 구조 지원 (중첩 테이블 등) |
| 193 | + |
| 194 | +## 참고 자료 |
| 195 | + |
| 196 | +- 한글 문서 파일 형식 5.0: https://cdn.hancom.com/link/docs/한글문서파일형식_5.0_revision1.3.pdf |
| 197 | +- 한컴 기술 블로그: https://tech.hancom.com/python-hwp-parsing-1/ |
| 198 | +- pyhwp 문서: https://pyhwp.readthedocs.io/ |
| 199 | +- OpenHWP: https://github.com/openhwp/openhwp |
| 200 | +- unhwp: https://lib.rs/crates/unhwp |
| 201 | +- hwp.js: https://github.com/hahnlee/hwp.js |
| 202 | +- mscfb (Go OLE2): https://github.com/richardlehane/mscfb |
0 commit comments