mcpField
H
hwpx

hwpx

github.com/canine89文档处理327247 次安装18 周前更新
代码仓库

关于 hwpx

面向 Hancom Office HWPX 文件的 XML 优先处理技能,适合需要生成、读取或编辑韩文文档的用户。提供参考文件时,它优先保留原有结构与版式,并通过结构、页数和内容检查验证结果;仅支持 HWPX 输出。

主要功能

  1. 01直接处理 OWPML XML
  2. 02尽量保留原文档结构与样式
  3. 03按槽位修改段落和表格单元格
  4. 04检查结构、页数与内容

适用场景

  1. 01在现有 HWPX 表单中填入信息
  2. 02修改文档文字并保留格式
  3. 03从模板生成 HWPX 文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/canine89/hwpxskill --skill hwpx

安装后在 Claude Code 中输入/hwpx确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.gitignore
README.md
references/
jkf87-hwpx-skill-comparison.md
hwpx-format.md
scripts/
create_document.py
content_guard.py
build_hwpx.py
analyze_template.py
finalize_hwpx.py
fix_namespaces.py
gonmun_lint.py
hwpx_slots.py
validate.py
text_extract.py
edit_hwpx.py
page_guard.py
scripts/office/
pack.py
unpack.py
templates/base/
mimetype
settings.xml
version.xml
templates/base/Contents/
content.hpf
header.xml
section0.xml
templates/base/META-INF/
container.xml
container.rdf
manifest.xml
templates/base/Preview/
PrvText.txt
templates/gonmun/
header.xml
templates/minutes/
header.xml

详细介绍

HWPX 文档技能——优先恢复参考文档的 XML 优先工作流

这项技能以直接编写 XML为核心,用于创建、编辑和读取韩文(Hancom Office)HWPX 文件。 HWPX 是基于 ZIP 的 XML 容器(OWPML 标准)。在需要保留格式的编辑中,会直接处理 OWPML XML,以便精细控制 charPr、paraPr 和表格结构。

拒绝处理 HWP 二进制文件的规则(必需)

这项技能仅支持 HWPX。如果用户要求编写、创建、保存、编辑、填写 .hwp 二进制文件或生成其转换结果,则不执行该任务。

  • 拒绝创建 .hwp 格式的结果文件。
  • 拒绝直接修改或填写 .hwp 原文件。
  • 可以读取 .hwp 作为参考,但只有在用户明确允许使用 HWPX 的情况下,才将结果制作为 .hwpx。
  • 如果用户要求编写 .hwp,但未明确允许以 HWPX 作为替代产物,则不得擅自创建 .hwpx 文件来代替完成任务。
  • 提示语应简洁:“这项技能仅支持 HWPX,无法编写 .hwp。您可以提供在 Hancom 中另存为 HWPX 的文件,或选择将结果保存为 .hwpx。”

默认工作模式(必需):分析附件 HWPX → 恢复原有 XML(接近 99%)→ 根据请求重写

用户附加 .hwpx 文件时,这项技能默认按以下顺序操作。

  1. 获取参考文档:将附件中的 HWPX 用作基准文档
  2. 提取槽位:使用 hwpx_slots.py 将可编辑段落/单元格槽位列表生成为 JSON
  3. 映射用户提供的值:将新值映射到槽位键(p:12、cell:0:2:1)
  4. 保留结构并编辑:使用 edit_hwpx.py --slot-json 复制原始软件包,并仅修改槽位文本
  5. 构建/验证:使用 validate.py 检查 edit_hwpx.py 或 build_hwpx.py 的结果是否完整
  6. 最终处理/布局警告:使用 fix_namespaces.py、finalize_hwpx.py --strip-linesegarray --layout 和 validate.py --layout 检查命名空间与渲染风险
  7. 字符预算/页数保护(必需):使用 page_guard.py 检查各段落/单元格的字符预算及相对于参考文档的页数偏移风险
  8. 内容完整性保护(必需):使用 content_guard.py 检查原文残留、占位符和必需关键词缺失
接近 99% 的恢复标准(实务检查清单)
  • charPrIDRef、paraPrIDRef、borderFillIDRef 的引用体系相同
  • 表格的 rowCnt、colCnt、colSpan、rowSpan、cellSz、cellMargin 相同
  • 段落顺序、段落数量、主要空行/分节位置相同
  • 页面/页边距/分节(secPr)相同
  • 修改仅限于用户请求的范围(正文文本、值、项目名称等)
  • 如果内容修改要求转换整份文档的视角(例如:韩国机构 → 美国机构、某家公司 → 另一家公司),仍残留旧机构名称/负责人/电话号码/政策名称即视为失败
