| name | hwpx |
| description | Use this skill whenever the user wants to create, read, edit, analyze, or manipulate Hangul/Korean word processor documents (.hwpx, .hwp, .owpml files). Triggers include: any mention of 'hwpx', 'hwp', 'ํ๊ธ ํ์ผ', 'ํ๊ธ ๋ฌธ์', 'ํ์ปด', 'OWPML', or requests to produce Korean government forms, fill templates, clone forms, or convert documents. Also use when converting HWP to HWPX (hwp2hwpx), extracting text from .hwpx files, filling form fields, checking/unchecking boxes, generating multiple filled documents, converting HTML/Markdown to .hwpx format, extracting images from HWP/HWPX documents, analyzing document contents (text, tables, images), or extracting/applying document themes and styles. If the user asks to '์ด๋ฏธ์ง ์ถ์ถ', '๋ฌธ์ ๋ถ์', 'ํ ๋ฐ์ดํฐ ์ถ์ถ', '์ด๋ฏธ์ง ๋ด์ฉ ํ์
', or any Korean document operation, use this skill. Do NOT use for .docx Word documents, PDFs, or spreadsheets. |
HWPX creation, editing, and form automation
์ ๋ ๊ท์น
- pyhwpxlib๋ง ์ฌ์ฉ โ XML ์ง์ ์์ฑ/๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ธ์ง
.hwp โ pyhwpxlib.hwp2hwpx.convert()๋ก HWPX ๋ณํ๋ถํฐ
.hwpx ์ฝ๊ธฐ โ pyhwpxlib.api.extract_text()
- ์ ๋ฌธ์ โ
from pyhwpxlib import HwpxBuilder
- ํธ์ง โ
unpack โ ๋ฌธ์์ด ๊ต์ฒด โ pack (ET.tostring ๊ธ์ง)
- ์์ฑ/ํธ์ง ํ validate + lint ํ์
- SVG ํ๋ฆฌ๋ทฐ ์์ฑ โ Read tool๋ก ์ง์ ํ์ธ (์๋ต ๊ธ์ง)
๋์์ธ ๊ท์น
- ์ฃผ์ ์ ๋ง๋ ํ
๋ง ์ ํ โ ํ๋์ ๋ํดํธ ๊ธ์ง
- ์๊ฐ ์์๋ ๋ด์ฉ์ ๋ง์ ๋๋ง โ ์ต์ง๋ก ํ๋ฅผ ๋ง๋ค์ง ์๋๋ค
- ๊ฐ์ ๋ ์ด์์ ๋ฐ๋ณต ๊ธ์ง
- ์ด๋ฏธ์ง ์ ๊ทน ํ์ฉ โ ์ฌ์ฉ์๊ฐ ์ ๊ณตํ๋ฉด ๋ฐ๋์ ์ฝ์
- ๋ฌธ๋จ ๊ฐ๊ฒฉ ํ์ โ
add_paragraph("")๋ก ํค๋ฉ/ํ/์ด๋ฏธ์ง ์๋ค์ ๋น ์ค
On Load โ ์คํฌ ๋ก๋ ์ ์ฆ์ ์คํ
Step 1: ๋ฑ๋ก๋ ์์ ์๋ ์ธ์ (v0.17.0+, ์ฑํ
๊ฐ ์ปจํ
์คํธ ์ ์ง์ ํต์ฌ)
from pyhwpxlib.templates import list_templates
items = list_templates()
- ์ฌ์ฉ์๊ฐ ์์๋ช
ยทํ๊ธ๋ช
ยท๊ธฐ๋ฅ ํค์๋๋ฅผ ์ธ๊ธํ๋ฉด ๋งค์นญ๋๋ ์์์ด ์๋์ง ๋จผ์ ํ์ธ.
- ๋งค์นญ๋๋ฉด ๊ณง์ฅ Step 2: ์ปจํ
์คํธ ๋ก๋.
- "์์์ด ๋ฑ๋ก๋ผ ์๋ค / ์๋ก ๋ฑ๋ก / ๊ฒ์" ์ค์ ๋ฅ๋ ์๋ด.
Step 2: ๋ฑ๋ก ์์ ๋งค์นญ ์ ์ปจํ
์คํธ ์๋ ์ฃผ์
from pyhwpxlib.templates.context import load_context
ctx = load_context(name)
print(ctx.to_markdown())
์ด์ ์ฑํ
์ ๊ฒฐ์ ์ฌํญ (structure_type/page_standard/notes/decisions) ๊ณผ
์ต๊ทผ ์ฑ์ฐ๊ธฐ ๊ฐ (recent_data) ์ด ์๋ ๋ณต์๋๋ค โ ์ฌ์ฉ์๊ฐ ์์ ๋ค์ ์
๋ก๋ํ๊ฑฐ๋
์ค๋ช
๋ค์ ํ ํ์ ์์.
Step 3: ์ฌ์ฉ์ ๋ฉ์์ง์ ๊ตฌ์ฒด์ ์์
์ด ์์ผ๋ฉด AskUserQuestion
"์ด๋ค ์์
์ ํ์๊ฒ ์ด์?"
1. ์ ๋ฌธ์ ๋ง๋ค๊ธฐ
2. ๊ธฐ์กด ๋ฌธ์ ํธ์ง
3. ์์ ์๋ํ (๋ฑ๋ก๋ ์์ N๊ฐ โ ์ด๋ฆ ๋ง์ํ์๋ฉด ์ปจํ
์คํธ ์ฆ์ ๋ก๋)
4. ๋ฌธ์ ๋ณํ
5. ๋ฌธ์ ๋ถ์ โ ํ
์คํธยทํยท์ด๋ฏธ์ง ์ถ์ถ + Vision
์๋ ๊ฐ์ง: .hwp โ ๋ณํ ํ ์งํ, .hwpx โ ๋ฐ๋ก ์งํ, .md โ md2hwpx
SessionStart hook (์ ํ): pyhwpxlib install-hook ํ ๋ฒ ์คํํ๋ฉด Claude Code ๊ฐ
์ ์ฑํ
์์ ์ ๋ฑ๋ก ์์ ๋ชฉ๋ก์ ์๋์ผ๋ก sessionContext ์ ์ฃผ์
ํ๋ค โ ๋งค๋ฒ
list_templates() ํธ์ถ ์์ด๋ ๋ชจ๋ธ์ด ๋ฐ๋ก ์ธ์.
์ํฌํ๋ก์ฐ [1] ์ ๋ฌธ์ ๋ง๋ค๊ธฐ
Step A: ํ
๋ง ์ ํ
from pyhwpxlib.themes import _THEMES_DIR, BUILTIN_THEMES, load_theme
customs = sorted(_THEMES_DIR.glob('*.json')) if _THEMES_DIR.exists() else []
์ ์ฅ๋ ์์์ด ์์ผ๋ฉด ๋จผ์ ์ ์. ์์ผ๋ฉด ์ฃผ์ ์ ๋ง๋ ๋ด์ฅ ํ
๋ง ์ ํ (10์ข
).
Step B: ๋ด์ฉ ํ์ธ โ AskUserQuestion
Rich Mode ์ฒดํฌ๋ฆฌ์คํธ โ "์ต๋ํ ๊ธฐ๋ฅ ํ์ฉ", "ํ๋ถํ๊ฒ", ๋ณด๊ณ ์/์ ์์ ์์ฒญ ์ ์ ๊ฒ:
| ๋ถ๋ฅ | Builder ๋ฉ์๋ | ์ธ์ |
|---|
| ๊ตฌ์กฐ | add_heading add_paragraph add_page_break add_line | ํญ์ |
| ๊ฐ์กฐ | add_highlight add_footnote add_equation | ํต์ฌ ๋ฉ์์งยท๊ฐ์ฃผยท์์ |
| ๋ฆฌ์คํธ | add_bullet_list add_numbered_list add_nested_bullet_list add_nested_numbered_list | ๋จ๊ณยทํญ๋ชฉยท๊ณ์ธต |
| ์๊ฐ | add_image add_image_from_url add_table add_rectangle add_draw_line | ๊ทธ๋ํยทํยท๋ํ |
| ๊ฒฐ๋ฌธ | add_header add_footer add_page_number | ๋ณด๊ณ ์ยท๊ณต์ ๋ฌธ์ |
โ ๋ด์ฉ์ ์์ฐ์ค๋ฌ์ด ๊ฒ๋ง ์ ํ. ๋์์ธ ๊ท์น #2 (์ต์ง ์ฝ์
๊ธ์ง) ์ ์ง. ๊ณจ๋ ์ํ: references/rich_document_example.md.
Step C: ์คํ
from pyhwpxlib import HwpxBuilder
doc = HwpxBuilder(theme='forest')
doc.add_heading("์ ๋ชฉ", level=1)
doc.add_paragraph("")
doc.add_paragraph("๋ณธ๋ฌธ")
doc.add_paragraph("")
doc.add_table([["A", "B"]])
doc.add_paragraph("")
doc.add_image("photo.png", width=42520, height=23918)
doc.add_paragraph("")
doc.save("output.hwpx")
Step D: pyhwpxlib validate + pyhwpxlib lint
Step E: ์๊ฐ ๊ฒํ (์๋ต ๊ธ์ง)
from pyhwpxlib.api import render_to_png
png = render_to_png("output.hwpx", page=0)
from pyhwpxlib.rhwp_bridge import RhwpEngine
engine = RhwpEngine()
doc = engine.load("output.hwpx")
svg = doc.render_page_svg(0, embed_fonts=True)
โ ๏ธ PNG ๋ณํ ํจ์ : render_page_svg(embed_fonts=True) + cairosvg ์กฐํฉ์ cairosvg ์ @font-face ํ๊ณ๋ก ํ๊ธ์ด โกโกโก (tofu) ๋ก ๊นจ์ง๋ค. PNG ๊ฐ ํ์ํ๋ฉด pyhwpxlib.api.render_to_png() ๋๋ CLI pyhwpxlib png <file> ์ฌ์ฉ (font-family ๋ฅผ fontconfig ๋ฑ๋ก๋ NanumGothic ์ผ๋ก ์ผ๊ด ์นํ).
rhwp ํ๋ฆฌ๋ทฐ ์๋ ค์ง ํ๊ณ (Whale์์๋ ์ ์):
- ์ด๋ฏธ์ง์ ํ
์คํธ ๊ฒน์นจ โ rhwp๊ฐ textWrap ๋ฏธ์ง์
- linesegarray ๋ถ์ผ์น โ ํ
์คํธ ๊ต์ฒด ํ ์ค ๋ญ์นจ
- cairosvg ๋ก PNG ๋ณํ ์ ํ๊ธ ๊นจ์ง โ
pyhwpxlib.api.render_to_png() ์ฌ์ฉ (v0.17.3+)
๋ณด์กฐ ๋ ๋ API (์ฉ๋๋ณ ์ ํ):
html = doc.render_page_html(0)
tree = doc.get_page_render_tree(0)
n = doc.render_page_canvas_count(0)
๋ธ๋ผ์ฐ์ ๋นํธ๋งต ๋ ๋(renderPageToCanvas)๋ HtmlCanvasElement ํ์๋ผ Python ๋ถ๊ฐ.
7๊ฐ์ง ๋น์ฃผ์ผ ์ฒดํฌํฌ์ธํธ:
1. ์๊ฐ์ ๊ณ์ธต (Visual Hierarchy)
- ์ ๋ชฉ/์์ ๋ชฉ/๋ณธ๋ฌธ ํฌ๊ธฐ ๊ตฌ๋ถ์ด ๋๋๊ฐ? (๊ณต๋ฌธ์ ๊ธฐ์ค: ๋ณธ๋ฌธ 15pt, ์์ ๋ชฉ 16pt, ์ ๋ชฉ 18~20pt)
- ํ์ง๊ฐ ์์ผ๋ฉด: ์ ๋ชฉ์ด ์ถฉ๋ถํ ํฌ๊ฒ ๋ณด์ด๋? (22pt+)
2. ์์ & ๋๋น (Color & Contrast)
- ํ
๋ง primary ์์์ด ์ ์ฉ๋์๋?
- ํ ํค๋ ์ ํ
์คํธ๊ฐ ์ฝํ๋๊ฐ?
- ๊ธฐ๋ณธ ํ๋์(#395da2)์ด ์๋ ์ฃผ์ ์ ๋ง๋ ์์์ธ๊ฐ?
3. ํ์ดํฌ๊ทธ๋ํผ (Typography)
- ํฐํธ ๊นจ์ง(โก) ์๋๊ฐ?
- ๊ธ์ ๊ฐ๊ฒฉ์ด ๊ฒน์น์ง ์๋๊ฐ?
4. ๋ ์ด์์ & ๊ณต๊ฐ (Layout & Spacing)
- ๋์นจ/์๋ฆผ/๋น ํ์ด์ง ์๋?
- ์ฌ๋ฐฑ์ด ๊ท ๋ฑํ๊ฐ?
5. ํ ์คํ์ผ (Table)
- ํค๋ ๋ฐฐ๊ฒฝ์ ์ ์ฉ, ์
ํจ๋ฉ ์ ์ , ์ปฌ๋ผ ๋๋น ๋ถ๋ฐฐ
6. ์๋ณธ ๋์กฐ (ํธ์ง/์์ ์)
- ์๋ณธ๊ณผ ๊ฐ์ ๊ตฌ์กฐ์ธ๊ฐ? ๊ต์ฒด ์ ๋ ํ
์คํธ ์๋?
7. AI ํจํด ํผํ๊ธฐ (Anti-Slop)
- ๋ชจ๋ ์น์
๋์ผ ๋ ์ด์์ ์๋๊ฐ?
- ํ
์คํธ๋ง ์๋ ์น์
์ ์ต์ง๋ก ํ ๋ฃ์ง ์์๋?
Step F: AskUserQuestion โ "Whale์์๋ ํ์ธํด์ฃผ์ธ์"
Step G: ์์ ์ ์ฅ ์ ์ โ ์์ฑ ์ extract_theme + save_theme
์ํฌํ๋ก์ฐ [2] ๊ธฐ์กด ๋ฌธ์ ํธ์ง
Step 0: ๋ฉํ ์ธ์ง (์๋ต ๊ธ์ง) โ ํธ์ง ์ ๋ฐ๋์ ์๊ฐ ํ์ธ + 5์ง๋ฌธ ๋ตํ๊ธฐ.
- ๋ชจ๋ ํ์ด์ง PNG ๋ ๋๋ง โ Read tool๋ก ์ง์ ํ์ธ
- โ ์ด ๋ฌธ์๋ ๋ฌด์์ธ๊ฐ? (์์ยท๋ณด๊ณ ์ยท๊ณต๋ฌธยท๊ณ์ฝยท์ฆ๋นยท๊ต์ฌโฆ)
- โก ๋๊ฐ ์์ฑยท์์ ํ๋๊ฐ?
- โข ํ์ด์ง ํ์ค์ด ์๋๊ฐ? ("1๊ฑด 1๋งค" / "1ํ์ด์ง ์์น" / ์์ )
- โฃ ๋ณด์กดํ ๋ถ๋ถ vs ๋ณ๊ฒฝํ ๋ถ๋ถ? (์์ยท์๋ช
๋ยท๊ธฐ๊ด๋ช
ยท๋์ฅ์ ๋ณด์กด)
- โค ๊ฒฐ๊ณผ๋ฌผ์ ์ฌ์ฉ์ฒ? (์ ์ถยท์ ์ฅยท์ธ์ยท์ ์๊ฒฐ์ฌ)
- โ AskUserQuestion์ผ๋ก ๋ถ์ ๊ฒฐ๊ณผ ํ์ธ ํ ์งํ. ๋ฉํ ์ธ์ง ๊ฑด๋๋ฐ๋ฉด ๋จ์ ํ
์คํธ ์นํ์ ๋จธ๋ฌผ๋ฌ ์ฌ์ฉ์ ์๋์ ์ด๊ธ๋จ.
Step A: ํ์ผ ๊ฒฝ๋ก โ HWP๋ฉด HWPX ๋ณํ
Step B: extract_text() โ ๋ด์ฉ ๋ณด์ฌ์ฃผ๊ธฐ
Step C: ํธ์ง ์ ํ ํ์ธ โ ํ
์คํธ ๊ต์ฒด/์์ ์ฑ์ฐ๊ธฐ
Step D: unpack โ ์๋ณธ ๋ฌธ์์ด ๊ต์ฒด โ pack โ validate
์ํฌํ๋ก์ฐ [3] ์์ ์ฑ์ฐ๊ธฐ โ ์ ์ฉ ์ํฌํ๋ก์ฐ ๋ฌธ์
๊ธฐ์กด ์์(.hwpx)์ ๋ฐ์ดํฐ๋ฅผ ์ฑ์ฐ๋ ์์
์ ์ํฌํ๋ก์ฐ ์ ์ฉ ๋ฌธ์ hwpx-form/WORKFLOW.md ์ ๋ถ๋ฆฌ๋์ด ์๋ค (ํ์ด์ง ํ์คยท1๋งค ๊ฐ์ ยท๊ตฌ์กฐ A/B ํ์ ยทpage-guardยท๋ค์ด์ด๋ฆฌ์ ์ด์
๋ฑ ๊ณ ์ ์ ์ฐจ + ํ์ต ๋ฃจํ ๋ค์ด์ด๊ทธ๋จ + ์ต์ด ๋ฑ๋ก ๋ณต๋ถ ๋ธ๋ก์ ํ ํ์ด์ง์ ์์ง).
์์ฝ ์ ์ฐจ (์์ธํ ๋ด์ฉ์ hwpx-form ์คํฌ):
- Step 0:
template context <name> โ ๋ฑ๋ก ์์์ด๋ฉด ๊ฒฐ์ ์ฌํญยท์ด์ ๊ฐ ์๋ ๋ณต์
- Step 0': ๋ฉํ ์ธ์ง (5์ง๋ฌธ) โ ๋ฏธ๋ฑ๋ก ์์ ํ์
- Step AโD: ํ๋ฆฌ๋ทฐ ๋ ๋๋ง โ ํ๋ ์
๋ ฅ โ ๊ตฌ์กฐ A/B ํ์ โ ์ฑ์ฐ๊ธฐ โ ๊ฒ์ฆ
- Step E: 1ํ์ด์ง fit (ํ์ ์
GongmunBuilder(autofit=True) โ ์๊ณ ๋ฆฌ์ฆ ์์ญ)
- Step F:
page-guard ํต๊ณผ ๊ฒ์ดํธ (Critical Rule #13)
- Step G:
hwpx_template_save_session(name, data, decision) โ ๋ค์ ์ธ์
์ํด ๋ฐ์
์ํฌํ๋ก์ฐ [4] ๋ฌธ์ ๋ณํ
from pyhwpxlib.hwp2hwpx import convert
from pyhwpxlib.api import convert_html_file_to_hwpx
์ํฌํ๋ก์ฐ [5] ๊ณต๋ฌธ(๊ธฐ์๋ฌธ) ์์ฑ โ ํธ๋ ์ค์
ํ์ ์์ ๋ถ ใ2025 ํ์ ์
๋ฌด์ด์ ํธ๋ใ ๊ท์ ์๋ ์ค์. ์ผ๋ฐ๊ธฐ์๋ฌธ / ๊ฐ์ด๊ธฐ์๋ฌธ / ์ผ๊ด๊ธฐ์ / ๊ณต๋๊ธฐ์ ์ง์.
from pyhwpxlib.gongmun import Gongmun, GongmunBuilder, signer, validate_file
doc = Gongmun(
๊ธฐ๊ด๋ช
="ํ์ ์์ ๋ถ",
์์ ="์์ ์ ์ฐธ์กฐ",
์ ๋ชฉ="2024๋
์ ๋ณด๊ณต๊ฐ ์ข
ํฉํ๊ฐ ๊ณํ ์๋ด",
๋ณธ๋ฌธ=["...", "..."],
๋ถ์=["๊ณํ์ 1๋ถ."],
๋ฐ์ ๋ช
์="ํ์ ์์ ๋ถ์ฅ๊ด",
๊ธฐ์์=signer("ํ์ ์ฌ๋ฌด๊ด", "๊นOO"),
๊ฒฐ์ฌ๊ถ์=signer("์ ๋ณด๊ณต๊ฐ๊ณผ์ฅ", "๊นOO", ์ ๊ฒฐ=True, ์๋ช
์ผ์="2025. 9. 30."),
์ํ_์ฒ๋ฆฌ๊ณผ๋ช
="์ ๋ณด๊ณต๊ฐ๊ณผ", ์ํ_์ผ๋ จ๋ฒํธ="000", ์ํ์ผ="2025. 9. 30.",
์ฐํธ๋ฒํธ="30112", ๋๋ก๋ช
์ฃผ์="์ธ์ข
ํน๋ณ์์น์ ๋์6๋ก 42",
์ ํ="(044)205-0000", ๊ณต๊ฐ๊ตฌ๋ถ="๋๊ตญ๋ฏผ๊ณต๊ฐ",
)
GongmunBuilder(doc).save("output.hwpx")
GongmunBuilder(doc, autofit=True).save("output.hwpx")
from pyhwpxlib.gongmun import format_report
print(format_report(validate_file("output.hwpx")))
์๋ ์ ์ฉ: ๋ ์ง ํฌ๋งท(2025. 9. 20.) ยท 2ํ ๋ค์ฌ์ฐ๊ธฐ ยท ํญ๋ชฉ๊ธฐํธ 8๋จ๊ณ ยท ๋ํ์ ยท ๋๋ฌธ/๋ณธ๋ฌธ/๊ฒฐ๋ฌธ ์์ ยท '๊ธฐ์์ยท๊ฒฐ์ฌ๊ถ์' ์ฉ์ด ์๋ต ยท ํ์ ๊ตฌ๋ถ์ .
์๋ ๊ฒ์ฌ: ์์์ ์ดํฌ("ํ ๊ฒ", "~๋ฐ๋") ยท ๊ถ์์ ํํ("์นํํ๋ค") ยท ์ฐจ๋ณ์ ํํ("๊ฒฐ์๊ฐ์ ") ยท ํ๊ธํธํ์์ญ ํน์๋ฌธ์(ใฎ ๋ฑ) ยท ๋์๋ฒ์น ์ค๋ฅ ยท ์ธ๋์ด ์คํ๊ธฐ ยท ๋ํ์ ๋๋ฝ.
์์ธ: references/gongmun.md ยท ํธ๋ ๊ท์น YAML: pyhwpxlib/gongmun/rules.yaml
์ํฌํ๋ก์ฐ [6] ๋ฌธ์ ๋ถ์
from pyhwpxlib.json_io.overlay import extract_overlay
overlay = extract_overlay(hwpx_path)
BinData์์ ์ด๋ฏธ์ง ์ถ์ถ โ Read tool๋ก ๋ด์ฉ ํ์
(Vision)
์ํฌํ๋ก์ฐ [7] JSON โ HWPX (v0.15.0+, ์ธ๋ถ LLM/MCP ์นํ)
JSON ํ ๋ฉ์ด๋ฆฌ๋ก builder 19๊ฐ add_* ๋ฉ์๋ ์ ๋ถ ํํ ๊ฐ๋ฅ (19/19, 100%).
heading, image, image_from_url, header/footer, lists, footnotes, equation,
highlight, shapes, page_number, page_break ๋ชจ๋ dispatch. v0.14.0
paragraphs/tables-only JSON ๋ ๊ทธ๋๋ก ๋์ (back-compat).
from pyhwpxlib.json_io import from_json, to_json
data = {
"header": {"text": "๊ธฐ๋ฐ"}, "footer": {"text": "ํ์ฌ X"},
"page_number": {"pos": "BOTTOM_CENTER"},
"sections": [{
"paragraphs": [
{"runs": [{"content": {
"type": "heading",
"heading": {"text": "1. ์๋ก ", "level": 1}}}]},
{"runs": [{"content": {"type": "text", "text": "๋ณธ๋ฌธ..."}}]},
{"runs": [{"content": {
"type": "bullet_list",
"bullet_list": {"items": ["๋ฐฐ๊ฒฝ", "๋ชฉ์ ", "๋ฒ์"]}}}]},
{"runs": [{"content": {
"type": "footnote",
"footnote": {"text": "์ฐธ์กฐ", "number": 1}}}]},
],
"tables": [], "page_settings": {}
}]
}
from_json(data, "out.hwpx")
parsed = to_json("out.hwpx")
RunContent.type 14์ข
: text, table, heading, image, bullet_list,
numbered_list, nested_bullet_list, nested_numbered_list, footnote,
equation, highlight, shape_rect, shape_line, shape_draw_line.
Paragraph flag: page_break: true โ add_page_break().
Top-level 3์ข
(deferred): header, footer, page_number.
Unknown type โ ValueError (rhwp ๋
ธ์ : silent skip ๊ธ์ง).
MCP hwpx_from_json ๋ ์ schema ์๋ ์ง์ (signature ๋ฌด๋ณ๊ฒฝ).
Quick Reference
| Task | Approach |
|---|
| ์ ๋ฌธ์ | HwpxBuilder(theme='forest') |
| ๊ณต๋ฌธ(๊ธฐ์๋ฌธ) | GongmunBuilder(Gongmun(...)).save() (ํธ๋ ์ค์) |
| ๊ณต๋ฌธ 1ํ์ด์ง ์๋ ๋ง์ถค | GongmunBuilder(doc, autofit=True).save() |
| ๊ณต๋ฌธ ๊ท์ ๊ฒ์ฆ | validate_file("doc.hwpx") (ERROR/WARNING/INFO) |
| ํ
์คํธ ์ฝ๊ธฐ | extract_text() |
| ํธ์ง | unpack โ replace โ pack |
| JSON โ HWPX (v0.15.0+) | from_json(data, "out.hwpx") โ 19/19 builder ๋ฉ์๋ ์ ๋ถ ๋๋ฌ |
| HWPX โ JSON | to_json("doc.hwpx") โ image/footnote/equation/shape ์๋ ์๋ณ |
| ์์ ๋ฑ๋ก (v0.13.3+) | pyhwpxlib template add my_form.hwp --name my_form |
| ์์ ์ฑ์ฐ๊ธฐ (schema) | pyhwpxlib template fill <name> -d data.json -o out.hwpx |
| ์์ ์๋ schema ์ง๋จ | pyhwpxlib template diagnose <hwpx> --schema MANUAL.json |
| ์์ ์ฑ์ฐ๊ธฐ ({{key}}) | fill_template(data={"key": "val"}) โ {{key}} ํจํด |
| ์ฒดํฌ๋ฐ์ค | fill_template_checkbox(checks=["๋์ํจ"]) |
| ์ด๋ฏธ์ง ์ฝ์
| add_image(path, width=42520, height=๋น์จ) |
| ๊ธฐ์กด ๋ฌธ์์ ์ด๋ฏธ์ง | insert_image_to_existing() |
| ์ด๋ฏธ์ง ๊ต์ฒด | overlay new_data_b64 |
| HWPโHWPX | hwp2hwpx.convert() |
| ๊ฒ์ฆ (dual-mode, v0.14.0+) | pyhwpxlib validate --mode {strict|compat|both} |
| ๋นํ์ค ์ง๋จ/๋ณด์ (v0.14.0+) | pyhwpxlib doctor <file> [--fix] |
| page-guard (v0.16.0+) | pyhwpxlib page-guard --reference REF --output OUT [--threshold N] โ ๊ฐ์ ๊ฒ์ดํธ |
| check-fill (v0.18.0+) | pyhwpxlib check-fill <name> -d data.json --json โ XML-level ๋น์นธ/placeholder ๊ฒ์ฆ (~10ms, ์ค๊ฐ ๊ฒ์ฆ ๊ถ์ฅ. ์ต์ข
์๋ง PNG) |
| check-fill MCP | hwpx_check_fill(name, data_json) โ schema ์ฐ์ + ํจํด ํด๋ฐฑ, is_complete ๋ฐํ |
| render_to_png ์บ์ DI (v0.18.0+) | render_to_png(path, engine=eng) โ N์ฅ ๋ฐฐ์น ์ RhwpEngine 1ํ๋ง init |
| render_pages_to_png (v0.18.0+) | render_pages_to_png(file, out_dir) โ 1 engine + ๋ณ๋ ฌ SVG + ๋ณ๋ ฌ cairosvg, byte-identical |
| ํ ์๋ ํ์ด์ง ๋๊น (v0.18.1+) | add_table(data, page_break="TABLE", repeat_header=True) โ Hancom "์ฌ๋ฌ ์ชฝ ์ง์"/"์ ๋ชฉ ์ค ๋ฐ๋ณต" |
| structure ์ฒญ์ฌ์ง (v0.16.0+) | pyhwpxlib analyze FILE --blueprint [--depth 1|2|3] [--json] |
| Lint | pyhwpxlib lint <file> |
| Font check | pyhwpxlib font-check <file> |
| ํ
๋ง ์ถ์ถ | extract_theme() โ save_theme() |
| ํ
๋ง ๋ชฉ๋ก | pyhwpxlib themes list |
| ํ๋ฆฌ๋ทฐ (SVG) | RhwpEngine().load().render_page_svg() |
| ํ๋ฆฌ๋ทฐ (PNG, v0.17.3+) | render_to_png(file, page=0) ๋๋ CLI pyhwpxlib png file โ ํ๊ธ ์์ |
| ํ๋ฆฌ๋ทฐ (HTML, ๊ฒฝ๋) | doc.render_page_html(0) |
| ๋ ์ด์์ ๊ฒ์ฆ | doc.get_page_render_tree(0) โ bbox ์ขํ๋ก overflow ํ์ |
ํ
๋ง (10์ข
)
| ํ
๋ง๋ช
| Primary | ์ฉ๋ |
|---|
default | #395da2 | ๊ณต๋ฌธ์ |
forest | #2C5F2D | ํ๊ฒฝ, ESG |
warm_executive | #B85042 | ์ ์์ |
ocean_analytics | #065A82 | ๋ฐ์ดํฐ |
coral_energy | #F96167 | ๋ง์ผํ
|
charcoal_minimal | #36454F | ๊ธฐ์ |
teal_trust | #028090 | ์๋ฃ, ๊ธ์ต |
berry_cream | #6D2E46 | ๊ต์ก |
sage_calm | #84B59F | ์ฐ๋น |
cherry_bold | #990011 | ๊ฒฝ๊ณ |
์ปค์คํ
: extract_theme("ref.hwpx") โ save_theme() โ HwpxBuilder(theme='custom/name')
์ด๋ฏธ์ง ํฌ๊ธฐ ๊ท์น
๊ธฐ๋ณธ๊ฐ์ ํญ์ ์ ์ฒด ๋๋น(42520). ๋น์จ ์ ์ง:
from PIL import Image
img = Image.open("photo.png")
width = 42520
height = int(42520 * img.size[1] / img.size[0])
doc.add_image("photo.png", width=width, height=height)
๋ก๊ณ /์์ด์ฝ๋ง 8000~12000. ์ด๋ฏธ์ง ์๋ค ๋น ์ค ํ์.
Critical Rules
| # | Rule | Consequence |
|---|
| 1 | <hp:t> ์์ \n ๊ธ์ง | Whale ์๋ฌ |
| 2 | ET.tostring ๊ธ์ง | ๋ค์์คํ์ด์ค ๋ณ๊ฒฝ |
| 3 | ์๋ณธ ๋ฌธ์์ด ์ง์ ๊ต์ฒด | ์์ ๋ณด์กด ์ ์ผํ ๋ฐฉ๋ฒ |
| 4 | mimetype STORED | OPC ๊ท๊ฒฉ |
| 5 | condense ๋ณด์กด | JUSTIFY ๋ฒ์ด์ง ๋ฐฉ์ง |
| 6 | ํค๋ฉ/ํ/์ด๋ฏธ์ง ์๋ค ๋น ์ค | ๊ฐ๊ฒฉ ์์ผ๋ฉด ๋ถ์ |
| 7 | ํ๋ ํ์ํ ๋๋ง | ์ต์ง ์ฝ์
๊ธ์ง |
| 8 | hwpx ํธ์ง ํ lineseg ์ ํฉ์ฑ ๊ฒ์ฆ | ํ์ปด ๋ณด์๊ฒฝ๊ณ ํํผ (v0.14.0+ opt-in) |
| 9 | ์ฐ๋ฆฌ๋ ๋นํ์ค ์๋ก ์์ฐ ์ ํจ | rhwp ๋
ธ์ : ๊ฐ์ง+๊ณ ์ง+๋์ ํ ๋ณด์ |
| 10 | ์นํ ์ฐ์ ํธ์ง โ ์์ยท๊ธฐ์กด ๋ฌธ์ ํธ์ง ์ ์ ๋ฌธ๋จ/ํ ์ถ๊ฐ ๋์ ํ
์คํธ ๋
ธ๋ ์นํ ์ฐ์ | ์์ ๋ณด์กด, ํ์ด์ง ๋ณ๋ ์ต์ํ |
| 11 | ๊ตฌ์กฐ ๋ณ๊ฒฝ ์ ํ โ ์ฌ์ฉ์ ๋ช
์ ์์ฒญ ์์ด <hp:p> <hp:tbl> rowCnt colCnt ์ถ๊ฐ/์ญ์ /๋ถํ /๋ณํฉ ๊ธ์ง | ๋ ํผ๋ฐ์ค ์ถฉ์ค๋ |
| 12 | ํ์ด์ง ๋์ผ ํ์ (๋ ํผ๋ฐ์ค ์์
) โ ๋ ํผ๋ฐ์ค ์์ผ๋ฉด ๊ฒฐ๊ณผ ์ชฝ์ ๋์ผ | ์์ยท๊ณต๋ฌธ ์ ๋ขฐ๋ |
| 13 | page-guard ํต๊ณผ ํ์ โ validate ํต๊ณผ โ ์๋ฃ. pyhwpxlib page-guard ๋ ํต๊ณผํด์ผ ์๋ฃ ์ฒ๋ฆฌ | ๊ฐ์ ๊ฒ์ดํธ (v0.16.0+) |
์์ธ API, ํ๋ฆฌ์
, ํ ํ๋ผ๋ฏธํฐ, ํธ์ง ์ธ๋ถ์ฌํญ โ references/ ์ฐธ์กฐ
ํ์ปด ๋ณด์๊ฒฝ๊ณ + rhwp ๋
ธ์ (Rule 8/9 ์์ธ)
์ง์ง ํธ๋ฆฌ๊ฑฐ (ํ์ 2026-04-27): section*.xml ์์์ ๋ค์ ์กฐ๊ฑด์ด ๋จ ํ ๋ฒ์ด๋ผ๋
๋ํ๋๋ฉด ํ์ปด์ด "๋ฌธ์ ๋ณด์ ์ค์ ์ ๋ฎ์ถค" ๊ฒฝ๊ณ ๋ฅผ ๋์ด๋ค:
<hp:lineseg textpos="N"/> AND N > UTF-16(paragraph ์ hp:t ํ
์คํธ ํฉ)
(์ธ๋ถ ๋๊ตฌ๊ฐ ํ
์คํธ๋ฅผ ์งง๊ฒ ๋ฐ๊ฟจ๋๋ฐ lineseg ์บ์๋ ๊ทธ๋๋ก โ ํ์ปด์ด ์ธ๋ถ ์์ ์ผ๋ก ํ์ )
v0.14.0 ์ ์ฑ
๋ณ๊ฒฝ (rhwp ๋
ธ์ ): ํ์ปด ์์ฒด๊ฐ silent reflow ๋ก ๋นํ์ค lineseg
๋ฅผ ๋ฎ์ด ํ๋ฐ์ฃผ์์๊ฒ ๋น์ฉ ์ ๊ฐํ๋ ๊ตฌ์กฐ์ ๋์กฐํ์ง ์์. write_zip_archive ์
silent precise fix ๋ฅผ opt-in ์ผ๋ก ์ ํ. ์ฌ์ฉ์๊ฐ ๋ช
์ ๋์(--fix / strip_linesegs="precise")ํ ๋๋ง ๋ณด์ .
from pyhwpxlib.package_ops import read_zip_archive, write_zip_archive
arch = read_zip_archive(input_path)
n_fixed = write_zip_archive(output_path, arch)
n_fixed = write_zip_archive(output_path, arch, strip_linesegs="precise")
write_zip_archive(output_path, arch, strip_linesegs="remove")
๊ฒ์ฆ + ์ง๋จ + ๋ณด์ ์ํฌํ๋ก (v0.14.0+):
pyhwpxlib validate <file>
pyhwpxlib validate <file> --mode strict
pyhwpxlib validate <file> --mode compat
pyhwpxlib doctor <file>
pyhwpxlib doctor <file> --fix
pyhwpxlib doctor <file> --fix --inplace
pyhwpxlib doctor <file> --fix -o out.hwpx
pyhwpxlib template fill <name> -d data.json -o out.hwpx --fix
pyhwpxlib reflow-linesegs <file>
์ฐ๋ฆฌ ์์ ์ ์์น (Rule 9):
- HwpxBuilder ๊ฐ ๋ง๋๋ ์ ๋ฌธ์๋ ํญ์ ์ ํํ lineseg ์ถ๋ ฅ
- ์ธ๋ถ ์
๋ ฅ์ ๋นํ์ค ๊ตฌ์กฐ๋ lint/doctor ๋ก ๊ฐ์ง+๊ณ ์ง ํ๋ ์๋ ๋ณด์ ์ ํจ
- ์๋ชป๋ ์
๋ ฅ (unknown JSON type ๋ฑ) ์ silent skip ๊ธ์ง โ ๋ช
์ ValueError
Reference Files
| File | Contents |
|---|
| references/api_full.md | HwpxBuilder ์ ์ฒด ๋ฉ์๋, ํ ํ๋ผ๋ฏธํฐ, ํฌ๊ธฐ ๋ณํ |
| references/design_guide.md | ์ฃผ์ ๋ณ ํ๋ ํธ 10์ข
, ๋ ์ด์์, QA |
| references/editing.md | unpack/pack ์์ธ, XML ๊ท์น, ๊ณ ๊ธ ํธ์ง |
| references/form_automation.md | fill_template, batch, schema, checkbox |
| references/document_types.md | ๋ฌธ์ ์ ํ, ํ๋ฆฌ์
, ํ์ง, ๊ฒฐ๋ฌธ |
| templates/README.md | ๋ฒ๋ค ์์ ๋ชจ์ (์ ์ Makers ๊ฒฐ๊ณผ๋ณด๊ณ ์ ๋ฑ) + schema |
| references/gongmun.md | ๊ณต๋ฌธ ์์ฑ (ํธ๋ ์ค์) โ ์ผ๋ฐ/๊ฐ์ด/์ผ๊ด/๊ณต๋๊ธฐ์ + validator |
| references/HWPX_RULEBOOK.md | Critical Rules ์ ์ฒด + ์์ธ ์ค๋ช
|
| references/rich_document_example.md | Rich Mode ๊ณจ๋ ์ํ โ 14/15 builder ๋ฉ์๋ ํ์ฉ ๋ณด๊ณ ์ |
Versions
| Version | Highlights |
|---|
| 0.18.3 | MCP entry-point fix โ python -m pyhwpxlib.mcp_server ์ด์ ๋์ (__main__.py ์ถ๊ฐ). Claude Desktop/Cursor MCP ์ค์ ์์ .server ์ ๋ฏธ์ด ๋ถํ์. ๋ผ์ด์ ์ค ๋ฌธ์ ํต์ผ (ratiertm@gmail.com) |
| 0.18.2 | License declaration โ PolyForm Noncommercial 1.0.0 + Apache 2.0 (dual). ์ฝ๋ ๋ฌด๋ณ๊ฒฝ, 0.18.1 ๊ณผ byte-identical. ๊ฐ์ธ/ํ์ /๋น์๋ฆฌ ๋ฌด๋ฃ, ์๋ฆฌ ํ๋ (์ธ์ ๋ฌด๊ด) ์์
๋ผ์ด์ ์ค ํ์ |
| 0.18.1 | ํ ์๋ ํ์ด์ง ๋๊ธฐ๊ธฐ โ add_table(page_break, repeat_header) keyword-only ๋
ธ์ถ (Hancom UI "์ฌ๋ฌ ์ชฝ ์ง์"/"์ ๋ชฉ ์ค ๋ฐ๋ณต"). ์๊ฐ ์๋์กฐ์ ์ 0.19.0 deferred |
| 0.18.0 | render-perf-opt โ wasmtime Engine/Module ๋ชจ๋-๋ ๋ฒจ ์บ์ (render_to_png warm 1.2s โ 70ms, -94%) + _TextMeasurer LRU + _register_bundled_fonts ๊ฐ๋ + render_to_png(*, engine=) DI + ์ ๊ท pyhwpxlib check-fill CLI / MCP hwpx_check_fill (~10ms XML-level ๊ฒ์ฆ) + MCP docstring ์์ถ (-45%) + Workflow [3] Step D ๊ฒ์ดํ
(์ค๊ฐ check-fill / ์ต์ข
PNG) |
| 0.17.3 | PNG export โ pyhwpxlib.api.render_to_png() + CLI pyhwpxlib png + MCP hwpx_render_png. cairosvg ์ @font-face ํ๊ธ ํ๊ณ๋ฅผ font-family ์ผ๊ด ์นํ์ผ๋ก ์ฐํ. ๋ฒ๋ค NanumGothic ์๋ ๋ฑ๋ก |
| 0.17.2 | docs โ ๋ด์ฅ LLM ๊ฐ์ด๋ (pyhwpxlib.llm_guide.GUIDE, MCP hwpx_guide()) v0.10.0 โ v0.17.2 ๊ฐฑ์ + chatgpt_hwpx_guide.md ์ ๊ฑฐ |
| 0.17.1 | font-check ๊ฐํ โ --font-map <path> ์ฌ์ฉ์ ๋งคํ + ์ํ ok/alias/fallback/missing ์ ๋ฐํ + rhwp_bridge lazy wasmtime ([preview] ๋ฏธ์ค์น ์ฌ์ฉ์ font-check ์ ์ ๋์) + MCP hwpx_template_save_session (log_fill+annotate ํ ๋ฒ ํธ์ถ) |
| 0.17.0 | ์ปจํ
์คํธ ์ง์์ฑ โ ์์๋ณ ์ํฌ์คํ์ด์ค ํด๋ (~/.local/share/pyhwpxlib/templates/<name>/) + decisions.md / history.json / outputs/ ์๋ ๋์ + template context/annotate/log-fill/open/migrate/install-hook CLI + MCP hwpx_template_context / workspace_list / log_fill / save_session |
| 0.16.1 | ๋ผ์ด์ ์ค ์์ โ default ํฐํธ ํจ์ด๋กฌ/๋ง์ ๊ณ ๋ โ ๋๋๊ณ ๋ (SIL OFL 1.1), pyhwpxlib/font/ 148 MB ์ ๊ฑฐ, vendor NanumGothic ๋ณด์กด |
| 0.16.0 | reference-fidelity-toolkit โ pyhwpxlib page-guard ๊ฐ์ ๊ฒ์ดํธ (rhwp+static ์ด์ค ๊ฒฝ๋ก) + pyhwpxlib analyze --blueprint ์ฒญ์ฌ์ง + Critical Rules ์๋ ๋ฃฐ 4๊ฐ (#10~#13) |
| 0.15.0 | JSON ๊ฒฝ๋ก 19/19 builder ๋ฉ์๋ ์ ๋ถ ๋๋ฌ (์ต์
A) โ heading/image/image_from_url/list/footnote/equation/shape/header/footer/page_number/page_break, encoder rich-type emission |
| 0.14.0 | rhwp ๋
ธ์ ์ฑํ โ silent fix opt-in, pyhwpxlib doctor, validate --mode strict|compat|both |
| 0.13.4 | auto_schema cellSpan-aware grid + row-group label, template diagnose |
| 0.13.3 | template workflow (์ต์
B) โ add/fill/show/list/diagnose, XDG ๊ณ์ธต |
| 0.13.2 | precise textpos-overflow fix |
| 0.13.0 | ํ๊ตญ ๊ณต๋ฌธ์ ํ์ค โ ํฐํธ/ํฌ๊ธฐ/์ฌ๋ฐฑ/์ค๊ฐ๊ฒฉ |