hwp-ingest
Rust-backed Python package for Korean HWP/HWPX ingestion.
Convert HWP documents to PDF or page-level SVG artifacts through a typed Python facade.
hwp-ingestuses a vendoredrhwpsubset as its current HWP parsing, layout, and rendering backend. The vendored backend can carry hwp-ingest-local compatibility patches, while product-facing conversion policy stays in the hwp-ingest Rust/Python layers.
Features
- Convert HWP files to PDF bytes or PDF files.
- Render page-level SVG artifacts for inspection and downstream pipelines.
- Read basic document metadata such as renderable page count.
- Keep conversion orchestration in Rust while Python handles paths, overwrite policy, and typed facade ergonomics.
Install from PyPI:
uv pip install hwp-ingest
# or
python -m pip install hwp-ingest
Runtime PDF dependency
PDF conversion requires the rsvg-convert executable from librsvg at runtime.
The Python wheel does not bundle this executable.
SVG output does not require rsvg-convert; that executable is only needed for PDF conversion.
Check availability:
rsvg-convert --version
Install examples:
# Debian/Ubuntu
sudo apt-get install librsvg2-bin
# Fedora/RHEL
sudo dnf install librsvg2-tools
# Arch Linux
sudo pacman -S librsvg
# macOS
brew install librsvg
On Windows, install librsvg/rsvg-convert and ensure rsvg-convert.exe is on
PATH. If rsvg-convert is missing, PDF conversion raises
hwp_ingest.HwpIngestError with a clear message. svg2pdf is not used as an
automatic fallback because it is known to drop clipped table text in some HWP
documents.
Font fallback and PUA glyphs
hwp-ingest does not bundle Hancom or Microsoft fonts. PDF conversion asks
rsvg-convert/fontconfig to resolve the SVG font-family chain from fonts
installed on the system. If a document contains visible Private Use Area (PUA)
characters, such as Hancom symbol slots, a generic Unicode font is not enough:
PUA codepoints do not define a standard glyph shape. They render correctly only
when the owning document font is installed, or when the project adds a
source-backed codepoint mapping for that specific symbol set.
The current renderer preserves the document font name first, then appends Korean-compatible fallback families:
| Text family in the document | Fallback order |
|---|---|
| Batang/Myeongjo/serif text | Document font → Batang/바탕 → Nanum Myeongjo → AppleMyungjo → Noto Serif KR / Noto Serif CJK KR → HCR Batang Ext-B / 함초롬바탕 확장B → HCR Batang Ext / 함초롬바탕 확장 → HCR Batang / 함초롬바탕 → Source Han Serif K Old Hangul → serif |
| Dotum/Gothic/Gulim/sans-serif text | Document font → Malgun Gothic / 맑은 고딕 → Apple SD Gothic Neo → Noto Sans KR ExtraLight → Noto Sans KR → Pretendard → HCR Batang Ext-B / 함초롬바탕 확장B → HCR Batang Ext / 함초롬바탕 확장 → HCR Batang / 함초롬바탕 → Source Han Serif K Old Hangul → sans-serif |
For Hancom-origin documents with PUA symbols, install the matching licensed font on the machine running conversion:
| Document/font origin | Families to check first |
|---|---|
| Hancom/HCR body or PUA symbols | HCR Batang / 함초롬바탕, HCR Dotum / 함초롬돋움 |
| Windows-origin documents | Batang, Dotum, Gulim, Gungsuh, Malgun Gothic |
| Older HY/Hanyang documents | HY울릉도L, HY헤드라인M, HY견고딕, HY그래픽, HY견명조, HY신명조 |
Linux/fontconfig check:
fc-match "HCR Batang"
fc-match "함초롬바탕"
fc-match "HCR Dotum"
If these commands resolve to a fallback like Noto or DejaVu instead of the expected Hancom/HCR family, install the licensed TTF/OTF files into a system or user font directory and refresh fontconfig:
# user-local example
mkdir -p ~/.local/share/fonts/hancom
cp HCRBatang.ttf HCRBatang-Bold.ttf ~/.local/share/fonts/hancom/
fc-cache -f ~/.local/share/fonts/hancom
Do not commit restricted font files into this repository.
Quick start
Convert an HWP file to a PDF next to the input file:
from pathlib import Path
import hwp_ingest
input_path = Path("document.hwp")
output_path = hwp_ingest.to_pdf_file(input_path, overwrite=True)
print(output_path)
Convert one zero-based page to bytes:
from pathlib import Path
import hwp_ingest
pdf_bytes = hwp_ingest.to_pdf_bytes(
Path("document.hwp").read_bytes(),
page_index=0,
)
Path("page-1.pdf").write_bytes(pdf_bytes)
Render SVG page artifacts without the PDF backend:
from pathlib import Path
import hwp_ingest
svg_pages = hwp_ingest.to_svg_pages(Path("document.hwp").read_bytes())
Path("page-1.svg").write_bytes(svg_pages[0])
Read basic document metadata:
from pathlib import Path
import hwp_ingest
info = hwp_ingest.analyze_bytes(Path("document.hwp").read_bytes())
print(info.page_count)
Python API
The package is typed and ships py.typed plus checked-in .pyi stubs.
hwp_ingest.analyze_bytes(data: bytes) -> hwp_ingest.DocumentInfo
Returns a DocumentInfo object. DocumentInfo.page_count is the number of
renderable pages discovered in the HWP bytes.
hwp_ingest.to_pdf_bytes(
data: bytes,
*,
page_index: int | None = None,
) -> bytes
Returns PDF bytes. page_index is zero-based. Pass None to convert the full
document.
hwp_ingest.to_svg_pages(
data: bytes,
*,
page_index: int | None = None,
) -> list[bytes]
Returns one SVG document per selected page as bytes. page_index is
zero-based. Pass None to render all pages in document order. SVG output does
not require rsvg-convert.
hwp_ingest.to_pdf_file(
input_path: str | os.PathLike[str],
output_path: str | os.PathLike[str] | None = None,
*,
page_index: int | None = None,
overwrite: bool = False,
) -> pathlib.Path
Writes a PDF and returns the output path. If output_path is None, the output
uses the input path with a .pdf suffix. Existing output files are protected
unless overwrite=True.
hwp_ingest.to_svg_files(
input_path: str | os.PathLike[str],
output_dir: str | os.PathLike[str] | None = None,
*,
page_index: int | None = None,
overwrite: bool = False,
) -> list[pathlib.Path]
Writes one SVG file per selected page and returns paths in page order. If
output_dir is None, SVG files are written beside the input file. Generated
filenames are stable and use one-based visible page numbers:
<stem>.page-0001.svg, <stem>.page-0002.svg, and so on. Existing output
files are protected unless overwrite=True.
Errors
ValueError: negativepage_index, empty documents, or out-of-range pages.FileExistsError: output exists andoverwrite=False.FileNotFoundError: input file path does not exist.hwp_ingest.HwpIngestError: parse, render, PDF backend, or output write failures.
Local development
cargo test --workspace
uv run maturin develop
uv run pytest
Build distributable artifacts locally:
uv run maturin sdist --out dist
uv run --with ziglang maturin build --release --locked --out dist --compatibility pypi --zig
Notices
This project includes vendored portions of rhwp.
본 제품은 한글과컴퓨터의 HWP 문서 파일(.hwp) 공개 문서를 참고하여 개발하였습니다.
"한글", "한컴", "HWP", and "HWPX" are registered trademarks of Hancom Inc.
hwp-ingest is an independent open-source project and is not affiliated with,
sponsored by, or endorsed by Hancom Inc.
See NOTICE, THIRD_PARTY_LICENSES.md, and vendor/rhwp-subset/NOTICE for
vendored source attribution and license details.
Project direction
See docs/rhwp-python-binding-plan.md for batch engine, semantic output, vendor/upstream sync, and production pipeline direction.
Metadata
Release files for hwp-ingest 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hwp_ingest-0.1.1.tar.gz | 2.5 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| hwp_ingest-0.1.1-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| hwp_ingest-0.1.1-cp310-abi3-manylinux_2_28_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| hwp_ingest-0.1.1-cp310-abi3-manylinux_2_28_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| hwp_ingest-0.1.1-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| hwp_ingest-0.1.1-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 16.2 MB
Release files / hwp_ingest-0.1.1.tar.gz
| Download URL | hwp_ingest-0.1.1.tar.gz |
|---|---|
| Size | 2.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87a424c13a7d9e67c1a4cc0dba6925eb53a950479a78989bc78f1aed01cdc4bc
|
|
BLAKE2b-256 checksum How to use checksums |
f06b4d1aae017377accb88c84983157852f813de7e3af38e7d59288d469a7bb2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency logRelease files / hwp_ingest-0.1.1-cp310-abi3-win_amd64.whl
| Download URL | hwp_ingest-0.1.1-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 2.8 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
674b15133e67c26203857afb28c8efab1a4172a90d55637daabe0d9fdebb608b
|
|
BLAKE2b-256 checksum How to use checksums |
056f69939afbbd08ee195b2eeb1f172fd0e197f57c553d33db54de5a7ad6aa64
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency logRelease files / hwp_ingest-0.1.1-cp310-abi3-manylinux_2_28_x86_64.whl
| Download URL | hwp_ingest-0.1.1-cp310-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 3.0 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d94da40a840713fa79ddfed70c376d97810705070a4e217c3001070cd33877bb
|
|
BLAKE2b-256 checksum How to use checksums |
15b87547795834076a342eb1f989fec82779dcc8f47d5d2d0e5a0765ac03f974
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency logRelease files / hwp_ingest-0.1.1-cp310-abi3-manylinux_2_28_aarch64.whl
| Download URL | hwp_ingest-0.1.1-cp310-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
7d6303fab5df6d080b464e946d86f1f70cea049e5790e73f6c3d544e23ed430b
|
|
BLAKE2b-256 checksum How to use checksums |
83ac47e5ea379b55504496084a19840c44ff12514ac2a273e5f3dd611c30ac4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency logRelease files / hwp_ingest-0.1.1-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | hwp_ingest-0.1.1-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
1a9b249fd197d3f38343ff34cd25e8c0ebf31c2dad551dd5223cc5777ff7ac3f
|
|
BLAKE2b-256 checksum How to use checksums |
42ac52bf6124b724844af08cc9f007071fb9bde7dddbe6d771c9be0f264beeb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency logRelease files / hwp_ingest-0.1.1-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | hwp_ingest-0.1.1-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
5549e19f7a88fc8386493dd64e3533ec35892374e5262da030cb8a1551a23afd
|
|
BLAKE2b-256 checksum How to use checksums |
5206cd9a8d3febebedd25e8c7c9a45372156e8b1e7f568132e35b3a04365c643
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency log