mcp server
HwpForge
Rust library for programmatic control of Korean HWPX documents โ MarkdownโHWPX conversion, JSON round-trip editing, MCP server for AI agents ๐ฅ
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 17, 2026HwpForge ๐ฅ
Rust๋ก ํ๊ธ(HWP/HWPX) ๋ฌธ์๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ ์ด
Hancom ํ๊ธ ํ์ผ ์ฝ๊ธฐ, ์ฐ๊ธฐ, ๋ณํ
๋ณธ ์ ํ์ ํ์ปด์ HWP ๋ฌธ์ ํ์ผ(.hwp) ๊ณต๊ฐ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ ๊ฐ๋ฐํ์์ต๋๋ค.
HwpForge๋?
HwpForge๋ HWPX ๋ฌธ์(ZIP + XML, KS X 6101)๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ์คํ์์ค ์์ Rust ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ํ๊ตญ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ์๋ํ๋ก์ธ์์ธ Hancom ํ๊ธ์ ์ต์ ํฌ๋งท์ ์ง์ํฉ๋๋ค.
์ง์ ๋ฒ์
| ํ๊ธ ๋ฒ์ | ํฌ๋งท | ์ฝ๊ธฐ | ์ฐ๊ธฐ | ์คํ์ผ ์ธํธ |
|---|---|---|---|---|
| ํ๊ธ 2014 ~ 2020 | HWPX (.hwpx) | โ | โ | Classic (18 styles) |
| ํ๊ธ 2022 ~ 2024 | HWPX (.hwpx) | โ | โ | Modern (22 styles, ๊ธฐ๋ณธ๊ฐ) |
| ํ๊ธ 2025+ | HWPX (.hwpx) | โ | โ | Latest (23 styles) |
| ํ๊ธ 97 ~ 2010 | HWP5 (.hwp) | โ | โ | โ |
- HWPX: OWPML ๊ตญ๊ฐํ์ค (KS X 6101) ๊ธฐ๋ฐ, ZIP + XML ์ปจํ ์ด๋
- HWP5: ๊ตฌํ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท. ํ์ฌ๋ CLI ์ค์ฌ์ ์ฝ๊ธฐ/์ ๊ฒ/์ฌ์ถ๋ ฅ ๊ฒฝ๋ก๋ฅผ ์ ๊ณตํฉ๋๋ค (
convert-hwp5,audit-hwp5,census-hwp5) - ์คํ์ผ ์ธํธ๋
HancomStyleSetenum์ผ๋ก ์ ํ ๊ฐ๋ฅ (๊ธฐ๋ณธ: Modern)
LLM-first ์ค๊ณ ๐ฅ โ AI ์นํ์ ์ธ Markdown๊ณผ ๊ณต์ ํ๊ธ ๋ฌธ์ ํฌ๋งท(HWPX), ๋ ์ธ๊ณ๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ์์ต๋๋ค. LLM์ด Markdown์ผ๋ก ์์ฑํ ๋ด์ฉ์ ๊ณต๋ฌธ์ ๊ท๊ฒฉ์ HWPX๋ก ์ปดํ์ผ๋๊ณ ๐, ๋ฐ๋๋ก ๊ธฐ์กด HWPX ๋ฌธ์๋ AI๊ฐ ์ฝ๊ฒ ์ฝ์ ์ ์๋ ๊ตฌ์กฐ๋ก ๊บผ๋ผ ์ ์์ต๋๋ค โ๏ธ.
- ๐ HWPX ์์ ๊ฐ์ด๋ ๋ค์ด๋ก๋ โ HwpForge API๋ก ์์ฑํ 4์น์ ๋ฐ๋ชจ ๋ฌธ์ (ํ๊ธ์์ ์ด์ด๋ณด์ธ์)
- ๐ ๋ฌธ์ ์ฌ์ดํธ โ ai-scream.ai/HwpForge: ์์ํ๊ธฐ, ์ฌ์ฉ ๊ฐ์ด๋(PythonยทMarkdown ๋ณํยท์คํ์ผ ํ ํ๋ฆฟ ๋ฑ), API ๋ ํผ๋ฐ์ค
- HWPX Reader for AI โ ๊ธฐ์กด ํ๊ธ ๋ฌธ์(.hwpx)๋ฅผ Markdown์ผ๋ก ๋ณํํ์ฌ LLM์ด ์ฆ์ ์ดํด ๊ฐ๋ฅ
- Full HWPX codec โ HWPX ํ์ผ์ ์์ค ์์ด ๋์ฝ๋ฉ/์ธ์ฝ๋ฉ (lossless roundtrip)
- Markdown bridge โ GFM Markdown๊ณผ HWPX ๊ฐ ์๋ฐฉํฅ ๋ณํ (์ฝ๊ธฐ + ์ฐ๊ธฐ)
- YAML style template โ Figma Design Token์ฒ๋ผ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์คํ์ผ ์ ์ (ํฐํธ, ํฌ๊ธฐ, ์์)
- Type-safe API โ branded index, typestate validation, zero unsafe code
๋น ๋ฅธ ์์
์ค์น
# Cargo.toml์ ์ถ๊ฐ
cargo add hwpforge
# Markdown ์ง์ ํฌํจ
cargo add hwpforge --features full
๋๋ Cargo.toml์ ์ง์ ์ถ๊ฐ:
[dependencies]
hwpforge = "0.16"
๐จ Hammer โ CLI๋ก ์์ํ๊ธฐ
CLI ๋๊ตฌ hwpforge(Hammer)๋ฅผ ์ค์นํ๋ฉด ํฐ๋ฏธ๋์์ ๋ฐ๋ก ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ํธ์งํ ์ ์์ต๋๋ค. hwpforge-bindings-cli๋ crates.io์ ๋ฐฐํฌ๋์ง ์์ผ๋ฏ๋ก(publish = false), git ๋๋ ๋ก์ปฌ ๊ฒฝ๋ก์์ ์ค์นํฉ๋๋ค. hwpforge-smithy-pdf(krilla) ์์กด์ผ๋ก ์ํฌ์คํ์ด์ค MSRV(1.89)๋ณด๋ค ๋์ Rust 1.92+๊ฐ ํ์ํฉ๋๋ค.
cargo install --git https://github.com/ai-screams/HwpForge hwpforge-bindings-cli
# ๋๋ clone ํ ๋ก์ปฌ ๊ฒฝ๋ก์์
git clone https://github.com/ai-screams/HwpForge && cd HwpForge
cargo install --path crates/hwpforge-bindings-cli
# Markdown โ HWPX ๋ณํ
hwpforge convert report.md -o report.hwpx
# HWPX ๊ตฌ์กฐ ํ์ธ
hwpforge inspect report.hwpx
# ๋ฌธ์ ๋ด๋น๊ฒ์ด์
๋งต ํ์ธ (์ ๋ชฉยทํยทํ๋ยท์ฑ
๊ฐํผ)
hwpforge outline report.hwpx
# HWPX โ Markdown ๋ณํ (AI๊ฐ ํ๊ธ ๋ฌธ์ ์ฝ๊ธฐ)
hwpforge to-md report.hwpx -o report.out.md
# HWPX โ JSON ์ถ์ถ (AI ํธ์ง์ฉ) โ -o ๋ ํ์, stdout ๋ด๋ณด๋ด๊ธฐ๋ ์์ต๋๋ค
hwpforge to-json report.hwpx --section 0 -o section0.json # ์น์
ํ๋ (patch ์ฉ)
hwpforge to-json report.hwpx -o full.json # ๋ฌธ์ ์ ์ฒด (from-json ์ฉ)
# ์น์
JSON์ ์๋ณธ์ ๋์ฐ๊ธฐ (ํ
์คํธ๋ง ๋ฐ๋, ์๋ณธ์ด base)
hwpforge patch report.hwpx --section 0 section0.json -o updated.hwpx
# ๋ฌธ์ ์ ์ฒด JSON์ผ๋ก ์ ๋ฌธ์ ๋ง๋ค๊ธฐ (๊ตฌ์กฐ ๋ณ๊ฒฝ๊น์ง ๊ฐ๋ฅ)
hwpforge from-json full.json -o new.hwpx
# ๋ฌธ์ ๊ตฌ์กฐ ๊ฒ์ฆ (Core ๋ถ๋ณ์กฐ๊ฑด ํต๊ณผ ์ฌ๋ถ)
hwpforge validate report.hwpx
# JSON Schema ์ถ๋ ฅ (AI agent์ฉ)
hwpforge schema document
๋ JSON์ ์๋ก ๋ค๋ฅธ ํ์์
๋๋ค. --section ์์ด ๋ฝ์ ๋ฌธ์ ์ ์ฒด JSON์ from-json์ด ์ฝ๊ณ , --section N์ผ๋ก ๋ฝ์ ์น์
JSON์ patch๊ฐ ์๋ณธ ๋ฌธ์๋ฅผ base๋ก ์ผ์ ๋์๋๋ค. ์๋ก ๋ฐ๊ฟ ๋ฃ์ผ๋ฉด ์คํค๋ง ๋ถ์ผ์น๋ก ๊ฑฐ๋ถ๋ฉ๋๋ค.
๋๋ฆํ ์ฑ์ฐ๊ธฐ๋ ๋๋ฆํ(click-here field)์ด ๋ค์ด ์๋ ์์ ๋ฌธ์์์๋ง ๋์ํฉ๋๋ค. ๋ฐฉ๊ธ convert๋ก ๋ง๋ ๋ฌธ์์๋ ๋๋ฆํ์ด ์์ด FIELD_NOT_FOUND๊ฐ ๋ฉ๋๋ค โ fields๋ก ์ด๋ฆ์ ๋จผ์ ํ์ธํ์ธ์.
hwpforge fields form.hwpx # ์ฑ์ธ ์ ์๋ ํ๋ ์ด๋ฆ
hwpforge fill form.hwpx --set ํ์ฌ๋ช
=HwpForge -o filled.hwpx # ๋๋จธ์ง ํจํค์ง๋ ๋ฐ์ดํธ ๊ทธ๋๋ก
PDF ๋ด๋ณด๋ด๊ธฐ๋ ๋ฌธ์์ ๋ค์ด ์๋ ์กฐํ ์บ์๋ฅผ ์ฌ์ํ๋ ๋ฐฉ์์ด๋ผ, ์บ์๊ฐ ์๋ ๋ฌธ์๋ง ๋ ๋ํ ์ ์์ต๋๋ค. ์บ์์ ์ถ์ฒ๋ ๋์
๋๋ค โ ํ์ปด์ด ์ ์ฅํ HWPX, ๊ทธ๋ฆฌ๊ณ HWP5์์ ์บ์๋ฅผ ์ค์ด ๋ณํํ HWPX(convert-hwp5 --carry-layout-cache). to-pdf๋ .hwp๋ฅผ ์ง์ ๋ฐ์ ๊ทธ ๋ณํ์ ๋์ ํด ์ฃผ๊ธฐ๋ ํฉ๋๋ค. ๋ฐ๋ฉด convert๋ from-json์ด ์๋ก ๋ง๋ ๋ฌธ์์๋ ์บ์๊ฐ ์์ด PDF_RENDER_FAILED๋ก ๊ฑฐ๋ถ๋ฉ๋๋ค. ๋ฌธ์๊ฐ ์ฐ๋ ํฐํธ๋ ํธ์คํธ์ ์์ด์ผ ํฉ๋๋ค(--font-dirยท--discovery๋ก ์ง์ , ์๋ ํฐํธ๋ฅผ ๋์ฒด ๊ธ๊ผด๋ก ๋ ๋ํ๋ ค๋ฉด --degraded). ์กฐํ ์บ์๊ฐ ์๋ ๋ฌธ๋จ์ด ํ๋๋ผ๋ ์์ผ๋ฉด ๊ฑฐ๋ถํ๋ ค๋ฉด --partial-cache-reject๋ฅผ ์ค๋๋ค(๊ธฐ๋ณธ์ ๊ฒฝ๊ณ ํ ๊ทธ ๋ฌธ๋จ์ ๊ฑด๋๋).
hwpforge to-pdf hancom-saved.hwpx -o report.pdf # ํ์ปด์ด ์ ์ฅํ ๋ฌธ์
hwpforge to-pdf legacy.hwp -o legacy.pdf # HWP5 โ ์บ์๋ฅผ ์ค์ด ๋ณํํ ๋ค ๋ ๋
HWP5์์ ์ค์ด ์จ ์บ์๋ PDF ์ฌ์ยท๋์กฐ ์ ์ฉ์
๋๋ค. ๊ทธ๋ ๊ฒ ๋ง๋ .hwpx๋ฅผ ํ์ปด์์ ๋ค์ ์ด ์ฉ๋๋ก ์ฐ์ง ๋ง์ธ์.
AI-first ์ค๊ณ: CLI๋ AI agent(Claude Code ๋ฑ)๊ฐ ์ฃผ ์ฌ์ฉ์์ ๋๋ค. Markdown์ผ๋ก ๋ฌธ์๋ฅผ ์์ฑํ ๋ค, JSON round-trip์ผ๋ก ๊ธฐ์กด ์คํ์ผ์ ๋ณด์กดํ๋ฉด์ section ๋จ์๋ก ์ ๋ฐํ๊ฒ ํธ์งํ ์ ์์ต๋๋ค.
--jsonํ๋๊ทธ๋ก ๋ชจ๋ ๋ช ๋ น์ด๊ฐ machine-readable ์ถ๋ ฅ์ ์ง์ํฉ๋๋ค.
๐๏ธ Vise โ MCP Server (Beta)๋ก AI๊ฐ ์ง์ ํ๊ธ ๋ฌธ์๋ฅผ ๋ค๋ฃจ๋ค
Claude Code, Codex CLI, Claude, ChatGPT, Cursor, Antigravity ๋ฑ MCP ์ง์ AI ๋๊ตฌ์์ ํ๊ธ ๋ฌธ์๋ฅผ ์ง์ ์์ฑํ๊ณ ํธ์งํ ์ ์์ต๋๋ค. ํ์ฌ MCP surface๋ ๋ฒ ํ์ด๋ฉฐ, HWP5 ๊ฒฝ๋ก๋ MCP๊ฐ ์๋๋ผ CLI workflow๋ฅผ ์ฐ์ ํฉ๋๋ค. "๋ณด๊ณ ์ ๋ง๋ค์ด์ค"๋ผ๊ณ ๋งํ๋ฉด, AI๊ฐ ์์์ .hwpx ํ์ผ์ ๋๋ฑ ๋ง๋ค์ด๋
๋๋ค.
AI ๋๊ตฌ์ ๋ฑ๋ก
ํ ์ค์ด๋ฉด ์ค์น + ๋ฑ๋ก์ด ๋๋ฉ๋๋ค. npm์ npx -y๊ฐ ์๋์ผ๋ก ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ค์ด๋ก๋ํฉ๋๋ค.
# npm (๊ถ์ฅ โ Rust ํด์ฒด์ธ ๋ถํ์)
claude mcp add hwpforge -- npx -y @hwpforge/mcp
# Cargo (Rust ๊ฐ๋ฐ์์ฉ)
cargo install hwpforge-bindings-mcp && claude mcp add hwpforge hwpforge-mcp
# ๋ชจ๋ ํ๋ก์ ํธ์์ ์ฌ์ฉ (user ๋ฒ์)
claude mcp add --scope user hwpforge -- npx -y @hwpforge/mcp
~/.codex/config.toml์ ์ถ๊ฐ:
[mcp_servers.hwpforge]
command = "npx"
args = ["-y", "@hwpforge/mcp"]
๋๋ CLI๋ก:
codex mcp add hwpforge -- npx -y @hwpforge/mcp
์ค์ ํ์ผ์ ํธ์งํฉ๋๋ค:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
Settings โ Tools โ Add MCP Server์์:
- Name:
hwpforge - Command:
npx -y @hwpforge/mcp
๋๋ ์ค์ ํ์ผ์ ์ง์ ํธ์ง:
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
ํ๋ก์ ํธ ๋ฃจํธ์ .cursor/mcp.json ์์ฑ:
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
... ๋๋กญ๋ค์ด โ MCP Store โ Manage MCP Servers โ View raw config (mcp_config.json)์ ์ถ๊ฐ:
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
๋ฑ๋กํ๋ฉด 19๊ฐ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค
| ๋๊ตฌ | ํ๋ ์ผ | ํ๋ง๋ |
|---|---|---|
hwpforge_convert | Markdown โ HWPX ๋ณํ | "์ด ๋งํฌ๋ค์ด์ ํ๊ธ ํ์ผ๋ก!" |
hwpforge_inspect | HWPX ๊ตฌ์กฐ ํ์ธ | "์ด ๋ฌธ์ ๋ญ๊ฐ ๋ค์ด์์ด?" |
hwpforge_to_json | HWPX โ JSON ์ถ์ถ | "์ด ์น์ ๋ด์ฉ ์ข ๊บผ๋ด๋ด" |
hwpforge_patch | JSON์ผ๋ก ์น์ ๊ต์ฒด | "์ด ๋ถ๋ถ๋ง ๋ฐ๊ฟ์ ๋ค์ ์ ์ฅํด" |
hwpforge_templates | ์คํ์ผ ํ๋ฆฌ์ ์กฐํ | "์ด๋ค ํ ํ๋ฆฟ ์ธ ์ ์์ด?" |
hwpforge_validate | HWPX ๊ตฌ์กฐ/๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ | "์ด ํ์ผ ๋ฌธ์ ์๋์ง ํ์ธํด" |
hwpforge_restyle | ์คํ์ผ ํ๋ฆฌ์ ์ผ๊ด ์ ์ฉ | "์ด ๋ฌธ์ ํฐํธ ๋ฐ๊ฟ์ค" |
hwpforge_from_json | JSON โ HWPX ์ง์ ์์ฑ | "์ด JSON์ผ๋ก ํ๊ธ ํ์ผ ๋ง๋ค์ด" |
hwpforge_to_md | HWPX โ Markdown ๋ณํ | "์ด ํ๊ธ ๋ฌธ์๋ฅผ Markdown์ผ๋ก ๊บผ๋ด์ค" |
hwpforge_outline | ๋ฌธ์ ๋ด๋น๊ฒ์ด์ ๋งต(์ ๋ชฉยทํยทํ๋ยท์ฑ ๊ฐํผ) ์กฐํ | "์ด ๋ฌธ์ ๊ตฌ์กฐ๊ฐ ์ด๋ป๊ฒ ์๊ฒผ์ด?" |
hwpforge_diff | ๋ HWPX ํ์ผ์ semantic/package ์ฑ๋๋ก ๋น๊ต | "์ด ํธ์ง์ด ๋ญ ๋ฐ๊ฟจ๋์ง ํ์ธํด์ค" |
hwpforge_delete_para | ์ต์์ ๋ฌธ๋จ ์ญ์ (๊ตฌ์กฐ ํธ์ง) | "์ด ๋ฌธ๋จ ์ง์์ค" |
hwpforge_insert_para | ์ต์ปค ๋ฌธ๋จ ๊ธฐ์ค ์ ๋ฌธ๋จ ์ฝ์ | "์ด ๋ฌธ๋จ ๋ค์์ ๋ด์ฉ ์ถ๊ฐํด์ค" |
hwpforge_read | ๋ฌธ๋จ/ํ/ํ๋ ์ค ํ๋๋ฅผ ํ๊ฒ ์ฝ๊ธฐ | "์ด ๋ถ๋ถ๋ง ๋ฑ ์ฝ์ด์ค" |
hwpforge_fields | ๋๋ฆํ ํ๋ ๋ชฉ๋ก ์กฐํ | "์ฑ์ธ ์ ์๋ ์นธ์ด ๋ญ๊ฐ ์์ด?" |
hwpforge_fill | ๋๋ฆํ ํ๋์ ๊ฐ ์ฑ์ฐ๊ธฐ | "์ด ํ๋๋ค ์ฑ์์ค" |
hwpforge_stamp_plan | ๋ฌธ์ฅ ์ ๋น์นธ ์คํฌํ ํ๋ณด ํ์ | "์ฑ์ธ ์ ์๋ ๋น์นธ ํ๋ณด ์ฐพ์์ค" |
hwpforge_stamp | ํ๋ณด๋ฅผ ๋๋ฆํ ํ๋๋ก ์น๊ฒฉ | "์ด ๋น์นธ๋ค์ ํ๋๋ก ๋ง๋ค์ด์ค" |
hwpforge_set_cell | ํ ์ ์ ๊ทธ๋ฆฌ๋ ์ฃผ์๋ก ํธ์ง | "์ด ํ ์นธ ๊ฐ ๋ฐ๊ฟ์ค" |
์ ๋ฐ์ดํธ / ์ญ์
npm์ npx -y๊ฐ ํญ์ ์ต์ ๋ฒ์ ์ ๊ฐ์ ธ์ค๋ฏ๋ก ๋ณ๋ ์
๋ฐ์ดํธ๊ฐ ํ์ ์์ต๋๋ค.
# Cargo ์ฌ์ฉ์๋ง ํด๋น
cargo install hwpforge-bindings-mcp --force # ์
๋ฐ์ดํธ
cargo uninstall hwpforge-bindings-mcp # ์ญ์
์ MCP? CLI(Hammer)๋ AI๊ฐ
bash๋ช ๋ น์ ์คํํด์ผ ํ์ง๋ง, MCP(Vise)๋ AI๊ฐ ๋ค์ดํฐ๋ธ ๋๊ตฌ๋ก ์ง์ ํธ์ถํฉ๋๋ค. ํ์ผ ๊ฒฝ๋ก ํ์ฑ๋, stdout ํด์๋ ํ์ ์์ต๋๋ค. JSON-RPC๋ก ์์ฒญํ๋ฉด ๊ตฌ์กฐํ๋ JSON์ผ๋ก ์๋ต โ ๊น๋ํฉ๋๋ค.
๐ Tongs โ Python์ผ๋ก ์์ํ๊ธฐ
pip install hwpforge
CPython 3.9+ ์ ์ฉ abi3 wheel์ Linux(manylinux_2_28 x86_64/aarch64)ยทmacOS(11+ arm64, 10.12+ x86_64)ยทWindows(x64)์ ๋ฐฐํฌํ๋ฉฐ, ๋ฐํ์ ์์กด์ฑ์ 0๊ฐ์
๋๋ค. pyproject.toml์ ์ง์ ํํ ๋๋ ์ ํ ํ(==)๋ณด๋ค ~=๋ฅผ ๊ถ์ฅํฉ๋๋ค โ Python ์ ์ฉ ํจ์น(X.Y.Z.N)๋ ์ ํ ํ์ผ๋ก๋ ๋ฐ์ ์ ์์ต๋๋ค.
import hwpforge
doc = hwpforge.Document.open("proposal.hwpx")
print(doc.outline()["outline"]["title"])
doc = doc.fill({"applicant": "ํ๊ธธ๋", "date": "2026-09-17"}).document
doc.save("proposal-filled.hwpx")
Document๋ ๋ถ๋ณ ๊ฐ์
๋๋ค โ ๋ชจ๋ ํธ์ง ๋ฉ์๋๋ ์๋ณธ์ ๊ทธ๋๋ก ๋ ์ฑ ์ ๋ฌธ์์ ๋ณด๊ณ ์๋ฅผ ๋ด์ DocumentResult๋ฅผ ๋ฐํํฉ๋๋ค. ๊ณต์ ์ฐ์ฐ ๊ณ์ธต์ ์ฐ๋ 18๊ฐ document ๋ฉ์๋ + 5๊ฐ ๋ชจ๋ ํจ์๋ฅผ ์ ๊ณตํ๋ฉฐ, ํธ์ถ ์ด๋ฆยท์ต์
ํ๊ธฐยทํธํ ์๋ฌ ์ฝ๋๋ ๋ฐ์ธ๋ฉ๋ง๋ค ๋ค๋ฅผ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ hwpforge ํจํค์ง README๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๐จ ๋ฌธ์ ์์ฑ
use hwpforge::core::{Document, Draft, Paragraph, Run, Section, PageSettings};
use hwpforge::foundation::{CharShapeIndex, ParaShapeIndex};
let mut doc = Document::<Draft>::new();
doc.add_section(Section::with_paragraphs(
vec![Paragraph::with_runs(
vec![Run::text("Hello, ํ๊ธ!", CharShapeIndex::new(0))],
ParaShapeIndex::new(0),
)],
PageSettings::a4(),
));
โ๏ธ HWPX๋ก ์ธ์ฝ๋ฉ
use hwpforge::hwpx::{HwpxEncoder, HwpxStyleStore};
use hwpforge::core::ImageStore;
let validated = doc.validate().unwrap();
let style_store = HwpxStyleStore::with_default_fonts("ํจ์ด๋กฌ๋ฐํ");
let image_store = ImageStore::new();
let bytes = HwpxEncoder::encode(&validated, &style_store, &image_store).unwrap();
std::fs::write("output.hwpx", &bytes).unwrap();
โ๏ธ HWPX ๋์ฝ๋ฉ
use hwpforge::hwpx::HwpxDecoder;
let result = HwpxDecoder::decode_file("input.hwpx").unwrap();
println!("์น์
์: {}", result.document.sections().len());
โ๏ธ HWPX โ Markdown ๋ณํ (AI๊ฐ ํ๊ธ ๋ฌธ์ ์ฝ๊ธฐ)
use hwpforge::hwpx::HwpxDecoder;
use hwpforge::md::MdEncoder;
let decoded = HwpxDecoder::decode_file("government_report.hwpx").unwrap();
let validated = decoded.document.validate().unwrap();
let markdown = MdEncoder::encode_lossy(&validated).unwrap();
println!("{}", markdown); // LLM์ด ๋ฐ๋ก ์ดํดํ ์ ์๋ Markdown
๊ธฐ์กด .hwpx ํ์ผ์ Markdown์ผ๋ก ๋ณํํ๋ฉด Claude, GPT ๋ฑ ์ด๋ค LLM์ด๋ ํ๊ธ ๊ณต๋ฌธ์๋ฅผ ์ฆ์ ์ฝ๊ณ ๋ถ์ํ ์ ์์ต๋๋ค.
โ๏ธ Markdown โ HWPX ๋ณํ
use hwpforge::md::MdDecoder;
use hwpforge::hwpx::{HwpxEncoder, HwpxRegistryBridge};
let md_doc = MdDecoder::decode_with_default("# ์ ๋ชฉ\n\nMarkdown์์ ๋ณํ!").unwrap();
let bridge = HwpxRegistryBridge::from_registry(&md_doc.style_registry).unwrap();
let rebound = bridge.rebind_draft_document(md_doc.document).unwrap();
let validated = rebound.validate().unwrap();
let image_store = hwpforge::core::ImageStore::new();
let bytes = HwpxEncoder::encode(&validated, bridge.style_store(), &image_store).unwrap();
Feature Flags
| Feature | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
hwpx | Yes | HWPX encoder/decoder |
md | โ | Markdown โ Core ๋ณํ |
ops-hwpx | โ | HWPX ์ ์ฉ ํ๋ฉด(inspectยทexchangeยทreadยทeditยทdiffยทstampยทstyle)์ ๊ณต์ ์ฐ์ฐ ๊ณ์ธต |
ops-md | โ | ops์ Markdown ์ฐ์ฐ(convert_md, to_md) ์ถ๊ฐ (ops-hwpx + md ํฌํจ) |
ops | โ | ์ ์ฒด ์ฐ์ฐ ๊ณ์ธต์ ๋ณ์นญ (ops-md์ ๊ฐ์) |
schemars | โ | ops๊ฐ ์์ ํ wire DTO์ exchange DTO์ JsonSchema derive ์ถ๊ฐ |
full | โ | hwpx + md + ops (schemars๋ ํฌํจํ์ง ์์) |
# Markdown ์ง์๊ณผ ๊ณต์ ์ฐ์ฐ ๊ณ์ธต ํฌํจ (schemars ์ ์ธ)
hwpforge = { version = "0.16", features = ["full"] }
๐ ์ง์ ์ฝํ ์ธ
| ์นดํ ๊ณ ๋ฆฌ | ์์ |
|---|---|
| ํ ์คํธ | Run, character shape, paragraph shape, style (ํ์ปด ๊ธฐ๋ณธ ์คํ์ผ 18/22/23๊ฐ, ์คํ์ผ ์ธํธ๋ณ) |
| ๊ตฌ์กฐ | Table (์ค์ฒฉ), Image (๋ฐ์ด๋๋ฆฌ + ๊ฒฝ๋ก), TextBox, Caption |
| ๋ ์ด์์ | ๋ค๋จ, ํ์ด์ง ์ค์ , ๊ฐ๋ก/์ธ๋ก ๋ฐฉํฅ, ์ ๋ณธ ์ฌ๋ฐฑ, master page |
| ๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธ | Header, Footer, ์ชฝ๋ฒํธ (autoNum) |
| ๊ฐ์ฃผ/๋ฏธ์ฃผ | ๊ฐ์ฃผ, ๋ฏธ์ฃผ |
| ๋ํ | ์ , ์ฌ๊ฐํ, ํ์, ๋ค๊ฐํ, ํธ, ๊ณก์ , ์ฐ๊ฒฐ์ , ๋ฌถ์(Group), ๊ธ๋งต์(TextArt) (์ฑ์, ํ์ , ํ์ดํ ์ง์) |
| ์์ | HancomEQN script ํ์ |
| ์ฐจํธ | 18์ข chart type (OOXML ํธํ) |
| ์ฐธ์กฐ | ์ฑ ๊ฐํผ, ์ํธ ์ฐธ์กฐ, ํ๋ (๋ ์ง/์๊ฐ/์์ฝ), ๋ฉ๋ชจ, ์์ธ |
| ๋ง๋ง/๊ฒน์นจ | ๋ง๋ง (dutmal), ๊ธ์ ๊ฒน์นจ |
| Markdown | GFM decode, lossy + lossless encode, YAML frontmatter |
| PDF ๋ด๋ณด๋ด๊ธฐ | ํ์ปด ์กฐํ ์บ์ ์ฌ์(๊ณ์ฐ ์๋) ๋ ๋ โ ํยท๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธยท์ชฝ๋ฒํธยทํฐํธ ํ์ดํ๋ผ์ธ |
์ํคํ ์ฒ
HwpForge๋ ๋ ์ด์ด๋ฅผ ๋๋ ์ ์๊ฐํ๋ ํ๋ก์ ํธ์ ๋๋ค.
foundation: ๊ณตํต primitive, unit, index, errorcore: ํฌ๋งท ๋ ๋ฆฝ ๋ฌธ์ ๋ชจ๋ธ๊ณผ shared semanticsblueprint: ์คํ์ผ ์ ์์ ํ ํ๋ฆฟ ๊ณ์ธตsmithy-*: ํฌ๋งท๋ณ codec๊ณผ bridge (๊ฐ ํฌ๋ ์ดํธ๋ ๋จ์ผ ํฌ๋งท๋ง ๋ด๋น)convert: ํฌ๋งท ๊ฐ ๋ณํ ์ค์ผ์คํธ๋ ์ดํฐ (HWP5 โ HWPX, smithy ์์์ ๋ ํฌ๋งท์ ์ฎ์)์ PDF ๋ ๋ ์ฐ์ฐ(ops::to_pdf)bindings-*: CLI / MCP / Python ์ง์ ์
hwpforge(umbrella crate)์ ops ๋ชจ๋๊ณผ hwpforge-convert์ ops ๋ชจ๋์ ์ธ ๋ฐ์ธ๋ฉ์ด ๊ณต์ ํ๋ ์ฐ์ฐ ๊ณ์ธต์
๋๋ค โ CLIยทMCPยทPython ๋ชจ๋ ์ด ๊ณ์ธต์ ๊ฑฐ์ณ ํธ์ถํฉ๋๋ค. ์๋ ๊ทธ๋ฆผ์ ํ์ดํ๋ ๊ฐ ํฌ๋ ์ดํธ Cargo.toml์ [dependencies] ์ง์ ์์กด์ ๋ฐ๋ฅด๋ฉฐ, ๋ชจ๋ ํฌ๋ ์ดํธ๊ฐ ์์กดํ๋ foundation๊ณผ ๋ฐ์ธ๋ฉ์ core ์ง์ ์์กด์ ์๋ตํ์ต๋๋ค.
๋ ์ด์ด ๊ตฌ์กฐ
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px', 'lineColor': '#BDBDBD'}}}%%
flowchart TB
F["foundation<br/>primitive types<br/>units / indices / errors"]:::foundation
C["core<br/>shared document model<br/>list / tab / paragraph semantics"]:::core
B["blueprint<br/>style registry<br/>template authoring"]:::blueprint
SHX["smithy-hwpx<br/>HWPX read/write"]:::smithy
SH5["smithy-hwp5<br/>HWP5 read / audit / re-emission path"]:::smithy
SMD["smithy-md<br/>Markdown bridge"]:::smithy
SPDF["smithy-pdf<br/>layout-cache replay renderer"]:::smithy
HWPF["hwpforge<br/>umbrella crate + ops<br/>(feature: hwpx / md / ops)"]:::umbrella
CONV["convert<br/>HWP5 โ HWPX + PDF ops"]:::convert
CLI["bindings-cli<br/>Hammer"]:::binding
MCP["bindings-mcp<br/>Vise MCP Server"]:::binding
PY["bindings-py<br/>Tongs (Python)"]:::binding
F --> C
C --> B
C --> SHX
C --> SH5
C --> SMD
C --> SPDF
C --> CONV
C --> HWPF
B --> SHX
B --> SMD
B --> HWPF
SHX --> HWPF
SMD --> HWPF
SHX --> CONV
SH5 --> CONV
SPDF --> CONV
HWPF --> CLI
SHX --> CLI
SH5 --> CLI
SMD --> CLI
SPDF --> CLI
CONV --> CLI
HWPF --> MCP
SHX --> MCP
SMD --> MCP
HWPF --> PY
CONV --> PY
SPDF --> PY
classDef file fill:#FFFDE7,stroke:#F9A825,color:#5D4037
classDef smithy fill:#FFF3E0,stroke:#FB8C00,color:#E65100
classDef convert fill:#E0F2F1,stroke:#00897B,color:#004D40
classDef core fill:#E3F2FD,stroke:#42A5F5,color:#0D47A1
classDef blueprint fill:#F3E5F5,stroke:#AB47BC,color:#4A148C
classDef foundation fill:#FAFAFA,stroke:#BDBDBD,color:#424242
classDef umbrella fill:#FFFFFF,stroke:#6D4C41,color:#3E2723
classDef binding fill:#E8F5E9,stroke:#43A047,color:#1B5E20
๋ฐ์ดํฐ ํ๋ฆ
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px', 'lineColor': '#BDBDBD'}}}%%
flowchart LR
HWP5[".hwp"]:::file --> SH5["smithy-hwp5"]:::smithy
MD[".md"]:::file --> SMD["smithy-md"]:::smithy
HWPX[".hwpx"]:::file <--> SHX["smithy-hwpx"]:::smithy
SH5 --> CORE["core document"]:::core
SHX <--> CORE
SMD <--> CORE
CORE --> SHX
CONV["convert<br/>HWP5โHWPX ์ค์ผ์คํธ๋ ์ด์
+ PDF"]:::convert --> SH5
CONV --> SHX
CLI["CLI"]:::binding --> CONV
CLI --> SH5
CLI --> SHX
CLI --> SMD
MCP["MCP"]:::binding --> SHX
MCP --> SMD
classDef file fill:#FFFDE7,stroke:#F9A825,color:#5D4037
classDef smithy fill:#FFF3E0,stroke:#FB8C00,color:#E65100
classDef convert fill:#E0F2F1,stroke:#00897B,color:#004D40
classDef core fill:#E3F2FD,stroke:#42A5F5,color:#0D47A1
classDef binding fill:#E8F5E9,stroke:#43A047,color:#1B5E20
ํต์ฌ ์์น: ๊ตฌ์กฐ(Structure)์ ์คํ์ผ(Style)์ ๋ถ๋ฆฌํฉ๋๋ค. core๋ shared semantics์ ์คํ์ผ ์ฐธ์กฐ๋ฅผ ๋ค๊ณ , blueprint๋ ์คํ์ผ ์ ์๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. smithy-* ๊ณ์ธต์ด ํฌ๋งท surface๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค.
์ค์ ์์ ๊ฐ์ฅ ์์ฃผ ์ฐ๋ ๊ฒฝ๋ก๋ ๋ค์ ์ ์ ๋๋ค.
- Markdown โ HWPX
- HWPX โ Markdown / JSON
- HWP5 โ HWPX โ audit / inspect
ํ๋ก์ ํธ ํํฉ
| ์งํ | ๊ฐ |
|---|---|
| Crate ์ | 12๊ฐ |
| ์ปค๋ฒ๋ฆฌ์ง | CI ๊ฒ์ดํธ 90% ์ด์ (์ค ๊ธฐ์ค) |
| Clippy ๊ฒฝ๊ณ | 0 |
| Unsafe ์ฝ๋ | 0 |
๊ฐ๋ฐ
ํ์ ์๊ตฌ์ฌํญ
- Rust 1.89+ (์ํฌ์คํ์ด์ค MSRV) โ
hwpforge-bindings-cliCLI๋ฅผ ์ง์ ๋น๋/์ค์นํ๋ ค๋ฉด krilla ์์กด์ผ๋ก 1.92+ ํ์ - (๊ถ์ฅ) cargo-nextest โ ๋ณ๋ ฌ ํ ์คํธ ์คํ
- (์ ํ) pre-commit โ git hook ์๋ํ
MSRV ์ ์ฑ
- ์ํฌ์คํ์ด์ค ๊ธฐ๋ณธ MSRV๋ Rust 1.89์ด๋ฉฐ, stable์์ 4 ๋ฆด๋ฆฌ์ค ๋ค์ฒ์ง ๋ฒ์ ์ ๊ธฐ๋ณธ ์ ์ฑ ์ผ๋ก ์ ์งํฉ๋๋ค.
- krilla ์์กด ๊ฒฝ๋ก์ ์๋ ๋ค ํฌ๋ ์ดํธ(
hwpforge-smithy-pdfยทhwpforge-convertยทhwpforge-bindings-cliยทhwpforge-bindings-py)๋ Rust 1.92+๊ฐ ํ์ํฉ๋๋ค(rust-version์ ํฌ๋ ์ดํธ๋ณ๋ก ์ํฅ ์ง์ ). CI์Verify โบ MSRVjob์ ์ด ๋ท์ 1.89 ๊ฒ์ฆ ํจ์ค์์๋ ์ ์ธํ์ง๋ง, ๊ฐ์ job ์์์cargo +1.92 check๋ก ๋ฐ๋ก ๊ฒ์ฆํฉ๋๋ค โ ๊ฒ์ฆ ๋์์์ ๋น ์ง๋ ๊ฒ์ด ์๋๋๋ค. - ๊ฐ ํฌ๋ ์ดํธ์
Cargo.toml์rust-version์ด ๊ทธ ํฌ๋ ์ดํธ์ ์ค์ MSRV์ด๋ฉฐ, CI์Verify โบ MSRVjob์ด ์ํฌ์คํ์ด์ค ๊ธฐ๋ณธ๊ฐ(1.89)์ ๊ฒ์ฆํฉ๋๋ค. - MSRV ์ํฅ์ด ํ์ํ๋ฉด PR์์ ์ด์ ๋ฅผ ๋ช
์ํ๊ณ ,
Cargo.toml, CI, CHANGELOG๋ฅผ ํจ๊ป ๊ฐฑ์ ํฉ๋๋ค. - ๊ฐ๋ฐ์ฉ ๊ธฐ๋ณธ ํด์ฒด์ธ์ ๋ ์ต์ ์ผ ์ ์์ต๋๋ค. ํธํ์ฑ ํ๋จ ๊ธฐ์ค์ ์ต์ stable์ด ์๋๋ผ MSRV + CI ํต๊ณผ ์ฌ๋ถ์ ๋๋ค.
โ๏ธ ๋ช ๋ น์ด
make ci # ๋น ๋ฅธ ๋ก์ปฌ ๊ฒ์ฆ โ fmt + clippy + test + deny + lint-md (ci-fast ๋ณ์นญ)
make ci-full # ์์ coverage + MSRV ์ถ๊ฐ (๋ฆด๋ฆฌ์คยทํฐ ๋ณ๊ฒฝ ์ )
make py-all # Python ๋ฐ์ธ๋ฉ ๊ฒ์ฌ (lint + ํ์
+ Rust/Python ํ
์คํธ + coverage)
make test # cargo nextest run
make clippy # cargo clippy (๋ชจ๋ target, ๋ชจ๋ feature, -D warnings)
make fmt-fix # rustfmt ์๋ ํฌ๋งท
make doc # rustdoc ์์ฑ (๋ธ๋ผ์ฐ์ ์์ ์ด๋ฆผ)
make cov # coverage ๋ฆฌํฌํธ (90% gate)
make ci๋ CI ์ ์ฒด๊ฐ ์๋๋ผ ๊ทธ์ค ๋น ๋ฅธ ๋ค์ฏ ๋ ์ธ์
๋๋ค. CI๋ ์ฌ๊ธฐ์ CoverageยทMSRVยทHWP5 Audit GateยทDocs BuildยทPythonยทWorkflow Lint๋ฅผ ๋ ๋๋ฆฝ๋๋ค. ๋ฆด๋ฆฌ์ค๋ ํฐ ๋ณ๊ฒฝ ์ ์๋ make ci-full๊ณผ make py-all์, ๋ฌธ์๋ฅผ ๊ณ ์ณค๋ค๋ฉด mdbook build๋ฅผ ํจ๊ป ๋๋ฆฌ์ธ์.
๋น๋ ๊ฐ์ (์ ํ):
sccache๊ฐ PATH์ ์์ผ๋ฉดmakeํ๊น์ด ์๋์ผ๋ก ์ปดํ์ผ ์บ์๋ก ์ฌ์ฉํฉ๋๋ค(์์ผ๋ฉด ๊ทธ๋๋ก ๋์ โ ์๋ฌด๊ฒ๋ ๊นจ์ง์ง ์์). ๋ฐ๋ณตmake ci๊ฐ ํฌ๊ฒ ๋นจ๋ผ์ง๋๋ค. ์ค์น:cargo install sccache๋๋brew install sccache. (๋ฆด๋ฆฌ์ค ํ์ดํ๋ผ์ธ release-plz ๋ ์ํฅ๋ฐ์ง ์์ต๋๋ค.)
ํ๋ก์ ํธ ๊ตฌ์กฐ
HwpForge/
โโโ crates/
โ โโโ hwpforge/ # Umbrella crate (re-exports)
โ โโโ hwpforge-foundation/ # ๊ธฐ๋ณธ ํ์
(HwpUnit, Color, Index<T>)
โ โโโ hwpforge-core/ # ๋ฌธ์ ๋ชจ๋ธ (์คํ์ผ ์ฐธ์กฐ๋ง)
โ โโโ hwpforge-blueprint/ # YAML ํ
ํ๋ฆฟ (Figma ํจํด)
โ โโโ hwpforge-smithy-hwpx/ # HWPX codec (ZIP+XML โ Core)
โ โโโ hwpforge-smithy-md/ # Markdown codec (MD โ Core)
โ โโโ hwpforge-smithy-hwp5/ # HWP5 decode/projection + inspect helpers
โ โโโ hwpforge-smithy-pdf/ # PDF ๋ ๋๋ฌ (์กฐํ ์บ์ ์ฌ์ ๋ ๋๋ฌ, to-pdf ๊ฐ ์ฌ์ฉ)
โ โโโ hwpforge-convert/ # ํฌ๋งท ๊ฐ ๋ณํ ์ค์ผ์คํธ๋ ์ดํฐ (HWP5 โ HWPX)
โ โโโ hwpforge-bindings-py/ # Python bindings (shipped, PyPI)
โ โโโ hwpforge-bindings-cli/ # CLI ๋๊ตฌ (hwpforge, shipped)
โ โโโ hwpforge-bindings-mcp/ # MCP Server (hwpforge-mcp)
โโโ tests/ # ํตํฉ ํ
์คํธ + golden fixture
โโโ examples/ # curated showcase + interop artifacts
โโโ showcase/
โโโ interop/
๊ธฐ์ฌ
๋ฒ๊ทธ ์์ , ํฌ๋งท ๋ฆฌ์์น, ํ ์คํธ ๋ณด๊ฐ, ๋ฌธ์ ๊ฐ์ ๋ชจ๋ ํ์ํฉ๋๋ค.
- ์์ ์ ๊ฐ์ด๋: CONTRIBUTING.md
- ํนํ ํ์ธํ ๊ฒ: release-plz๊ฐ ์ฐ๋ ์ปค๋ฐ prefix (
feat,fix,perf,refactor) - ํนํ ํ์ธํ ๊ฒ: MSRV ์ ์ฑ ๊ณผ dependency/MSRV ์์น ๊ธฐ์ค
- ํนํ ํ์ธํ ๊ฒ: ๋ฌธ์ ๋ณ๊ฒฝ ์
mdbook build์ markdown lint ๊ฒ์ฆ - ํนํ ํ์ธํ ๊ฒ: ๋ก์ปฌ docs toolchain์ CI์ ๊ฐ์ pinned ๋ฒ์ (
mdbook 0.4.52,mdbook-admonish 1.20.0,mdbook-mermaid 0.16.2)์ ์ฐ๋ ํธ์ด ๋ซ์ต๋๋ค. ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์make install-tools - ํนํ ํ์ธํ ๊ฒ: CI required checks๋ฅผ ๊นจ์ง ์๋ ๋ฒ์์์์ ๋ณ๊ฒฝ ๋ถ๋ฆฌ
๋ก๋๋งต
ํ์ฌ ์ํ์ ๋ค์ ๋จ๊ณ
- HWP5 ์ฝ๊ธฐ/์ ๊ฒ/์ฌ์ถ๋ ฅ ๊ฒฝ๋ก โ
convert-hwp5,audit-hwp5,census-hwp5 - HWP5 public API ํ๋ โ umbrella crate surface์ broader parity ์ ๋ฆฌ
- MCP ์๋ฒ โ Claude, Cursor ๋ฑ AI ๋๊ตฌ๊ฐ tool๋ก ์ง์ HWPX ์์ฑยท๊ฒ์ฆยทํธ์ง (19๊ฐ ๋๊ตฌ + 4 ๋ฆฌ์์ค + 3 ํ๋กฌํํธ)
- CLI ๋๊ตฌ โ
hwpforge convert doc.md -o doc.hwpxํ ์ค ๋ณํ (23๊ฐ ๋ช ๋ น์ด: 20 core + 3 HWP5) - HWPX ์์ ์ง์ โ ์์ ์ปจํธ๋กค, ๋ณ๊ฒฝ ์ถ์ , OLE ๊ฐ์ฒด
- Python ๋ฐ์ธ๋ฉ โ
pip install hwpforge๋ก ์ค์น (PyPI, 0.16.5๋ถํฐ wheel ๋ฐฐํฌ)
๋ผ์ด์ ์ค
๋ค์ ์ค ํ๋๋ฅผ ์ ํํ ์ ์์ต๋๋ค:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
Acknowledgements
HwpForge๋ ๊ฑฐ์ธ๋ค์ ์ด๊นจ ์์ ์ ์์ต๋๋ค.
- Hancom โ HWPX ํฌ๋งท์ ๊ณต๊ฐ ๋ฌธ์์ KS X 6101 (OWPML) ๊ตญ๊ฐ ํ์ค์ด ์์๋ค๋ฉด ์ด ํ๋ก์ ํธ๋ ์์์กฐ์ฐจ ํ ์ ์์์ต๋๋ค. ํฌ๋งท์ ๊ณต๊ฐํด ์ฃผ์ Hancom์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค. ๋ณธ ์ ํ์ ํ์ปด์ HWP ๋ฌธ์ ํ์ผ(.hwp) ๊ณต๊ฐ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ ๊ฐ๋ฐํ์์ต๋๋ค. (ํ์ปด ๋ฌธ์ ํ์ผ ํ์ ๊ณต๊ฐ)
- openhwp โ Rust๋ก HWP/HWPX๋ฅผ ๋ค๋ฃจ๋ IR ๊ธฐ๋ฐ ์ํคํ ์ฒ ์ค๊ณ์์ ํฐ ์๊ฐ์ ๋ฐ์์ต๋๋ค. HwpForge์ Core ๋ ์ด์ด๊ฐ ์กด์ฌํ ์ ์์๋ ๊ฒ์ openhwp์ด ๋จผ์ ๊ทธ ๊ธธ์ ๊ฑธ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
- hwpxlib โ Java๋ก ์์ฑ๋ ๊ฐ์ฅ ์ฑ์ํ HWPX ๊ตฌํ์ฒด์ ๋๋ค. ์คํ๊ณผ ์ค์ ๋์์ ์ฐจ์ด๋ฅผ ํ์ ํ๋ ๋ฐ ๊ฒฐ์ ์ ์ธ ์ฐธ๊ณ ๊ฐ ๋์์ต๋๋ค.
- hwp.js โ HWP5 ํฌ๋งท์ quirks์ edge case๋ฅผ ๊ผผ๊ผผํ ๋ฌธ์ํํ ํ๋ก์ ํธ์ ๋๋ค. ๋ฐ์ด๋๋ฆฌ ํฌ๋งท์ ์ด๋์ด ๊ตฌ์์ ๋ฐํ ์ค ๋๋ถ์ ์ํ์ฐฉ์ค๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์์์ต๋๋ค.
- hwpx-owpml-model โ Hancom์ด ์ง์ ๊ณต๊ฐํ C++ OWPML ๋ชจ๋ธ ๊ตฌํ์ฒด๋ก, ์คํค๋ง ํด์์ ์ต์ข ๊ธฐ์ค์ผ๋ก ์ผ์์ต๋๋ค.
- Rust ์ํ๊ณ โ serde, quick-xml, pulldown-cmark, zip ๋ฑ ๋ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ๋๋ถ์ HwpForge ์ ์ฒด๋ฅผ zero unsafe ์์ Rust๋ก ๊ตฌํํ ์ ์์์ต๋๋ค. Rust ์ปค๋ฎค๋ํฐ์ Ferris ๐ฆ์๊ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- Claude by Anthropic โ HwpForge์ ์ค๊ณ, ๊ตฌํ, ํ ์คํธ, ๋ฌธ์ํ ์ ๊ณผ์ ์์ Claude Code๊ฐ ๊ฐ๋ฐ ํํธ๋๋ก ํจ๊ปํ์ต๋๋ค. LLM-first๋ฅผ ํ๋ฐฉํ๋ ํ๋ก์ ํธ๋ต๊ฒ, AI์ ์ฌ๋์ด ํ์ ํ์ฌ ๋ง๋ค์ด๋ธ ๊ฒฐ๊ณผ๋ฌผ์ ๋๋ค.
- Codex by OpenAI โ HwpForge์ ๊ตฌํ ๊ฒํ , ๋ฆฌํฉํฐ๋ง, ํ๊ท ์ ๊ฒ, ์์ ํ๋ฆ ์ ๋ฆฌ ๊ณผ์ ์์ ์ค์ง์ ์ธ ๊ฐ๋ฐ ํํธ๋๋ก ๊ธฐ์ฌํ์ต๋๋ค.
์ ๋ถ๋ฆฌ Anvilscribe (SรถBuri Anvilscribe) ํ์ปด ๋ฌธ์๋ฅผ ๋ถ์ ๋ฌ๊ตฌ์ด ๋จ๋จํ๊ฒ ๋ฒผ๋ ค๋ด๋ ๋์ฅ์ฅ์ด ์ค๋ฆฌ๋๊ตฌ๋ฆฌ ๐ฅ