agentleFS
kist-aixkist-aix/hwpx/SKILL.md

hwpx

Skill18 starsChanged 4 months agoMITView on GitHub

한글(HWPX) 보고서 생성/편집/읽기 스킬. .hwpx 파일, 한글 문서, Hancom, OWPML 관련 요청 시 사용.

  • Installs packages

8 other projects use a version of this, 5 of them edited.

Adopted by

Most recently taken up by

The file

Files
SKILL.md

HWPX 보고서 생성 스킬 — XML-first 워크플로우

한글(Hancom Office)의 HWPX 보고서를 XML 직접 작성 중심으로 생성, 편집, 읽기할 수 있는 스킬. HWPX는 ZIP 기반 XML 컨테이너(OWPML 표준)이다. python-hwpx API의 서식 버그를 완전히 우회하며, 세밀한 서식 제어가 가능하다.

이 스킬은 templates/report/에 정의된 report 보고서 양식 한 종류를 기준으로 동작한다. 사용자가 .hwpx 파일을 첨부하더라도 그 구조를 그대로 복원하지는 않는다 — 첨부 파일은 내용 참고용으로만 활용하고, 출력은 항상 report 양식으로 생성한다.

0. 환경 점검 (스킬 시작 시 1회, 필수)

이 스킬은 비개발자 사용자를 위해 배포된다. 따라서 의존성 누락 시 사용자에게 pip install을 요구하지 말고, 아래 절차로 스킬이 직접 설치한다.

본격적인 빌드 작업 전에 다음을 순서대로 수행한다:

  1. 필수 의존성 확인 (lxml)
    python3 -c "import lxml" 2>/dev/null
    성공하면 다음 단계로. 실패하면 2번으로 이동.
  2. 자동 설치 (실패 시 1회 안내 후 진행) 사용자에게 한 줄로 안내한다: "한글파일 스킬 첫 사용 — 의존성 자동 설치 중입니다(약 10~30초)…" 그 다음 다음 명령을 실행한다:
    python3 -m pip install --user --quiet -r "$SKILL_DIR/requirements.txt"
    설치 후 다시 python3 -c "import lxml"로 확인. 여전히 실패하면 사용자에게 환경 문제(파이썬/네트워크/권한)를 보고하고 작업을 중단한다.
  3. 텍스트 추출(text_extract.py)이 필요한 요청에 한해서만 python-hwpx 확인 requirements.txt에 이미 포함되어 있어 2번에서 함께 설치된다. 일반적인 보고서 생성 흐름에서는 별도 확인이 불필요하다.
  4. 재확인 후 본 작업 계속 환경 점검이 한 번 통과하면 같은 세션에서는 다시 점검하지 않는다(이미 import 가능 상태).

참고: 설치 명령은 --user 플래그로 사용자 홈에 설치하므로 시스템 권한이 필요 없고, --quiet로 출력을 최소화한다. requirements.txt가 패키지 목록의 단일 출처(single source of truth)다.

환경

SKILL_DIR는 이 SKILL.md가 위치한 디렉터리의 절대 경로다. 설치 방식에 따라 다음과 같이 결정한다:

설치 방식SKILL_DIR 값
사용자 전역 설치$HOME/.claude/skills/hwpx
프로젝트 전용 설치$(pwd)/.claude/skills/hwpx (프로젝트 루트 기준)
리포 클론 직접 사용$(pwd) (리포 루트 자체가 곧 스킬)

Claude agent는 이 SKILL.md를 읽어 들인 실제 위치를 기준으로 SKILL_DIR를 자연스럽게 결정한다. 아래 모든 예시의 $SKILL_DIR는 이 값으로 치환해 사용한다.

Python 명령은 현재 agent 환경에서 사용 가능한 python3로 실행한다. 필수 의존성은 위 "0. 환경 점검" 절차로 자동 처리되므로, 사용자가 별도로 pip install할 필요는 없다. 의존성 목록의 정의는 requirements.txt에 있다.

결과 저장 위치 (모든 출력에 적용 — 필수)

