hwpx
npx skills add https://github.com/canine89/hwpxskill --skill hwpx
Agent 安装分布
Skill 文档
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 ì¤í ì:
# íë¡ì í¸ì .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 문ì ìì± (주 ìí¬íë¡ì°)
íë¦
- í í릿 ì í (base/gonmun/report/minutes/proposal)
- section0.xml ìì± (본문 ë´ì©)
- (ì í) header.xml ìì (ì ì¤íì¼ ì¶ê° íì ì)
- build_hwpx.pyë¡ ë¹ë
- validate.pyë¡ ê²ì¦
기본 ì¬ì©ë²
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">
<!-- secPr í¬í¨ 첫 ë¬¸ë¨ (base/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 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 ìì ê°ì´ë
커ì¤í ì¤íì¼ ì¶ê° ë°©ë²
templates/base/Contents/header.xmlë³µì¬- íìí charPr/paraPr/borderFill ì¶ê°
- ê° ê·¸ë£¹ì
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 idhangul="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ì í: ë²í¸ + ì 목):
<!-- borderFillIDRef="5" + charPrIDRef="10" â ë
¹ìë°°ê²½ í°ì ë¡ë§ì«ì -->
<!-- borderFillIDRef="6" + charPrIDRef="8" â íìë°°ê²½ ê²ì ë³¼ë ì 목 -->
ìí목 í¤ë (2ì í: ë²í¸ë°°ì§ + ì 목):
<!-- borderFillIDRef="7" + charPrIDRef="11" â íëë°°ê²½ í°ì ìë¼ë¹ìì«ì -->
<!-- borderFillIDRef="8" + charPrIDRef="8" â íë¨ì ë§ ê²ì ë³¼ë ì 목 -->
ìí¬íë¡ì° 2: 기존 문ì í¸ì§ (unpack â Edit â pack)
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: ì½ê¸°/í ì¤í¸ ì¶ì¶
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 íì¼ì ë¶ìíì¬ ëì¼í ë ì´ììì 문ì를 ìì±íë ìí¬íë¡ì°.
íë¦
- ë¶ì â
analyze_template.pyë¡ ë í¼ë°ì¤ 문ì ì¬ì¸µ ë¶ì - header.xml ì¶ì¶ â ë í¼ë°ì¤ì ì¤íì¼ ì ì를 ê·¸ëë¡ ì¬ì©
- section0.xml ìì± â ë¶ì ê²°ê³¼ì 구조를 ë°ë¼ ì ë´ì©ì¼ë¡ ìì±
- ë¹ë â ì¶ì¶í header.xml + ì section0.xmlë¡ ë¹ë
- ê²ì¦
ì¬ì©ë²
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
- HWPXë§ ì§ì:
.hwp(ë°ì´ë리) íì¼ì ì§ìíì§ ìëë¤. ì¬ì©ìê°.hwpíì¼ì ì ê³µíë©´ íê¸ ì¤í¼ì¤ìì.hwpxë¡ ë¤ì ì ì¥íëë¡ ìë´í ê². (íì¼ â ë¤ë¥¸ ì´ë¦ì¼ë¡ ì ì¥ â íì¼ íì: HWPX) - secPr íì: section0.xml 첫 문ë¨ì 첫 runì ë°ëì secPr + colPr í¬í¨
- mimetype ìì: HWPX í¨í¤ì§ ì mimetypeì 첫 ë²ì§¸ ZIP ìí¸ë¦¬, ZIP_STORED
- ë¤ìì¤íì´ì¤ ë³´ì¡´: XML í¸ì§ ì
hp:,hs:,hh:,hc:ì ëì¬ ì ì§ - itemCnt ì í©ì±: header.xmlì charProperties/paraProperties/borderFills itemCntê° ì¤ì ìì ìì ì¼ì¹
- ID 참조 ì í©ì±: section0.xmlì charPrIDRef/paraPrIDRefê° header.xml ì ìì ì¼ì¹
- venv ì¬ì©: íë¡ì í¸ì
.venv/bin/python3(lxml í¨í¤ì§ íì) - ê²ì¦: ìì± í ë°ëì
validate.pyë¡ ë¬´ê²°ì± íì¸ - ë í¼ë°ì¤: ìì¸ XML 구조ë
$SKILL_DIR/references/hwpx-format.md참조 - build_hwpx.py ì°ì : ì 문ì ìì±ì build_hwpx.py ì¬ì© (python-hwpx API ì§ì í¸ì¶ ì§ì)
- ë¹ ì¤:
<hp:t/>ì¬ì© (self-closing tag)