# HwpForge ๐ฅ
> **Rust๋ก ํ๊ธ(HWP/HWPX) ๋ฌธ์๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ ์ด**
>
> [Hancom](https://www.hancom.com/) ํ๊ธ ํ์ผ ์ฝ๊ธฐ, ์ฐ๊ธฐ, ๋ณํ
<div align="center">
[](https://github.com/ai-screams/HwpForge/actions/workflows/ci.yml)
[](https://github.com/ai-screams/HwpForge)
[](https://github.com/ai-screams/HwpForge)
[](https://github.com/ai-screams/HwpForge)
[](https://github.com/ai-screams/HwpForge)
[](https://crates.io/crates/hwpforge)
[](https://docs.rs/hwpforge)
[](https://crates.io/crates/hwpforge)
[](Cargo.toml)
[](LICENSE-MIT)
[](https://modelcontextprotocol.io/)
[](https://github.com/ai-screams/HwpForge/releases)
[](https://github.com/ai-screams/HwpForge)
[](https://github.com/ai-screams/HwpForge)
[](SECURITY.md)
[](CONTRIBUTING.md)
[](https://github.com/ai-screams/HwpForge/pulls)
[](https://github.com/ai-screams/HwpForge)
[](https://buymeacoffee.com/pignuante)
</div>
<div align="center">
<img src="https://raw.githubusercontent.com/ai-screams/HwpForge/main/assets/banner-main.png" alt="HwpForge Banner" width="600">
</div>
---
## HwpForge๋?
HwpForge๋ HWPX ๋ฌธ์(ZIP + XML, KS X 6101)๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ **์คํ์์ค** ์์ Rust ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค. ํ๊ตญ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ์๋ํ๋ก์ธ์์ธ [Hancom ํ๊ธ](https://www.hancom.com)์ ์ต์ ํฌ๋งท์ ์ง์ํฉ๋๋ค.
### ์ง์ ๋ฒ์
| ํ๊ธ 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**: ๊ตฌํ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท (v2.0์์ ์ฝ๊ธฐ ์ง์ ์์ )
- ์คํ์ผ ์ธํธ๋ `HancomStyleSet` enum์ผ๋ก ์ ํ ๊ฐ๋ฅ (๊ธฐ๋ณธ: Modern)
**LLM-first ์ค๊ณ** ๐ฅ โ AI ์นํ์ ์ธ Markdown๊ณผ ๊ณต์ ํ๊ธ ๋ฌธ์ ํฌ๋งท(HWPX), ๋ ์ธ๊ณ๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ์์ต๋๋ค. LLM์ด Markdown์ผ๋ก ์์ฑํ ๋ด์ฉ์ ๊ณต๋ฌธ์ ๊ท๊ฒฉ์ HWPX๋ก ์ปดํ์ผ๋๊ณ ๐, ๋ฐ๋๋ก ๊ธฐ์กด HWPX ๋ฌธ์๋ AI๊ฐ ์ฝ๊ฒ ์ฝ์ ์ ์๋ ๊ตฌ์กฐ๋ก ๊บผ๋ผ ์ ์์ต๋๋ค โ๏ธ.
- **๐ [HWPX ์์ ๊ฐ์ด๋ ๋ค์ด๋ก๋](examples/hwpx_complete_guide.hwpx)** โ HwpForge API๋ก ์์ฑํ 4์น์
๋ฐ๋ชจ ๋ฌธ์ (ํ๊ธ์์ ์ด์ด๋ณด์ธ์)
- **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
## ๋น ๋ฅธ ์์
### ์ค์น
```bash
# Cargo.toml์ ์ถ๊ฐ
cargo add hwpforge
# Markdown ์ง์ ํฌํจ
cargo add hwpforge --features full
```
๋๋ `Cargo.toml`์ ์ง์ ์ถ๊ฐ:
```toml
[dependencies]
hwpforge = "0.1"
```
### ๐จ Hammer โ CLI๋ก ์์ํ๊ธฐ
CLI ๋๊ตฌ `hwpforge`(Hammer)๋ฅผ ์ค์นํ๋ฉด ํฐ๋ฏธ๋์์ ๋ฐ๋ก ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ํธ์งํ ์ ์์ต๋๋ค.
```bash
cargo install hwpforge-bindings-cli
```
```bash
# Markdown โ HWPX ๋ณํ
hwpforge convert report.md -o report.hwpx
# HWPX ๊ตฌ์กฐ ํ์ธ
hwpforge inspect report.hwpx
# HWPX โ JSON ์ถ์ถ (AI ํธ์ง์ฉ)
hwpforge to-json report.hwpx --section 0 > section0.json
# JSON์ผ๋ก ์น์
๊ต์ฒด
hwpforge patch report.hwpx --section 0 < modified.json -o updated.hwpx
# JSON Schema ์ถ๋ ฅ (AI agent์ฉ)
hwpforge schema document
```
> **AI-first ์ค๊ณ**: CLI๋ AI agent(Claude Code ๋ฑ)๊ฐ ์ฃผ ์ฌ์ฉ์์
๋๋ค.
> Markdown์ผ๋ก ๋ฌธ์๋ฅผ ์์ฑํ ๋ค, JSON round-trip์ผ๋ก ๊ธฐ์กด ์คํ์ผ์ ๋ณด์กดํ๋ฉด์
> section ๋จ์๋ก ์ ๋ฐํ๊ฒ ํธ์งํ ์ ์์ต๋๋ค. `--json` ํ๋๊ทธ๋ก ๋ชจ๋ ๋ช
๋ น์ด๊ฐ
> machine-readable ์ถ๋ ฅ์ ์ง์ํฉ๋๋ค.
### โ๏ธ Anvil โ MCP Server๋ก AI๊ฐ ์ง์ ํ๊ธ ๋ฌธ์๋ฅผ ๋ค๋ฃจ๋ค
Claude Code, Codex CLI, ChatGPT, Cursor, Windsurf ๋ฑ [MCP](https://modelcontextprotocol.io/) ์ง์ AI ๋๊ตฌ์์ **ํ๊ธ ๋ฌธ์๋ฅผ ์ง์ ์์ฑํ๊ณ ํธ์ง**ํ ์ ์์ต๋๋ค. "๋ณด๊ณ ์ ๋ง๋ค์ด์ค"๋ผ๊ณ ๋งํ๋ฉด, AI๊ฐ ์์์ `.hwpx` ํ์ผ์ ๋๋ฑ ๋ง๋ค์ด๋
๋๋ค.
#### 1๋จ๊ณ: ์ค์น
npm (Node.js 20+) ๋๋ Cargo ์ค ํ๋๋ฅผ ์ ํํฉ๋๋ค:
```bash
# ๋ฐฉ๋ฒ A: npm (๊ถ์ฅ โ Rust ํด์ฒด์ธ ๋ถํ์)
npx -y @hwpforge/mcp
# ๋ฐฉ๋ฒ B: Cargo (Rust ๊ฐ๋ฐ์์ฉ)
cargo install hwpforge-bindings-mcp
```
#### 2๋จ๊ณ: AI ๋๊ตฌ์ ๋ฑ๋ก
<details>
<summary><strong>Claude Code</strong> (ํฐ๋ฏธ๋)</summary>
```bash
# npm์ผ๋ก ์ค์นํ ๊ฒฝ์ฐ
claude mcp add hwpforge -- npx -y @hwpforge/mcp
# cargo๋ก ์ค์นํ ๊ฒฝ์ฐ
claude mcp add hwpforge hwpforge-mcp
# ๋ชจ๋ ํ๋ก์ ํธ์์ ์ฌ์ฉ (๊ธ๋ก๋ฒ)
claude mcp add --global hwpforge -- npx -y @hwpforge/mcp
```
</details>
<details>
<summary><strong>Codex CLI</strong> (ํฐ๋ฏธ๋)</summary>
`~/.codex/config.toml`์ ์ถ๊ฐ:
```toml
[mcp_servers.hwpforge]
command = "npx"
args = ["-y", "@hwpforge/mcp"]
```
๋๋ CLI๋ก:
```bash
codex mcp add hwpforge -- npx -y @hwpforge/mcp
```
</details>
<details>
<summary><strong>Claude Desktop</strong> (์ฑ)</summary>
์ค์ ํ์ผ์ ํธ์งํฉ๋๋ค:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
```
</details>
<details>
<summary><strong>ChatGPT Desktop</strong> (์ฑ)</summary>
Settings โ Tools โ Add MCP Server์์:
- Name: `hwpforge`
- Command: `npx -y @hwpforge/mcp`
๋๋ ์ค์ ํ์ผ์ ์ง์ ํธ์ง:
```json
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
```
</details>
<details>
<summary><strong>Cursor</strong> (์๋ํฐ)</summary>
ํ๋ก์ ํธ ๋ฃจํธ์ `.cursor/mcp.json` ์์ฑ:
```json
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
```
</details>
<details>
<summary><strong>Windsurf</strong> (์๋ํฐ)</summary>
`~/.codeium/windsurf/mcp_config.json`์ ์ถ๊ฐ:
```json
{
"mcpServers": {
"hwpforge": {
"command": "npx",
"args": ["-y", "@hwpforge/mcp"]
}
}
}
```
</details>
#### ๋ฑ๋กํ๋ฉด 5๊ฐ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค
| `hwpforge_convert` | Markdown โ HWPX ๋ณํ | "์ด ๋งํฌ๋ค์ด์ ํ๊ธ ํ์ผ๋ก!" |
| `hwpforge_inspect` | HWPX ๊ตฌ์กฐ ํ์ธ | "์ด ๋ฌธ์ ๋ญ๊ฐ ๋ค์ด์์ด?" |
| `hwpforge_to_json` | HWPX โ JSON ์ถ์ถ | "์ด ์น์
๋ด์ฉ ์ข ๊บผ๋ด๋ด" |
| `hwpforge_patch` | JSON์ผ๋ก ์น์
๊ต์ฒด | "์ด ๋ถ๋ถ๋ง ๋ฐ๊ฟ์ ๋ค์ ์ ์ฅํด" |
| `hwpforge_templates` | ์คํ์ผ ํ๋ฆฌ์
์กฐํ | "์ด๋ค ํ
ํ๋ฆฟ ์ธ ์ ์์ด?" |
#### ์
๋ฐ์ดํธ / ์ญ์
```bash
cargo install hwpforge-bindings-mcp --force # ์
๋ฐ์ดํธ
cargo uninstall hwpforge-bindings-mcp # ์ญ์
```
> **์ MCP?** CLI(Hammer)๋ AI๊ฐ `bash` ๋ช
๋ น์ ์คํํด์ผ ํ์ง๋ง, MCP(Anvil)๋ AI๊ฐ **๋ค์ดํฐ๋ธ ๋๊ตฌ**๋ก
> ์ง์ ํธ์ถํฉ๋๋ค. ํ์ผ ๊ฒฝ๋ก ํ์ฑ๋, stdout ํด์๋ ํ์ ์์ต๋๋ค.
> JSON-RPC๋ก ์์ฒญํ๋ฉด ๊ตฌ์กฐํ๋ JSON์ผ๋ก ์๋ต โ ๊น๋ํฉ๋๋ค.
### ๐จ ๋ฌธ์ ์์ฑ
```rust
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๋ก ์ธ์ฝ๋ฉ
```rust
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 ๋์ฝ๋ฉ
```rust
use hwpforge::hwpx::HwpxDecoder;
let result = HwpxDecoder::decode_file("input.hwpx").unwrap();
println!("์น์
์: {}", result.document.sections().len());
```
### โ๏ธ HWPX โ Markdown ๋ณํ (AI๊ฐ ํ๊ธ ๋ฌธ์ ์ฝ๊ธฐ)
```rust
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 ๋ณํ
```rust
use hwpforge::md::MdDecoder;
use hwpforge::hwpx::{HwpxEncoder, HwpxStyleStore};
let md_doc = MdDecoder::decode_with_default("# ์ ๋ชฉ\n\nMarkdown์์ ๋ณํ!").unwrap();
let validated = md_doc.document.validate().unwrap();
let style_store = HwpxStyleStore::from_registry(&md_doc.style_registry);
let image_store = hwpforge::core::ImageStore::new();
let bytes = HwpxEncoder::encode(&validated, &style_store, &image_store).unwrap();
```
## Feature Flags
| `hwpx` | Yes | HWPX encoder/decoder |
| `md` | โ | Markdown โ Core ๋ณํ |
| `full` | โ | ๋ชจ๋ ๊ธฐ๋ฅ ํฌํจ |
```toml
# Markdown ์ง์ ํฌํจ
hwpforge = { version = "0.1", features = ["full"] }
```
## ๐ ์ง์ ์ฝํ
์ธ
| ํ
์คํธ | Run, character shape, paragraph shape, style (22๊ฐ ํ์ปด ๊ธฐ๋ณธ ์คํ์ผ) |
| ๊ตฌ์กฐ | Table (์ค์ฒฉ), Image (๋ฐ์ด๋๋ฆฌ + ๊ฒฝ๋ก), TextBox, Caption |
| ๋ ์ด์์ | ๋ค๋จ, ํ์ด์ง ์ค์ , ๊ฐ๋ก/์ธ๋ก ๋ฐฉํฅ, ์ ๋ณธ ์ฌ๋ฐฑ, master page |
| ๋จธ๋ฆฌ๊ธ/๋ฐ๋ฅ๊ธ | Header, Footer, ์ชฝ๋ฒํธ (autoNum) |
| ๊ฐ์ฃผ/๋ฏธ์ฃผ | ๊ฐ์ฃผ, ๋ฏธ์ฃผ |
| ๋ํ | ์ , ํ์, ๋ค๊ฐํ, ํธ, ๊ณก์ , ์ฐ๊ฒฐ์ (์ฑ์, ํ์ , ํ์ดํ ์ง์) |
| ์์ | HancomEQN script ํ์ |
| ์ฐจํธ | 18์ข
chart type (OOXML ํธํ) |
| ์ฐธ์กฐ | ์ฑ
๊ฐํผ, ์ํธ ์ฐธ์กฐ, ํ๋ (๋ ์ง/์๊ฐ/์์ฝ), ๋ฉ๋ชจ, ์์ธ |
| ๋ง๋ง/๊ฒน์นจ | ๋ง๋ง (dutmal), ๊ธ์ ๊ฒน์นจ |
| Markdown | GFM decode, lossy + lossless encode, YAML frontmatter |
## ์ํคํ
์ฒ
```mermaid
%%{init: {'theme': 'base', 'themeVariables': {'fontSize': '14px', 'lineColor': '#BDBDBD'}}}%%
flowchart TD
subgraph formats["ํฌ๋งท"]
HF(["๐ .hwpx<br/>ํ๊ธ ํ์ผ"]):::file
MF(["๐ .md<br/>Markdown"]):::file
end
subgraph smithy["โ๏ธ Smithy โ ํฌ๋งท ๋ณํ๊ธฐ"]
SH["hwpforge-smithy-hwpx"]:::smithy
SM["hwpforge-smithy-md"]:::smithy
end
C["๐จ hwpforge-core<br/>ํฌ๋งท ๋
๋ฆฝ ๋ฌธ์ ๋ชจ๋ธ (IR)"]:::core
BP["๐ hwpforge-blueprint<br/>YAML ์คํ์ผ ยท ํฐํธ ยท ์์"]:::blueprint
F["๐ฉ hwpforge-foundation<br/>HwpUnit ยท Color ยท Index"]:::foundation
HF <--> SH
MF <--> SM
SH & SM <--> C
BP --> SH & SM
F --> C & BP
classDef file fill:#FFFDE7,stroke:#F9A825,color:#5D4037
classDef smithy fill:#FFF3E0,stroke:#FB8C00,color:#E65100
classDef core fill:#E3F2FD,stroke:#42A5F5,color:#0D47A1
classDef blueprint fill:#F3E5F5,stroke:#AB47BC,color:#4A148C
classDef foundation fill:#FAFAFA,stroke:#BDBDBD,color:#424242
```
**ํต์ฌ ์์น**: ๊ตฌ์กฐ(Structure)์ ์คํ์ผ(Style)์ ๋ถ๋ฆฌ โ HTML + CSS์ ๊ฐ์ ํจํด์
๋๋ค.
Core๋ ์คํ์ผ _์ฐธ์กฐ_(index)๋ง ๊ฐ์ง๊ณ , Blueprint๋ ์คํ์ผ _์ ์_(ํฐํธ, ํฌ๊ธฐ, ์์)๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
Smithy compiler๊ฐ Core + Blueprint๋ฅผ ํฉ์ณ ์ต์ข
ํฌ๋งท์ ์์ฑํฉ๋๋ค.
## ํ๋ก์ ํธ ํํฉ
| ์ด LOC | ~52,700 |
| ํ
์คํธ | 1,602๊ฐ (cargo-nextest) |
| ์์ค ํ์ผ | 116 .rs |
| Crate ์ | 10๊ฐ (7๊ฐ ๋ฐฐํฌ) |
| ์ปค๋ฒ๋ฆฌ์ง | 92.65% |
| Clippy ๊ฒฝ๊ณ | 0 |
| Unsafe ์ฝ๋ | 0 |
## ๊ฐ๋ฐ
### ํ์ ์๊ตฌ์ฌํญ
- Rust 1.88+ (MSRV)
- (๊ถ์ฅ) [cargo-nextest](https://nexte.st/) โ ๋ณ๋ ฌ ํ
์คํธ ์คํ
- (์ ํ) [pre-commit](https://pre-commit.com/) โ git hook ์๋ํ
### MSRV ์ ์ฑ
- ํ์ฌ MSRV๋ **Rust 1.88**์
๋๋ค.
- HwpForge๋ **stable์์ 4 ๋ฆด๋ฆฌ์ค ๋ค์ฒ์ง ๋ฒ์ **์ ๊ธฐ๋ณธ MSRV ์ ์ฑ
์ผ๋ก ์ ์งํฉ๋๋ค.
- `Cargo.toml`์ `rust-version`์ด ๋จ์ผ ์ง์ค์์ด๋ฉฐ, CI์ `Verify โบ MSRV` job์ด ์ด ๊ณ์ฝ์ ๊ฒ์ฆํฉ๋๋ค.
- MSRV ์ํฅ์ด ํ์ํ๋ฉด PR์์ ์ด์ ๋ฅผ ๋ช
์ํ๊ณ , `Cargo.toml`, CI, CHANGELOG๋ฅผ ํจ๊ป ๊ฐฑ์ ํฉ๋๋ค.
- ๊ฐ๋ฐ์ฉ ๊ธฐ๋ณธ ํด์ฒด์ธ์ ๋ ์ต์ ์ผ ์ ์์ต๋๋ค. ํธํ์ฑ ํ๋จ ๊ธฐ์ค์ ์ต์ stable์ด ์๋๋ผ **MSRV + CI ํต๊ณผ ์ฌ๋ถ**์
๋๋ค.
### โ๏ธ ๋ช
๋ น์ด
```bash
make ci # fmt + clippy + test + deny + lint (CI์ ๋์ผ)
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)
```
### ํ๋ก์ ํธ ๊ตฌ์กฐ
```
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 decoder (์์ )
โ โโโ hwpforge-bindings-py/ # Python bindings (์์ )
โ โโโ hwpforge-bindings-cli/ # CLI ๋๊ตฌ (hwpforge)
โ โโโ hwpforge-bindings-mcp/ # MCP Server (hwpforge-mcp)
โโโ tests/ # ํตํฉ ํ
์คํธ + golden fixture
โโโ examples/ # ๐ ์ฌ์ฉ ์์ + ์์ฑ๋ HWPX ํ์ผ
```
## ๊ธฐ์ฌ
๋ฒ๊ทธ ์์ , ํฌ๋งท ๋ฆฌ์์น, ํ
์คํธ ๋ณด๊ฐ, ๋ฌธ์ ๊ฐ์ ๋ชจ๋ ํ์ํฉ๋๋ค.
- ์์ ์ ๊ฐ์ด๋: [CONTRIBUTING.md](CONTRIBUTING.md)
- ํนํ ํ์ธํ ๊ฒ: release-plz๊ฐ ์ฐ๋ ์ปค๋ฐ prefix (`feat`, `fix`, `perf`, `refactor`)
- ํนํ ํ์ธํ ๊ฒ: MSRV ์ ์ฑ
๊ณผ dependency/MSRV ์์น ๊ธฐ์ค
- ํนํ ํ์ธํ ๊ฒ: ๋ฌธ์ ๋ณ๊ฒฝ ์ `mdbook build`์ markdown lint ๊ฒ์ฆ
- ํนํ ํ์ธํ ๊ฒ: CI required checks๋ฅผ ๊นจ์ง ์๋ ๋ฒ์์์์ ๋ณ๊ฒฝ ๋ถ๋ฆฌ
## ๋ก๋๋งต
### ์ถ์ ์์
- [ ] HWP5 ์ฝ๊ธฐ โ ๊ตฌํ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท(`.hwp`) ๋์ฝ๋
- [x] MCP ์๋ฒ โ Claude, Cursor ๋ฑ AI ๋๊ตฌ๊ฐ tool๋ก ์ง์ HWPX ์์ฑ (5๊ฐ ๋๊ตฌ)
- [x] CLI ๋๊ตฌ โ `hwpforge convert doc.md doc.hwpx` ํ ์ค ๋ณํ (7๊ฐ ๋ช
๋ น์ด)
- [ ] HWPX ์์ ์ง์ โ ์์ ์ปจํธ๋กค, ๋ณ๊ฒฝ ์ถ์ , OLE ๊ฐ์ฒด
- [ ] Python ๋ฐ์ธ๋ฉ โ `pip install hwpforge`๋ก ์ค์น, PyPI ๋ฐฐํฌ
## ๋ผ์ด์ ์ค
๋ค์ ์ค ํ๋๋ฅผ ์ ํํ ์ ์์ต๋๋ค:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
- MIT license ([LICENSE-MIT](LICENSE-MIT))
## Acknowledgements
HwpForge๋ ๊ฑฐ์ธ๋ค์ ์ด๊นจ ์์ ์ ์์ต๋๋ค.
- **[Hancom](https://www.hancom.com)** โ HWPX ํฌ๋งท์ ๊ณต๊ฐ ๋ฌธ์์ [KS X 6101 (OWPML)](https://www.kssn.net/) ๊ตญ๊ฐ ํ์ค์ด ์์๋ค๋ฉด ์ด ํ๋ก์ ํธ๋ ์์์กฐ์ฐจ ํ ์ ์์์ต๋๋ค. ํฌ๋งท์ ๊ณต๊ฐํด ์ฃผ์ Hancom์ ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- **[openhwp](https://github.com/openhwp/openhwp)** โ Rust๋ก HWP/HWPX๋ฅผ ๋ค๋ฃจ๋ IR ๊ธฐ๋ฐ ์ํคํ
์ฒ ์ค๊ณ์์ ํฐ ์๊ฐ์ ๋ฐ์์ต๋๋ค. HwpForge์ Core ๋ ์ด์ด๊ฐ ์กด์ฌํ ์ ์์๋ ๊ฒ์ openhwp์ด ๋จผ์ ๊ทธ ๊ธธ์ ๊ฑธ์๊ธฐ ๋๋ฌธ์
๋๋ค.
- **[hwpxlib](https://github.com/neolord0/hwpxlib)** โ Java๋ก ์์ฑ๋ ๊ฐ์ฅ ์ฑ์ํ HWPX ๊ตฌํ์ฒด์
๋๋ค. ์คํ๊ณผ ์ค์ ๋์์ ์ฐจ์ด๋ฅผ ํ์
ํ๋ ๋ฐ ๊ฒฐ์ ์ ์ธ ์ฐธ๊ณ ๊ฐ ๋์์ต๋๋ค.
- **[hwp.js](https://github.com/hahnlee/hwp.js)** โ HWP5 ํฌ๋งท์ quirks์ edge case๋ฅผ ๊ผผ๊ผผํ ๋ฌธ์ํํ ํ๋ก์ ํธ์
๋๋ค. ๋ฐ์ด๋๋ฆฌ ํฌ๋งท์ ์ด๋์ด ๊ตฌ์์ ๋ฐํ ์ค ๋๋ถ์ ์ํ์ฐฉ์ค๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์์์ต๋๋ค.
- **[hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** โ Hancom์ด ์ง์ ๊ณต๊ฐํ C++ OWPML ๋ชจ๋ธ ๊ตฌํ์ฒด๋ก, ์คํค๋ง ํด์์ ์ต์ข
๊ธฐ์ค์ผ๋ก ์ผ์์ต๋๋ค.
- **Rust ์ํ๊ณ** โ [serde](https://serde.rs), [quick-xml](https://github.com/tafia/quick-xml), [pulldown-cmark](https://github.com/pulldown-cmark/pulldown-cmark), [zip](https://github.com/zip-rs/zip2) ๋ฑ ๋ฐ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ๋๋ถ์ HwpForge ์ ์ฒด๋ฅผ zero unsafe ์์ Rust๋ก ๊ตฌํํ ์ ์์์ต๋๋ค. Rust ์ปค๋ฎค๋ํฐ์ Ferris ๐ฆ์๊ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- **[Claude](https://claude.ai) by [Anthropic](https://www.anthropic.com)** โ HwpForge์ ์ค๊ณ, ๊ตฌํ, ํ
์คํธ, ๋ฌธ์ํ ์ ๊ณผ์ ์์ Claude Code๊ฐ ๊ฐ๋ฐ ํํธ๋๋ก ํจ๊ปํ์ต๋๋ค. LLM-first๋ฅผ ํ๋ฐฉํ๋ ํ๋ก์ ํธ๋ต๊ฒ, AI์ ์ฌ๋์ด ํ์
ํ์ฌ ๋ง๋ค์ด๋ธ ๊ฒฐ๊ณผ๋ฌผ์
๋๋ค.
---
<div align="center">
<img src="https://raw.githubusercontent.com/ai-screams/HwpForge/main/assets/mascot-main.png" width="260" alt="์ ๋ถ๋ฆฌ Anvilscribe (SoeBuri Anvilscribe)">
<br/><br/>
<strong>์ ๋ถ๋ฆฌ Anvilscribe (SoeBuri Anvilscribe)</strong><br/>
<em>ํ์ปด ๋ฌธ์๋ฅผ ๋ถ์ ๋ฌ๊ตฌ์ด ๋จ๋จํ๊ฒ ๋ฒผ๋ ค๋ด๋ ๋์ฅ์ฅ์ด ์ค๋ฆฌ๋๊ตฌ๋ฆฌ ๐ฅ</em>
<br/><br/>
<a href="https://buymeacoffee.com/pignuante">
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me a Coffee" height="50" width="217">
</a>
</div>