Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
</p>
</p>

<p align="center">한국어 | <a href="README_EN.md">English</a></p>

---

## 🧩 HWPX Stack (3종)
Expand Down Expand Up @@ -174,6 +176,7 @@ hwpx-analyze-template 보고서.hwpx
- **[🚀 빠른 시작](docs/quickstart.md)** · **[📚 사용 가이드](docs/usage.md)** — 첫 파일 열기부터 문단·표·메모·섹션 편집, 텍스트 추출·검증까지
- **[💡 예제 모음](docs/examples.md)** · [`examples/`](examples/) — `build_release_checklist.py`(메모·스타일 편집 HWPX 생성), `extract_text.py`(CLI 텍스트 추출), `find_objects.py`(OWPML 노드 추적) 등
- **[📐 스키마 개요](docs/schema-overview.md)** · **[🔧 설치 검증](docs/installation.md)**
- **[🔬 HWPX 내부 실전 가이드](docs/internals/)** — 실제 한/글 동작에서 확인된 HWPUNIT·조판 캐시·목차 필드·OPC 재패킹·메모·오라클 한계
- **[📖 전체 문서 (Sphinx)](https://airmang.github.io/python-hwpx/)** — API 레퍼런스·50+ 실전 패턴·FAQ
- **[📝 CHANGELOG](CHANGELOG.md)** · **[🤝 CONTRIBUTING](CONTRIBUTING.md)** · **[👥 CONTRIBUTORS](CONTRIBUTORS.md)**

Expand Down
217 changes: 217 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<p align="center">
<h1 align="center">python-hwpx</h1>
<p align="center">
<strong>Read, edit, generate, and structurally validate HWPX documents in Python — without Hancom Office.</strong>
</p>
<p align="center">
<a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/v/python-hwpx?color=blue&label=PyPI" alt="PyPI"></a>
<a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/pyversions/python-hwpx" alt="Python"></a>
<a href="https://github.com/airmang/python-hwpx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
<a href="https://airmang.github.io/python-hwpx/"><img src="https://img.shields.io/badge/docs-Sphinx-8CA1AF" alt="Docs"></a>
</p>
</p>

<p align="center"><a href="README.md">한국어</a> | English</p>

---

## 🧩 HWPX Stack (3 components)

| Layer | Repo | Role |
|---|---|---|
| 📦 Library | **[`python-hwpx`](https://github.com/airmang/python-hwpx)** | Pure-Python HWPX parsing / editing / generation core |
| 🔌 MCP server | [`hwpx-mcp-server`](https://github.com/airmang/hwpx-mcp-server) | Manipulate HWPX from MCP clients (Claude Desktop, VS Code, etc.) |
| 🎯 Agent skill | [`hwpx-plugin`](https://github.com/airmang/hwpx-plugins) | First-party plugin / skill bundle that lets agents use HWPX directly |

`python-hwpx` is the core library providing HWPX parsing, editing, and generation,
while `hwpx-mcp-server` and `hwpx-plugin` are first-party integration components
maintained directly by the same project.
"First-party" refers to the project's maintenance relationship; it does not imply
official certification by Hancom or any third party.

The current public PyPI release is `python-hwpx 3.6.0`. A plain
`pip install python-hwpx` installs this release.
The current package classifier is `Development Status :: 3 - Alpha`. This classifier
reflects the maturity of the API and product; it does not stand in for the public
version or the minimum compatible version of the plugin.

---

## We speak in measurements — Published Corpus

The outputs of this stack are verified by **exhaustive measurement against real
Hancom Office**, not by claims (frozen corpus N=497, 2026-07-19; details and caveats
in the
[measured corpus metrics](https://airmang.github.io/python-hwpx/corpus-metrics.html)):

- **Hancom open-acceptance rate 100%** (476/476 all-pass, lower bound ≥99.4%) · parsing 96.2%
- **Byte preservation of untouched regions 100%** (497/497, patch path) · **personal-info 0-leak**
- 416 render-verified cases + an honesty bucket (PDF export of tracked-change documents is refused by Hancom itself — published as a measured limitation)
- Form-fill differential is 49.2% on wild public forms — **we publish the low number as-is** and record it as remaining work

> These numbers are on the *output acceptance* axis (does real Hancom accept the files we produce).
> This is a different axis from document *parsing recall*, so do not compare it side by side with parser-project figures.

---

## Why python-hwpx

- **No Hancom Office required for core editing** — HWPX is a ZIP+XML (OWPML/OPC) structure, so pure Python reads and writes it anywhere: Windows, macOS, Linux, CI.
- **From reading to generation in one core** — text/format extraction, paragraph/table/form editing, new-document generation, and XSD schema validation are all handled by one API.
- **Agent- and automation-friendly** — `hwpx-mcp-server` and `hwpx-plugin`, maintained by the same project, connect to the core.

Document parsing, editing, and generation can be done in pure Python. However, to
assert final visual quality — page breaks, table overflow, font substitution, and so
on — you separately use a real Hancom render oracle as needed.

## Quick start

```bash
pip install python-hwpx # Python 3.10+ · lxml ≥ 4.9
```

```python
from hwpx import HwpxDocument

# Open an existing document → edit → save
doc = HwpxDocument.open("보고서.hwpx")
doc.add_paragraph("자동화로 추가한 문단입니다.")
doc.save_to_path("보고서-수정.hwpx")

# Create a new document
new = HwpxDocument.new()
new.add_paragraph("python-hwpx로 만든 새 문서")
new.save_to_path("새문서.hwpx")
```

> 💡 A context manager is also supported — resources are cleaned up automatically on leaving the `with` block:
> ```python
> with HwpxDocument.open("보고서.hwpx") as doc:
> doc.add_paragraph("자동으로 리소스가 정리됩니다.")
> doc.save_to_path("결과물.hwpx")
> ```

Once you have the `open`/`new` → `edit`/`extract` → `save_to_path` flow, you can expand into the rest as needed.

## What it does

### 🔍 Read · Extract
- Text/HTML/Markdown export — `export_text()` · `export_html()` · `export_markdown()`
- **Rich Markdown** — `export_rich_markdown()` preserves inline formatting (`**bold**` · `*italic*` · `~~strikethrough~~`), nested tables (colspan/rowspan safe), shape text, images, footnotes/endnotes, hyperlinks, and heading auto-detection (`#`/`##`)
- **Document ingest gateway** — `hwpx.ingest.DocumentIngestor` detects HWPX and normalizes it into rich Markdown plus section/table metadata
- `TextExtractor` / `ObjectFinder` — iterate sections/paragraphs, find objects by tag/attribute/XPath (`hp:tab` is preserved as `\t`, roundtrip-safe)

```python
doc = HwpxDocument.open("보고서.hwpx")
md = doc.export_rich_markdown(
image_dir="out/images", # extract BinData images to disk
image_ref_prefix="images/", # prefix for ![](images/...) paths in the markdown
detect_headings=True, # auto #/## based on Ⅰ./1. patterns
)
```

### ✏️ Edit
- Add/remove/format paragraphs, run-level bold/italic/underline/color
- Add/remove sections (`add_section(after=)` · `remove_section()`, manifest managed automatically)
- Create tables, cell text, merge/split, nested tables, image embedding, headers/footers, memos (anchor-based), footnotes/endnotes, bookmarks/hyperlinks, multi-column editing
- **Edit formatting of existing documents** — alignment · line spacing · indentation · paragraph spacing, paper · margins · orientation, page numbers, bullets/numbering
- **Style-based replacement** — filter runs by color · underline · `charPrIDRef` and replace selectively (`replace_text_in_runs` · `find_runs_by_style`)

```python
# Find and replace only red text
doc.replace_text_in_runs("임시", "확정", text_color="#FF0000")
```

### 🖊️ Form filling (byte-preserving)
- Query and format-preserving fill of click-here (누름틀) fields, label-based cell lookup (`find_cell_by_label`) · path-based fill (`fill_by_path`)
- **Byte-preserving structural editing** — cell filling / row·column·table delete·insert / column-width autofit / shrink-to-fit fonts performed without reassembling the document, preserving the form's formatting exactly. Untouched regions are left byte-for-byte intact by `hwpx.patch`, which splices section XML bytes

```python
doc = HwpxDocument.open("신청서.hwpx")
result = doc.fill_by_path({
"성명 > right": "홍길동",
"소속 > right": "플랫폼팀",
})
doc.save_to_path("신청서-작성완료.hwpx")
print(result["applied_count"], result["failed_count"])
```

### 🏗️ Generation · Official-document tools
- `hwpx.builder` — assembly-style generation of Section/Heading/Table/Image/Header + a hard-gated save report
- Official-document tools — `official_lint` (item-marker hierarchy · "끝." marker · attachments · date lint), approval-block presets
- `advanced_generators` — photo boards (image_grid) · meeting nameplates · table-based org charts
- `mail_merge` — bulk generation of N copies from template + data, table sum/average computation
- `doc_diff` — paragraph LCS diff · old/new comparison tables · reference-consistency lint
- `style_profile` — extract and apply reference-document profiles, template registry

### ✅ Validation · Safety · Low-level
- XSD schema + package structure validation — CLI `hwpx-validate` · `hwpx-validate-package`, `hwpx-analyze-template`
- `validate_editor_open_safety` — gate for save/pack/repair/builder output, returns `openSafety` evidence
- `hwpx.tools.fuzz` (seeded deterministic scenarios · triple oracle) · `hwpx.tools.layout_preview` (page-box approximation HTML/PNG, self-verifying) · `opc.security` (XML entity · ZIP compression-bomb guards)
- Directly manipulate OWPML schema ↔ Python objects via `hwpx.oxml` dataclasses, with automatic HWPML 2016→2011 namespace normalization

```bash
hwpx-validate-package 보고서.hwpx
hwpx-analyze-template 보고서.hwpx
```

> For the full list of features, classes, and methods, see the [usage guide](docs/usage.md) and the [API reference](https://airmang.github.io/python-hwpx/api_reference.html).

## Comparison with competing libraries

| | python-hwpx | pyhwpx | pyhwp |
|---|---|---|---|
| **Target format** | `.hwpx` (OWPML/OPC) | `.hwpx` | `.hwp` (v5 binary) |
| **Hancom install** | Not required | Required (Windows COM) | Not required |
| **Cross-platform** | ✅ Linux / macOS / Windows / CI | ❌ Windows only | ✅ |
| **Edit/generate API** | ✅ | ✅ (COM) | ❌ mostly read-only |
| **Schema validation** | ✅ | ❌ | ❌ |
| **AI agent integration (MCP)** | ✅ `hwpx-mcp-server` | ❌ | ❌ |

> HWP (v5 binary) files are not supported. Convert to HWPX in Hancom Office first.

## Known limitations

- `add_shape()` / `add_control()` do not generate every child element Hancom requires. When adding complex objects, verify by opening in Hancom.
- Image binary embedding is supported, but full automatic generation of the `<hp:pic>` element is not provided.
- Encryption/decryption of encrypted HWPX files is not supported.

## More

- **[🚀 Quick start](docs/quickstart.md)** · **[📚 Usage guide](docs/usage.md)** — from opening your first file to editing paragraphs/tables/memos/sections and text extraction/validation
- **[💡 Examples](docs/examples.md)** · [`examples/`](examples/) — `build_release_checklist.py` (generate HWPX with memo/style edits), `extract_text.py` (CLI text extraction), `find_objects.py` (trace OWPML nodes), and more
- **[📐 Schema overview](docs/schema-overview.md)** · **[🔧 Install verification](docs/installation.md)**
- **[🔬 HWPX internals field guide](docs/internals/)** — HWPUNIT · layout cache · TOC fields · OPC repacking · memos · oracle limits, verified against real Hancom behavior
- **[📖 Full docs (Sphinx)](https://airmang.github.io/python-hwpx/)** — API reference · 50+ practical patterns · FAQ
- **[📝 CHANGELOG](CHANGELOG.md)** · **[🤝 CONTRIBUTING](CONTRIBUTING.md)** · **[👥 CONTRIBUTORS](CONTRIBUTORS.md)**

## Contributing

Bug reports, feature proposals, and PRs are all welcome.

```bash
git clone https://github.com/airmang/python-hwpx.git
cd python-hwpx
pip install -e ".[dev]"
pytest
```

## Acknowledgements

This project is indebted to the following open standards and projects.

- **[OWPML — Open Word-processor Markup Language (KS X 6101)](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985)** — the Korean industrial standard HWPX is based on
- **[hancom-io/hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** — reference model for OWPML element structure · **[neolord0/hwpxlib](https://github.com/neolord0/hwpxlib)** — oracle sample corpus
- **[edwardkim/rhwp](https://github.com/edwardkim/rhwp)** — inspiration for idempotence / verification-gate design
- **범정부오피스 (Whole-of-government Office)** — ideas for official-document editing workflows

## License

Apache License 2.0. See LICENSE and NOTICE.

## Maintainer

Primary maintainer/contact: **Kohkyuhyun** ([@airmang](https://github.com/airmang))

- ✉️ [[email protected]](mailto:[email protected])
- 🐙 [@airmang](https://github.com/airmang)
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ examples

corpus-metrics
schema-overview
internals/README
faq
changelog
```
Expand Down
40 changes: 40 additions & 0 deletions docs/internals/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# HWPX/OWPML 내부 실전 가이드

```{toctree}
:maxdepth: 1
:hidden:

units
lineseg
toc-dirty
opc-packaging
memo-structure
oracle-limits
```

코드만으로는 알 수 없는, **실제 한/글 동작에서 확인된** HWPX/OWPML 실전 지식을 모은 문서입니다.

HWPX는 [KS X 6101 OWPML](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985) 표준 위에 세워진 ZIP+XML 포맷이지만, 표준 문서에도 한컴 공식 문서에도 적혀 있지 않은 동작이 많습니다. 파일을 만들어 실제 한/글에서 열어 보면서만 알 수 있는 것들 — 저장 시점에 캐시되는 조판 결과, 필드 재계산 트리거, OPC 재패킹 규칙, 메모의 참조 구조, 렌더 기반 검증의 한계 같은 것들이 그렇습니다.

이 가이드는 그런 지식을 정리해, 기여자가 "왜 이렇게 처리해야 하는가"를 코드와 함께 이해하도록 돕는 것을 목표로 합니다. 각 문서의 모든 주장은 이 저장소의 코드·테스트(`src/hwpx/...`, `tests/...`)나 공개 표준으로 검증할 수 있는 것만 담았고, 실측으로 관찰한 한/글 동작은 "실제 한/글에서 확인된 동작"으로 명시했습니다.

## 문서 목록

| 주제 | 문서 | 한 줄 요약 |
|---|---|---|
| 좌표 단위 | [units.md](units.md) | HWPUNIT 좌표계(1 inch = 7,200)와 라이브러리가 pt/mm/%를 내부에서 변환하는 이유 |
| 조판 캐시 | [lineseg.md](lineseg.md) | `hp:linesegarray`는 한/글이 저장 시점에 캐시한 줄나눔 결과 — 편집 후 stale로 남기면 글자 겹침 |
| 목차 필드 | [toc-dirty.md](toc-dirty.md) | `TABLEOFCONTENTS` 필드의 `dirty="1"`이 여는 시점 재계산을 트리거하는 메커니즘 |
| OPC 패키징 | [opc-packaging.md](opc-packaging.md) | `mimetype` 첫 엔트리·STORED 규칙, version.xml/manifest, 2016→2011 네임스페이스 정규화 |
| 메모 구조 | [memo-structure.md](memo-structure.md) | 메모 본문·MEMO 필드·`MemoShapeIDRef` 참조가 맞아야 한/글이 메모를 표시하는 이유 |
| 오라클 한계 | [oracle-limits.md](oracle-limits.md) | 한/글 export 기반 검증이 침묵 실패할 수 있는 경우와 픽셀 검증이 필요한 이유 |

## 읽는 순서

처음이라면 [units.md](units.md) → [opc-packaging.md](opc-packaging.md) → [lineseg.md](lineseg.md) 순서를 권합니다. 좌표 단위와 컨테이너 구조를 먼저 잡으면 나머지 주제의 코드가 훨씬 잘 읽힙니다.

## 전제

- 이 가이드는 HWPX(OWPML/OPC) 포맷을 다룹니다. HWP v5 바이너리 포맷은 대상이 아닙니다.
- 인용된 코드 경로는 이 저장소 기준입니다(`src/hwpx/...`). 버전에 따라 줄 번호는 달라질 수 있으니 함수·클래스 이름으로 찾으세요.
- "실제 한/글에서 확인된 동작"은 실제 한컴오피스로 파일을 열거나 저장해 관찰한 결과를 뜻합니다. 표준에 명문화되어 있지 않을 수 있습니다.
Loading