Skip to main content

HWP 5.x / HWPX / HWP 3.x to YAML converter with structure preservation

Project description

hwp2yaml

HWP 5.x / HWPX / HWP 3.x 문서를 YAML로 직접 변환하는 Python 라이브러리.

Python 3.10+ License: MIT

요구사항

  • Python: 3.10 이상
  • OS: Windows, macOS, Linux

빠른 시작

pip install hwp2yaml
from hwp2yaml import extract_hwp_text

result = extract_hwp_text("document.hwp")
if result.success:
    print(result.text)

지원 형식

형식 지원 방식 비고
HWP 5.x 직접 파싱 OLE2 기반 (2002~)
HWPX 직접 파싱 XML + ZIP 기반 (2014~)
HWP 3.x LibreOffice + Docling 구형식 (1990년대)

특징

  • HWP → YAML: 중간 변환 없이 직접 YAML 출력
  • 순수 Python: pyhwp 등 AGPL 라이브러리 대체
  • MIT 라이선스: 상업적 사용 가능
  • 전 버전 지원: HWP 3.x ~ HWPX 모두 처리
  • 트리아지: 파일 버전 자동 감지 및 분류
  • 구조 보존: 단락, 테이블, 섹션 구조 완전 보존 (HWP 5.x)

설치

PyPI (권장)

pip install hwp2yaml

소스에서 설치

git clone https://github.com/seunghyuoffice-design/hwp2yaml.git
cd hwp2yaml
pip install -e .

개발 환경 설치

pip install -e ".[dev]"

HWP 3.x 변환 의존성 (선택)

HWP 3.x 파일을 변환하려면 추가 설치가 필요합니다:

Ubuntu/Debian:

sudo apt install libreoffice poppler-utils
pip install docling

macOS:

brew install --cask libreoffice
brew install poppler
pip install docling

Windows:

  1. LibreOffice 설치
  2. Poppler for Windows 설치 후 PATH에 추가
  3. pip install docling

CLI 사용법

# 단일 파일 변환
hwp2yaml document.hwp

# 출력 파일 지정
hwp2yaml document.hwp -o output.yaml

# 디렉토리 일괄 변환
hwp2yaml /path/to/files/ -o /path/to/output/

API 사용법

1. 텍스트 추출 (가장 간단)

from hwp2yaml import extract_hwp_text

result = extract_hwp_text("document.hwp")

if result.success:
    print(result.text)       # 추출된 텍스트
    print(result.method)     # "prvtext", "bodytext", "hwpx" 중 하나
else:
    print(f"실패: {result.error}")

2. 구조 보존 추출 (HWP 5.x)

단락, 테이블, 섹션 구조를 보존하여 YAML로 변환:

from hwp2yaml import extract_hwp_structure

result = extract_hwp_structure("document.hwp")

if result.success:
    # 구조 정보 접근
    for section in result.structure["sections"]:
        print(f"단락 수: {len(section['paragraphs'])}")
        print(f"테이블 수: {len(section['tables'])}")

    # 테이블 데이터 접근
    for table in result.tables:
        print(f"테이블: {table['rows']}x{table['cols']}")
        for row in table['data']:
            print(row)

    # YAML 문자열로 출력
    print(result.to_yaml())

    # YAML 파일로 저장
    with open("output.yaml", "w", encoding="utf-8") as f:
        f.write(result.to_yaml())

3. 파일 버전 감지 (트리아지)

from hwp2yaml import detect_hwp_version, HWPVersion, triage_directory

# 단일 파일 버전 확인
version = detect_hwp_version("document.hwp")

if version == HWPVersion.HWP_5X:
    print("HWP 5.x - 직접 처리 가능")
elif version == HWPVersion.HWPX:
    print("HWPX - 직접 처리 가능")
elif version == HWPVersion.HWP_3X:
    print("HWP 3.x - LibreOffice 필요")
elif version == HWPVersion.UNKNOWN:
    print("알 수 없는 형식")

# 디렉토리 일괄 트리아지
summary = triage_directory("/path/to/files")
print(f"총 파일: {summary.total}")
print(f"HWP 5.x: {summary.hwp5_count}")
print(f"HWPX: {summary.hwpx_count}")
print(f"HWP 3.x: {summary.hwp3_count}")
print(f"알 수 없음: {summary.unknown_count}")

4. HWP 3.x 변환

from hwp2yaml import convert_hwp3

result = convert_hwp3("old_document.hwp")

if result.success:
    print(result.text)                    # 추출된 텍스트
    print(f"테이블 수: {len(result.tables)}")
    print(f"방법: {result.method}")       # "hwp3_docling" 또는 "hwp3_pdftotext"

    # YAML로 변환
    yaml_str = result.to_yaml()
    yaml_dict = result.to_yaml_dict()

5. 배치 처리

from hwp2yaml import BatchProcessor, batch_convert_to_yaml

# 방법 1: BatchProcessor 클래스
processor = BatchProcessor(workers=4)
result = processor.process_directory("/path/to/files")
print(f"성공: {result.success}/{result.total}")

# 방법 2: 함수 직접 호출
yaml_dicts = batch_convert_to_yaml(
    filepaths=["file1.hwp", "file2.hwp", "file3.hwp"],
    output_dir="/path/to/yaml",           # 개별 YAML 파일 저장
    combined_output="/path/to/all.yaml",  # 통합 파일 저장 (선택)
)

반환 타입

ExtractResult

텍스트 추출 결과를 담는 dataclass:

@dataclass
class ExtractResult:
    success: bool          # 성공 여부
    text: str              # 추출된 텍스트 (실패 시 빈 문자열)
    method: str            # 사용된 방법 ("prvtext", "bodytext", "hwpx", "hwp3_docling", ...)
    error: str | None      # 에러 메시지 (성공 시 None)
    tables: list[dict]     # 추출된 테이블 목록
    structure: dict | None # 구조 정보 (extract_hwp_structure 사용 시)

    def to_yaml(self) -> str:
        """YAML 문자열로 변환"""

    def to_yaml_dict(self) -> dict:
        """YAML 호환 딕셔너리로 변환"""

HWPVersion

파일 버전을 나타내는 Enum:

class HWPVersion(Enum):
    HWP_3X = "hwp3"      # HWP 3.x (1990년대)
    HWP_5X = "hwp5"      # HWP 5.x (2002~)
    HWPX = "hwpx"        # HWPX (2014~)
    UNKNOWN = "unknown"  # 알 수 없음

YAML 출력 스키마

구조 보존 스키마 (HWP 5.x)

metadata:
  source_file: /path/to/document.hwp
  method: hwp5_structure
  extracted_at: "2026-01-19T12:34:56"

structure:
  sections:
    - index: 0
      paragraphs:
        - text: "첫 번째 단락"
          level: 0
        - text: "두 번째 단락"
          level: 0
      tables:
        - rows: 3
          cols: 2
          data:
            - ["헤더1", "헤더2"]
            - ["셀1", "셀2"]
            - ["셀3", "셀4"]

tables:  # 전체 테이블 목록 (편의용)
  - rows: 3
    cols: 2
    data: [...]

raw_text: |
  평탄화된 전체 텍스트

HWP 3.x 변환 스키마

metadata:
  source_file: /path/to/file.hwp
  version: hwp3
  method: hwp3_docling
  converted_at: "2026-01-19T12:34:56"

content:
  parties: 당사자 정보
  request: 신청취지
  facts: 사실관계
  decision: 위원회 판단

tables:
  - table_id: table_0
    rows: 3
    cols: 2
    cells:
      - {row: 0, col: 0, text: "..."}

raw_text: |
  원본 전문

의존성

필수 (자동 설치)

패키지 라이선스 용도
olefile BSD OLE2 파일 파싱
pyyaml MIT YAML 출력
tqdm MIT 진행률 표시

HWP 3.x 변환 (선택)

패키지/도구 라이선스 용도
LibreOffice MPL-2.0 HWP → PDF 변환
docling MIT PDF 구조 추출 (권장)
poppler-utils GPL-2.0 pdftotext (Docling 폴백)

에러 처리

from hwp2yaml import extract_hwp_text, HWPVersion, detect_hwp_version

# 파일이 존재하지 않는 경우
result = extract_hwp_text("nonexistent.hwp")
if not result.success:
    print(f"에러: {result.error}")  # "파일을 찾을 수 없습니다"

# 지원하지 않는 형식
version = detect_hwp_version("document.pdf")
if version == HWPVersion.UNKNOWN:
    print("HWP 파일이 아닙니다")

# 암호화된 문서
result = extract_hwp_text("encrypted.hwp")
if not result.success:
    print(f"에러: {result.error}")  # "암호화된 문서입니다"

제한사항

제한 설명
암호화된 문서 미지원 (HWP 파일 형식 한계)
이미지/OLE 객체 미지원 (텍스트 추출에 집중)
HWP 3.x LibreOffice 설치 필요
DRM 보호 문서 미지원

Changelog

v0.6.1 (2026-01-19)

버그 수정:

  1. 테이블 종료 감지 개선 - 레코드 레벨 기반 상태 추적으로 테이블 후 단락이 셀로 잘못 포함되는 문제 해결
  2. 다중 레코드 단락 누적 - 여러 PARA_TEXT 레코드로 분할된 긴 단락을 완전히 캡처
  3. 셀 진행 로직 개선 - 테이블 외부에서 LIST_HEADER 오작동 방지, 범위 초과 크래시 방지
  4. HWPX 섹션 순서 정렬 - 숫자 기준 정렬로 section10.xml이 section2.xml 앞에 오는 문제 해결
  5. 네임스페이스 안전 파싱 - 네임스페이스 포함 HWPX XML 파싱 개선, Fallback 메커니즘 추가
  6. 마지막 셀 텍스트 누출 방지 - 테이블 종료 시 대기 중인 텍스트 flush 순서 수정

테스트:

  • 420+ 줄 신규 테스트 코드
  • 26개 테스트 케이스 (구조 보존, HWPX 파싱, 에지 케이스)

v0.6.0 (2026-01-19)

  • HWP 5.x 구조 보존 추출 기능 추가
  • 단락, 테이블, 섹션 구조 완전 보존
  • YAML 스키마 정의

v0.5.0 (2025-12)

  • HWP → YAML 직접 변환
  • HWP 5.x 직접 파싱
  • HWPX 지원 추가

라이선스

MIT License

Copyright (c) 2025-2026 Dyarchy Project

기여

이슈와 PR을 환영합니다:

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

hwp2yaml-0.6.1.tar.gz (33.8 kB view details)

Uploaded Source

Built Distribution

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

hwp2yaml-0.6.1-py3-none-any.whl (38.2 kB view details)

Uploaded Python 3

File details

Details for the file hwp2yaml-0.6.1.tar.gz.

File metadata

  • Download URL: hwp2yaml-0.6.1.tar.gz
  • Upload date:
  • Size: 33.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hwp2yaml-0.6.1.tar.gz
Algorithm Hash digest
SHA256 3c5f520c50ce1b892c786283c7d8d80c72cd4ba7b734f357a72e17a5fb2bf557
MD5 f3311b0d243a4b4591c20cdd00e93ae1
BLAKE2b-256 4d8e8bab4a2b75d2ffb1a8c42da5bde81a9bb4150e59672716b662b5475a4d74

See more details on using hashes here.

File details

Details for the file hwp2yaml-0.6.1-py3-none-any.whl.

File metadata

  • Download URL: hwp2yaml-0.6.1-py3-none-any.whl
  • Upload date:
  • Size: 38.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hwp2yaml-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1edcdf34174aba46d0c2d90774b7272177a72dd2d21dbaf9fbcce87139e0d9b4
MD5 9aef2e8bfe5cc79e401e43732c8f1321
BLAKE2b-256 f5aa135bccabf79e0d4a3ab3c002e6c4dddd7d687b435c4148d131b689d2f7f9

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