页数必须相同(100%)的标准
  • 用户提供参考文档时,结果文档的最终页数必须与参考文档相同
  • 如果看起来页数可能增加,应先压缩/概括文本,使其适应现有版式
  • 未经用户明确要求,不得更改 hp:p、hp:tbl、rowCnt、colCnt、pageBreak、secPr
  • 不能仅凭 validate.py 通过就视为完成;还必须通过 page_guard.py 和 content_guard.py
  • 如果 page_guard.py 或 content_guard.py 检查失败,不得提交结果作为已完成;应先修复原因(内容过长/结构变更/原文残留/缺少必需关键词),然后重新构建
  • 尽可能以 Hancom(或用户确认)为准核实最终页数,并再次确认与参考文档一致
基本执行命令(有附件参考文档时)
source "$VENV"

# 1) 레퍼런스 분석 + XML 추출
python3 "$SKILL_DIR/scripts/analyze_template.py" reference.hwpx \
  --extract-header /tmp/ref_header.xml \
  --extract-section /tmp/ref_section.xml

# 2) 편집 가능한 슬롯 추출
python3 "$SKILL_DIR/scripts/hwpx_slots.py" reference.hwpx \
  --output /tmp/reference.slots.json

# 3) 원본 양식의 문단/셀별 글자 예산 저장
python3 "$SKILL_DIR/scripts/page_guard.py" \
  --reference reference.hwpx \
  --write-budget /tmp/reference.budget.json \
  --write-structure /tmp/reference.structure.json

# 3-1) 내용 완성도 규칙 작성
# 예: 한국 고용노동부 보도자료를 미국노동부 문서로 바꾸는 경우
cat > /tmp/content.rules.json <<'JSON'
{
  "require": ["미국노동부"],
  "forbid": ["고용노동부", "김영훈", "044-"],
  "forbid_regex": ["○○+", "\\.\\s*\\.\\s*\\."]
}
JSON

# 4) 양식 보존 편집
python3 "$SKILL_DIR/scripts/edit_hwpx.py" reference.hwpx \
  --output result.hwpx \
  --slot-json values.json

# 5) 검증 + 최종화
python3 "$SKILL_DIR/scripts/validate.py" result.hwpx
python3 "$SKILL_DIR/scripts/fix_namespaces.py" result.hwpx
python3 "$SKILL_DIR/scripts/finalize_hwpx.py" result.hwpx --strip-linesegarray --layout
python3 "$SKILL_DIR/scripts/validate.py" result.hwpx --layout

# 6) 글자 예산 + 쪽수 드리프트 가드 (필수)
python3 "$SKILL_DIR/scripts/page_guard.py" \
  --reference reference.hwpx \
  --output result.hwpx \
  --budget-profile /tmp/reference.budget.json \
  --structure-profile /tmp/reference.structure.json \
  --no-strict-paragraph-budget \
  --skip-text-drift \
  --allow-empty-fill

# 7) 내용 완성도 가드 (필수)
python3 "$SKILL_DIR/scripts/content_guard.py" result.hwpx \
  --rules /tmp/content.rules.json

# 문서 전체 관점 전환/전면 재작성인 경우 원본 긴 문장 잔존율도 제한
python3 "$SKILL_DIR/scripts/content_guard.py" result.hwpx \
  --reference reference.hwpx \
  --rules /tmp/content.rules.json \
  --max-unchanged-ratio 0.35

# 공문서라면 날짜/시간/금액/붙임 표기도 점검
python3 "$SKILL_DIR/scripts/gonmun_lint.py" --hwpx result.hwpx --format text

hwpx_slots.py 会排除包含表格/图片/文本框的容器段落,并将实际可编辑的段落和表格单元格作为槽位提供。用户只需为槽位键填写值。

edit_hwpx.py 会原样复制原始文件中的 mimetype、content.hpf、header.xml、settings.xml、Preview/* 和 BinData/*。默认情况下,仅修改 Contents/section0.xml 中的槽位文本。因此,如果用户提供了现有模板,且只需填写文本/单元格,应优先使用 edit_hwpx.py --slot-json,而不是 build_hwpx.py。

禁止通过常规 ZIP 重新压缩来打包。应原样复制原始 ZIP 的本地文件头和压缩数据,只替换需要修改的条目。未修改的条目必须与原文件具有相同的 CRC、压缩后大小、文件大小、标志位、压缩方式、日期、创建系统以及外部/内部属性。

edit_hwpx.py 使用规则
# 전체 텍스트 치환
python3 "$SKILL_DIR/scripts/edit_hwpx.py" form.hwpx \
  -o out.hwpx \
  --replace "기존문구=새문구"

# JSON 매핑 치환
python3 "$SKILL_DIR/scripts/edit_hwpx.py" form.hwpx \
  -o out.hwpx \
  --replace-json values.json

# 표 셀 채우기: row,col은 0부터 시작, table 생략 시 첫 번째 표
python3 "$SKILL_DIR/scripts/edit_hwpx.py" form.hwpx \
  -o out.hwpx \
  --cell "0,2,1=테스트 법인"

# 본문 문단 재작성: 문장 품질을 우선하고 원본 문단 예산 이하로만 제한
python3 "$SKILL_DIR/scripts/edit_hwpx.py" form.hwpx \
  -o out.hwpx \
  --paragraph-json paragraphs.json

# 권장: 슬롯 키 기반 채우기
python3 "$SKILL_DIR/scripts/hwpx_slots.py" form.hwpx -o slots.json
python3 "$SKILL_DIR/scripts/edit_hwpx.py" form.hwpx \
  -o out.hwpx \
  --slot-json values.json
  • --replace 会先在单个 hp:t 中进行替换,以尽可能保留文本运行的格式。
  • 如果替换目标分布在多个文本运行中,应合并到段落级别,但不要无条件使用第一个文本运行。检查 header.xml 中的 charPr,选择强调格式(如粗体/颜色/下划线)较少,且字高接近该段落中非强调文本常用字高的主要文本运行。
  • --cell 会保留对应 hp:tc 中第一个文本运行的格式,仅替换文本。
  • --paragraph/--paragraph-json 用于实际正文重写。不要将现有句子机械地拆分到多个文本运行中,而应将自然语言文本放入段落的一个主要文本运行中。选择主要文本运行时,不仅要考虑字符数,还要根据 charPrIDRef 的粗体/颜色/下划线属性以及段落中占主导地位的正文字高进行判断。
  • hh:strikeout shape="3D" 之类的值在部分 HWPX 中也会出现在普通正文的 charPr 上。如果无法明确判断其实际删除线渲染效果,不要单独将其作为强调或排除依据。
  • --slot/--slot-json 是默认接口。槽位键只能使用 hwpx_slots.py 生成的键。
  • --paragraph 的目标必须是直接包含 hp:run/hp:t 的实际文本段落。不要直接修改包含表格/图片/文本框的容器段落。修改容器段落会导致内部文本与新文本重叠渲染。
  • 对于像空输入单元格这样包含 hp:run、但不含 hp:t 的情况,只需在现有文本运行中添加 hp:t。
  • 绝不能删除 hp:t 内部的 hp:fwSpace、hp:lineBreak 或其他子控件。即使清空文本,也应保留子标签,仅清空 text/tail。
  • 删除已修改段落的 hp:linesegarray。此标签是段落换行布局缓存;修改正文后若保留该标签,Hancom 可能会发出损坏/篡改警告。
  • 不得更改 hp:tbl、hp:tr、hp:tc、cellAddr、cellSpan、cellSz、cellMargin、borderFillIDRef。
실무 입력 품질 규칙

본문 문단은 맞춤법과 띄어쓰기가 우선이다. 양식을 보존하려고 정부는현장점검과공개보고...처럼 공백을 제거하거나 문장을 중간에서 자르지 않는다.

  • 본문 재작성은 --paragraph-json을 사용한다.
  • 문단 인덱스를 고를 때 analyze_template.py 또는 텍스트 추출 결과에서 컨테이너 문단이 아니라 내부 실제 텍스트 문단을 고른다. 예를 들어 상단 표 전체 문단이 아니라 표 셀 안의 기관명/제목 문단을 수정한다.
  • 본문은 원본 문단의 공백 제외 글자 수 이하로 짧게 다시 쓴다. 원본과 정확히 같은 글자 수로 억지 보강하지 않는다.
  • 이름, 날짜, 전화번호, 직책, 금액, 짧은 표 셀 같은 필드만 정확 길이/셀 예산을 엄격 적용한다.
  • 과도하게 긴 무공백 한글 문자열은 edit_hwpx.py의 기본 품질 가드에서 실패한다. 정상 문장이 아니라면 결과를 제출하지 않는다.
  • 원본 런의 볼드/강조가 본문 새 문장 중간에 묻어 들어가면 실패다. 본문은 --paragraph 계열로 단일 주 런에 넣고, 제목/소제목처럼 의도된 강조만 원본 스타일을 유지한다.
  • 원본보다 글자 크기가 달라 보이면 header.xml이 바뀐 것인지, 새 텍스트가 다른 charPrIDRef에 들어간 것인지 먼저 비교한다. header.xml이 같아도 14pt 본문 대신 12pt 괄호/각주 run을 선택하면 표시 서체가 달라진다.
글자 예산 가드 규칙

page_guard.py --write-budget는 원본 양식에서 다음 예산을 계산한다.

  • 일반 문단: 직접 hp:run/hp:t 텍스트의 공백 제외 글자 수
  • 표 셀: 셀 내부 전체 hp:t 텍스트의 공백 제외 글자 수와 셀 폭/글자 크기 기반 추정 수용량 중 큰 값
  • 빈 입력 셀: 기존 셀 폭과 첫 런의 charPrIDRef 글자 크기를 이용해 보수적 수용량 산정

결과 검증 시 --budget-profile을 반드시 사용한다. 실무 본문 재작성 결과는 --no-strict-paragraph-budget --skip-text-drift를 함께 사용해 원본 글자 수 “일치”가 아니라 예산 이하와 구조 보존 여부를 본다. 표 셀과 짧은 입력 필드는 예산을 초과하면 실패한다. 셀 내부 문단은 셀 예산으로 검사하므로 빈 칸 입력이 가능하다.

전체 구조 fingerprint 가드 규칙

page_guard.py --write-structure는 원본 양식에서 다음을 저장한다.

  • ZIP 패키지 파일 목록과 순서
  • 각 엔트리의 압축 방식, 날짜, 생성 시스템, 버전, flag bits, 외부/내부 속성
  • 바이너리/부속 파일의 SHA-256 해시
  • 모든 XML 파일의 태그명, 속성, 계층, 자식 순서
  • hp:t의 직접 텍스트와 hp:t 하위 컨트롤의 tail 텍스트는 입력값으로 보고 제외
  • hp:t 내부의 hp:fwSpace, hp:lineBreak 같은 자식 컨트롤 태그/순서는 구조 fingerprint에 포함
  • hp:linesegarray는 줄 배치 캐시로 보고 구조 fingerprint에서 제외

결과 검증 시 --structure-profile을 반드시 사용한다. hp:t 텍스트 입력과 hp:linesegarray 캐시 제거를 제외한 태그/속성/파일/이미지/메타 구조가 바뀌면 실패한다. 특히 hp:t 내부 컨트롤이 삭제되면 손상 가능성이 높으므로 실패해야 한다.

ZIP 엔트리의 flag_bits도 원본과 같아야 한다. Python zipfile.writestr()로 전체 엔트리를 다시 쓰면 flag_bits와 압축 바이트가 바뀔 수 있으므로 사용하지 않는다. 반드시 원시 ZIP 복사 방식으로 변경 대상 엔트리만 교체한다.

edit_hwpx.py는 기본적으로 입력 전 글자수 예산과 기본 문장 품질을 검사한다. --paragraph 값은 원본 문단 예산 이하여야 하고, --cell 값은 원본 셀 예산을 넘으면 안 된다. --replace의 새 값이 기존 자리보다 길면 쓰기 전에 실패한다. --allow-over-budget은 사용자가 명시적으로 위험을 감수할 때만 쓴다.

내용 완성도 가드 규칙

content_guard.py는 문서가 열리는지와 무관하게 결과 텍스트가 작업 의도와 일치하는지 검사한다. 다음 경우에는 반드시 규칙 파일을 만들어 실행한다.

  • 문서의 기관/국가/회사/브랜드를 바꾸는 경우: 이전 명칭, 이전 담당자, 이전 전화번호, 이전 정책명을 forbid에 넣는다.
  • 양식 placeholder를 채우는 경우: ○○, {{name}}, ... . . 같은 잔여 placeholder를 금지한다.
  • 새 문서 관점의 핵심어가 반드시 있어야 하는 경우: 새 기관명, 새 법인명, 새 담당 부서 등을 require에 넣는다.
  • 여러 붙임/참고/담당자 표가 있는 문서에서는 앞부분만 바꾸지 말고 전체 텍스트 추출 결과에 대해 content_guard.py를 실행한다.
  • “내용을 싹 바꾸라”, “미국에서 작성한 것처럼 바꾸라”처럼 전면 재작성 요청이면 --reference와 --max-unchanged-ratio를 함께 사용한다. 원본의 긴 문장이 많이 남으면 구조가 정상이어도 실패다.

예시:

{
  "require": ["미국노동부", "좋은 일자리"],
  "forbid": ["고용노동부", "김영훈", "최영범", "장지훈", "044-"],
  "forbid_regex": ["○○+", "\\(\\s*\\.\\s*\\.\\s*\\.\\s*\\)"]
}

content_guard.py가 실패하면 구조가 아무리 정상이어도 결과를 완료로 제출하지 않는다. 실패 목록을 보고 남은 슬롯을 추가 수정하거나, 문서 전체 재작성 범위를 넓힌 뒤 다시 빌드한다.

환경

# SKILL_DIR는 이 SKILL.md가 위치한 디렉토리의 절대 경로로 설정
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"   # 스크립트 내에서
# 또는 Claude Code가 자동으로 주입하는 base directory 경로를 사용

# Python 가상환경 (프로젝트에 맞게 설정)
VENV="<프로젝트>/.venv/bin/activate"

모든 Python 실행 시:

# 프로젝트의 .venv를 활성화 (pip install lxml 필요)
source "$VENV"

디렉토리 구조

.claude/skills/hwpx/
├── SKILL.md                              # 이 파일
├── scripts/
│   ├── office/
│   │   ├── unpack.py                     # HWPX → 디렉토리 (기본 XML 바이트 보존, --pretty는 검사 전용)
│   │   └── pack.py                       # 디렉토리 → HWPX
│   ├── build_hwpx.py                     # 템플릿 + XML → .hwpx 조립 (핵심)
│   ├── edit_hwpx.py                      # 원본 패키지 보존 + 텍스트/셀 최소 수정
│   ├── analyze_template.py               # HWPX 심층 분석 (레퍼런스 기반 생성용)
│   ├── validate.py                       # HWPX 구조 검증
│   ├── fix_namespaces.py                 # 표준 네임스페이스 프리픽스/header itemCnt 보정
│   ├── finalize_hwpx.py                  # 줄 배치 캐시 제거 + 레이아웃 위험 경고
│   ├── page_guard.py                     # 레퍼런스 대비 페이지 드리프트 위험 검사
│   ├── content_guard.py                  # 원문 잔재/placeholder/필수 키워드 검사
│   ├── gonmun_lint.py                    # 공문서 날짜/시간/금액/붙임 표기 검수
│   └── 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/                         # 제안서/사업개요 오버레이 (색상 헤더바, 번호 배지)
└── 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로 검증

원칙: 사용자가 레퍼런스 HWPX를 제공한 경우에는 이 워크플로우 대신 상단의 "기본 동작 모드(레퍼런스 복원 우선)"를 사용한다.

기본 사용법
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을 인라인 작성 → 빌드
# 1. section0.xml을 임시파일로 작성
SECTION=$(mktemp /tmp/section0_XXXX.xml)
cat > "$SECTION" << 'XMLEOF'
<?xml version='1.0' encoding='UTF-8'?>
<hs:sec xmlns:hp="http://www.hancom.co.kr/hwpml/2011/paragraph"
        xmlns:hs="http://www.hancom.co.kr/hwpml/2011/section">
  
  
  <hp:p id="1000000002" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
    <hp:run charPrIDRef="0">
      <hp:t>본문 내용</hp:t>
    </hp:run>
  </hp:p>
</hs:sec>
XMLEOF

# 2. 빌드
python3 "$SKILL_DIR/scripts/build_hwpx.py" --section "$SECTION" --output result.hwpx

# 3. 정리
rm -f "$SECTION"

section0.xml 작성 가이드

필수 구조

section0.xml의 첫 문단(<hp:p>)의 첫 런(<hp:run>)에 반드시 <hp:secPr>과 <hp:colPr> 포함:

<hp:p id="1000000001" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
  <hp:run charPrIDRef="0">
    <hp:secPr ...>
      
    </hp:secPr>
    <hp:ctrl>
      <hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/>
    </hp:ctrl>
  </hp:run>
  <hp:run charPrIDRef="0"><hp:t/></hp:run>
</hp:p>

Tip: templates/base/Contents/section0.xml 의 첫 문단을 그대로 복사하면 된다.

문단
<hp:p id="고유ID" paraPrIDRef="문단스타일ID" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
  <hp:run charPrIDRef="글자스타일ID">
    <hp:t>텍스트 내용</hp:t>
  </hp:run>
</hp:p>
빈 줄
<hp:p id="고유ID" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
  <hp:run charPrIDRef="0"><hp:t/></hp:run>
</hp:p>
서식 혼합 런 (한 문단에 여러 스타일)
<hp:p id="고유ID" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
  <hp:run charPrIDRef="0"><hp:t>일반 텍스트 </hp:t></hp:run>
  <hp:run charPrIDRef="7"><hp:t>볼드 텍스트</hp:t></hp:run>
  <hp:run charPrIDRef="0"><hp:t> 다시 일반</hp:t></hp:run>
</hp:p>
표 작성법
<hp:p id="고유ID" paraPrIDRef="0" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0">
  <hp:run charPrIDRef="0">
    <hp:tbl id="고유ID" zOrder="0" numberingType="TABLE" textWrap="TOP_AND_BOTTOM"
            textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" pageBreak="CELL"
            repeatHeader="0" rowCnt="행수" colCnt="열수" cellSpacing="0"
            borderFillIDRef="3" noAdjust="0">
      <hp:sz width="42520" widthRelTo="ABSOLUTE" height="전체높이" heightRelTo="ABSOLUTE" protect="0"/>
      <hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="1" allowOverlap="0"
              holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="COLUMN" vertAlign="TOP"
              horzAlign="LEFT" vertOffset="0" horzOffset="0"/>
      <hp:outMargin left="0" right="0" top="0" bottom="0"/>
      <hp:inMargin left="0" right="0" top="0" bottom="0"/>
      <hp:tr>
        <hp:tc name="" header="0" hasMargin="0" protect="0" editable="0" dirty="1" borderFillIDRef="4">
          <hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="CENTER"
                     linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0"
                     hasTextRef="0" hasNumRef="0">
            <hp:p paraPrIDRef="21" styleIDRef="0" pageBreak="0" columnBreak="0" merged="0" id="고유ID">
              <hp:run charPrIDRef="9"><hp:t>헤더 셀</hp:t></hp:run>
            </hp:p>
          </hp:subList>
          <hp:cellAddr colAddr="0" rowAddr="0"/>
          <hp:cellSpan colSpan="1" rowSpan="1"/>
          <hp:cellSz width="열너비" height="행높이"/>
          <hp:cellMargin left="0" right="0" top="0" bottom="0"/>
        </hp:tc>
        
      </hp:tr>
    </hp:tbl>
  </hp:run>
</hp:p>
표 크기 계산
  • 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)
<hh:charPr id="8" height="1400" textColor="#000000" shadeColor="none"
           useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="2">
  <hh:fontRef hangul="1" latin="1" hanja="1" japanese="1" other="1" symbol="1" user="1"/>
  <hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
  <hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
  <hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
  <hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
  <hh:bold/>
  <hh:underline type="NONE" shape="SOLID" color="#000000"/>
  <hh:strikeout shape="NONE" color="#000000"/>
  <hh:outline type="NONE"/>
  <hh:shadow type="NONE" color="#C0C0C0" offsetX="10" offsetY="10"/>
</hh:charPr>
폰트 참조 체계
  • 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셀 표: 번호 + 제목):



소항목 헤더 (2셀 표: 번호배지 + 제목):




워크플로우 2: 기존 문서 편집 (unpack → Edit → pack)

source "$VENV"

# 1. HWPX → 디렉토리 (기본값은 XML 바이트 보존)
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

unpack.py --pretty는 사람이 XML을 읽기 좋게 확인할 때만 사용한다. HWPX의 hp:t는 텍스트와 hp:fwSpace, hp:lineBreak 같은 자식 컨트롤이 섞인 mixed content를 가질 수 있고, pretty-print가 삽입한 줄바꿈/공백이 한컴에서 실제 텍스트처럼 렌더링될 수 있다. 따라서 --pretty로 푼 디렉토리를 다시 pack.py 입력으로 사용하지 않는다.


워크플로우 3: 읽기/텍스트 추출

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
from hwpx import TextExtractor
with TextExtractor("document.hwpx") as ext:
    text = ext.extract_text(include_nested=True, object_behavior="nested")
    print(text)

워크플로우 4: 검증

source "$VENV"
python3 "$SKILL_DIR/scripts/validate.py" document.hwpx

검증 항목: ZIP 유효성, 필수 파일 존재, mimetype 내용/위치/압축방식, XML well-formedness


워크플로우 5: 레퍼런스 기반 문서 생성 (첨부 HWPX가 있을 때 기본 적용)

사용자가 제공한 HWPX 파일을 분석하여 동일한 레이아웃의 문서를 생성하는 워크플로우. 이 스킬에서는 첨부 레퍼런스가 존재하면 본 워크플로우를 기본으로 사용한다.

흐름
  1. 분석 — analyze_template.py로 레퍼런스 문서 심층 분석
  2. header.xml 추출 — 레퍼런스의 스타일 정의를 그대로 사용
  3. section0.xml 작성 — 분석 결과의 구조를 따라 새 내용으로 작성
  4. 빌드 — 추출한 header.xml + 새 section0.xml로 빌드
  5. 검증 — validate.py
  6. 쪽수 가드 — page_guard.py (실패 시 재수정)
사용법
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

# 6. 쪽수 드리프트 가드 (필수)
python3 "$SKILL_DIR/scripts/page_guard.py" \
  --reference reference.hwpx \
  --output 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.pyHWPX 심층 분석 (레퍼런스 기반 생성의 청사진)
scripts/office/unpack.pyHWPX → 디렉토리 (기본 XML 바이트 보존, --pretty는 검사 전용)
scripts/office/pack.py디렉토리 → HWPX (mimetype first)
scripts/validate.pyHWPX 파일 구조 검증
scripts/fix_namespaces.py표준 네임스페이스 프리픽스와 header itemCnt 보정
scripts/finalize_hwpx.py줄 배치 캐시 제거, 레이아웃 위험 경고, Windows Hancom 열림 검사
scripts/page_guard.py레퍼런스 대비 페이지 드리프트 위험 검사 (필수 게이트)
scripts/content_guard.py원문 잔재, placeholder, 필수 키워드 누락 검사
scripts/gonmun_lint.py공문서 날짜/시간/금액/붙임 표기 검수
scripts/text_extract.pyHWPX 텍스트 추출

단위 변환

값HWPUNIT의미
1pt100기본 단위
10pt1000기본 글자크기
1mm283.5밀리미터
1cm2835센티미터
A4 폭59528210mm
A4 높이84186297mm
좌우여백850430mm
본문폭42520150mm (A4-좌우여백)

Critical Rules

  1. HWPX만 지원 / HWP 작성 거부: .hwp(바이너리) 파일 작성, 생성, 저장, 직접 편집, 채우기 요청은 거부한다. 사용자가 명시적으로 HWPX 대체 산출물을 허용한 경우에만 .hwpx로 작업한다. 사용자가 .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. 빈 줄: <hp:t/> 사용 (self-closing tag)
  12. 레퍼런스 우선 강제: 사용자가 HWPX를 첨부하면 반드시 analyze_template.py + 추출 XML 기반으로 복원/재작성할 것
  13. examples 폴더 미사용: 작업 중 .claude/skills/hwpx/examples/* 파일은 읽기/참조/복사에 사용하지 말 것
  14. 쪽수 동일 필수: 레퍼런스 기반 작업에서는 최종 결과의 쪽수를 레퍼런스와 동일하게 유지할 것
  15. 무단 페이지 증가 금지: 사용자 명시 요청/승인 없이 쪽수 증가를 유발하는 구조 변경 금지
  16. 구조 변경 제한: 사용자 요청이 없는 한 문단/표의 추가·삭제·분할·병합 금지 (치환 중심 편집)
  17. page_guard 필수 통과: validate.py와 별개로 page_guard.py를 반드시 통과해야 완료 처리

评论

支持 Markdown