Skip to main content

Read, edit & extract text from Korean HWP & HWPX (Hancom Office / 한글) files in pure Python — no Hancom, no Windows. Fill forms, insert seals/signatures, and feed Korean documents to LLM/RAG pipelines.

Project description

hwpkit — read, edit & extract Korean HWP / HWPX documents in pure Python

hwpkit

The pure-Python toolkit for Korean HWP & HWPX (Hancom Office) documents.
Read it, edit it, extract its text — no Hancom, no Windows, no COM automation.

PyPI Python versions CI License: MIT


Korean government, universities, courts, and most Korean enterprises run on HWP (한글, Hangul Word Processor) — the .hwp and .hwpx formats from Hancom Office. The rest of the world's tooling (python-docx, pdfplumber, unstructured, LibreOffice) can't read them, and the few that try need a Windows box with Hancom installed driving it over COM.

hwpkit is the missing piece. Pure Python, cross-platform, zero external apps — install it and start reading, editing, and extracting Korean documents in three lines.

pip install hwpkit[full]
from hwpkit import extract_text_from_file
print(extract_text_from_file("계약서.hwp"))      # …or .hwpx — auto-detected

That's it. No Hancom license. No Windows. No headless office server.

Why hwpkit

  • 📄 Both formats, one API. Binary .hwp (HWP 5.0) and XML .hwpx (OWPML) — extract, edit, and insert images through the same calls. open_document() hands you the same object either way, so you learn it once and never branch on format.
  • 🐍 Pure Python, runs anywhere. Linux, macOS, Windows, containers, Lambda. No Hancom, no pywin32, no COM, no LibreOffice subprocess.
  • 🤖 Built for LLM / RAG. Clean Korean text out of any .hwp/.hwpx, ready to chunk and embed. The preprocessing step your retrieval pipeline was missing.
  • ✍️ Edit without corrupting. Fill government & university forms, tick checkboxes, rewrite cells — and hwpkit rewrites the binary container while preserving the directory-tree structure Hancom validates on open. (This is the genuinely hard part. We solved it. See the gotchas.)
  • 🖋️ Insert seals & signatures. Stamp a 도장/직인/서명 image into a form cell — into both .hwp and .hwpx.
  • 🪶 Tiny core. The base install is just olefile; lxml and Pillow are optional extras, loaded lazily only when you use .hwpx or images.
  • ⚖️ MIT licensed. Use it anywhere, commercially or not.

Install

Python 3.9+.

pip install hwpkit            # core: binary .hwp read + edit
pip install hwpkit[hwpx]      # + .hwpx (OWPML) support  (adds lxml)
pip install hwpkit[image]     # + seal / signature insertion  (adds Pillow)
pip install hwpkit[full]      # everything

Quickstart

Extract text from any Korean document — for RAG, search, or an LLM:

from hwpkit import extract_text_from_file

text = extract_text_from_file("notice.hwpx")   # .hwp or .hwpx, auto-detected
# from the shell — works on both formats
hwpkit-text contract.hwp | llm "Summarize the key obligations in Korean"

Fill a form, tick a checkbox, stamp a seal — one API, either format:

from hwpkit import open_document

doc = open_document("template.hwp")            # or "template.hwpx" — auto-detected
print(doc.describe())                           # list paragraphs to find field indices
doc.inject_text(24, "홍길동")                    # fill an empty cell
doc.swap_in_para_text(40, "□ 석사", "☑ 석사")    # tick a checkbox
doc.replace_text(75, "2026. 05. 19.")           # overwrite a cell
doc.place_image(42, "seal.png", width_mm=30)    # stamp a 도장 / signature
doc.save("out.hwp")

open_document returns an HwpFile or HwpxFile depending on the file — both expose the same methods, so your code never branches on format.

Prefer plain functions? The originals are still there: fill_hwp(...), inject_text(records, i, text), and the file-to-file place_image("in.hwp", "out.hwp", "seal.png", paragraph_index=42).

Find which paragraph is which field:

hwpkit-inspect template.hwp        # one line per record, with a text preview

Built for Korean RAG & LLM pipelines

Korean enterprises ship contracts, policies, regulations, government notices, court filings, internal memos, and academic papers as .hwp / .hwpx. If your retrieval pipeline can't read HWP, it simply can't index Korean enterprise data — and the standard stack (pdfplumber, python-docx, unstructured) doesn't cover it.

hwpkit is a clean, dependency-light text source you can plug into anything — no LLM SDK required:

import glob
from hwpkit import extract_text_from_file

for path in glob.glob("corpus/**/*.hwp*", recursive=True):   # .hwp and .hwpx
    vector_db.add(doc_id=path, content=extract_text_from_file(path))

Extraction strips inline controls (tables, images, footnote refs, autonumbers, page-number controls, bookmarks) and returns clean, one-line-per-paragraph text — table-cell content included — ready for chunkers, embeddings, or any context window.

Who uses this

  • AI / RAG engineers indexing Korean documents into vector DBs.
  • Gov-tech & RPA teams auto-filling 관공서·대학 forms at scale.
  • Data engineers migrating HWP archives to text / structured data.
  • Anyone who needs to edit a .hwp without clicking through Hancom.

hwpkit vs the alternatives

pyhwp pyhwpx olefile hwpkit
Pure Python (no Hancom / no Windows) (needs Hancom + COM)
Extract text — .hwp
Extract text — .hwpx
Edit text without corrupting the file (via Hancom)
Rewrite a stream that grew / shrank n/a
Insert an image (seal / signature) (via Hancom)
One API across .hwp and .hwpx
Runs in CI / Linux / containers

hwpkit is the only option that does all of it in portable, pure Python — no Hancom, no Windows, no COM bridge.

How it works (for the curious)

Under the hood, .hwp is a Microsoft Compound File Binary (MS-CFB) container of deflate-compressed record streams; .hwpx is a ZIP of OWPML XML. olefile can only rewrite a stream if its byte length is unchanged — almost never true when you inject Korean text. hwpkit rewrites the whole container while preserving the red-black-tree directory topology Hancom validates on open, and patches the record-level layout caches so text re-flows correctly.

  • docs/OBJECT_MODEL.md — how .hwp records map onto their .hwpx (OWPML) twins; the shared HWPUNIT geometry.
  • docs/RECORD_FORMAT.md — the binary record format.
  • docs/GOTCHAS.md — the traps that take a week to find the first time (layout cache, 7-slot CharShape, RB-tree validation, image sizing). Read this if Hancom is rejecting your output.

For semantic HWP → XML (OWPML) conversion, see pyhwp.

Contributing

Issues, ideas, and PRs welcome. If hwpkit saved you from a Windows VM and a Hancom license, a ⭐ on GitHub helps others find it.

License

MIT — see LICENSE.


Made by Ebenworks

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hwpkit-1.0.0.tar.gz (44.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hwpkit-1.0.0-py3-none-any.whl (34.5 kB view details)

Uploaded Python 3

File details

Details for the file hwpkit-1.0.0.tar.gz.

File metadata

  • Download URL: hwpkit-1.0.0.tar.gz
  • Upload date:
  • Size: 44.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hwpkit-1.0.0.tar.gz
Algorithm Hash digest
SHA256 608c26b89af82ee7dfd69f234924593bffd9da189489ae6d0a04eb5417b14828
MD5 4ed3a22cf59338cfad9c97ed858d91af
BLAKE2b-256 d974404b37530f1fc5f146cbf49f127f3b697423568610b3f14346be853ab9bd

See more details on using hashes here.

Provenance

The following attestation bundles were made for hwpkit-1.0.0.tar.gz:

Publisher: publish.yml on psychofict/hwpkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hwpkit-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: hwpkit-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 34.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hwpkit-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1fa4187caed49e1a932f9820ef56dd5ed5f20eca02251bef3226388384eee17a
MD5 970837ce0b4bf75a18592253f7de9bcd
BLAKE2b-256 ea0bd5543f752fc75bd169cfd3fce52349a9d16c401d44d676ab348b421d56c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for hwpkit-1.0.0-py3-none-any.whl:

Publisher: publish.yml on psychofict/hwpkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page