hwpforge
Tongs — Read, edit and generate Korean HWP/HWPX documents from Python.
본 제품은 한컴의 HWP 문서 파일(.hwp) 공개 문서를 참고하여 개발하였습니다. (Developed with reference to Hancom's public HWP file format documents: https://www.hancom.com/etc/hwpDownload.do.)
hwpforge wraps the HwpForge Rust library: a
document model for the HWPX (OWPML) format, an HWP5 reader, a Markdown bridge and a PDF
renderer. The Python package is a thin layer over that library, so it performs the same
operations, with the same meaning, as the command line tool and the MCP server. What each
frontend calls them differs: method names, argument spellings and some failure codes are
Python's own public contract, and the other two keep theirs.
What it reads and writes
Reads .hwpx, and .hwp (HWP5) through hwpforge.convert_hwp5. That conversion is one
way: the result is an HWPX document, and the original .hwp is never written back.
Writes .hwpx only. There is no .hwp output, and Hancom Office opens .hwpx natively.
Documents also export to Markdown, JSON and PDF. A PDF needs the document's own fonts present
on the host and named through font_dirs or discovery: by default a face that cannot be
resolved fails the render rather than being guessed at. to_pdf(degraded=True) relaxes that
and renders the missing face with a fallback, which changes how the page looks.
Rendering replays a layout stored in the document, so to_pdf needs a document that carries
one. Two do: an HWPX Hancom saved, and an HWPX converted from HWP5, which carries the layout
across by default — hwpforge.convert_hwp5(data).document.to_pdf(...). A document
this library generated from Markdown or JSON carries no layout and is refused with
PDF_RENDER_FAILED.
A paragraph whose line starts cannot be carried over exactly loses its layout and is reported
as LAYOUT_CACHE_DROPPED; convert_hwp5(data, carry_layout_cache=False) leaves the layout out.
Install
pip install hwpforge
With uv: uv add hwpforge in a project, or uv pip install hwpforge in an environment.
Pin with ~= rather than ==. A Python-only fix ships as X.Y.Z.N, and an exact pin never receives it.
What is published
| Artifact | Platform | Needs |
|---|---|---|
cp39-abi3-manylinux_2_28_x86_64 |
Linux x86_64 | glibc 2.28 or newer |
cp39-abi3-manylinux_2_28_aarch64 |
Linux aarch64 | glibc 2.28 or newer |
cp39-abi3-macosx_11_0_arm64 |
macOS arm64 | macOS 11 or newer |
cp39-abi3-macosx_10_12_x86_64 |
macOS x86_64 | macOS 10.12 or newer |
cp39-abi3-win_amd64 |
Windows x64 | — |
hwpforge-<version>.tar.gz |
source distribution | Rust 1.92 and maturin |
One abi3 wheel per platform covers CPython 3.9 and newer. Free-threaded builds are not
supported, because the stable ABI does not cover them.
Hosts without an index
The wheel is a zip file and the package declares no runtime dependencies, so it can be unpacked and imported without pip. The requirement is glibc 2.28 or newer, not a particular distribution — on any Linux x86_64 host with CPython 3.9+ (Debian 10/11/12, Ubuntu 20.04 and later; the file name below is the x86_64 wheel):
python3 -c "import zipfile; zipfile.ZipFile('hwpforge-<version>-cp39-abi3-manylinux_2_28_x86_64.whl').extractall('/opt/hf')"
PYTHONPATH=/opt/hf python3 -c "import hwpforge; print(hwpforge.__version__)"
GitHub Releases are immutable once published, so wheels are never attached there after the
fact. Every wheel and the source distribution instead live on PyPI's
files page, with the same bytes and sha256 that
pip sees, so an air-gapped host can download a file straight from that page and unpack it
the same way as above. A Python-only fix, versioned X.Y.Z.N, is published to PyPI alone and
has no Release of its own.
Example
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 is immutable. Every editing method returns a new document in a result object
alongside the operation's report, and leaves the receiver unchanged, so the re-assignment
above is not optional.
Operations
A failure raises HwpForgeError, whose code is the stable string to branch on. The codes
below are the ones each operation raises for its own characteristic failure; any operation
that has to decode the document first can also raise DECODE_FAILED.
Document methods
| Method | Returns | Characteristic failures |
|---|---|---|
inspect(*, styles=False) |
InspectReport |
DECODE_FAILED |
outline() |
OutlineReport |
DECODE_FAILED |
fields() |
FieldsReport |
DECODE_FAILED |
validate() |
ValidateReport |
DECODE_FAILED (an invalid document reports, not raises) |
read(*, section, paras, table, field) |
ReadReport |
READ_TARGET_REQUIRED, READ_PARAS_INVALID, READ_PARA_RANGE_INVALID, READ_TABLE_OUT_OF_RANGE |
diff(revised) |
DiffReport |
DECODE_FAILED |
stamp_plan() |
StampPlanReport |
DECODE_FAILED |
to_json(*, styles=True) |
TextResult[ToJsonReport] |
DECODE_FAILED |
export_section(*, section, styles=True) |
TextResult[ExportSectionReport] |
SECTION_OUT_OF_RANGE |
to_md(*, mode="styled") |
TextResult[ToMdReport] |
ENCODE_FAILED (mode="lossless": see the note below) |
to_pdf(*, font_dirs, discovery, degraded, partial_cache_reject) |
BytesResult[ToPdfReport] |
PDF_RENDER_FAILED |
fill(values) |
DocumentResult[FillReport] |
FIELD_NOT_FOUND, FIELD_NOT_FILLABLE, EMPTY_FIELD_VALUE |
set_cell(*, table, at, right_of, below, text, specs) |
DocumentResult[SetCellReport] |
TABLE_NOT_FOUND, CELL_NOT_FOUND, INPUT_ENTRIES_NOT_CARRIED |
patch(*, section, patch) |
DocumentResult[PatchReport] |
JSON_PARSE_FAILED, PATCH_FAILED |
insert_para(*, section, anchor, text, before=False) |
DocumentResult[StructuralReport] |
PARAGRAPH_OUT_OF_RANGE, INPUT_ENTRIES_NOT_CARRIED |
delete_para(*, section, indexes) |
DocumentResult[StructuralReport] |
PARAGRAPH_OUT_OF_RANGE, INPUT_ENTRIES_NOT_CARRIED |
stamp(request, *, manifest=True) |
DocumentResult[StampReport] |
INPUT_ENTRIES_NOT_CARRIED, ENCODE_SEMANTIC_LOSS |
restyle(*, preset) |
DocumentResult[RestyleReport] |
PRESET_NOT_FOUND, ENCODE_SEMANTIC_LOSS |
to_md(mode="lossless") keeps what the preservation scope lists, raises ENCODE_FAILED for
an object the scope lists as refused, and may leave out anything else (paragraph styles,
headers and footers …) without an error — the scope is the "Lossless 보존 범위" section of the
Markdown bridge guide.
Constructors and accessors beside these: Document.open(path) (bounded at 100 MB, raising
INPUT_TOO_LARGE beyond it), Document.from_bytes(data) (no bound — the caller already holds
the bytes), to_bytes(), save(path), and len() / == / hash() over the bytes.
set_cell, insert_para, delete_para and stamp re-encode the whole package, so they
refuse a document Hancom saved with INPUT_ENTRIES_NOT_CARRIED or
INPUT_NOT_ROUNDTRIP_SAFE rather than drop the entries it carries. fill and patch work on
such a document.
Module functions
| Function | Returns | Characteristic failures |
|---|---|---|
convert_md(text, *, preset="default", base_dir=None) |
DocumentResult[ConvertMdReport] |
PRESET_NOT_FOUND |
from_json(text, *, base=None) |
DocumentResult[EncodeReport] |
JSON_PARSE_FAILED |
convert_hwp5(data, *, carry_layout_cache=True) |
DocumentResult[ConvertHwp5Report] |
HWP5_DECODE_FAILED |
templates() |
TemplatesReport |
— |
schema(*, kind="document") |
SchemaReport |
— |
Every report but templates() and schema() carries a warnings key, which is always
present and worth reading before saving the result.
Documentation
- Guide: https://ai-scream.ai/HwpForge/guide/python.html
- Project: https://github.com/ai-screams/HwpForge
License
MIT OR Apache-2.0.
Metadata
Release files for hwpforge 0.17.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| hwpforge-0.17.0.tar.gz | 4.0 MB | Without provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| hwpforge-0.17.0-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| hwpforge-0.17.0-cp39-abi3-manylinux_2_28_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| hwpforge-0.17.0-cp39-abi3-manylinux_2_28_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| hwpforge-0.17.0-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| hwpforge-0.17.0-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 37.8 MB
Release files / hwpforge-0.17.0.tar.gz
| Download URL | hwpforge-0.17.0.tar.gz |
|---|---|
| Size | 4.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
648249c6ec20b53dd3f9d080f846a8ab805f1b6bffa96acb36031da0db593d4f
|
|
BLAKE2b-256 checksum How to use checksums |
7ab8b3ed5f887e91987bbf41b853d10b17709f5322b89db4707910e874ae667d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / hwpforge-0.17.0-cp39-abi3-win_amd64.whl
| Download URL | hwpforge-0.17.0-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 7.3 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
84d0f44c60f4203453072ab1dbb86f8c3c379fcc57ad5834faeae88fbd7ca8e8
|
|
BLAKE2b-256 checksum How to use checksums |
0db5651c2be430c4baf12104e8c0463154b25b5c5003f62ce8f7553be2d0c7be
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / hwpforge-0.17.0-cp39-abi3-manylinux_2_28_x86_64.whl
| Download URL | hwpforge-0.17.0-cp39-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 7.0 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
23e38285adb3420e34107711e7dd8590ae9176423643ffceded3b970f2fd8670
|
|
BLAKE2b-256 checksum How to use checksums |
6516a72db1b6a5634e5bdae1db2cd41c5aeae23d57491267a8ec8a6f83486dd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / hwpforge-0.17.0-cp39-abi3-manylinux_2_28_aarch64.whl
| Download URL | hwpforge-0.17.0-cp39-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 6.5 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
a223f17f3c53cc7e9bcbcd82e53d250243a4fdd7c69469deecf07433a9e4b4c6
|
|
BLAKE2b-256 checksum How to use checksums |
844cebd3cf6f13045475080683ea54e5034885fcfcda1892b8395940ce599d88
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / hwpforge-0.17.0-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | hwpforge-0.17.0-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 6.3 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
64b700cf72f34917e9b548ac81c931f288de388f9adccee71a08fdccc2ca9a0c
|
|
BLAKE2b-256 checksum How to use checksums |
f0cb8f4f2b1d1b18daf156e5eac03bf1cb8c42b300e9f40445099874879744e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / hwpforge-0.17.0-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | hwpforge-0.17.0-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 6.7 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
3d00f59be3f5ad29ddb61dfb9bc57bafb6caaa515660fa28fad21f2b255d4881
|
|
BLAKE2b-256 checksum How to use checksums |
e956014604553341ef025b81877c4b8a22aef37a3dd700004125f53989cfb5ef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for hwpforge 0.17.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.