이 스킬이 생성하는 모든 .hwpx 결과물은 사용자의 현재 작업 디렉터리($(pwd)) 아래 output/ 폴더에 저장한다.

  • 예: --output output/result.hwpx, --output output/2026_보고서.hwpx
  • output/ 폴더가 없으면 build_hwpx.py(및 office/pack.py)가 자동 생성한다 — 사용자에게 미리 만들도록 요구하지 않는다.
  • 파일명은 보고서 내용을 식별할 수 있는 한국어/영문 짧은 이름으로 명명한다(예: output/업무추진계획.hwpx).
  • 사용자가 명시적으로 다른 경로(예: 데스크탑, 특정 폴더)를 지정한 경우에만 그 경로를 우선한다.
  • 중간 산출물(임시 section XML 등)은 output/ 대신 tempfile//tmp에 두고 사용 후 삭제한다.

디렉토리 구조

hwpx/
├── SKILL.md                              # 이 파일
├── scripts/
│   ├── office/
│   │   ├── unpack.py                     # HWPX → 디렉토리 (XML pretty-print)
│   │   └── pack.py                       # 디렉토리 → HWPX
│   ├── build_hwpx.py                     # report 템플릿 + XML → .hwpx 조립 (핵심)
│   ├── table_builder.py                  # 표 템플릿 → XML 생성
│   ├── preview_table.py                  # 표 템플릿 + 샘플 데이터 → 미리보기 .hwpx
│   ├── extract_table_templates.py        # 표 라이브러리 HWPX → 표 템플릿 .xml 재생성
│   ├── validate.py                       # HWPX 구조 검증
│   ├── style_check.py                    # 개조식 문체 규칙 검사 (□/❍ 길이·종결)
│   └── text_extract.py                   # 텍스트 추출 (python-hwpx 필요)
├── templates/
│   ├── base/                             # 내부 스켈레톤 (mimetype, META-INF, content.hpf 등 — build_hwpx.py가 자동 사용)
│   ├── report/                           # 보고서 양식 (header.xml, section0.xml) — 모든 출력의 기본 오버레이
│   └── tables/                           # 표 템플릿 (table_builder.py 사용)
│       ├── basic.xml                     # 기본 표 (4열: 연번/구분/내용/비고)
│       ├── status.xml                    # 현황표 (4열: 번호/항목/추진현황/진행률)
│       ├── budget.xml                    # 예산표 (5열, 합계행 포함)
│       ├── schedule.xml                  # 일정표 (3열: 일자/추진내용/담당)
│       └── checklist.xml                 # 점검표 (5열: 번호/점검항목/담당/확인/비고)
├── assets/
│   ├── report-template.hwpx              # report 템플릿 시각 기준 샘플 (런타임 미사용)
│   └── all_tables_preview.hwpx           # 표 라이브러리 — extract_table_templates.py의 추출 소스
└── references/
    └── hwpx-format.md                    # OWPML XML 요소 레퍼런스

templates/base/는 HWPX 컨테이너에 반드시 들어가야 하는 파일 골격(mimetype, META-INF, Preview, content.hpf 등)을 제공하는 내부 스켈레톤이다. build_hwpx.py가 자동으로 사용하며, 사용자가 직접 의식할 필요는 없다.

assets/는 최종 문서 생성에 직접 투입되는 런타임 템플릿이 아니라, 필요할 때 한글에서 열어 레이아웃을 확인하는 기준 샘플을 보관하는 위치다.


보고서 생성 워크플로우 (메인)

핵심 원칙: 템플릿은 양식이지 콘텐츠 틀이 아니다

  • header.xml → templates/report/header.xml을 그대로 사용한다 (스타일 정의: 폰트, 크기, 색상, 문단 간격 등)
  • section0.xml → templates/report/section0.xml의 secPr(페이지 설정) 첫 문단만 가져오고, 본문은 내용에 맞게 새로 작성한다
  • 템플릿의 section0.xml은 "어떤 스타일 ID를 어떤 용도로 쓰는지" 보여주는 서식 참고용이다
  • 템플릿의 문단 수, 표 행 수, 섹션 수를 그대로 따르지 않는다
  • 내용이 5개 섹션이면 5개를 만들고, 표가 10행이면 10행으로 만든다
  • 본문 문체·분량: □/❍ 본문의 줄 길이와 문장 종결은 아래 「본문 문체 규칙」 절을 따른다 (□ 줄은 3035자, ❍ 줄은 6570자, 명사형 종결)
  • 표 빈 셀 금지: 표의 모든 셀에 반드시 내용을 채운다. 빈 문자열("") 대신 "-" 또는 적절한 값 사용
  • 표 열 폭 조정: 각 셀에 들어가는 글자 수를 고려하여 열 너비를 조정한다. 내용이 긴 열은 넓게, 짧은 열(번호, 확인 등)은 좁게 설정
  • 표 밀도 제한: 한 페이지에 표는 최대 1개만 배치한다. 표가 여러 개 필요하면 본문 텍스트로 충분히 간격을 두거나 페이지를 분리한다

