ํ๊ธ ์์ด HWPX ๋ฌธ์๋ฅผ Python์ผ๋ก ์ฝ๊ณ , ํธ์งํ๊ณ , ์์ฑํ๊ณ , ๊ฒ์ฆํฉ๋๋ค.
| ๊ณ์ธต | ๋ ํฌ | ์ญํ |
|---|---|---|
| ๐ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ | python-hwpx |
์์ ํ์ด์ฌ HWPX ํ์ฑยทํธ์งยท์์ฑ ์ฝ์ด |
| ๐ MCP ์๋ฒ | hwpx-mcp-server |
MCP ํด๋ผ์ด์ธํธ(Claude Desktop, VS Code ๋ฑ)์์ HWPX ์กฐ์ |
| ๐ฏ ์์ด์ ํธ ์คํฌ | hwpx-skill |
์์ด์ ํธ๊ฐ HWPX๋ฅผ ๋ฐ๋ก ์ฐ๊ฒ ํด์ฃผ๋ ๊ณต์ ์จ๋ณด๋ฉ ์คํฌ |
- ํ์ปด์คํผ์ค ์ค์น ๋ถํ์ โ ์์ ํ์ด์ฌ์ผ๋ก ์ด๋์๋ ๋์
- XML-first ์ํฌํ๋ก โ ์คํค๋ง ๊ฒ์ฆยทunpack/pack๊น์ง ํฌํจ
- ์์ด์ ํธยท์๋ํ ์นํ โ MCP ์๋ฒยทSkill์ด ๊ฐ์ ์คํ ์์์ ์ง๊ฒฐ
| ํญ๋ชฉ | python-hwpx | pyhwp(x) ๋ฅ | ole+bin ์์์ |
|---|---|---|---|
| HWPX Open XML ์ง์ | โ | โ | |
| ํ์ปด์คํผ์ค ์ค์น ๋ถํ์ | โ | โ | โ |
| ํธ์ง/์์ฑ API | โ | โ ๋๋ถ๋ถ ์ฝ๊ธฐ | โ |
| ์คํค๋ง ๊ฒ์ฆ | โ | โ | โ |
| AI ์์ด์ ํธ ์ฐ๋ (MCP) | โ (hwpx-mcp-server) | โ | โ |
from hwpx import HwpxDocument
document = HwpxDocument.open("๋ณด๊ณ ์.hwpx")
document.add_paragraph("์๋ํ๋ก ์ถ๊ฐํ ๋ฌธ๋จ์
๋๋ค.")
document.save_to_path("๋ณด๊ณ ์-์์ .hwpx")from hwpx import HwpxDocument
doc = HwpxDocument.open("์ ์ฒญ์.hwpx")
result = doc.fill_by_path({
"์ฑ๋ช
> right": "ํ๊ธธ๋",
"์์ > right": "ํ๋ซํผํ",
})
doc.save_to_path("์ ์ฒญ์-์์ฑ์๋ฃ.hwpx")
print(result["applied_count"], result["failed_count"])from hwpx import HwpxDocument
text = HwpxDocument.open("๋ณด๊ณ ์.hwpx").export_markdown()
print(text[:500])hwpx-validate-package ๋ณด๊ณ ์.hwpx
hwpx-analyze-template ๋ณด๊ณ ์.hwpxexport_markdown()๋ ๋จ์ ํ๋ฌธ ์ถ์ถ์ด๊ณ , export_rich_markdown()๋ ์ธ๋ผ์ธ ์์(**๊ตต๊ฒ**, *๊ธฐ์ธ์*, ~~์ทจ์์ ~~),
ํ(์ค์ฒฉ ํฌํจ, colspan/rowspan ์์ ), ๋ํ ํ
์คํธ, ์ด๋ฏธ์ง, ๊ฐ์ฃผ/๋ฏธ์ฃผ, ํ์ดํผ๋งํฌ, ์ ๋ชฉ(#/##) ์๋ ๊ฐ์ง๊น์ง ๋ณด์กดํ๋ค.
from hwpx import HwpxDocument
doc = HwpxDocument.open("๋ณด๊ณ ์.hwpx")
md = doc.export_rich_markdown(
image_dir="out/images", # BinData ์ด๋ฏธ์ง๋ฅผ ๋์คํฌ์ ์ถ์ถ
image_ref_prefix="images/", # ๋งํฌ๋ค์ด ๋ด  ๊ฒฝ๋ก ์ ๋
detect_headings=True, # โ
./1. ํจํด ๊ธฐ๋ฐ #/## ์๋
)
print(md)๋ฌธ์์ดยท๊ฒฝ๋กยท๋ฐ์ดํธ๋ ๊ทธ๋๋ก ๋ฐ๋๋ค:
from hwpx.tools.markdown_export import export_markdown
md = export_markdown("๋ณด๊ณ ์.hwpx") # ๊ฒฝ๋ก
md = export_markdown(open("a.hwpx", "rb").read()) # bytesHwpxOxmlNote์ body_paragraph, add_run, add_hyperlink helper๊ฐ ์์ด ๊ฐ์ฃผ ๋ณธ๋ฌธ์
์ง์ paragraph๋ก ๋ค๋ฃจ์ง ์๊ณ ๋ ์ธ๋ผ์ธ ์์ยท๋งํฌ๋ฅผ ์์ฝ๊ฒ ์ฑ์ธ ์ ์๋ค.
para = section.paragraphs[0]
note = para.add_footnote("") # ๋น ๊ฐ์ฃผ ์์ฑ ํ ๋ณธ๋ฌธ ๊ตฌ์ฑ
note.add_run("์์ธํ ๋ด์ฉ์ ", )
note.add_run("์ ๋ถ ๊ณต์ ์ฌ์ดํธ", bold=True)
note.add_run("๋ฅผ ์ฐธ๊ณ ํ๋ผ: ")
note.add_hyperlink("https://www.kasa.go.kr", "์ฐ์ฃผํญ๊ณต์ฒญ")์ฒ์์๋ open/new -> edit/extract -> save_to_path ํ๋ฆ๋ง ์ก์ผ๋ฉด ๋๋ค. ํจํค์ง ๊ตฌ์กฐ, XML ํํธ, ํ
ํ๋ฆฟ ํ๊ท ์ ๊ฒ์ ํ์ํ ๋๋ง ํ์ฅํ๋ฉด ๋๋ค.
ํ์ํ ์์ ๋ถํฐ ๋ฐ๋ก ๋ค์ด๊ฐ๋ฉด ๋๋ค.
- ์ฒซ ํ์ผ์ ์ด๊ณ ์ ์ฅํ๋ ์ต์ ๊ฒฝ๋ก โ
docs/quickstart.md - ๋ฌธ๋จ, ํ, ๋ฉ๋ชจ, ์น์
ํธ์ง ํจํด โ
docs/usage.md - ํ
์คํธ ์ถ์ถ, ๊ตฌ์กฐ ์กฐํ, ๊ฒ์ฆ/ํจํค์ง ์์
โ
docs/usage.md - ์คํ ๊ฐ๋ฅํ ์์ ๋ชจ์ โ
docs/examples.md - ํจํค์ง ๊ตฌ์กฐ์ ์คํค๋ง ์ฌํ โ
docs/schema-overview.md - ์ค์น ๊ฒ์ฆ๊ณผ ๊ฐ๋ฐ ํ๊ฒฝ ํ์ธ โ
docs/installation.md
|
build_release_checklist.py ๋ฉ๋ชจ์ ์คํ์ผ ํธ์ง์ด ํฌํจ๋ ๋ฆด๋ฆฌ์ค ์ฒดํฌ๋ฆฌ์คํธ์ฉ HWPX๋ฅผ ์์ฑํ๋ค. |
extract_text.py ๋ณธ๋ฌธ๊ณผ ์ค์ฒฉ ๊ฐ์ฒด ํ ์คํธ๋ฅผ CLI๋ก ๋น ๋ฅด๊ฒ ์ถ์ถํ๋ค. |
find_objects.py ํ๊ทธยท์์ฑ ๊ธฐ์ค์ผ๋ก OWPML XML ๋ ธ๋๋ฅผ ์ถ์ ํ๋ค. |
์ ๋ฌธ์๋ฅผ ๋ฐ๋ก ๋ง๋ค๊ณ ์ถ๋ค๋ฉด ์ด๋ ๊ฒ ์์ํ๋ฉด ๋๋ค.
from hwpx import HwpxDocument
document = HwpxDocument.new()
document.add_paragraph("python-hwpx๋ก ๋ง๋ ์ ๋ฌธ์")
document.save_to_path("์๋ฌธ์.hwpx")๐ก ์ปจํ ์คํธ ๋งค๋์ ๋ ์ง์ํฉ๋๋ค:
with HwpxDocument.open("๋ณด๊ณ ์.hwpx") as doc: doc.add_paragraph("์๋์ผ๋ก ๋ฆฌ์์ค๊ฐ ์ ๋ฆฌ๋ฉ๋๋ค.") doc.save_to_path("๊ฒฐ๊ณผ๋ฌผ.hwpx")
ํ, ๋ฉ๋ชจ, ํ
์คํธ ์ถ์ถ, ๊ฒ์ฆ, ํจํค์ง/XML ์ฌํ๋ docs/quickstart.md์ docs/usage.md์์ ๋ฐ๋ก ์ด์ด์ง๋ค.
pyhwpx / pyhwp์ ๋ค๋ฅธ ์ ?
python-hwpx pyhwpx pyhwp ๋์ ํฌ๋งท .hwpx(OWPML/OPC).hwpx.hwp(v5 ๋ฐ์ด๋๋ฆฌ)ํ/๊ธ ์ค์น ๋ถํ์ ํ์ (Windows COM) ๋ถํ์ ํฌ๋ก์ค ํ๋ซํผ โ Linux / macOS / Windows / CI โ Windows ์ ์ฉ โ ๋ฐฉ์ ์ง์ XML ํ์ฑ COM ์๋ํ OLE ํ์ฑ
The per-host bundles in the hwpx-plugins repository consume python-hwpx through
hwpx-mcp-server and the local quickcheck scripts. During local development, set
PYTHON_HWPX_REPO=/absolute/path/to/python-hwpx so the plugin launcher uses this checkout as an
editable dependency.
HWPX ํ์ผ์ ZIP + XML ๊ตฌ์กฐ์ด๋ฏ๋ก, ํ/๊ธ ํ๋ก๊ทธ๋จ ์์ด Python๋ง์ผ๋ก ์ฝ๊ณ ํธ์งํ๋ ์ํฌํ๋ก๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
| ํ๋ซํผ | ์ฝ๊ธฐ | ์ฐ๊ธฐ | ๋น๊ณ |
|---|---|---|---|
| โ Windows | โ | โ | ํ์ปด์คํผ์ค |
| โ macOS | โ | โ | ํ์ปด์คํผ์ค Mac |
| โ Linux | โ | โ | ํ์ปด์คํผ์ค Linux |
| โ CI/CD | โ | โ | Docker, GitHub Actions ๋ฑ |
| ์นดํ ๊ณ ๋ฆฌ | ๊ธฐ๋ฅ | ์ค๋ช |
|---|---|---|
| ๐ ๋ฌธ์ I/O | ์ด๊ธฐ/์ ์ฅ/์์ฑ | ํ์ผ, ๋ฐ์ดํธ, ์คํธ๋ฆผ ์ ์ถ๋ ฅ ยท ์์์ ์ ์ฅ ยท ZIP ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ |
| ๐ ๋จ๋ฝ | ์ถ๊ฐ/์ญ์ /ํธ์ง/์์ | ํ
์คํธ ์ค์ , ๋จ๋ฝ ์ญ์ (remove_paragraph), ์คํ์ผ ์ฐธ์กฐ |
| โ๏ธ Run | ํ ์คํธ ์กฐ๊ฐ | ์ถ๊ฐ, ๊ต์ฒด, ๋ณผ๋/์ดํค๋ฆญ/๋ฐ์ค/์์ ์์ |
| ๐ ํ(Table) | ์์ฑ/ํธ์ง/๋ณํฉ | NรM ํ ์์ฑ, ์ ํ ์คํธ, ์ ๋ณํฉ/๋ถํ , ์ค์ฒฉ ํ ์ด๋ธ |
| ๐งญ ํ ์๋ํ | ํ์/์ฑ์ฐ๊ธฐ | ํ ์ด๋ธ ๋งต, ๋ผ๋ฒจ ๊ธฐ๋ฐ ์ ํ์, ๊ฒฝ๋ก ๊ธฐ๋ฐ ๋ฐฐ์น ์ฑ์ฐ๊ธฐ |
| ๐ ์น์ | ์ถ๊ฐ/์ญ์ | add_section(after=), remove_section(), manifest ์๋ ๊ด๋ฆฌ |
| ๐ผ๏ธ ์ด๋ฏธ์ง | ์๋ฒ ๋/์ญ์ | ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ๊ด๋ฆฌ, manifest ์๋ ๋ฑ๋ก |
| โ๏ธ ๋ํ | ์ /์ฌ๊ฐํ/ํ์ | OWPML ๋ช ์ธ ์ค์ ๋ํ ์ฝ์ |
| ๐ ๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธ | ์ค์ /์ ๊ฑฐ | ํ์/์ง์/์์ชฝ ํ์ด์ง ๊ตฌ๋ถ |
| ๐ฌ ๋ฉ๋ชจ | ์ถ๊ฐ/์ญ์ | ์ต์ปค ๊ธฐ๋ฐ ๋ฉ๋ชจ, ๋ฉ๋ชจ ์ ฐ์ดํ ์ฐธ์กฐ |
| ๐ ๊ฐ์ฃผ/๋ฏธ์ฃผ | ์ถ๊ฐ | ํ ์คํธ ์ ๊ทผ |
| ๐ ๋ถ๋งํฌ/ํ์ดํผ๋งํฌ | ์ฝ์ /์กฐํ | URL ๋งํฌ, ๋ด๋ถ ๋ถ๋งํฌ |
| ๐ฐ ๋ค๋จ ํธ์ง | ์ปฌ๋ผ ์ ์ | ๋ค๋จ ๋ ์ด์์ ์ ์ด |
| ๐ ํ ์คํธ ์ถ์ถ | ํ์ดํ๋ผ์ธ | ์น์ /๋จ๋ฝ ์ํ, ์ฃผ์ ๋ ๋๋ง, ์ค์ฒฉ ๊ฐ์ฒด ์ ์ด |
| ๐ ๊ฐ์ฒด ๊ฒ์ | ํ๊ทธ/์์ฑ/XPath | ํน์ ์์ ํ์, ์ฃผ์ ์ดํฐ๋ ์ดํฐ |
| ๐จ ์คํ์ผ ์นํ | ์์ ๊ธฐ๋ฐ ํํฐ | ์์/๋ฐ์ค/charPrIDRef ๊ธฐ๋ฐ Run ๊ฒ์ ๋ฐ ๊ต์ฒด |
| ๐ค ๋ด๋ณด๋ด๊ธฐ | ํ ์คํธ/HTML/Markdown | ๋ฌธ์ ๋ณํ ์ถ๋ ฅ |
| โ ์ ํจ์ฑ ๊ฒ์ฌ | XSD + ํจํค์ง ๊ตฌ์กฐ | CLI(hwpx-validate, hwpx-validate-package) ๋ฐ API |
| ๐งฐ ์์ ๋๊ตฌ | unpack/pack/๋ถ์/๋น๊ต | pack-ready ์์ ๋๋ ํฐ๋ฆฌ ์ถ์ถ๊ณผ ์ฌ๊ตฌ์ฑ ์ ๊ฒ |
| ๐๏ธ ์ ์์ค XML | ๋ฐ์ดํฐํด๋์ค ๋งคํ | OWPML ์คํค๋ง โ Python ๊ฐ์ฒด ์ง์ ์กฐ์ |
| ๐ ๋ค์์คํ์ด์ค ํธํ | ์๋ ์ ๊ทํ | HWPML 2016 โ 2011 ์๋ ๋ณํ |
| ๐๏ธ ๋น๋ | ์กฐ๋ฆฝํ ์์ฑ | hwpx.builder โ Section/Heading/Table/Image/Header ์กฐ๋ฆฝ, ํ๋๊ฒ์ดํธ ์ ์ฅ ๋ฆฌํฌํธ |
| โ ํธ์ง๊ธฐ ์คํ ์์ | validate_editor_open_safety |
์ ์ฅ/ํฉ/๋ฆฌํ์ด/๋น๋ ์ถ๋ ฅ ๊ฒ์ดํธ, openSafety ์ฆ๊ฑฐ ๋ฐํ |
| ๐งช ํผ์ง ์๋ ด ๋ฃจํ | hwpx.tools.fuzz |
์๋ ๊ฒฐ์ ์ ์๋๋ฆฌ์ค ์์ฑ ยท 3์ค ์ค๋ผํด ๋ฌ๋ ยท ํ๊ท fixture ๋ฐ์ |
| ๐ฅ๏ธ ๋ ์ด์์ ํ๋ฆฌ๋ทฐ | hwpx.tools.layout_preview |
ํ์ด์ง ๋ฐ์คยทํยท์ฌ๋ฐฑ ๊ทผ์ฌ HTML/PNG (์์ด์ ํธ ์๊ธฐ๊ฒ์ฆ์ฉ) |
| ๐งท ๋ฐ์ดํธ ๋ณด์กด ํจ์น | hwpx.patch |
section XML ๋ฐ์ดํธ splice โ ๋ฏธ์์ ์์ญ ๋ฐ์ดํธ ๋ณด์กด |
| ๐ ๊ธฐ์กด ๋ฌธ์ ์์ ํธ์ง | ๋ฌธ๋จยทํ์ด์ง | ์ ๋ ฌยท์ค๊ฐ๊ฒฉยท๋ค์ฌ์ฐ๊ธฐยท๋ฌธ๋จ ๊ฐ๊ฒฉ, ์ฉ์งยท์ฌ๋ฐฑยท๋ฐฉํฅ, ๋จธ๋ฆฌ๋ง/์ชฝ๋ฒํธ, ๋ถ๋ฆฟ/๋ฒํธ |
| ๐๏ธ ๋๋ฆํ | ์์ ํ๋ | ํด๋ฆญํ์ด ํ๋ ์กฐํยท์์ ๋ณด์กด ์ฑ์ |
| ๐๏ธ ๊ณต๋ฌธ์ ๋๊ตฌ | official_lint ยท ๊ฒฐ์ฌ๋ |
ํญ๋ชฉ๊ธฐํธ ์๊ณยท"๋." ํ์ยท๋ถ์ยท๋ ์ง ํ๊ธฐ lint, ๊ฒฐ์ฌ๋ ํ๋ฆฌ์ |
| ๐ท ๊ณ ๊ธ ์์ฑ๊ธฐ | advanced_generators |
์ฌ์ง๋์ง(image_grid)ยทํ์ ๋ช ํจยทํ ๊ธฐ๋ฐ ์กฐ์ง๋ |
| ๐ ์ ๊ตฌ๋์กฐ | doc_diff |
๋ฌธ๋จ LCS diffยท์ ๊ตฌ๋์กฐํ ์์ฑยท์ฐธ์กฐ ์ ํฉ lint |
| ๐จ ๋ฉ์ผ๋จธ์งยทํ ๊ณ์ฐ | mail_merge |
ํ ํ๋ฆฟ+๋ฐ์ดํฐ N๋ถ ๋๋ ์์ฑ, ํ ํฉ๊ณยทํ๊ท |
| ๐ช ์์ ์ด์ | style_profile |
์ฐธ์กฐ ๋ฌธ์ ํ๋กํ์ผ ์ถ์ถยท์ ์ฉ, ํ ํ๋ฆฟ ๋ ์ง์คํธ๋ฆฌ |
| ๐ก๏ธ ์ ๋ ฅ ๊ฐ๊ฑดํ | opc.security |
XML entity ํญํยทZIP ์์ถ ํญํ ๊ฐ๋ |
๋ฌธ๋จ, ํ, ๋ฉ๋ชจ, ๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธ์ Python ๊ฐ์ฒด๋ก ๋ค๋ฃน๋๋ค.
# ๋จ๋ฝ ์ถ๊ฐยท์ญ์
doc.add_paragraph("์ ๋ฌธ๋จ")
doc.remove_paragraph(doc.paragraphs[-1]) # ๋ง์ง๋ง ๋จ๋ฝ ์ญ์
# ์น์
์ถ๊ฐยท์ญ์
new_sec = doc.add_section() # ๋ฌธ์ ๋์ ์น์
์ถ๊ฐ
new_sec.add_paragraph("๋ ๋ฒ์งธ ์น์
๋ด์ฉ")
doc.remove_section(1) # ์ธ๋ฑ์ค๋ก ์น์
์ญ์
# ๋จธ๋ฆฌ๊ธยท๋ฐ๋ฅ๊ธ
doc.set_header_text("๊ธฐ๋ฐ ๋ฌธ์", page_type="BOTH")
doc.set_footer_text("1 / 10", page_type="BOTH")
# ํ ์
๋ณํฉยท๋ถํ
table.merge_cells(0, 0, 1, 1) # (0,0)~(1,1) ๋ณํฉ
table.set_cell_text(0, 0, "๋ณํฉ๋ ์
", logical=True, split_merged=True)
table.set_cell_text(0, 0, "line 1\nline 2", split_paragraphs=True)
# ์์ํ ํ ์๋ ์ฑ์ฐ๊ธฐ
form = doc.add_table(2, 2)
form.cell(0, 0).text = "์ฑ๋ช
:"
form.cell(1, 0).text = "์์"
doc.find_cell_by_label("์ฑ๋ช
") # {"matches": [...], "count": 1}
doc.fill_by_path({
"์ฑ๋ช
> right": "ํ๊ธธ๋",
"์์ > right": "ํ๋ซํผํ",
})doc.paragraphs์ ์ธ๋ฑ์ค๋ ๋ณธ๋ฌธ ์ง์ ๋ฌธ๋จ 0-based ๊ธฐ์ค์
๋๋ค. ํ ์ ๋ฌธ๋จ์
๋ณธ๋ฌธ paragraph_index์ ์์ง ์๊ณ get_table_map()์ cell location
(table_index, row, col, cell_paragraph_index)์ผ๋ก ๋ค๋ฃน๋๋ค.
get_table_map()์ caption_text์ preceding_paragraph_text๋ฅผ ๋ถ๋ฆฌํด
๋ฐํํ๊ณ , ์
๋ฏธ๋ฆฌ๋ณด๊ธฐ์ ์ฌ๋ฌ ๋ฌธ๋จ์ \n์ผ๋ก ์ ์งํฉ๋๋ค.
from hwpx import TextExtractor, ObjectFinder
# ํ
์คํธ ์ถ์ถ
with TextExtractor("๋ฌธ์.hwpx") as extractor:
for section in extractor.iter_sections():
for para in extractor.iter_paragraphs(section):
print(para.text())
# ํน์ ๊ฐ์ฒด ํ์
for obj in ObjectFinder("๋ฌธ์.hwpx").find_all(tag="tbl"):
print(obj.tag, obj.path)hp:tab๊ณผ ctrl id="tab"์ ํญ ๋ฌธ์(\t)๋ก ๋ณด์กด๋ฉ๋๋ค. ๋ฐ๋ผ์ Paragraph.text, TextExtractor, export_text()/export_html()/export_markdown() ๊ฒฝ๋ก์์ ๊ฐ์ ํญ ์๋ฏธ๋ฅผ ์ ์งํ ์ฑ roundtrip ํ ์ ์์ต๋๋ค. ํ์ํ๋ฉด preserve_breaks=False๋ก ์ค๋ฐ๊ฟ/ํญ์ ๊ณต๋ฐฑ ๊ธฐ๋ฐ์ผ๋ก ํํํํ ์ ์์ต๋๋ค.
์์(์์, ๋ฐ์ค, charPrIDRef)์ผ๋ก ๋ฐ์ ํํฐ๋งํด ์ ํ์ ์ผ๋ก ๊ต์ฒดํฉ๋๋ค.
# ๋นจ๊ฐ์ ํ
์คํธ๋ง ์ฐพ์์ ์นํ
doc.replace_text_in_runs(
"์์", "ํ์ ",
text_color="#FF0000",
)
# ํน์ ์์์ ๋ฐ ๊ฒ์
runs = doc.find_runs_by_style(underline_type="SINGLE")# ํ
์คํธ, HTML, Markdown์ผ๋ก ๋ณํ
text = doc.export_text()
html = doc.export_html()
md = doc.export_markdown()OWPML ์คํค๋ง์ ๋งคํ๋ ๋ฐ์ดํฐํด๋์ค๋ก XML ๊ตฌ์กฐ๋ฅผ ์ง์ ๋ค๋ฃน๋๋ค.
# ํค๋ ์ฐธ์กฐ ๋ชฉ๋ก
doc.border_fills # ํ
๋๋ฆฌ ์ฑ์ฐ๊ธฐ
doc.bullets # ๊ธ๋จธ๋ฆฌํ
doc.styles # ์คํ์ผ
doc.track_changes # ๋ณ๊ฒฝ ์ถ์
# ๋ฐํ์ชฝยท์ด๋ ฅยท๋ฒ์ ํํธ
doc.master_pages
doc.histories
doc.versionpython-hwpx
โโโ hwpx.document # ๊ณ ์์ค ํธ์ง API (HwpxDocument)
โโโ hwpx.opc # OPC ์ปจํ
์ด๋ ์ฝ๊ธฐ/์ฐ๊ธฐ (์์์ ์ ์ฅ, ZIP ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ)
โโโ hwpx.oxml # OWPML XML โ ๋ฐ์ดํฐํด๋์ค ๋งคํ
โ โโโ document.py # ์น์
, ๋ฌธ๋จ, ํ, ๋ฐ, ๋ฉ๋ชจ, ๋ํ, ๋
ธํธ
โ โโโ header.py # ํค๋ ์ฐธ์กฐ ๋ชฉ๋ก (์คํ์ผ, ๊ธ๋จธ๋ฆฌํ, ๋ณ๊ฒฝ์ถ์ ๋ฑ)
โ โโโ body.py # ํ์
์ด ์ง์ ๋ ๋ณธ๋ฌธ ๋ชจ๋ธ
โ โโโ common.py # ๋ฒ์ฉ XML โ ๋ฐ์ดํฐํด๋์ค
โโโ hwpx.tools
โ โโโ archive_cli # unpack/pack CLI ๋ฐ ์ฌํจํน ๋ฉํ๋ฐ์ดํฐ
โ โโโ text_extractor # ํ
์คํธ ์ถ์ถ ํ์ดํ๋ผ์ธ
โ โโโ text_extract_cli # ํ
์คํธ ์ถ์ถ CLI
โ โโโ object_finder # ๊ฐ์ฒด ํ์ ์ ํธ๋ฆฌํฐ
โ โโโ exporter # ํ
์คํธ/HTML/Markdown ๋ด๋ณด๋ด๊ธฐ
โ โโโ validator # ์คํค๋ง ์ ํจ์ฑ ๊ฒ์ฌ (hwpx-validate CLI)
โ โโโ package_validator# ZIP/OPC/HWPX ๊ตฌ์กฐ ๊ฒ์ฌ
โ โโโ page_guard # ๊ตฌ์กฐ ๋ณํ ์งํ ์ ๊ฒ
โ โโโ template_analyzer# ๋ ํผ๋ฐ์ค ๋ฌธ์ ๋ถ์/์ถ์ถ
โโโ hwpx.templates # ๋ด์ฅ ๋น ๋ฌธ์ ํ
ํ๋ฆฟ
| ๐ ์ ์ฒด ๋ฌธ์ | Sphinx ๊ธฐ๋ฐ API ๋ ํผ๋ฐ์ค, ์ฌ์ฉ ๊ฐ์ด๋, FAQ |
| ๐ ๋น ๋ฅธ ์์ | 5๋ถ ์์ HWPX ๋ฌธ์ ๋ค๋ฃจ๊ธฐ |
| ๐ ์ฌ์ฉ ๊ฐ์ด๋ | 50+ ์ค์ ์ฌ์ฉ ํจํด |
| ๐ง API ๋ ํผ๋ฐ์ค | ํด๋์คยท๋ฉ์๋ ์์ธ ๋ช ์ธ |
| ๐ ์คํค๋ง ๊ฐ์ | OWPML ์คํค๋ง ๊ตฌ์กฐ ์ค๋ช |
| ๐งช ์คํ ํตํฉ ์๋ฃ | fixture, smoke, validation, compatibility ์ด์ ์๋ฃ |
| ํฌ๋งท | ํ์ฅ์ | ์ฝ๊ธฐ | ์ฐ๊ธฐ |
|---|---|---|---|
| HWPX | .hwpx |
โ | โ |
| HWP | .hwp |
โ | โ |
Note: HWP(v5 ๋ฐ์ด๋๋ฆฌ) ํ์ผ์ ์ง์ํ์ง ์์ต๋๋ค. ํ์ปด์คํผ์ค์์ HWPX๋ก ๋ณํ ํ ์ฌ์ฉํ์ธ์.
- Python 3.10+
- lxml โฅ 4.9
add_shape()/add_control()์ ํ/๊ธ์ด ์๊ตฌํ๋ ๋ชจ๋ ํ์ ์์๋ฅผ ์์ฑํ์ง ์์ต๋๋ค. ๋ณต์กํ ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ ๋๋ ํ/๊ธ์์ ์ด์ด ๊ฒ์ฆํด ์ฃผ์ธ์.- ์ด๋ฏธ์ง ์ฝ์
์ ๋ฐ์ด๋๋ฆฌ ์๋ฒ ๋๋ ์ง์ํ์ง๋ง,
<hp:pic>์์์ ์์ ํ ์๋ ์์ฑ์ ์ ๊ณตํ์ง ์์ต๋๋ค. - ์ํธํ๋ HWPX ํ์ผ์ ์๋ณตํธํ๋ ์ง์ํ์ง ์์ต๋๋ค.
๋ฒ๊ทธ ๋ฆฌํฌํธ, ๊ธฐ๋ฅ ์ ์, PR ๋ชจ๋ ํ์ํฉ๋๋ค. ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ๊ณผ ํ ์คํธ ๋ฐฉ๋ฒ์ CONTRIBUTING.md๋ฅผ ์ฐธ๊ณ ํ์ธ์.
git clone https://github.com/airmang/python-hwpx.git
cd python-hwpx
pip install -e ".[dev]"
pytest๋จธ์ง๋ ๊ธฐ์ฌ์ ๋ชฉ๋ก์ CONTRIBUTORS.md์์ ํ์ธํ ์ ์์ต๋๋ค.
Apache License 2.0. See LICENSE and NOTICE.
Primary maintainer/contact: ๊ณ ๊ทํ โ ๊ด๊ต๊ณ ๋ฑํ๊ต ์ ๋ณดยท์ปดํจํฐ ๊ต์ฌ
- โ๏ธ kokyuhyun@hotmail.com
- ๐ @airmang