Files
test/domain/hwpx/hwpx_domain_guide.md

769 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HWP/HWPX ↔ HTML/CSS 도메인 가이드
> **목적**: HWPX에서 문서 유형·스타일·템플릿을 추출하거나, HTML → HWPX → HWP 변환 시
> 하드코딩 없이 이 가이드를 참조하여 정확한 매핑을 수행한다.
> **출처**: 한글과컴퓨터 공식 "글 문서 파일 구조 5.0" (revision 1.3, 2018-11-08)
> **범위**: HWP 5.0 바이너리 스펙의 개념 체계 + HWPX XML 태그 + HTML/CSS 매핑
---
## 0. 문서 형식 관계
```
HWP (바이너리) HWPX (XML) HTML/CSS
───────────────── ───────────────────── ─────────────────
Compound File ZIP Archive 단일 HTML 파일
├─ FileHeader ├─ META-INF/ ├─ <head>
├─ DocInfo │ └─ manifest.xml │ ├─ <meta>
│ (글꼴, 스타일, ├─ Contents/ │ └─ <style>
│ 테두리/배경, │ ├─ header.xml └─ <body>
│ 글자모양 등) │ │ (DocInfo 대응) ├─ 헤더 영역
├─ BodyText/ │ ├─ section0.xml │ ├─ 본문
│ └─ Section0 │ │ (본문 대응) │ └─ 푸터 영역
├─ BinData/ │ └─ section1.xml └─ @page CSS
│ └─ 이미지 등 ├─ BinData/
└─ PrvImage │ └─ 이미지 파일
└─ version.xml
```
**핵심**: HWP 바이너리의 레코드 구조와 HWPX XML의 엘리먼트는 1:1 대응한다.
이 가이드는 두 형식의 공통 개념 체계를 기준으로, CSS 변환까지 연결한다.
---
## 1. 단위 체계
### 1.1 HWPUNIT (글 내부 단위)
HWP는 1/7200 인치를 기본 단위로 사용한다.
| 변환 대상 | 공식 | 예시 |
|-----------|------|------|
| HWPUNIT → mm | `hwpunit / 7200 * 25.4` | 7200 → 25.4mm (= 1인치) |
| HWPUNIT → pt | `hwpunit / 7200 * 72` | 7200 → 72pt |
| HWPUNIT → px (96dpi) | `hwpunit / 7200 * 96` | 7200 → 96px |
| mm → HWPUNIT | `mm / 25.4 * 7200` | 25.4mm → 7200 |
| pt → HWPUNIT | `pt / 72 * 7200` | 10pt → 1000 |
```python
def hwpunit_to_mm(hwpunit): return hwpunit / 7200 * 25.4
def hwpunit_to_pt(hwpunit): return hwpunit / 7200 * 72
def hwpunit_to_px(hwpunit): return hwpunit / 7200 * 96
def mm_to_hwpunit(mm): return mm / 25.4 * 7200
```
### 1.2 글자 크기 (CharShape)
HWP의 글자 크기는 HWPUNIT 단위이지만 100배 스케일이 적용되어 있다.
| HWP 값 | 실제 크기 | CSS |
|--------|----------|-----|
| 1000 | 10pt | `font-size: 10pt` |
| 1200 | 12pt | `font-size: 12pt` |
| 2400 | 24pt | `font-size: 24pt` |
```python
def charsize_to_pt(hwp_size): return hwp_size / 100 # 1000 → 10pt
```
### 1.3 COLORREF (색상)
HWP는 0x00BBGGRR 형식(리틀 엔디안 BGR). CSS는 #RRGGBB.
| HWP COLORREF | 분해 | CSS |
|-------------|------|-----|
| 0x00000000 | R=0, G=0, B=0 | `#000000` (검정) |
| 0x00FF0000 | R=0, G=0, B=255 | `#0000ff` (파랑) |
| 0x0000FF00 | R=0, G=255, B=0 | `#00ff00` (초록) |
| 0x000000FF | R=255, G=0, B=0 | `#ff0000` (빨강) |
| 0x00FFFFFF | R=255, G=255, B=255 | `#ffffff` (흰색) |
```python
def colorref_to_css(colorref):
r = colorref & 0xFF
g = (colorref >> 8) & 0xFF
b = (colorref >> 16) & 0xFF
return f'#{r:02x}{g:02x}{b:02x}'
```
**HWPX XML에서의 색상**: `#RRGGBB` 형식으로 직접 기록됨 (변환 불필요).
---
## 2. 테두리/배경 (BorderFill)
> HWP: `HWPTAG_BORDER_FILL` (DocInfo 레코드)
> HWPX: `<hh:borderFill>` (header.xml 내)
> 용도: 표 셀, 문단, 쪽 테두리/배경에 공통 적용
### 2.1 테두리선 종류
| HWP 값 | 이름 | HWPX type 속성 | CSS border-style |
|--------|------|---------------|-----------------|
| 0 | 실선 | `SOLID` | `solid` |
| 1 | 긴 점선 | `DASH` | `dashed` |
| 2 | 점선 | `DOT` | `dotted` |
| 3 | -.-.-. | `DASH_DOT` | `dashed` (근사) |
| 4 | -..-.. | `DASH_DOT_DOT` | `dashed` (근사) |
| 5 | 긴 Dash | `LONG_DASH` | `dashed` |
| 6 | 큰 동그라미 | `CIRCLE` | `dotted` (근사) |
| 7 | 2중선 | `DOUBLE` | `double` |
| 8 | 가는선+굵은선 | `THIN_THICK` | `double` (근사) |
| 9 | 굵은선+가는선 | `THICK_THIN` | `double` (근사) |
| 10 | 가는+굵은+가는 | `THIN_THICK_THIN` | `double` (근사) |
| 11 | 물결 | `WAVE` | `solid` (근사) |
| 12 | 물결 2중선 | `DOUBLE_WAVE` | `double` (근사) |
| 13 | 두꺼운 3D | `THICK_3D` | `ridge` |
| 14 | 두꺼운 3D(역) | `THICK_3D_REV` | `groove` |
| 15 | 3D 단선 | `3D` | `outset` |
| 16 | 3D 단선(역) | `3D_REV` | `inset` |
| — | 없음 | `NONE` | `none` |
### 2.2 테두리선 굵기
| HWP 값 | 실제 굵기 | HWPX width 속성 | CSS border-width |
|--------|----------|----------------|-----------------|
| 0 | 0.1 mm | `0.1mm` | `0.1mm``0.4px` |
| 1 | 0.12 mm | `0.12mm` | `0.12mm``0.5px` |
| 2 | 0.15 mm | `0.15mm` | `0.15mm``0.6px` |
| 3 | 0.2 mm | `0.2mm` | `0.2mm``0.8px` |
| 4 | 0.25 mm | `0.25mm` | `0.25mm``1px` |
| 5 | 0.3 mm | `0.3mm` | `0.3mm``1.1px` |
| 6 | 0.4 mm | `0.4mm` | `0.4mm``1.5px` |
| 7 | 0.5 mm | `0.5mm` | `0.5mm``1.9px` |
| 8 | 0.6 mm | `0.6mm` | `0.6mm``2.3px` |
| 9 | 0.7 mm | `0.7mm` | `0.7mm``2.6px` |
| 10 | 1.0 mm | `1.0mm` | `1mm``3.8px` |
| 11 | 1.5 mm | `1.5mm` | `1.5mm``5.7px` |
| 12 | 2.0 mm | `2.0mm` | `2mm``7.6px` |
| 13 | 3.0 mm | `3.0mm` | `3mm``11.3px` |
| 14 | 4.0 mm | `4.0mm` | `4mm``15.1px` |
| 15 | 5.0 mm | `5.0mm` | `5mm``18.9px` |
```python
BORDER_WIDTH_MAP = {
0: 0.1, 1: 0.12, 2: 0.15, 3: 0.2, 4: 0.25, 5: 0.3,
6: 0.4, 7: 0.5, 8: 0.6, 9: 0.7, 10: 1.0, 11: 1.5,
12: 2.0, 13: 3.0, 14: 4.0, 15: 5.0
}
def border_width_to_css(hwp_val):
mm = BORDER_WIDTH_MAP.get(hwp_val, 0.12)
return f'{mm}mm' # 또는 mm * 3.7795px
```
### 2.3 테두리 4방향 순서
| HWP 배열 인덱스 | HWPX 속성 | CSS 대응 |
|:---:|:---:|:---:|
| [0] | `<left>` / `<hh:left>` | `border-left` |
| [1] | `<right>` / `<hh:right>` | `border-right` |
| [2] | `<top>` / `<hh:top>` | `border-top` |
| [3] | `<bottom>` / `<hh:bottom>` | `border-bottom` |
### 2.4 채우기 (Fill) 정보
| 채우기 종류 (type 비트) | HWPX 엘리먼트 | CSS 대응 |
|:---:|:---:|:---:|
| 0x00 — 없음 | (없음) | `background: none` |
| 0x01 — 단색 | `<hh:windowBrush>` 또는 `<hh:colorFill>` | `background-color: #...` |
| 0x02 — 이미지 | `<hh:imgBrush>` | `background-image: url(...)` |
| 0x04 — 그러데이션 | `<hh:gradation>` | `background: linear-gradient(...)` |
**단색 채우기 구조** (가장 빈번):
```xml
<!-- HWPX header.xml -->
<hh:borderFill id="4">
<hh:slash .../>
<hh:backSlash .../>
<hh:left type="SOLID" width="0.12mm" color="#000000"/>
<hh:right type="SOLID" width="0.12mm" color="#000000"/>
<hh:top type="SOLID" width="0.12mm" color="#000000"/>
<hh:bottom type="SOLID" width="0.12mm" color="#000000"/>
<hh:diagonal .../>
<hh:fillBrush>
<hh:windowBrush faceColor="#E8F5E9" hatchColor="none" .../>
</hh:fillBrush>
</hh:borderFill>
```
```css
/* CSS 대응 */
.cell-bf4 {
border-left: 0.12mm solid #000000;
border-right: 0.12mm solid #000000;
border-top: 0.12mm solid #000000;
border-bottom: 0.12mm solid #000000;
background-color: #E8F5E9;
}
```
### 2.5 HWPX borderFill → CSS 변환 함수 (의사 코드)
```python
def borderfill_to_css(bf_element):
"""HWPX <hh:borderFill> 엘리먼트 → CSS 딕셔너리"""
css = {}
for side in ['left', 'right', 'top', 'bottom']:
el = bf_element.find(f'hh:{side}')
if el is None:
css[f'border-{side}'] = 'none'
continue
btype = el.get('type', 'NONE')
width = el.get('width', '0.12mm')
color = el.get('color', '#000000')
if btype == 'NONE':
css[f'border-{side}'] = 'none'
else:
css_style = BORDER_TYPE_MAP.get(btype, 'solid')
css[f'border-{side}'] = f'{width} {css_style} {color}'
# 배경
fill = bf_element.find('.//hh:windowBrush')
if fill is not None:
face = fill.get('faceColor', 'none')
if face and face != 'none':
css['background-color'] = face
return css
```
---
## 3. 글꼴 (FaceName)
> HWP: `HWPTAG_FACE_NAME` (DocInfo)
> HWPX: `<hh:fontface>` → `<hh:font>` (header.xml)
> CSS: `font-family`
### 3.1 언어별 글꼴 시스템
HWP는 한글·영문·한자·일어·기타·기호·사용자 총 7개 언어 슬롯에 각각 다른 글꼴을 지정한다.
| 언어 인덱스 | HWPX lang 속성 | 주요 글꼴 예시 |
|:---:|:---:|:---:|
| 0 | `HANGUL` | 맑은 고딕, 나눔고딕 |
| 1 | `LATIN` | Arial, Times New Roman |
| 2 | `HANJA` | (한글 글꼴 공유) |
| 3 | `JAPANESE` | MS Mincho |
| 4 | `OTHER` | — |
| 5 | `SYMBOL` | Symbol, Wingdings |
| 6 | `USER` | — |
**CSS 매핑**: 일반적으로 한글(0)과 영문(1) 글꼴을 `font-family` 스택으로 결합.
```css
/* HWPX: hangul="맑은 고딕" latin="Arial" */
font-family: "맑은 고딕", Arial, sans-serif;
```
### 3.2 글꼴 관련 HWPX 구조
```xml
<!-- header.xml -->
<hh:fontfaces>
<hh:fontface lang="HANGUL">
<hh:font face="맑은 고딕" type="TTF" id="0"/>
</hh:fontface>
<hh:fontface lang="LATIN">
<hh:font face="Arial" type="TTF" id="0"/>
</hh:fontface>
...
</hh:fontfaces>
```
---
## 4. 글자 모양 (CharShape)
> HWP: `HWPTAG_CHAR_SHAPE` (DocInfo, 72바이트)
> HWPX: `<hh:charPr>` (header.xml charProperties 내)
> CSS: font-*, color, text-decoration 등
### 4.1 주요 속성 매핑
| HWP 필드 | HWPX 속성 | CSS 속성 | 비고 |
|----------|----------|---------|------|
| 글꼴 ID [7] | `fontRef` | `font-family` | 언어별 참조 |
| 장평 [7] | `ratio` | `font-stretch` | 50%~200% |
| 자간 [7] | `spacing` | `letter-spacing` | -50%~50%, pt 변환 필요 |
| 기준 크기 | `height` | `font-size` | 값/100 = pt |
| 글자 색 | `color` 속성 | `color` | COLORREF → #RRGGBB |
| 밑줄 색 | `underline color` | `text-decoration-color` | |
| 진하게(bit 1) | `bold="true"` | `font-weight: bold` | |
| 기울임(bit 0) | `italic="true"` | `font-style: italic` | |
| 밑줄(bit 2-3) | `underline type` | `text-decoration: underline` | |
| 취소선(bit 18-20) | `strikeout type` | `text-decoration: line-through` | |
| 위첨자(bit 15) | `supscript` | `vertical-align: super; font-size: 70%` | |
| 아래첨자(bit 16) | `subscript` | `vertical-align: sub; font-size: 70%` | |
### 4.2 HWPX charPr 구조 예시
```xml
<hh:charPr id="1" height="1000" bold="false" italic="false"
underline="NONE" strikeout="NONE" color="#000000">
<hh:fontRef hangul="0" latin="0" hanja="0" japanese="0"
other="0" symbol="0" user="0"/>
<hh:ratio hangul="100" latin="100" .../>
<hh:spacing hangul="0" latin="0" .../>
<hh:relSz hangul="100" latin="100" .../>
<hh:offset hangul="0" latin="0" .../>
</hh:charPr>
```
---
## 5. 문단 모양 (ParaShape)
> HWP: `HWPTAG_PARA_SHAPE` (DocInfo, 54바이트)
> HWPX: `<hh:paraPr>` (header.xml paraProperties 내)
> CSS: text-align, margin, line-height, text-indent 등
### 5.1 정렬 방식
| HWP 값 (bit 2-4) | HWPX 속성값 | CSS text-align |
|:---:|:---:|:---:|
| 0 | `JUSTIFY` | `justify` |
| 1 | `LEFT` | `left` |
| 2 | `RIGHT` | `right` |
| 3 | `CENTER` | `center` |
| 4 | `DISTRIBUTE` | `justify` (근사) |
| 5 | `DISTRIBUTE_SPACE` | `justify` (근사) |
### 5.2 줄 간격 종류
| HWP 값 | HWPX 속성값 | CSS line-height | 비고 |
|:---:|:---:|:---:|:---:|
| 0 | `PERCENT` | `160%` (예) | 글자 크기 기준 % |
| 1 | `FIXED` | `24pt` (예) | 고정 pt |
| 2 | `BETWEEN_LINES` | — | 여백만 지정 |
| 3 | `AT_LEAST` | — | 최소값 |
### 5.3 주요 속성 매핑
| HWP 필드 | HWPX 속성 | CSS 속성 | 단위 |
|----------|----------|---------|------|
| 왼쪽 여백 | `margin left` | `margin-left` / `padding-left` | HWPUNIT → mm |
| 오른쪽 여백 | `margin right` | `margin-right` / `padding-right` | HWPUNIT → mm |
| 들여쓰기 | `indent` | `text-indent` | HWPUNIT → mm |
| 문단 간격 위 | `spacing before` | `margin-top` | HWPUNIT → mm |
| 문단 간격 아래 | `spacing after` | `margin-bottom` | HWPUNIT → mm |
| 줄 간격 | `lineSpacing` | `line-height` | 종류에 따라 다름 |
| BorderFill ID | `borderFillIDRef` | border + background | ID로 참조 |
### 5.4 HWPX paraPr 구조 예시
```xml
<hh:paraPr id="0" align="JUSTIFY">
<hh:margin left="0" right="0" indent="0"/>
<hh:spacing before="0" after="0"
lineSpacingType="PERCENT" lineSpacing="160"/>
<hh:border borderFillIDRef="1"
left="0" right="0" top="0" bottom="0"/>
<hh:autoSpacing eAsianEng="false" eAsianNum="false"/>
</hh:paraPr>
```
---
## 6. 표 (Table) 구조
> HWP: `HWPTAG_TABLE` (본문 레코드)
> HWPX: `<hp:tbl>` (section*.xml 내)
> HTML: `<table>`, `<tr>`, `<td>`/`<th>`
### 6.1 표 속성 매핑
| HWP 필드 | HWPX 속성 | HTML/CSS 대응 | 비고 |
|----------|----------|-------------|------|
| RowCount | `rowCnt` | (행 수) | |
| nCols | `colCnt` | (열 수) | `<colgroup>` 참조 |
| CellSpacing | `cellSpacing` | `border-spacing` | HWPUNIT16 |
| 안쪽 여백 | `cellMargin` left/right/top/bottom | `padding` | |
| BorderFill ID | `borderFillIDRef` | 표 전체 테두리 | |
| 쪽나눔(bit 0-1) | `pageBreak` | `page-break-inside` | 0=avoid, 1=auto |
| 제목줄 반복(bit 2) | `repeatHeader` | `<thead>` 출력 | |
### 6.2 열 너비
```xml
<!-- HWPX -->
<hp:tbl colCnt="3" rowCnt="5" ...>
<hp:colSz>
<hp:widthList>8504 8504 8504</hp:widthList> <!-- HWPUNIT -->
</hp:colSz>
...
</hp:tbl>
```
```html
<!-- HTML 변환 -->
<colgroup>
<col style="width: 33.33%"> <!-- 8504 / 총합 * 100 -->
<col style="width: 33.33%">
<col style="width: 33.33%">
</colgroup>
```
### 6.3 셀 (Cell) 속성
| HWP 필드 | HWPX 속성 | HTML 속성 | 비고 |
|----------|----------|----------|------|
| Column 주소 | `colAddr` | — | 0부터 시작 |
| Row 주소 | `rowAddr` | — | 0부터 시작 |
| 열 병합 개수 | `colSpan` | `colspan` | 1 = 병합 없음 |
| 행 병합 개수 | `rowSpan` | `rowspan` | 1 = 병합 없음 |
| 셀 폭 | `width` | `width` | HWPUNIT |
| 셀 높이 | `height` | `height` | HWPUNIT |
| 셀 여백 [4] | `cellMargin` | `padding` | HWPUNIT16 → mm |
| BorderFill ID | `borderFillIDRef` | `border` + `background` | 셀별 스타일 |
### 6.4 HWPX 셀 구조 예시
```xml
<hp:tc colAddr="0" rowAddr="0" colSpan="2" rowSpan="1"
width="17008" height="2400" borderFillIDRef="4">
<hp:cellMargin left="510" right="510" top="142" bottom="142"/>
<hp:cellAddr colAddr="0" rowAddr="0"/>
<hp:subList ...>
<hp:p ...>
<!-- 셀 내용 -->
</hp:p>
</hp:subList>
</hp:tc>
```
```html
<!-- HTML 변환 -->
<td colspan="2" style="
width: 60mm;
height: 8.5mm;
padding: 0.5mm 1.8mm;
border: 0.12mm solid #000;
background-color: #E8F5E9;
">셀 내용</td>
```
### 6.5 병합 셀 처리 규칙
HWP/HWPX에서 병합된 셀은 **왼쪽 위 셀만 존재**하고, 병합에 포함된 다른 셀은 아예 없다.
HTML에서는 colspan/rowspan으로 표현하고, 병합된 위치의 `<td>`를 생략한다.
```
HWPX: colSpan="2", rowSpan="3" at (col=0, row=0)
→ 이 셀이 col 0~1, row 0~2를 차지
→ col=1/row=0, col=0/row=1, col=1/row=1, col=0/row=2, col=1/row=2 셀은 없음
HTML: <td colspan="2" rowspan="3">...</td>
→ 해당 행/열 위치에서 <td> 생략
```
---
## 7. 용지 설정 (PageDef / SecPr)
> HWP: `HWPTAG_PAGE_DEF` (구역 정의 하위)
> HWPX: `<hp:secPr>` → `<hp:pageDef>` (section*.xml 내)
> CSS: `@page`, `@media print`
### 7.1 용지 크기 사전 정의
| 용지 이름 | 가로 (mm) | 세로 (mm) | HWPUNIT (가로×세로) |
|----------|----------|----------|:---:|
| A4 | 210 | 297 | 59528 × 84188 |
| A3 | 297 | 420 | 84188 × 119055 |
| B5 | 176 | 250 | 49896 × 70866 |
| Letter | 215.9 | 279.4 | 61200 × 79200 |
| Legal | 215.9 | 355.6 | 61200 × 100800 |
### 7.2 여백 매핑
```xml
<!-- HWPX section0.xml -->
<hp:secPr>
<hp:pageDef width="59528" height="84188"
landscape="NARROWLY"> <!-- 좁게 = 세로 -->
<hp:margin left="8504" right="8504"
top="5668" bottom="4252"
header="4252" footer="4252"
gutter="0"/>
</hp:pageDef>
</hp:secPr>
```
```css
/* CSS 변환 */
@page {
size: A4 portrait; /* 210mm × 297mm */
margin-top: 20mm; /* 5668 / 7200 * 25.4 ≈ 20mm */
margin-bottom: 15mm; /* 4252 → 15mm */
margin-left: 30mm; /* 8504 → 30mm */
margin-right: 30mm; /* 8504 → 30mm */
}
/* 머리말/꼬리말 여백은 CSS에서 body padding으로 근사 */
```
### 7.3 용지 방향
| HWP 값 (bit 0) | HWPX 속성값 | CSS |
|:---:|:---:|:---:|
| 0 | `NARROWLY` (좁게) | `portrait` |
| 1 | `WIDELY` (넓게) | `landscape` |
---
## 8. 머리말/꼬리말 (Header/Footer)
> HWP: `HWPTAG_CTRL_HEADER` → 컨트롤 ID `head` / `foot`
> HWPX: `<hp:headerFooter>` (section*.xml 내, 또는 별도 header/footer 영역)
> HTML: 페이지 상단/하단 고정 영역
### 8.1 머리말/꼬리말 적용 범위
| HWP/HWPX 설정 | 의미 |
|:---:|:---:|
| 양쪽 | 모든 쪽에 동일 |
| 짝수쪽만 | 짝수 페이지 |
| 홀수쪽만 | 홀수 페이지 |
### 8.2 HTML 근사 표현
```html
<!-- 머리말 -->
<div class="page-header" style="
position: absolute; top: 0; left: 0; right: 0;
height: 15mm; /* header margin 값 */
padding: 0 30mm; /* 좌우 본문 여백 */
">
<table class="header-table">...</table>
</div>
<!-- 꼬리말 -->
<div class="page-footer" style="
position: absolute; bottom: 0; left: 0; right: 0;
height: 15mm; /* footer margin 값 */
padding: 0 30mm;
">
<span class="footer-text">페이지 번호</span>
</div>
```
---
## 9. 구역 정의 (Section)
> HWP: 구역 정의 컨트롤 (`secd`)
> HWPX: `<hp:secPr>` (section*.xml 최상위)
### 9.1 구역 속성
| 속성 | HWPX | CSS/HTML 대응 | 비고 |
|------|------|-------------|------|
| 머리말 감춤 | `hideHeader` | header 영역 display:none | |
| 꼬리말 감춤 | `hideFooter` | footer 영역 display:none | |
| 텍스트 방향 | `textDirection` | `writing-mode` | 0=가로, 1=세로 |
| 단 정의 | `<hp:colDef>` | CSS `columns` / `column-count` | |
| 쪽 번호 | `pageStartNo` | 쪽 번호 출력 값 | 0=이어서 |
---
## 10. HTML → HWPX → HWP 변환 파이프라인
### 10.1 전체 흐름
```
[HTML (우리 출력)]
↓ (1) HTML 파싱 → CSS 속성 추출
[중간 표현 (JSON)]
↓ (2) 이 가이드의 역방향 매핑
[HWPX (XML ZIP)]
↓ (3) 한컴오피스 변환 도구
[HWP (바이너리)]
```
### 10.2 단계별 매핑 방향
| 단계 | 입력 | 출력 | 참조할 가이드 섹션 |
|:---:|:---:|:---:|:---:|
| HTML → HWPX | CSS border | `<hh:borderFill>` 생성 | §2 역방향 |
| HTML → HWPX | CSS font | `<hh:charPr>` + `<hh:fontface>` | §3, §4 역방향 |
| HTML → HWPX | CSS text-align 등 | `<hh:paraPr>` | §5 역방향 |
| HTML → HWPX | `<table>` | `<hp:tbl>` + `<hp:tc>` | §6 역방향 |
| HTML → HWPX | @page CSS | `<hp:pageDef>` | §7 역방향 |
| HTML → HWPX | header/footer div | `<hp:headerFooter>` | §8 역방향 |
### 10.3 CSS → HWPX 역변환 예시
```python
def css_border_to_hwpx(css_border):
"""'0.12mm solid #000000' → HWPX 속성"""
parts = css_border.split()
width = parts[0] # '0.12mm'
style = parts[1] # 'solid'
color = parts[2] # '#000000'
hwpx_type = CSS_TO_BORDER_TYPE.get(style, 'SOLID')
return {
'type': hwpx_type,
'width': width,
'color': color
}
CSS_TO_BORDER_TYPE = {
'solid': 'SOLID', 'dashed': 'DASH', 'dotted': 'DOT',
'double': 'DOUBLE', 'ridge': 'THICK_3D', 'groove': 'THICK_3D_REV',
'outset': '3D', 'inset': '3D_REV', 'none': 'NONE'
}
```
### 10.4 HWPX ZIP 구조 생성
```
output.hwpx (ZIP)
├── META-INF/
│ └── manifest.xml ← 파일 목록
├── Contents/
│ ├── header.xml ← DocInfo (글꼴, 스타일, borderFill)
│ ├── section0.xml ← 본문 (문단, 표, 머리말/꼬리말)
│ └── content.hpf ← 콘텐츠 메타
├── BinData/ ← 이미지 등
├── Preview/
│ └── PrvImage.png ← 미리보기
└── version.xml ← 버전 정보
```
**header.xml 필수 구조**:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<hh:head xmlns:hh="...">
<hh:beginNum .../>
<hh:refList>
<hh:fontfaces>...</hh:fontfaces> <!-- §3 -->
<hh:borderFills>...</hh:borderFills> <!-- §2 -->
<hh:charProperties>...</hh:charProperties> <!-- §4 -->
<hh:tabProperties>...</hh:tabProperties>
<hh:numberingProperties>...</hh:numberingProperties>
<hh:bulletProperties>...</hh:bulletProperties>
<hh:paraProperties>...</hh:paraProperties> <!-- §5 -->
<hh:styles>...</hh:styles>
</hh:refList>
</hh:head>
```
**section0.xml 필수 구조**:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<hp:sec xmlns:hp="...">
<hp:secPr>
<hp:pageDef .../> <!-- §7 -->
<hp:headerFooter .../> <!-- §8 -->
</hp:secPr>
<hp:p paraPrIDRef="0" styleIDRef="0"> <!-- 문단 -->
<hp:run charPrIDRef="0">
<hp:t>텍스트</hp:t>
</hp:run>
</hp:p>
<hp:p ...>
<hp:ctrl>
<hp:tbl ...>...</hp:tbl> <!-- §6 -->
</hp:ctrl>
</hp:p>
</hp:sec>
```
---
## 11. 시스템 적용 가이드
### 11.1 적용 대상 모듈
| 모듈 | 파일 | 이 가이드 활용 방식 |
|------|------|:---:|
| **doc_template_analyzer.py** | HWPX → HTML 템플릿 추출 | §2,6,7,8 정방향 (HWPX→CSS) |
| **template_manager.py** | 추출된 스타일 저장/로드 | §2 borderFill ID 매핑 |
| **custom_doc_type.py** | HTML 문서 생성 | §2,4,5 CSS 값 참조 |
| **hwpx_converter.py** (예정) | HTML → HWPX 변환 | §2~8 역방향 (CSS→HWPX) |
| **hwp_converter.py** (예정) | HWPX → HWP 변환 | §1 단위 변환 |
### 11.2 하드코딩 제거 전략
**현재 문제 (AS-IS)**:
```python
# doc_template_analyzer.py에 하드코딩됨
border_css = "2px solid var(--primary)"
header_bg = "#E8F5E9"
```
**해결 방향 (TO-BE)**:
```python
# style.json에서 추출된 borderFill 참조
bf = style['border_fills']['3'] # id=3
border_css = f"{bf['top']['width']} {bf['top']['css_style']} {bf['top']['color']}"
# → "0.12mm solid #000000"
header_bf = style['border_fills']['4'] # id=4 (헤더 배경 포함)
header_bg = header_bf.get('background', 'none')
# → "#E8F5E9"
```
### 11.3 이 가이드를 코드에서 참조하는 방식
이 문서(`hwpx_domain_guide.md`)는 다음과 같이 활용한다:
1. **변환 테이블을 JSON으로 추출**`hwpx_mappings.json`
- 테두리선 종류, 굵기, 색상 변환 등의 룩업 테이블
2. **변환 함수 라이브러리**`hwpx_utils.py`
- `hwpunit_to_mm()`, `borderfill_to_css()`, `css_border_to_hwpx()`
3. **AI 프롬프트 컨텍스트** → 문서 유형/구조 분석 시 참조
- "이 HWPX의 borderFill id=3은 실선 0.12mm 검정이므로 표 일반 셀에 해당"
4. **검증 기준** → 변환 결과물 검증 시 정확성 확인
- 추출된 CSS가 원본 HWPX의 스펙과 일치하는지
---
## 부록 A. 빠른 참조 — HWPX XML 태그 ↔ HWP 레코드 대응
| HWP 레코드 (Tag ID) | HWPX XML 엘리먼트 | 위치 |
|---------------------|------------------|------|
| HWPTAG_DOCUMENT_PROPERTIES | `<hh:beginNum>` 등 | header.xml |
| HWPTAG_ID_MAPPINGS | (암묵적) | header.xml |
| HWPTAG_FACE_NAME | `<hh:font>` | header.xml > fontfaces |
| HWPTAG_BORDER_FILL | `<hh:borderFill>` | header.xml > borderFills |
| HWPTAG_CHAR_SHAPE | `<hh:charPr>` | header.xml > charProperties |
| HWPTAG_TAB_DEF | `<hh:tabPr>` | header.xml > tabProperties |
| HWPTAG_NUMBERING | `<hh:numbering>` | header.xml > numberingProperties |
| HWPTAG_BULLET | `<hh:bullet>` | header.xml > bulletProperties |
| HWPTAG_PARA_SHAPE | `<hh:paraPr>` | header.xml > paraProperties |
| HWPTAG_STYLE | `<hh:style>` | header.xml > styles |
| HWPTAG_PARA_HEADER | `<hp:p>` | section*.xml |
| HWPTAG_TABLE | `<hp:tbl>` | section*.xml > p > ctrl |
| (셀 속성) | `<hp:tc>` | section*.xml > tbl > tr > tc |
| HWPTAG_PAGE_DEF | `<hp:pageDef>` | section*.xml > secPr |
| (머리말/꼬리말) | `<hp:headerFooter>` | section*.xml > secPr |
## 부록 B. 빠른 참조 — CSS → HWPX 역변환
| CSS 속성 | HWPX 대응 | 변환 공식 |
|----------|----------|----------|
| `font-family` | `<hh:font face="...">` | 첫 번째 값 → hangul, 두 번째 → latin |
| `font-size: 10pt` | `<hh:charPr height="1000">` | pt × 100 |
| `font-weight: bold` | `bold="true"` | |
| `font-style: italic` | `italic="true"` | |
| `color: #1a365d` | `color="#1a365d"` | 동일 |
| `text-align: center` | `align="CENTER"` | 대문자 |
| `margin-left: 30mm` | `left="8504"` | mm → HWPUNIT |
| `line-height: 160%` | `lineSpacing="160"` + `type="PERCENT"` | |
| `border: 1px solid #000` | `<hh:borderFill>` 내 각 방향 | §2 참조 |
| `background-color: #E8F5E9` | `<hh:windowBrush faceColor="...">` | |
| `padding: 2mm 5mm` | `<hp:cellMargin top="567" left="1417">` | mm → HWPUNIT |
| `width: 210mm` | `width="59528"` | mm → HWPUNIT |
| `@page { size: A4 }` | `<hp:pageDef width="59528" height="84188">` | |
---
*이 가이드는 한글과컴퓨터의 "글 문서 파일 구조 5.0 (revision 1.3)"을 참고하여 작성되었습니다.*