본문 문체 규칙 (개조식 작성)

□/❍ 본문은 공공기관 보고서의 개조식 문체로 작성한다. 줄의 성격에 따라 길이와 종결을 다르게 한다.

  • □ 상위 줄: □ 뒤 기준으로 공백·괄호·문장부호 포함 30자 이상 35자 이내로 쓴다. "□ 연구환경"처럼 내용 없는 단순 라벨로 두지 말고, 핵심을 압축한 한 줄 범주 문장으로 작성한다 — 세부 내용은 아래 ❍ 줄로 내린다
  • □ 라벨형 줄 예외: "□ 일시 : …", "□ 장소 : …"처럼 라벨 : 값 형식의 개요·항목 줄은 30자 하한을 적용하지 않는다 — 자연스러운 길이로 두고 억지로 늘리지 않는다 (35자 상한은 동일 적용)
  • ❍ 하위 줄: □ 아래 ❍ 줄은 한국어 65자 이상 70자 이내(약 두 줄 분량)의 구체적 설명 문장으로 작성한다. 범위를 벗어나면 활동·대상·방법·산출 등 내용을 더하거나 덜어 분량을 맞춘다
  • ❍ 줄 들여쓰기: ❍ 줄은 텍스트 앞에 공백 2칸 들여쓰기를 둔다( ❍ ...). □ 줄은 들여쓰기 없이 가장 왼쪽에서 시작한다. style_check는 들여쓰기 무관하게 길이를 보지만, 양식 일관성을 위해 들여쓰기는 반드시 넣는다
  • 양식 빈 줄: 본문 구성 시 두 가지 빈 줄을 양식대로 둔다 — (1) 제목 도형 직후 빈 줄(pp=14, cp=9, 양식 고정) — templates/report/section0.xml의 paras[:3]까지 그대로 복사하면 자동 포함, (2) 섹션 사이 구분 빈 줄(pp=2, cp=13, 11pt 한양중고딕). 일반 pp=0, cp=0 빈 줄은 양식과 어긋난다
  • 권장 종결: 명사형 종결을 쓴다 — ~ 전환, ~ 발전, ~ 구축, ~ 확대, ~ 강화, ~ 고도화, ~ 확보, ~ 운영, ~ 추진 등
  • 피해야 할 종결: 최종 문체 점검 시 다음 종결은 피한다 — ~합니다, ~습니다, ~한다, ~된다, ~있다, ~큼, ~함

생성한 .hwpx는 scripts/style_check.py로 위 규칙(□ 길이·❍ 길이·종결) 준수 여부를 점검한다.

흐름

  1. header.xml 확인 — 사용 가능한 스타일 ID(charPr, paraPr, borderFill) 파악 (필요 시 templates/report/header.xml 읽기)
  2. section0.xml 새로 작성 — secPr은 templates/report/section0.xml에서 복사, 본문은 내용 분량에 맞게 자유롭게 구성
  3. 머리말·제목 자리표시자 교체 (필수, 아래 섹션 참조)
  4. build_hwpx.py로 빌드 — 별도 --template 지정 없이 호출하면 report 양식이 자동 적용된다
  5. validate.py로 검증

머리말·제목 자리표시자 교체 (필수)

templates/report/section0.xml의 상단에는 다음 자리표시자가 들어 있다. 빌드 전에 반드시 실제 값으로 교체해야 한다 — 그대로 두면 결과 문서에 YY, 보고자료 제목 같은 더미 텍스트가 그대로 노출된다.

위치자리표시자 원문교체 대상
첫 문단 머리말(’YY. MM. DD., 부서명)보고일 + 작성 부서
제목 도형(rect) drawText보고자료 제목실제 보고서 제목

날짜는 빌드 시점의 시스템 날짜를 기본값으로 사용한다 (datetime.date.today()). 사용자가 다른 날짜를 명시한 경우만 예외.

