Skip to content

HWPX↔HWP5 파싱 parity: ParaShape indent(들여쓰기)가 정확히 2배 어긋남 (HwpUnitChar switch 경로 intent 2× 과잉) #1472

Description

@oksure

증상

동일 문서의 .hwpx.hwp 를 파싱하면 ParaShape indent(들여쓰기/내어쓰기) 값이 정확히 2배 어긋난다. 여백(margin_left/margin_right)·문단간격은 완전히 일치하는데 indent 만 불일치한다.

HwpUnitChar switch/case 를 가진 paraPr 에서만 발생하며, 어긋남은 항상 정확히 2배다(예외 0건).

$ rhwp ir-diff "samples/3-11월_실전_통합_2024-구분선위0미주사이0구분선아래0.hwpx" \
               "samples/3-11월_실전_통합_2024-구분선위0미주사이0구분선아래0.hwp"
  [차이] PS[12] indent: -3216vs-1608     ← HWPX -3216 vs HWP5 -1608 (정확히 2배)
  [차이] PS[15] indent: -9658vs-4829
  [차이] PS[17] indent: -15350vs-7675
  ... (이 파일에서 14건, 전부 A==2*B)

여러 시험지 샘플에서 동일하게 재현된다(systematic, 문서 독립적):

샘플 indent 불일치 margin 불일치 비율
3-11월_실전_통합_2024-구분선위0미주사이0구분선아래0 14건 0건 전부 정확히 2×
3-09월_교육_통합_2023 31건 0건 전부 정확히 2×
3-10월_교육_통합_2022 18건 0건 전부 정확히 2×

근본 원인 위치

src/parser/hwpx/header.rs parse_para_shape_switch()HwpUnitChar case 에서 값에 * 2 를 적용한다(L1143-1155):

if in_hwpunitchar_case {
    // HwpUnitChar 값은 실제 HWPUNIT(1× 스케일)이므로
    // HWP 바이너리와 동일한 2× 스케일로 변환
    let val2x = val * 2;
    match tag_name {
        b"left"   => ps.margin_left  = val2x,
        b"right"  => ps.margin_right = val2x,
        b"intent" => ps.indent       = val2x,   // ← 이 줄(L1150)이 indent 까지 2× 적용
        b"prev"   => ps.spacing_before = val2x,
        b"next"   => ps.spacing_after  = val2x,
        _ => {}
    }
}

HWPX <hp:switch> 가 같은 값을 두 표현으로 담는데, intent 만 두 표현 사이의 2배 관계가 margin 과 반대다:

samples/.../3-11월_...구분선위0미주사이0구분선아래0.hwpxparaPr[12]:

필드 HwpUnitChar case default (unit="HWPUNIT") 현재 코드 결과(case×2) HWP5 바이너리
left (margin) 1200 2400 2400 ✅ HWP5 일치 2400
intent (indent) -1608 -3216 -3216 -1608

margin 은 case×2 == default == HWP5 라서 2× 가 맞지만, indent 는 case == HWP5(= default 의 절반) 이라 2× 를 적용하면 HWP5 와 어긋난다. intent 에만 2× 가 과잉 적용되는 것으로 보인다.

(참고: 같은 파서의 평면 속성 경로 header.rs:1035 b"indent" => ps.indent = parse_i32(&attr)indent 를 1× 그대로 읽어, switch 경로와 스케일이 불일치한다.)

영향

Document IR 에서 renderermargin_leftindent같은 단위(HWPUNIT) 로 함께 사용한다. 현재:

  • HWPX 파싱: (margin=2400, indent=-3216)
  • HWP5 파싱: (margin=2400, indent=-1608)

→ 같은 문서인데 두 포맷의 내어쓰기 폭이 2배 차이로 렌더된다(한쪽이 한컴 조판과 불일치). v1.0.0 "한컴 동일 조판" 목표에 직접 영향.

방향 판정 — 한컴 시각 권위 필요

어느 쪽이 옳은지는 추론만으로 확정 불가다(두 일관성 논증이 충돌):

  • HWP5(-1608) 가 정답 가설: 렌더러는 주로 HWP5 기준으로 보정되어 왔고, 평면 indent 경로도 1× 로 읽는다 → switch 의 intent 2× 가 버그.
  • HWPX(-3216) 가 정답 가설: 같은 paraPr 안에서 margin 이 default 스케일(2400)로 쓰이므로, 동일 단위인 indent 도 default 스케일(-3216)이어야 일관 → HWP5 파서가 indent 를 절반으로 읽는 것이 버그.

정답지(한컴 2022 PDF)가 이 파일에 존재하므로 메인테이너 환경에서 시각 판정 가능:
pdf/3-11월_실전_통합_2024-구분선위0미주사이0구분선아래0.pdf — 내어쓰기 문단(①②③ 목록)의 첫 줄 시작 x 위치로 -1608/-3216 중 한쪽 확정 가능.

재현

cargo build --bin rhwp
./target/debug/rhwp ir-diff "samples/3-11월_실전_통합_2024-구분선위0미주사이0구분선아래0.hwpx" \
                            "samples/3-11월_실전_통합_2024-구분선위0미주사이0구분선아래0.hwp" \
  | grep indent

관련 이슈

제안

방향(어느 값이 한컴 정답인지)만 확정되면 수정은 1줄 수준이다(예: intentval2x 대상에서 제외, 또는 HWP5 파서 측 보정). 방향 지시 주시면 회귀 테스트(파일별 ir-diff indent parity=0)와 함께 PR 올리겠습니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghwp5현대 .hwp(HWP5/OLE) 포맷 처리: 파싱, IR 변환, 컨트롤·표·그림 속성 해석, 호환성 개선hwpxHWPX 포맷 처리: 파싱, 직렬화, roundtrip, XML/ZIP 패키지 보존, 호환성 개선

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions