Skip to main content

Round-trip safe HWPX reader/editor/writer for Python

Project description

jakal-hwpx

파이썬에서 HWPX와 HWP 문서를 읽고, 수정하고, 검증하고, 변환하는 라이브러리입니다.

이 프로젝트는 한컴 GUI를 그대로 흉내 내는 편집기가 아닙니다. 대신 코드로 문서를 만들고, 구조화된 내용을 바꾸고, 저장 전후를 검증하는 데 초점을 둡니다. 템플릿 생성, 대량 치환, 표/필드/머리말/꼬리말 수정 같은 자동화 작업에 잘 맞습니다.

목차

설치

요구 사항:

  • Python 3.11 이상
  • Git LFS 불필요

PyPI에서 설치:

python -m pip install --upgrade pip
python -m pip install jakal-hwpx

로컬 체크아웃에서 설치:

python -m pip install --upgrade pip
python -m pip install .

개발 의존성까지 설치:

python -m pip install -e .[dev]

패키지 이름은 jakal-hwpx, import 경로는 jakal_hwpx입니다.

빠른 시작

새 문서를 만들고 HWPX와 HWP 둘 다 저장하려면 HancomDocument가 가장 단순합니다.

from jakal_hwpx import HancomDocument

doc = HancomDocument.blank()
doc.metadata.title = "분기 보고서"

doc.append_header("사내 배포용")
doc.append_paragraph("매출 요약")
doc.append_table(rows=2, cols=2, cell_texts=[["Q1", "Q2"], ["120", "135"]])
doc.append_equation("x+y", width=3200, height=1800)

doc.write_to_hwpx("build/report.hwpx")
doc.write_to_hwp("build/report.hwp")

HWPX만 직접 만질 거라면 HwpxDocument가 가장 짧습니다.

from jakal_hwpx import HwpxDocument

doc = HwpxDocument.blank()
doc.set_metadata(title="Hello")
doc.append_paragraph("Hello HWPX")
doc.save("build/hello.hwpx")

기존 HWP를 바로 수정하려면 HwpDocument를 쓰면 됩니다.

from jakal_hwpx import HwpDocument

doc = HwpDocument.open("input.hwp")
doc.append_paragraph("추가 문단")
doc.append_hyperlink("https://example.com", text="Example")
errors = doc.format_strict_lint_errors()
if errors:
    print(errors)
doc.strict_validate()
doc.save("build/edited.hwp")

이 라이브러리로 할 수 있는 일

  • HWPX 문서를 직접 열고 수정하고 저장
  • HWP 문서를 순수 Python으로 열고 수정하고 저장
  • HWP와 HWPX를 HancomDocument 기준으로 오가며 같은 편집 모델로 다루기
  • 문단, 스타일, 표, 그림, 필드, 각주/미주, 머리말/꼬리말, 섹션 설정 같은 구조화된 요소 수정
  • 저장 전후에 엄격 검증과 왕복 테스트로 결과 확인
  • 필요하면 저수준 API로 내려가 HWP binary record를 직접 조사

이 라이브러리는 특히 이런 작업에 잘 맞습니다.

  • 문서 템플릿 생성
  • 필드 채우기
  • 대량 텍스트 치환
  • 표/도형/그림 데이터 갱신
  • 문서 포맷 변환 자동화
  • 저장 후 깨지지 않는지 확인하는 배치 처리

어떤 객체를 쓰면 되는가

처음 고를 때는 아래 표만 보면 됩니다.

객체 언제 쓰면 좋은가 설명
HancomDocument HWP와 HWPX를 같은 코드로 다루고 싶을 때 가장 권장되는 공통 편집 모델
HwpxDocument 입력과 출력이 둘 다 HWPX일 때 가장 넓은 직접 편집 범위
HwpDocument 기존 HWP를 직접 수정하거나 HWP로 저장할 때 HWP 전용 객체 API
HwpHwpxBridge HWP/HWPX 사이를 오가며 한쪽 결과물을 재료로 다른 쪽을 만들 때 문서 전환을 묶는 도우미 객체
HwpBinaryDocument binary record, stream, docinfo를 직접 봐야 할 때 가장 저수준 API

짧게 말하면:

  • 처음 시작: HancomDocument
  • HWPX만 편집: HwpxDocument
  • HWP 직접 편집: HwpDocument
  • reverse engineering이나 디버깅: HwpBinaryDocument

지원 범위

이 README의 지원 표는 "느낌"이 아니라 아래 기준으로 판정합니다.

  • 직접 수정·재오픈: 공개 API로 생성 또는 수정한 뒤 저장하고, 다시 열어서 같은 의미로 복원되는 자동 테스트가 있습니다.
  • 왕복 보존: HWP -> HWPX, HWPX -> HWP, 또는 A -> HancomDocument -> B 경로에서 의미가 유지되는 자동 테스트가 있습니다.
  • strict lint: 저장 전 구조 검사를 하는 strict_lint_errors(), strict_lint_report(), format_strict_lint_errors(), strict_validate()가 연결돼 있습니다.
  • metadata-backed: target 포맷에 native 자리가 없어 라이브러리 metadata나 carrier를 함께 써서 의미를 보존합니다. 현재 memo/commentauthor/memo_id/anchor_id/order/visibleform.placeholder가 대표적인 예시입니다. memo carrier는 Text parameter를 pairing key로 저장하고, 없으면 기존 순서 기반으로 병합합니다.
  • 정규화: richer source를 target 포맷 제약에 맞게 일부 normalize해서 저장합니다.

표에 있는 항목만 "지원 약속"으로 봐야 합니다. 표에 없는 항목은 읽히더라도 공개 API나 안정성 계약의 일부로 보지 않는 편이 안전합니다.

문서 흐름별 판정 기준

흐름 판정 최소 기준 대표 테스트/근거
HWPX -> HWPX 검증됨 직접 수정·재오픈 + strict lint tests/test_document_model.py, tests/test_extended_features.py
HWP -> HWP 검증됨 직접 수정·재오픈 + strict lint + binary/reencode 회귀 tests/test_hwp_binary.py, tests/test_hwp_document.py, scripts/audit_hwp_lossless_roundtrip.py
HWP -> HWPX 검증됨 HWP를 읽어 HWPX로 재생성하고 block/control 의미 보존 확인 tests/test_hancom_document.py, tests/test_bridge.py
HWPX -> HWP 검증됨, 단 정규화 있음 HWPX를 읽어 HWP로 재생성하고 reopen/bridge roundtrip 확인 tests/test_hancom_document.py, tests/test_bridge.py

대표 회귀 명령은 아래와 같습니다. 통과 개수는 테스트 추가나 환경에 따라 변하므로, 지원 표의 근거는 숫자보다 "어떤 명령과 어떤 파일이 어떤 경로를 검증하는가"를 기준으로 읽는 편이 안전합니다.

python -m pytest tests/test_document_model.py tests/test_extended_features.py tests/test_hwp_binary.py tests/test_hwp_document.py tests/test_hancom_document.py tests/test_bridge.py tests/test_hancom_interop.py -q

컨트롤 패밀리별 지원 범위

아래 표도 같은 기준을 씁니다. 직접 수정·재오픈은 해당 포맷을 직접 편집하는 public API가 있다는 뜻이고, 왕복 보존은 교차 포맷 변환에서 의미가 유지된다는 뜻입니다.

항목 HWPX -> HWPX HWP -> HWP HWP -> HWPX HWPX -> HWP 비고
문단, 텍스트, 스타일 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(정규화) 가장 넓게 테스트된 축
머리말, 꼬리말 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(정규화) apply_page_type 포함
필드, 북마크, 하이퍼링크 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(정규화) HWP native field subtype 보존
각주, 미주, 자동번호, 페이지번호 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(정규화) setter와 bridge parity 있음
섹션 설정, page border fill, visibility 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(정규화) strict lint와 section-level 회귀 포함
직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(정규화) cell, margin, spacing, border fill, span 계열 포함
그림 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(일부 정규화) crop, line style, layout, 일부 native probe 반영
도형, connectLine 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(일부 정규화) subtype wrapper와 주요 setter 포함
수식 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(일부 정규화) layout/outMargins/rotation surface 포함
OLE 직접 수정·재오픈 직접 수정·재오픈 왕복 보존 왕복 보존(일부 정규화) extent, line style, metadata 포함
차트 직접 수정·재오픈 직접 수정·재오픈(metadata-backed) 왕복 보존 왕복 보존(metadata-backed) HWPX는 native chart schema, HWP는 semantic metadata layer 사용
form object 직접 수정·재오픈 직접 수정·재오픈(metadata-backed) 왕복 보존 왕복 보존(metadata-backed) HWPX는 native form tag를 사용합니다. label/items/value/checked/locked는 native 쪽에 있고, placeholder는 Hancom native HWPX probe에서 전용 slot이 확인되지 않아 JAKAL_FORM_META carrier를 씁니다. HWP는 semantic metadata layer 사용
memo/comment 직접 수정·재오픈 직접 수정·재오픈(텍스트 native, 추가 metadata는 metadata-backed) 왕복 보존(native + carrier 혼합) 왕복 보존(native + metadata-backed) HWPX 텍스트는 native hiddenComment, 추가 metadata는 JAKAL_MEMO carrier 사용. carrier는 Text parameter로 native memo와 먼저 matching하고, 없을 때만 순서로 병합합니다. Hancom native HWPX/HWPML probe에서 전용 slot이 아직 확인되지 않음

안정성에 대해

이 프로젝트는 한컴 GUI보다 "자동화 자유도" 쪽이 강합니다. 사용자가 화면에서 임의 위치를 마우스로 편집하는 범용 GUI 편집기 역할보다는, 코드로 반복 작업을 처리하고 저장 후 안정성을 검증하는 데 더 잘 맞습니다.

무변경 HWP -> HWP 재인코드 쪽은 lossless audit이 붙어 있고, 최근 기준으로 corpus 64건이 byte-identical입니다. 지원된 control 계열은 save/reopen 회귀와 교차 포맷 roundtrip 테스트를 같이 유지합니다.

주요 API

HancomDocument

가장 권장되는 공통 편집 모델입니다.

주요 메서드:

  • HancomDocument.blank()
  • HancomDocument.read_hwpx(path)
  • HancomDocument.read_hwp(path)
  • append_paragraph()
  • append_table(), append_picture(), append_shape(), append_equation(), append_ole()
  • append_field(), append_hyperlink(), append_bookmark(), append_note()
  • append_header(), append_footer()
  • write_to_hwpx(path)
  • write_to_hwp(path)

HwpxDocument

HWPX를 직접 편집할 때 쓰는 주력 객체입니다.

주요 메서드:

  • HwpxDocument.open(path)
  • HwpxDocument.blank()
  • append_paragraph()
  • append_header(), append_footer()
  • append_table(), append_picture(), append_shape(), append_equation(), append_ole()
  • append_field(), append_hyperlink(), append_bookmark(), append_note()
  • section_settings()
  • strict_lint_errors(), strict_lint_report(), format_strict_lint_errors(), strict_validate()
  • save(path)

HwpDocument

HWP 편집에 쓰는 주력 객체입니다.

주요 메서드:

  • HwpDocument.open(path)
  • HwpDocument.blank()
  • append_paragraph()
  • append_table(), append_picture(), append_shape(), append_equation(), append_ole()
  • append_field(), append_hyperlink(), append_bookmark(), append_note()
  • append_header(), append_footer(), append_auto_number()
  • tables(), pictures(), fields(), notes(), section(index)
  • strict_lint_errors(), strict_lint_report(), format_strict_lint_errors(), strict_validate()
  • save(path)

HwpHwpxBridge

이미 가진 문서를 기준으로 HWP/HWPX/HancomDocument를 오가며 저장할 때 편리한 도우미 객체입니다.

주요 메서드:

  • HwpHwpxBridge.open(path)
  • HwpHwpxBridge.from_hwp(source)
  • HwpHwpxBridge.from_hwpx(source)
  • hancom_document()
  • hwp_document()
  • hwpx_document()
  • save_hwp(path)
  • save_hwpx(path)
  • save(path)

HwpBinaryDocument

가장 저수준의 HWP API입니다.

다음 상황에서 씁니다.

  • binary stream을 직접 보고 싶을 때
  • DocInfoModel, SectionModel을 다뤄야 할 때
  • reencode나 probe 작업을 할 때
  • 지원 매핑을 더 넓히기 위해 record tree를 조사할 때

예제

HWPX 열고 수정 후 저장

from jakal_hwpx import HwpxDocument

doc = HwpxDocument.open("input.hwpx")
doc.replace_text("초안", "최종")
doc.append_paragraph("승인 완료")
doc.strict_validate()
doc.save("build/edited.hwpx")

HWP 열고 수정 후 저장

from jakal_hwpx import HwpDocument

doc = HwpDocument.open("input.hwp")
doc.append_paragraph("추가 문단")
doc.append_hyperlink("https://example.com", text="Example")
errors = doc.format_strict_lint_errors()
if errors:
    print(errors)
doc.strict_validate()
doc.save("build/edited.hwp")

HWP를 HWPX로 변환

from jakal_hwpx import HancomDocument

doc = HancomDocument.read_hwp("input.hwp")
doc.write_to_hwpx("build/exported.hwpx")

HWPX를 HWP로 변환

from jakal_hwpx import HancomDocument

doc = HancomDocument.read_hwpx("input.hwpx")
doc.write_to_hwp("build/exported.hwp")

bridge 도우미로 포맷 전환

from jakal_hwpx import HwpHwpxBridge

bridge = HwpHwpxBridge.open("input.hwp")
bridge.save_hwpx("build/bridge.hwpx")
bridge.save_hwp("build/bridge-copy.hwp")

HWP record 살펴보기

from jakal_hwpx import HwpBinaryDocument

doc = HwpBinaryDocument.open("input.hwp")
print(doc.file_header().version)
print(doc.docinfo_model().id_mappings_record().named_counts())
print(doc.section_model(0).controls())

검증과 테스트

핵심 회귀 세트:

python -m pip install -e .[dev]
python -m pytest tests/test_document_model.py tests/test_extended_features.py tests/test_hwp_binary.py tests/test_hwp_document.py tests/test_hancom_document.py tests/test_bridge.py tests/test_hancom_interop.py -q

각 파일은 대략 아래 범위를 봅니다.

  • tests/test_document_model.py: HWPX 직접 authoring, native HWPX schema, strict lint
  • tests/test_extended_features.py: HWPX 고급 기능과 batch edit surface
  • tests/test_hwp_binary.py: HWP reencode, docinfo, section model, binary payload 안정성
  • tests/test_hwp_document.py: HWP 공개 wrapper/setter, strict lint, save/reopen 회귀
  • tests/test_hancom_document.py: HWP <-> HancomDocument <-> HWPX bridge parity
  • tests/test_bridge.py: explicit bridge helper와 HwpHwpxBridge 공개 API 회귀
  • tests/test_hancom_interop.py: Hancom smoke/security script contract

release gate와 안정성 검사:

python scripts/check_release.py

HWP lossless audit:

python scripts/audit_hwp_lossless_roundtrip.py

선택적 Hancom smoke validation:

powershell -ExecutionPolicy Bypass -File scripts/setup_hancom_security_module.ps1 -DownloadIfMissing
powershell -ExecutionPolicy Bypass -File scripts/run_hancom_smoke_validation.ps1 -InputPath examples\samples\hwpx\AI와_특이점_보고서.hwpx -OutputPath .codex-temp\hancom-smoke\sample.roundtrip.hwpx
powershell -ExecutionPolicy Bypass -File scripts/run_hancom_corpus_smoke_validation.ps1

일반적인 사용에선 pure Python 경로가 기본입니다. Hancom 쪽은 "정말 한컴에서 열어도 괜찮은지"를 추가로 확인할 때 쓰면 됩니다.

추가 문서

고급 도구도 루트 import에서 바로 쓸 수 있습니다.

  • build_hwp_pure_profile()
  • append_feature_from_profile()
  • run_template_lab()
  • hwp_collection donor scanning 도구

이 도구들은 corpus 분석이나 template-backed HWP 작업에 유용하지만, 일반적인 앱 코드의 기본 진입점은 아닙니다.

라이선스

프로젝트 소스 코드는 MIT 라이선스를 따릅니다.

샘플 문서와 생성 결과물은 별도 권리를 가질 수 있습니다. 재배포 전에는 THIRD_PARTY_NOTICES.md를 확인하세요.

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

jakal_hwpx-0.3.0.tar.gz (2.3 MB view details)

Uploaded Source

Built Distribution

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

jakal_hwpx-0.3.0-py3-none-any.whl (2.2 MB view details)

Uploaded Python 3

File details

Details for the file jakal_hwpx-0.3.0.tar.gz.

File metadata

  • Download URL: jakal_hwpx-0.3.0.tar.gz
  • Upload date:
  • Size: 2.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for jakal_hwpx-0.3.0.tar.gz
Algorithm Hash digest
SHA256 b355979fa3d06852cbfb4c072b1a826f85dbbcb643cbcb565c5074f99f37db3c
MD5 250314ac110c0c6b13a7835665b216c8
BLAKE2b-256 24ab311237f7d5163f2a23d791a8146d3c149bdc62b99322a6b157323c26ba96

See more details on using hashes here.

File details

Details for the file jakal_hwpx-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: jakal_hwpx-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 2.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for jakal_hwpx-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f975009514f77fd64703c071552d03df0d5e211a878eadd67b8426c04d0cce8f
MD5 d92ff194476bfef5c74743a0959dc11b
BLAKE2b-256 c98d23c7ec43762f78648e168bbdd2489c9838776f394eaedc946c0f0901d428

See more details on using hashes here.

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