원본 템플릿에서는 YY / . MM. DD., 부서명) / 보고자료 제목이 각각 별개의 <hp:t> 노드로 들어 있으므로, 텍스트 노드를 순회하며 정확한 일치(또는 부분 일치)로 치환한다:

from datetime import date
from lxml import etree

HP_T = "{http://www.hancom.co.kr/hwpml/2011/paragraph}t"
today = date.today()
yy = f"{today.year % 100:02d}"
mm_dd = f"{today.month}. {today.day}."

for t in root.iter(HP_T):
    if t.text == "YY":
        t.text = yy
    elif t.text == "보고자료 제목":
        t.text = "2026년 상반기 AI 활용 교육 추진계획 보고서"
    elif t.text and ". MM. DD., 부서명)" in t.text:
        t.text = t.text.replace(
            ". MM. DD., 부서명)", f". {mm_dd}, 인재개발팀)"
        )

--title / --creator CLI 옵션은 content.hpf의 메타데이터(파일 속성)만 갱신하며, 문서 본문에 보이는 제목 도형 텍스트는 위 절차로 별도 치환해야 한다.

기본 사용법

# 기본 보고서 (커스텀 section 없이 — 양식만 들어있는 빈 보고서)
python3 "$SKILL_DIR/scripts/build_hwpx.py" --output output/result.hwpx

# 커스텀 section0.xml로 본문 채우기
python3 "$SKILL_DIR/scripts/build_hwpx.py" --section my_section0.xml --output output/result.hwpx

# header도 오버라이드 (드물게 사용)
python3 "$SKILL_DIR/scripts/build_hwpx.py" --header my_header.xml --section my_section0.xml --output output/result.hwpx

# 메타데이터 설정
python3 "$SKILL_DIR/scripts/build_hwpx.py" --section my.xml \
  --title "제목" --creator "작성자" --output output/result.hwpx

실전 패턴: 템플릿에서 secPr만 복사 → 본문 자유 작성 → 빌드

Step 1: templates/report/section0.xml의 secPr 포함 첫 문단을 복사 (페이지 설정) Step 2: 나머지 본문은 내용 분량에 맞게 자유롭게 작성 (charPrIDRef/paraPrIDRef만 header.xml 참조) Step 3: 빌드

# 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">
  <!-- secPr 포함 첫 문단 (report template section0.xml에서 복사) -->
  <!-- ... -->

  <!-- ▼ 여기서부터 내용에 맞게 자유 작성 — 문단/표/섹션 수에 제한 없음 -->
  <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 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/report/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는 문서 내 고유해야 함

표 템플릿 시스템 (table_builder.py)

templates/tables/ 에 저장된 표 양식을 기반으로, 데이터만 넣으면 완성된 표 XML을 생성한다.

사용 가능한 템플릿

템플릿열 구성합계행용도
basic연번/구분/내용/비고 (4열)-범용 표
status번호/항목/추진현황/진행률 (4열)-현황 보고
budget연번/사업명/내용/예산(백만원)/비고 (5열)✓예산 내역
schedule일자/추진내용/담당 (3열)-일정 계획
checklist번호/점검항목/담당/확인/비고 (5열)-점검·체크리스트

