--- name: hwpx description: "한글(HWPX) 문서 생성/읽기/편집 스킬. .hwpx 파일, 한글 문서, Hancom, OWPML 관련 요청 시 사용." --- # HWPX 문서 스킬 — XML-first 워크플로우 한글(Hancom Office)의 HWPX 파일을 **XML 직접 작성** 중심으로 생성, 편집, 읽기할 수 있는 스킬. HWPX는 ZIP 기반 XML 컨테이너(OWPML 표준)이다. python-hwpx API의 서식 버그를 완전히 우회하며, 세밀한 서식 제어가 가능하다. ## 환경 ``` # SKILL_DIR는 이 SKILL.md가 위치한 디렉토리의 절대 경로로 설정 SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)" # 스크립트 내에서 # 또는 Claude Code가 자동으로 주입하는 base directory 경로를 사용 # Python 가상환경 (프로젝트에 맞게 설정) VENV="<프로젝트>/.venv/bin/activate" ``` 모든 Python 실행 시: ```bash # 프로젝트의 .venv를 활성화 (pip install lxml 필요) source "$VENV" ``` ## 디렉토리 구조 ``` .claude/skills/hwpx/ ├── SKILL.md # 이 파일 ├── scripts/ │ ├── office/ │ │ ├── unpack.py # HWPX → 디렉토리 (XML pretty-print) │ │ └── pack.py # 디렉토리 → HWPX │ ├── build_hwpx.py # 템플릿 + XML → .hwpx 조립 (핵심) │ ├── analyze_template.py # HWPX 심층 분석 (레퍼런스 기반 생성용) │ ├── validate.py # HWPX 구조 검증 │ └── text_extract.py # 텍스트 추출 ├── templates/ │ ├── base/ # 베이스 템플릿 (Skeleton 기반) │ │ ├── mimetype, META-INF/*, version.xml, settings.xml, Preview/* │ │ └── Contents/ (header.xml, section0.xml, content.hpf) │ ├── gonmun/ # 공문 오버레이 (header.xml, section0.xml) │ ├── report/ # 보고서 오버레이 │ ├── minutes/ # 회의록 오버레이 │ └── proposal/ # 제안서/사업개요 오버레이 (색상 헤더바, 번호 배지) ├── examples/ │ ├── 01_basic_document.sh # XML로 기본 문서 빌드 │ ├── 02_gonmun_example.sh # 공문 템플릿 사용 │ ├── 03_report_with_table.sh # 표 포함 보고서 │ ├── 04_read_and_extract.py # 기존 문서 읽기/추출 │ ├── 05_edit_existing.sh # unpack→편집→pack │ ├── sample_section0.xml # 주석 달린 section0 예제 │ └── sample_header.xml # 주석 달린 header 예제 └── references/ └── hwpx-format.md # OWPML XML 요소 레퍼런스 ``` --- ## 워크플로우 1: XML-first 문서 생성 (주 워크플로우) ### 흐름 1. **템플릿 선택** (base/gonmun/report/minutes/proposal) 2. **section0.xml 작성** (본문 내용) 3. **(선택) header.xml 수정** (새 스타일 추가 필요 시) 4. **build_hwpx.py로 빌드** 5. **validate.py로 검증** ### 기본 사용법 ```bash source "$VENV" # 빈 문서 (base 템플릿) python3 "$SKILL_DIR/scripts/build_hwpx.py" --output result.hwpx # 템플릿 사용 python3 "$SKILL_DIR/scripts/build_hwpx.py" --template gonmun --output result.hwpx # 커스텀 section0.xml 오버라이드 python3 "$SKILL_DIR/scripts/build_hwpx.py" --template gonmun --section my_section0.xml --output result.hwpx # header도 오버라이드 python3 "$SKILL_DIR/scripts/build_hwpx.py" --header my_header.xml --section my_section0.xml --output result.hwpx # 메타데이터 설정 python3 "$SKILL_DIR/scripts/build_hwpx.py" --template report --section my.xml \ --title "제목" --creator "작성자" --output result.hwpx ``` ### 실전 패턴: section0.xml을 인라인 작성 → 빌드 ```bash # 1. section0.xml을 임시파일로 작성 SECTION=$(mktemp /tmp/section0_XXXX.xml) cat > "$SECTION" << 'XMLEOF' 본문 내용 XMLEOF # 2. 빌드 python3 "$SKILL_DIR/scripts/build_hwpx.py" --section "$SECTION" --output result.hwpx # 3. 정리 rm -f "$SECTION" ``` --- ## section0.xml 작성 가이드 ### 필수 구조 section0.xml의 첫 문단(``)의 첫 런(``)에 반드시 ``과 `` 포함: ```xml ``` **Tip**: `templates/base/Contents/section0.xml` 의 첫 문단을 그대로 복사하면 된다. ### 문단 ```xml 텍스트 내용 ``` ### 빈 줄 ```xml ``` ### 서식 혼합 런 (한 문단에 여러 스타일) ```xml 일반 텍스트 볼드 텍스트 다시 일반 ``` ### 표 작성법 ```xml 헤더 셀 ``` ### 표 크기 계산 - **A4 본문폭**: 42520 HWPUNIT = 59528(용지) - 8504×2(좌우여백) - **열 너비 합 = 본문폭** (42520) - 예: 3열 균등 → 14173 + 14173 + 14174 = 42520 - 예: 2열 (라벨:내용 = 1:4) → 8504 + 34016 = 42520 - **행 높이**: 셀당 보통 2400~3600 HWPUNIT ### ID 규칙 - 문단 id: `1000000001`부터 순차 증가 - 표 id: `1000000099` 등 별도 범위 사용 권장 - 모든 id는 문서 내 고유해야 함 --- ## header.xml 수정 가이드 ### 커스텀 스타일 추가 방법 1. `templates/base/Contents/header.xml` 복사 2. 필요한 charPr/paraPr/borderFill 추가 3. 각 그룹의 `itemCnt` 속성 업데이트 ### charPr 추가 예시 (볼드 14pt) ```xml ``` ### 폰트 참조 체계 - `fontRef` 값은 `fontfaces`에 정의된 font id - `hangul="0"` → 함초롬돋움 (고딕) - `hangul="1"` → 함초롬바탕 (명조) - 7개 언어 모두 동일하게 설정 ### paraPr 추가 시 주의 - 반드시 `hp:switch` 구조 포함 (`hp:case` + `hp:default`) - `hp:case`와 `hp:default`의 값은 보통 동일 (또는 default가 2배) - `borderFillIDRef="2"` 유지 --- ## 템플릿별 스타일 ID 맵 ### base (기본) | ID | 유형 | 설명 | |----|------|------| | charPr 0 | 글자 | 10pt 함초롬바탕, 기본 | | charPr 1 | 글자 | 10pt 함초롬돋움 | | charPr 2~6 | 글자 | Skeleton 기본 스타일 | | paraPr 0 | 문단 | JUSTIFY, 160% 줄간격 | | paraPr 1~19 | 문단 | Skeleton 기본 (개요, 각주 등) | | borderFill 1 | 테두리 | 없음 (페이지 보더) | | borderFill 2 | 테두리 | 없음 + 투명배경 (참조용) | ### gonmun (공문) — base + 추가 | ID | 유형 | 설명 | |----|------|------| | charPr 7 | 글자 | 22pt 볼드 함초롬바탕 (기관명/제목) | | charPr 8 | 글자 | 16pt 볼드 함초롬바탕 (서명자) | | charPr 9 | 글자 | 8pt 함초롬바탕 (하단 연락처) | | charPr 10 | 글자 | 10pt 볼드 함초롬바탕 (표 헤더) | | paraPr 20 | 문단 | CENTER, 160% 줄간격 | | paraPr 21 | 문단 | CENTER, 130% (표 셀) | | paraPr 22 | 문단 | JUSTIFY, 130% (표 셀) | | borderFill 3 | 테두리 | SOLID 0.12mm 4면 | | borderFill 4 | 테두리 | SOLID 0.12mm + #D6DCE4 배경 | ### report (보고서) — base + 추가 | ID | 유형 | 설명 | |----|------|------| | charPr 7 | 글자 | 20pt 볼드 (문서 제목) | | charPr 8 | 글자 | 14pt 볼드 (소제목) | | charPr 9 | 글자 | 10pt 볼드 (표 헤더) | | charPr 10 | 글자 | 10pt 볼드+밑줄 (강조 텍스트) | | charPr 11 | 글자 | 9pt 함초롬바탕 (소형/각주) | | charPr 12 | 글자 | 16pt 볼드 함초롬바탕 (1줄 제목) | | charPr 13 | 글자 | 12pt 볼드 함초롬돋움 (섹션 헤더) | | paraPr 20~22 | 문단 | CENTER/JUSTIFY 변형 | | paraPr 23 | 문단 | RIGHT 정렬, 160% 줄간격 | | paraPr 24 | 문단 | JUSTIFY, left 600 (□ 체크항목 들여쓰기) | | paraPr 25 | 문단 | JUSTIFY, left 1200 (하위항목 ①②③ 들여쓰기) | | paraPr 26 | 문단 | JUSTIFY, left 1800 (깊은 하위항목 - 들여쓰기) | | paraPr 27 | 문단 | LEFT, 상하단 테두리선 (섹션 헤더용), prev 400 | | borderFill 3 | 테두리 | SOLID 0.12mm 4면 | | borderFill 4 | 테두리 | SOLID 0.12mm + #DAEEF3 배경 | | borderFill 5 | 테두리 | 상단 0.4mm 굵은선 + 하단 0.12mm 얇은선 (섹션 헤더) | **들여쓰기 규칙**: 공백 문자가 아닌 반드시 paraPr의 left margin 사용. □ 항목은 paraPr 24, 하위 ①②③ 는 paraPr 25, 깊은 - 항목은 paraPr 26. **섹션 헤더 규칙**: paraPr 27 + charPr 13 조합. 문단 테두리(borderFillIDRef="5")로 상단 굵은선 + 하단 얇은선 자동 표시. ### minutes (회의록) — base + 추가 | ID | 유형 | 설명 | |----|------|------| | charPr 7 | 글자 | 18pt 볼드 (제목) | | charPr 8 | 글자 | 12pt 볼드 (섹션 라벨) | | charPr 9 | 글자 | 10pt 볼드 (표 헤더) | | paraPr 20~22 | 문단 | CENTER/JUSTIFY 변형 | | borderFill 3 | 테두리 | SOLID 0.12mm 4면 | | borderFill 4 | 테두리 | SOLID 0.12mm + #E2EFDA 배경 | ### proposal (제안서/사업개요) — base + 추가 시각적 구분이 필요한 공식 문서용. 색상 배경 헤더바와 번호 배지를 표(table) 기반 레이아웃으로 구현. | ID | 유형 | 설명 | |----|------|------| | charPr 7 | 글자 | 20pt 볼드 함초롬바탕 (문서 제목) | | charPr 8 | 글자 | 14pt 볼드 함초롬바탕 (소제목) | | charPr 9 | 글자 | 10pt 볼드 함초롬바탕 (표 헤더) | | charPr 10 | 글자 | 14pt 볼드 흰색 함초롬돋움 (대항목 번호, 녹색 배경) | | charPr 11 | 글자 | 11pt 볼드 흰색 함초롬돋움 (소항목 번호, 파란 배경) | | paraPr 20 | 문단 | CENTER, 160% 줄간격 | | paraPr 21 | 문단 | CENTER, 130% (표 셀) | | paraPr 22 | 문단 | JUSTIFY, 130% (표 셀) | | borderFill 3 | 테두리 | SOLID 0.12mm 4면 | | borderFill 4 | 테두리 | SOLID 0.12mm + #DAEEF3 배경 | | borderFill 5 | 테두리 | 올리브녹색 배경 #7B8B3D (대항목 번호 셀) | | borderFill 6 | 테두리 | 연한 회색 배경 #F2F2F2 + 회색 테두리 (대항목 제목 셀) | | borderFill 7 | 테두리 | 파란색 배경 #4472C4 (소항목 번호 배지) | | borderFill 8 | 테두리 | 하단 테두리만 #D0D0D0 (소항목 제목 영역) | #### proposal 레이아웃 패턴 **대항목 헤더** (2셀 표: 번호 + 제목): ```xml ``` **소항목 헤더** (2셀 표: 번호배지 + 제목): ```xml ``` --- ## 워크플로우 2: 기존 문서 편집 (unpack → Edit → pack) ```bash source "$VENV" # 1. HWPX → 디렉토리 (XML pretty-print) python3 "$SKILL_DIR/scripts/office/unpack.py" document.hwpx ./unpacked/ # 2. XML 직접 편집 (Claude가 Read/Edit 도구로) # 본문: ./unpacked/Contents/section0.xml # 스타일: ./unpacked/Contents/header.xml # 3. 다시 HWPX로 패키징 python3 "$SKILL_DIR/scripts/office/pack.py" ./unpacked/ edited.hwpx # 4. 검증 python3 "$SKILL_DIR/scripts/validate.py" edited.hwpx ``` --- ## 워크플로우 3: 읽기/텍스트 추출 ```bash source "$VENV" # 순수 텍스트 python3 "$SKILL_DIR/scripts/text_extract.py" document.hwpx # 테이블 포함 python3 "$SKILL_DIR/scripts/text_extract.py" document.hwpx --include-tables # 마크다운 형식 python3 "$SKILL_DIR/scripts/text_extract.py" document.hwpx --format markdown ``` ### Python API ```python from hwpx import TextExtractor with TextExtractor("document.hwpx") as ext: text = ext.extract_text(include_nested=True, object_behavior="nested") print(text) ``` --- ## 워크플로우 4: 검증 ```bash source "$VENV" python3 "$SKILL_DIR/scripts/validate.py" document.hwpx ``` 검증 항목: ZIP 유효성, 필수 파일 존재, mimetype 내용/위치/압축방식, XML well-formedness --- ## 워크플로우 5: 레퍼런스 기반 문서 생성 사용자가 제공한 HWPX 파일을 분석하여 동일한 레이아웃의 문서를 생성하는 워크플로우. ### 흐름 1. **분석** — `analyze_template.py`로 레퍼런스 문서 심층 분석 2. **header.xml 추출** — 레퍼런스의 스타일 정의를 그대로 사용 3. **section0.xml 작성** — 분석 결과의 구조를 따라 새 내용으로 작성 4. **빌드** — 추출한 header.xml + 새 section0.xml로 빌드 5. **검증** ### 사용법 ```bash source "$VENV" # 1. 심층 분석 (구조 청사진 출력) python3 "$SKILL_DIR/scripts/analyze_template.py" reference.hwpx # 2. header.xml과 section0.xml을 추출하여 참고용으로 보관 python3 "$SKILL_DIR/scripts/analyze_template.py" reference.hwpx \ --extract-header /tmp/ref_header.xml \ --extract-section /tmp/ref_section.xml # 3. 분석 결과를 보고 새 section0.xml 작성 # - 동일한 charPrIDRef, paraPrIDRef 사용 # - 동일한 테이블 구조 (열 수, 열 너비, 행 수, rowSpan/colSpan) # - 동일한 borderFillIDRef, cellMargin # 4. 추출한 header.xml + 새 section0.xml로 빌드 python3 "$SKILL_DIR/scripts/build_hwpx.py" \ --header /tmp/ref_header.xml \ --section /tmp/new_section0.xml \ --output result.hwpx # 5. 검증 python3 "$SKILL_DIR/scripts/validate.py" result.hwpx ``` ### 분석 출력 항목 | 항목 | 설명 | |------|------| | 폰트 정의 | hangul/latin 폰트 매핑 | | borderFill | 테두리 타입/두께 + 배경색 (각 면별 상세) | | charPr | 글꼴 크기(pt), 폰트명, 색상, 볼드/이탤릭/밑줄/취소선, fontRef | | paraPr | 정렬, 줄간격, 여백(left/right/prev/next/intent), heading, borderFillIDRef | | 문서 구조 | 페이지 크기, 여백, 페이지 테두리, 본문폭 | | 본문 상세 | 모든 문단의 id/paraPr/charPr + 텍스트 내용 | | 표 상세 | 행×열, 열너비 배열, 셀별 span/margin/borderFill/vertAlign + 내용 | ### 핵심 원칙 - **charPrIDRef/paraPrIDRef를 그대로 사용**: 추출한 header.xml의 스타일 ID를 변경하지 말 것 - **열 너비 합계 = 본문폭**: 분석 결과의 열너비 배열을 그대로 복제 - **rowSpan/colSpan 패턴 유지**: 분석된 셀 병합 구조를 정확히 재현 - **cellMargin 보존**: 분석된 셀 여백 값을 동일하게 적용 --- ## 스크립트 요약 | 스크립트 | 용도 | |----------|------| | `scripts/build_hwpx.py` | **핵심** — 템플릿 + XML → HWPX 조립 | | `scripts/analyze_template.py` | HWPX 심층 분석 (레퍼런스 기반 생성의 청사진) | | `scripts/office/unpack.py` | HWPX → 디렉토리 (XML pretty-print) | | `scripts/office/pack.py` | 디렉토리 → HWPX (mimetype first) | | `scripts/validate.py` | HWPX 파일 구조 검증 | | `scripts/text_extract.py` | HWPX 텍스트 추출 | ## 단위 변환 | 값 | HWPUNIT | 의미 | |----|---------|------| | 1pt | 100 | 기본 단위 | | 10pt | 1000 | 기본 글자크기 | | 1mm | 283.5 | 밀리미터 | | 1cm | 2835 | 센티미터 | | A4 폭 | 59528 | 210mm | | A4 높이 | 84186 | 297mm | | 좌우여백 | 8504 | 30mm | | 본문폭 | 42520 | 150mm (A4-좌우여백) | ## Critical Rules 1. **HWPX만 지원**: `.hwp`(바이너리) 파일은 지원하지 않는다. 사용자가 `.hwp` 파일을 제공하면 **한글 오피스에서 `.hwpx`로 다시 저장**하도록 안내할 것. (파일 → 다른 이름으로 저장 → 파일 형식: HWPX) 2. **secPr 필수**: section0.xml 첫 문단의 첫 run에 반드시 secPr + colPr 포함 3. **mimetype 순서**: HWPX 패키징 시 mimetype은 첫 번째 ZIP 엔트리, ZIP_STORED 4. **네임스페이스 보존**: XML 편집 시 `hp:`, `hs:`, `hh:`, `hc:` 접두사 유지 5. **itemCnt 정합성**: header.xml의 charProperties/paraProperties/borderFills itemCnt가 실제 자식 수와 일치 6. **ID 참조 정합성**: section0.xml의 charPrIDRef/paraPrIDRef가 header.xml 정의와 일치 7. **venv 사용**: 프로젝트의 `.venv/bin/python3` (lxml 패키지 필요) 8. **검증**: 생성 후 반드시 `validate.py`로 무결성 확인 9. **레퍼런스**: 상세 XML 구조는 `$SKILL_DIR/references/hwpx-format.md` 참조 10. **build_hwpx.py 우선**: 새 문서 생성은 build_hwpx.py 사용 (python-hwpx API 직접 호출 지양) 11. **빈 줄**: `` 사용 (self-closing tag)