위 열 구성은 현재 templates/tables/*.xml 기준이며 extract_table_templates.py로 재생성하면 바뀔 수 있다. 최신 목록은 table_builder.py --list로 확인한다.

Python API 사용법

import sys; sys.path.insert(0, f"{SKILL_DIR}/scripts")
from table_builder import build_table, build_table_paragraph

# 표 XML만 생성 (hp:tbl)
table_xml = build_table(
    template="basic",
    data=[["1", "교원연수", "AI 직무연수 운영", "상반기"],
          ["2", "수업모델", "수업안 개발", "5개 교과"]],
    headers=["연번", "구분", "내용", "비고"],  # 선택: 헤더 텍스트 오버라이드
    start_id=1000000050,
)

# hp:p로 감싼 버전 (section0.xml에 바로 삽입 가능)
para_xml = build_table_paragraph(
    template="budget",
    data=[["1", "인건비", "개발자 3명", "500", ""],
          ["2", "장비", "서버 구매", "200", ""]],
    summary=["", "", "합  계", "700", ""],  # 합계행 (budget 전용)
    start_id=1000000050,
)

CLI 사용법

# 템플릿 목록 확인
python3 "$SKILL_DIR/scripts/table_builder.py" --list

# 표 XML 생성
python3 "$SKILL_DIR/scripts/table_builder.py" --template basic \
  --data '[["1","교원연수","AI 직무연수","상반기"]]' \
  --start-id 1000000050

# hp:p 포함 출력 (--paragraph)
python3 "$SKILL_DIR/scripts/table_builder.py" --template basic \
  --data '[["1","A","B","C"]]' --paragraph --start-id 1000000050

핵심 규칙

  • start_id: 문서 내 다른 요소와 겹치지 않아야 한다. 표마다 다른 범위 사용
  • 열 수 일치: data 각 행의 원소 수는 템플릿 열 수와 일치해야 한다
  • 합계행: summary 파라미터는 budget처럼 summary row가 정의된 템플릿에서만 동작
  • 헤더 오버라이드: headers 미지정 시 템플릿의 기본 헤더 텍스트 사용
  • 빈 셀 금지: 모든 셀에 반드시 내용을 채운다. 빈 문자열("") 대신 "-" 또는 적절한 값 사용
  • 날짜 표기: 표의 일자·날짜·기간 열은 ‘YY.MM.DD 형식으로 작성한다 (예: ‘26.05.01). 기간은 ‘26.05.01 ~ ‘26.06.30처럼 쓴다. "3월 2주", "상반기" 같은 모호한 표기는 피한다

표 미리보기 (preview_table.py)

표 템플릿에 샘플 데이터를 채워 한글에서 바로 열어볼 수 있는 .hwpx를 생성한다 (디자인 확인용).

# 사용 가능한 표 템플릿 목록
python3 "$SKILL_DIR/scripts/preview_table.py" --list

# 단일 템플릿 미리보기 (output/budget_preview.hwpx 로 저장)
python3 "$SKILL_DIR/scripts/preview_table.py" budget

# 전체 템플릿을 한 번에 미리보기 생성 (모두 output/ 에 저장)
python3 "$SKILL_DIR/scripts/preview_table.py" --all

표 템플릿 추가·수정 (extract_table_templates.py)

새 표 양식을 추가하거나 기존 표를 수정할 때는, 표를 모아 둔 표 라이브러리 HWPX(assets/all_tables_preview.hwpx)를 한글에서 편집한 뒤 이 스크립트로 templates/tables/*.xml을 다시 추출한다. XML을 직접 손대지 않고 한글에서 표를 디자인할 수 있다.

# 표 라이브러리 HWPX → templates/tables/*.xml 재생성
python3 "$SKILL_DIR/scripts/extract_table_templates.py"

# 다른 HWPX를 소스로 지정 / 미리보기만 / 사라진 템플릿 정리
python3 "$SKILL_DIR/scripts/extract_table_templates.py" assets/my_tables.hwpx
python3 "$SKILL_DIR/scripts/extract_table_templates.py" --dry-run
python3 "$SKILL_DIR/scripts/extract_table_templates.py" --prune

표 인식 규칙 — 각 표 바로 앞 문단에 N. 이름 - 설명 형식 라벨을 둔다(예: 4. schedule - 일정표 (일자/추진내용/담당)). 이름은 출력 파일명과 <meta><name>, 설명은 <meta><description>가 된다. 1행은 머리행, 표 끝에서 머리행과 같은 셀 서식이 연속되는 행은 합계행, 나머지는 본문행으로 인식한다.

스타일 정규화 규칙 (필수) — 추출 결과의 모든 charPrIDRef·paraPrIDRef·borderFillIDRef는 소스 한글파일이 아니라 templates/report/header.xml의 표준 표 스타일로 자동 재매핑된다 — 머리행 26/18/9, 본문행 27/18·22/10, 합계행 26/18/9, 컨테이너 4. 보고서는 항상 report 헤더와 함께 빌드되므로, 이 재매핑이 있어야 어떤 한글파일에서 뽑은 표든 글꼴·테두리가 깨지지 않는다. 표의 구조(열 수·너비·정렬·합계행)는 소스 그대로 유지되고 색·글꼴만 보고서 표준 표 서식으로 통일된다.

추출 표 너비가 보고서 본문폭(48190)을 넘으면 경고가 출력된다 — 한글에서 열 너비를 줄여 다시 추출한다.

셀 여백 주의 — 셀 안쪽 여백은 한글에서 표/셀 속성 → 안 여백으로 준다. 문단 모양 → 여백으로 준 값은 paraPr(스타일)에 저장돼 정규화 때 사라진다. 구조 속성인 <hp:cellMargin>만 추출 시 보존된다.

디자인 보존 모드 ([design] 마커)

스타일 정규화는 보통 합리적이지만, 마일스톤·로드맵·카드형 표처럼 셀마다 다른 borderFill·색·폰트로 시각화하는 표는 정규화하면 디자인이 평준화되어 의도가 사라진다(spacer 셀까지 헤더 회색으로 칠해지거나, 본문 N행이 1행으로 합쳐지는 등). 그런 표는 라벨 끝에 [design] (또는 [preserve]) 마커를 둔다.

6. roadmap - 일자별 진행일정 로드맵 [design]

마커가 있으면 추출 결과가 달라진다:

  • meta/preserve-design이 true로 기록
  • 모든 본문 행을 그대로 보존 (본문 N->1 정규화 안 함)
  • 표가 참조하는 모든 borderFill·charPr·paraPr 정의를 라이브러리 header.xml에서 자동 추출해 meta/extra-styles에 함께 저장
  • sample은 라이브러리 원본 ID 그대로 — 스타일 평준화 없음

빌드 시점에는 table_builder.merge_template_styles_into_header()가 이 extra-styles를 보고서 header.xml에 새 ID로 머지하고, build_table*(template, ..., id_mapping=mapping)이 sample의 ID 참조를 매핑된 ID로 치환한다. 보고서 빌더 패턴:

from lxml import etree
from table_builder import build_table_paragraph, merge_template_styles_into_header

# 1) 보고서 header.xml에 디자인 보존 표의 의존 스타일을 머지
rep_header = etree.parse("$SKILL_DIR/templates/report/header.xml")
mapping = merge_template_styles_into_header("roadmap", rep_header)
rep_header.write("/tmp/patched_header.xml", xml_declaration=True,
                 encoding="UTF-8", standalone=True)

# 2) 표 빌드 (preserve-design 모드는 build_table 안에서 자동 처리)
#    headers: header-row 셀 수만큼(스페이서 포함, 빈 문자열로) 채운 list
#    data:    body 행 수만큼, 각 row는 그 행의 셀 텍스트 list
table_xml = build_table_paragraph(
    template="roadmap",
    headers=["1단계", "", "2단계", "", "3단계", "", "4단계", "", "5단계"],
    data=[
        ["계획 수립", "공모·접수", "심사·평가", "본선·시상", "성과 확산"],
        ["’26. 6월", "’26. 7~8월", "’26. 9~10월", "’26. 11월", "’26. 12월"],
    ],
    id_mapping=mapping,
    start_id=1000000200,
)

# 3) build_hwpx.py에 --header /tmp/patched_header.xml 로 전달

preview_table.py는 preserve-design=true 템플릿을 자동 감지해 위 단계를 알아서 수행한다. 디자인 보존 표의 미리보기 데이터는 SAMPLE_DATA 딕셔너리에서 "headers": [...], "data": [[...], ...] 형태로 정의한다.

사용 시 주의 (preserve-design 모드 한정):

  • merge_template_styles_into_header()는 같은 header tree에 한 번만 호출한다(멱등하지 않음). 두 번 호출하면 lib 정의가 중복 추가된다. 한 보고서 빌드 흐름에서 한 번만 부르면 안전.
  • data/headers 행·셀 수는 sample 구조와 정확히 일치시킨다. 부족하면 해당 셀은 빈 셀로 출력된다(원본 placeholder 텍스트가 새지 않도록 안전 디폴트). 행 수를 일부러 줄이고 싶으면 한글에서 라이브러리 표를 수정한 뒤 재추출한다.
  • 라이브러리 라벨의 [design] 마커는 한글에서 라이브러리 편집 시에도 유지해야 한다. 마커가 지워지면 다음 extract 때 정규화 경로로 분류돼 디자인이 평준화된다.

header.xml 수정 가이드

커스텀 스타일 추가 방법

  1. templates/report/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" → 굴림, "1" → 궁서, "2" → 맑은고딕, "3" → 함초롬돋움
  • hangul="4" → 휴먼명조, "5" → HY헤드라인M, "7" → 한양중고딕, "8" → 바탕
  • 7개 언어(HANGUL/LATIN/HANJA/JAPANESE/OTHER/SYMBOL/USER) 모두 동일하게 설정

report 템플릿 스타일 ID 맵

templates/report/header.xml 기준.

본문 스타일 (section0.xml 작성 시 사용)

용도charPrIDRefparaPrIDRef설명
소제목 (1. 2. 3.)10216pt HY헤드라인M
소제목 후 빈줄1224pt 스페이서
□/❍ 본문 항목252213pt 휴먼명조
※ 참고 사항202113pt 한양중고딕
섹션 구분 빈줄13211pt 한양중고딕
제목 아래 빈줄914(양식 고정)

표 스타일 (table_builder 템플릿에서 사용)

용도charPrIDRefparaPrIDRefborderFillIDRef
표 헤더 셀26 (12pt 휴먼명조 bold)18 (CENTER)9 (회색 #D9D9D9)
표 본문 셀27 (12pt 휴먼명조)18 (CENTER) 또는 22 (JUSTIFY)10 (테두리만)
표 합계행26189
표 컨테이너--4 (SOLID 테두리)

헤더 영역 (secPr + 제목, 양식에서 그대로 복사)

용도charPrIDRefparaPrIDRef비고
날짜·부서 문단21, 9, 22, 1819secPr 포함, 다중 run
제목 drawText24 (내부: 25)2rect 도형 안 텍스트

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

# 1. HWPX → 디렉토리 (XML pretty-print)
python3 "$SKILL_DIR/scripts/office/unpack.py" document.hwpx ./unpacked/

# 2. XML 직접 편집 (agent가 파일 편집 도구로)
#    본문: ./unpacked/Contents/section0.xml
#    스타일: ./unpacked/Contents/header.xml

# 3. 다시 HWPX로 패키징
python3 "$SKILL_DIR/scripts/office/pack.py" ./unpacked/ output/edited.hwpx

# 4. 검증
python3 "$SKILL_DIR/scripts/validate.py" output/edited.hwpx

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

# 순수 텍스트
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)

보조 워크플로우: 검증

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

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


스크립트 요약

스크립트용도
scripts/build_hwpx.py핵심 — report 템플릿 + XML → HWPX 조립
scripts/table_builder.py표 템플릿 → 데이터 주입 → 표 XML 생성
scripts/preview_table.py표 템플릿 + 샘플 데이터 → 미리보기 .hwpx 생성
scripts/extract_table_templates.py표 라이브러리 HWPX → 표 템플릿 .xml 재생성
scripts/office/unpack.pyHWPX → 디렉토리 (XML pretty-print)
scripts/office/pack.py디렉토리 → HWPX (mimetype first)
scripts/validate.pyHWPX 파일 구조 검증
scripts/style_check.py개조식 문체 규칙 검사 (□/❍ 길이·종결)
scripts/text_extract.pyHWPX 텍스트 추출 (python-hwpx 필요)

단위 변환

값HWPUNIT의미
1pt100기본 단위
10pt1000기본 글자크기
1mm283.5밀리미터
1cm2835센티미터
A4 폭59528210mm
A4 높이84186297mm
좌우여백850430mm
본문폭42520150mm (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. Python 환경: 현재 환경의 python3 사용 (lxml 필요, 일부 보조 스크립트는 hwpx 패키지 필요)
  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 결과는 $(pwd)/output/ 폴더에 저장한다. 폴더 미존재 시 자동 생성. 사용자가 다른 경로를 명시한 경우만 예외. ([결과 저장 위치] 섹션 참조)
  13. 첨부 .hwpx의 위상: 사용자가 .hwpx 파일을 첨부하더라도 그 구조를 그대로 복원하지 않는다. 첨부 파일은 본문 내용 또는 스타일 의도 파악에만 참고하고, 출력은 항상 report 양식으로 생성한다. 원본 그대로의 구조 복원을 원하면 한글 오피스에서 직접 편집하도록 안내한다.
  14. 머리말 자리표시자 교체 필수: YY / . MM. DD., 부서명) / 보고자료 제목 자리표시자는 빌드 전에 반드시 실제 값으로 교체한다. 날짜는 datetime.date.today()를 기본값으로 사용한다(사용자가 다른 날짜를 명시한 경우만 예외). ([머리말·제목 자리표시자 교체] 섹션 참조)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.