Skip to content

fix: HWP5/HWP3 한컴 정합 종합 fix — 격차 A/B/C/D (closes #1001)#1005

Closed
jangster77 wants to merge 6 commits into
edwardkim:develfrom
jangster77:local/task1001-pr
Closed

fix: HWP5/HWP3 한컴 정합 종합 fix — 격차 A/B/C/D (closes #1001)#1005
jangster77 wants to merge 6 commits into
edwardkim:develfrom
jangster77:local/task1001-pr

Conversation

@jangster77

Copy link
Copy Markdown
Collaborator

closes #1001

개요

HWP5/HWP3 의 한컴 viewer 정합 격차 종합 fix. 4 카테고리 격차 해소 + 자동 변환본 식별 휴리스틱 도입.

격차 분류 + Fix

격차 A — 페이지 번호 외곽선 안/밖

Root cause: HWP5 spec 표 136 의 bit 1 (머리말 포함) / bit 2 (꼬리말 포함) 가 rhwp build_page_borders 에서 미처리. 변환본 pgbf.attr=0x01 의 paper-based 외곽선이 꼬리말 영역까지 확장 → 페이지 번호가 외곽선 안에 표시.

Fix: src/renderer/layout.rs build_page_borders 에 bit 1/2 처리 추가. !header_inside 시 outline 상단을 body_area.y 로 clip, !footer_inside 시 outline 하단을 body_area.y + height 로 clip.

격차 B — 변환본 styling (PUA 글머리표)

Root cause: 한컴 변환본의 PUA U+F03C5 (글머리표) 가 rhwp 의 display_text 매핑 부재로 깨진 글리프 표시.

Fix: src/renderer/composer.rs pua_plain_text_display0xF03C5 → \"□\" 매핑 추가. 한컴의 빈 체크박스 글머리표 정확 표시.

격차 C — Paragraph spacing drift (HwpUnitChar)

Root cause: 한컴 HWP3 → HWP5 변환본의 ParaShape spacing/margin 이 HwpUnitChar 단위 (HWPUNIT 의 2 배) 로 저장. rhwp 가 HWPUNIT 으로 해석하여 paragraph 간격 2 배 격차.

Fix:

  1. src/parser/cfb_reader.rs detect_hwp3_variant() 추가 — HwpSummaryInformation 안 1990-2003 년 검출
  2. src/model/document.rs Document::is_hwp3_variant 필드 추가
  3. src/parser/mod.rs 자동 식별 결합 휴리스틱 — HwpSummary HWP3 시대 년 + PS<0.20 + CS<0.20
  4. src/renderer/style_resolver.rs resolve_styles_with_variant — 변환본 시 ParaShape /4 보정 (기본 /2 + 추가 /2)

격차 D — Shape paragraph y_offset double count

Root cause: layout.rs:4778 layout_shape_itemtreat_as_char + empty paragraph Shape 가 PageItem::FullParagraphPageItem::Shape 양쪽에서 line_advance 만큼 진행되어 ~Shape height (130 px) double count → 박스 아래 ~140 px extra empty space 발생.

Fix: result_y 를 y_offset + line_spacing × 3 (~30 px buffer) 로 조정 — paragraph 간 minimum gap 보존하되 double count 차단.

자동 식별 sweep 검증 (8 sample)

파일 HwpSummary HWP3년 PS<0.20 CS<0.20 variant
hwp3-sample16-hwp5.hwp YES 0.17 ✓ 0.15 ✓ true
exam_kor.hwp NO - - false ✓
exam_math.hwp NO - - false ✓
exam_eng.hwp YES (FP) 0.27 ✗ 0.36 ✗ false ✓ (PS/CS 차단)
aift.hwp NO - - false ✓
biz_plan.hwp NO - - false ✓
통합재정통계(2014).hwp NO - - false ✓
복학원서.hwp YES (FP) - - false ✓ (para<50 차단)

8/8 정확 분류. False positive 차단 (PS/CS 비율 검증).

검증

  • cargo test --release --lib: 1301 passed, 0 failed
  • cargo clippy --release -- -D warnings: 0 warnings
  • WASM 빌드 (wasm-pack build --release --target web): 4.83 MB ✓
  • 사용자 시각 판정 (rhwp-studio): 격차 대폭 개선

변경 파일 (10개)

파일 변경
src/parser/cfb_reader.rs detect_hwp3_variant() 메서드
src/model/document.rs is_hwp3_variant: bool 필드
src/parser/mod.rs 자동 식별 휴리스틱
src/parser/hwpx/mod.rs Document 초기화
src/renderer/layout.rs build_page_borders bit 1/2 + layout_shape_item y_offset double-count 차단
src/renderer/style_resolver.rs resolve_styles_with_variant + /4 보정
src/renderer/composer.rs PUA U+F03C5 → \"□\" 매핑
src/document_core/mod.rs set_dpi 가 variant 전달
src/document_core/commands/document.rs from_bytes 가 variant 전달
src/serializer/cfb_writer/tests.rs Document literal 추가 필드

잔존 격차 (후속 issue)

본 PR 의 범위 외, 별도 task 권고:

  • 머릿말 inline image overlap (cover page) — selective outline push-down (paragraph 안 inline image 가 header 영역 침범 시) 필요. build_page_borders 의 paragraphs context 추가 + scanning logic.
  • 페이지 강제 나눔 정밀화 — 한컴의 page-fill 휴리스틱 미상. ParaShape 누적 계산 정밀화 또는 추가 buffer 필요.
  • Shape control gradient simplify — 변환본 시 Shape fill_type=Gradient 를 solid 로 simplify. drawing_to_shape_style 의 모든 callers 에 variant context 전달 필요 (broad refactor).
  • rhwp-studio WASM 렌더링 path 진단 — Canvas2D 와 native SVG 출력 차이 가능성.

Test plan

  • CI 빌드 + 테스트 통과
  • samples/hwp3-sample16-hwp5.hwp 페이지 1, 3 시각 검증 (rhwp-studio)
  • samples/hwp3-sample16.hwp (HWP3 원본) 회귀 확인
  • samples/exam_kor/math/eng.hwp 시험지 회귀 확인
  • samples/aift.hwp, biz_plan.hwp, 통합재정통계(2014.8월).hwp 일반 HWP5 회귀

🤖 Generated with Claude Code

- **격차 A** (페이지 번호 외곽선 안/밖): `src/renderer/layout.rs` `build_page_borders`
  에 HWP5 spec 표 136 bit 1 (머리말 포함) / bit 2 (꼬리말 포함) 처리 추가.
  변환본 `pgbf.attr=0x01` 의 paper-based + header/footer NOT included 시 outline
  을 body_area 범위로 clip. 페이지 번호가 외곽선 밖에 위치 정합.

- **격차 B** (변환본 styling): PUA 글머리표 `U+F03C5 → "□"` 매핑 추가
  (`src/renderer/composer.rs`). 한컴 변환본의 빈 체크박스 글머리표를 정확히 표시.

- **격차 C** (HwpUnitChar spacing drift): 한컴 HWP3→HWP5 변환본의 ParaShape
  spacing/margin 이 HwpUnitChar 단위 (HWPUNIT × 2) 로 저장. 자동 식별 휴리스틱
  (HwpSummary "1990-2003년" + PS<0.20 + CS<0.20) 추가 + `resolve_styles_with_variant`
  에서 변환본 시 ParaShape spacing/margin `/4` 보정 (기본 `/2` + 추가 `/2`).

- **격차 D** (Shape paragraph y_offset double count): `layout.rs:4778`
  `layout_shape_item` 의 treat_as_char + empty paragraph Shape 가 PageItem::
  FullParagraph 와 PageItem::Shape 양쪽에서 line_advance 만큼 진행되어 ~Shape
  height 만큼 double count. result_y 를 y_offset + line_spacing × 3 으로 조정.

8개 sample (sample16-hwp5, exam_kor/math/eng, biz_plan, aift, 통합재정통계, 복학원서)
모두 정확 분류 — sample16-hwp5 만 variant=true, 나머지 false. False positive
차단 (exam_eng 본문 "2002년" 인용 — PS/CS 비율로 차단, 복학원서 — para<50 차단).

- `cargo test --release --lib`: 1306 passed, 0 failed
- `cargo clippy --release -- -D warnings`: 0 warnings
- WASM build: 4.83 MB, 정상 동작

- 머릿말 inline image overlap (cover page) — selective outline push-down 필요
- 페이지 강제 나눔 정밀화 — 한컴의 page-fill 휴리스틱 미상
- Shape control gradient simplify — variant 시 fill simplify (broad refactor)
- rhwp-studio WASM 렌더링 path 진단 — Canvas2D vs SVG 차이

상세 보고서: mydocs/report/task_m100_1001_report.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
jangster77 added a commit to jangster77/rhwp that referenced this pull request May 19, 2026
apply_hwp3_origin_fixup 의 ratio 단독 (ps<0.05, cs<0.15) 분기가 hwpspec.hwp
(spec 문서, 1467 paragraphs, PS=70/0.048, CS=63/0.043) 를 false-positively
variant 로 분류 → typeset 단계의 variant-specific 분기 (ParaShape /4 spacing
등) 가 발동하여 page 수 변동 (175 → 172). issue_418 회귀 테스트 실패.

Fix: apply_hwp3_origin_fixup 에서 doc.is_hwp3_variant=true 설정 제거.
margin_bottom 보정 (Task edwardkim#554) 만 유지. variant 확정은 caller 의 더 정확한
조건 (HwpSummary HWP3-era AND ratio<0.20) 에서만 처리.

검증:
- hwpspec.hwp: 175 페이지 (devel baseline 정합) ✓
- issue_418 test: PASS ✓
- sample16-hwp5: 62 페이지 (variant=true 유지, PR edwardkim#1005 baseline)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
apply_hwp3_origin_fixup 의 ratio 단독 (ps<0.05, cs<0.15) 분기가 hwpspec.hwp
(spec 문서, 1467 paragraphs, PS=70/0.048, CS=63/0.043) 를 false-positively
variant 로 분류 → typeset 단계의 variant-specific 분기 (ParaShape /4 spacing
등) 가 발동하여 page 수 변동 (175 → 172). issue_418 회귀 테스트 실패.

Fix: apply_hwp3_origin_fixup 에서 doc.is_hwp3_variant=true 설정 제거.
margin_bottom 보정 (Task edwardkim#554) 만 유지. variant 확정은 caller 의 더 정확한
조건 (HwpSummary HWP3-era AND ratio<0.20) 에서만 처리.

검증:
- hwpspec.hwp: 175 페이지 (devel baseline 정합) ✓
- issue_418 test: PASS ✓
- sample16-hwp5: 62 페이지 (variant=true 유지, PR edwardkim#1005 baseline)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jangster77 jangster77 force-pushed the local/task1001-pr branch from 2181473 to 5148102 Compare May 19, 2026 12:08
@edwardkim edwardkim self-requested a review May 19, 2026 13:26
@edwardkim edwardkim added the enhancement New feature or request label May 19, 2026
@edwardkim edwardkim added this to the v1.0.0 milestone May 19, 2026
edwardkim pushed a commit that referenced this pull request May 19, 2026
apply_hwp3_origin_fixup 의 ratio 단독 (ps<0.05, cs<0.15) 분기가 hwpspec.hwp
(spec 문서, 1467 paragraphs, PS=70/0.048, CS=63/0.043) 를 false-positively
variant 로 분류 → typeset 단계의 variant-specific 분기 (ParaShape /4 spacing
등) 가 발동하여 page 수 변동 (175 → 172). issue_418 회귀 테스트 실패.

Fix: apply_hwp3_origin_fixup 에서 doc.is_hwp3_variant=true 설정 제거.
margin_bottom 보정 (Task #554) 만 유지. variant 확정은 caller 의 더 정확한
조건 (HwpSummary HWP3-era AND ratio<0.20) 에서만 처리.

검증:
- hwpspec.hwp: 175 페이지 (devel baseline 정합) ✓
- issue_418 test: PASS ✓
- sample16-hwp5: 62 페이지 (variant=true 유지, PR #1005 baseline)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 19, 2026
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 19, 2026
@jangster77#997#999 연장. HWP3 변환본 전반 한컴 정합. 격차 A(page
border spec 표136 bit1/2 body_area clip) + B(PUA F03C5→□) + C(변환본
4중 AND 가드 식별 + ParaShape /4) 실효. 격차 D(y_offset double count)
는 후속 커밋 4e3ad58 에서 revert (non-variant table-vpos-01 회귀
발견 후 컨트리뷰터 자정) — 최종 no-op.

옵션 A: 본질 2커밋 cherry-pick (8b7fba3 + 4e3ad58, 작성자 Taesup Jang
보존, orders 충돌 --ours 메인테이너 일지 보존, 소스 .rs 충돌 없음).
검증: cargo test 1307 + clippy -D + fmt 0 + WASM 4.83MB. sweep 8
fixture: 일반 HWP5 6종(exam/aift/biz_plan/복학원서) diff=0 variant=false
유지 (오판 0), HWP3 원본 sample16.hwp 외곽선만 이동·텍스트 무변동.
작업지시자 시각 판정 통과 (sample16-hwp5 page 1/3 + HWP3 원본 외곽선
spec 정합 + 일반 HWP5 무회귀).

잔존 5건 (머릿말 inline image / 페이지 강제나눔 / gradient simplify /
WASM path / 격차 D breathing room) 후속 issue 분리.
@edwardkim

Copy link
Copy Markdown
Owner

옵션 A로 devel에 반영했습니다 (본질 2커밋 8b7fba3 + 4e3ad58 cherry-pick, 작성자 메타데이터 보존, 소스 충돌 없음).

검증:

  • cargo test --release --lib 1307 passed / clippy -D / fmt 0 / WASM 4.83MB
  • sweep 8 fixture: 일반 HWP5 6종(exam_kor/math/eng, aift, biz_plan, 복학원서) 전부 diff=0 — variant 식별 false positive 없음 (4중 AND 가드 견고). HWP3 원본 sample16.hwp는 page border 외곽선만 이동(텍스트 무변동) — 격차 A의 HWP5 spec 표136 정합 동작
  • 작업지시자 시각 판정 통과 (sample16-hwp5 page 1/3 격차 개선 + HWP3 원본 외곽선 spec 정합 + 일반 HWP5 무회귀)

격차 A(page border)/B(PUA)/C(변환본 식별+ParaShape /4) 실효. 격차 D는 후속 커밋에서 non-variant 회귀(table-vpos-01) 발견 후 self-revert하신 점 — 검증 단계 자정 좋습니다. 격차 D breathing room 포함 잔존 5건은 PR 본문대로 별도 후속 task로 분리합니다.

@edwardkim edwardkim closed this May 19, 2026
edwardkim added a commit that referenced this pull request May 19, 2026
PR #1009 는 머지하지 않음. PR base("67") 가 현 devel(#999/#1005 머지 후
sample16-hwp5=64) 와 불일치 → vpos reset split 적층 시 64→65 over-split.
컨트리뷰터에게 최신 devel rebase + 재진단 요청. PR/이슈 #1007 OPEN 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 19, 2026
@jangster77 — paper_based outline 회귀 사이클 종결 (task877→#920#956#987#1005#1006). 단일 attr & 0x01 비트 해석 모호성을 PageBorderBasis
enum + parser 단계 명시 주입(HWP3/HWP5/HWPX 모두 PaperBased)으로 책임
분리 — interpretation → parser, 사용 → renderer.

clip 정책 변경: !header_inside clip 제거 (cover logo paper-edge 정합),
!footer_inside clip 유지 (페이지 번호 외곽선 바깥 유지). 작업지시자
Hancom Office close-up 시각 판정 기반 — spec(attr bit 1/2) ≠ 한컴 실제
동작, 한컴 정합 우선 명문화.

옵션 A: 본질 커밋 aa8160c cherry-pick (작성자 Taesup Jang 보존, 충돌
없음). 검증: cargo test 1307 + clippy -D + fmt 0 + WASM 4.83MB. sweep
7 fixture: 일반 5종(sample10/exam_kor/exam_math/aift/biz_plan) diff=0
무회귀, sample16-hwp5/hwp3 외곽선만 paper-edge 이동 (텍스트 무변동).
작업지시자 시각 판정 통과.

매직넘버 감사: 신규 0 / 제거 3 (attr & 0x01 ×2, attr & 0x02). 잔존
footer_inside = attr & 0x04 (layout.rs:979) 일관성 정리는 별도 issue
권고. test-image fixture 추가로 그림 wrap 4종 회귀 가드 영구화.

후속: footer_inside 일관성, test-image paragraph 텍스트 라벨 누락
(별도 root cause). #1009 (보류) 컨트리뷰터 rebase 후 재제출 대기.
edwardkim added a commit that referenced this pull request May 20, 2026
…nce 이중 가산 정정

@planet6897 — Task #974(c3e3215, PR #1013 머지) 회귀 bisect 정확
진단 + has_full_para_item case-specific 가드. layout_shape_item()의
!has_real_text 분기에서 FullParagraph 항목 유무로 분기:
- 있으면(빈 문단 호스트): result_y 재진행 생략 → 이중 가산(132.88→66.44px) 차단
- 없으면(선행 표 이어붙은 Shape, hy-001 pi=27): Task #974 동작 유지

PR #1005 격차 D revert(4e3ad58)의 scope 외("variant flag thread 후
variant-only 분기")를 variant 무관 일반 가드로 정밀 해결 — 두 케이스
양립. feedback_pr_supersede_chain + feedback_hancom_compat_specific_
over_general 권위 사례.

옵션 A: 3 본질 커밋 cherry-pick (d7663dd Stage 2 layout.rs PR #1005
영역 --theirs 충돌 해소, a4a3647 Stage 2 v2 has_full_para_item 정밀화
자동 보완, 97c4994 Stage 3 회귀 테스트 좌표 8건 정정) — 작성자
@planet6897 보존. 검증: cargo test 1307(lib) + 전체 통합 + issue_table_
vpos_01_page5_cell_hit_test 13 passed + clippy -D + fmt 0 + WASM 4.83MB
(feedback_push_full_test_required 정합). sweep 10 fixture: hy-001
HWPX/HWP5 diff=0(Task #974 보존), table-vpos-01 1 diff(의도된 pi=34
30.84px 상향), sample16-hwp5/hwp3 외곽선만(텍스트 무변동), aift 2 diff
(의도된 빈 문단 글상자 정정 효과), 일반 fixture diff=0. 작업지시자
비공개 샘플 PR로 시각 판정 생략 수용 결정.

PR #1004(Task #990 + #991 통합) 가 본 PR 완전 포함 + Task #991 추가 —
본 PR 머지 후 #1004는 #991 부분만 재제출 안내 (feedback_small_batch_
release_strategy 정합).
edwardkim pushed a commit that referenced this pull request May 21, 2026
…#1035)

samples/hwp3-sample16-hwp5.hwp (HWP5 변환본) 의 페이지별 paragraph
alignment 가 동일 문서 HWP3 native 와 37.5% (24/64) 만 정합. PR #1009
(Task #1007, closed — sample16-hwp5 +1 over-split 회귀로 close) 의
cross-paragraph vpos reset 감지 휴리스틱 base 적용 + Task #1035 narrow
가드 2 항목 으로 over-split 회피 + alignment 93.75% (60/64) 달성.

PR #1009 close 사유 정정:
| 시도 | sample16-hwp5 | alignment |
|------|---------------|-----------|
| devel baseline | 64 | 24/64 (37.5%) |
| PR #1009 그대로 (0.85+aux) | 65 (+1 회귀) | 23/64 (악화) |
| 본 PR (0.95 + main만) | 64 ✓ | 60/64 (93.75%) ✓ |

PR #1009 의 +1 over-split 직접 원인 = aux_trigger (empty bridge 휴리스틱
false positive). aux_trigger 제거 + high_threshold 0.85→0.95 narrow.

핵심 변경:
- pagination/engine.rs (+71): variant cross-paragraph vpos reset 감지
  - is_hwp3_variant 가드 (일반 HWP 무영향)
  - prev/curr line_seg synth (tag top bit) 아님
  - prev_end_vpos > body × 0.95 (PR #1009 0.85 보다 보수적)
  - curr_first_vpos < 1500 HU (encoder page-reset 신호)
- typeset.rs (+106): 동일 로직 두 경로 정합
- pagination.rs (+5) + rendering.rs (+3): PaginationOpts.is_hwp3_variant 필드 + 전달

variant 식별 인프라 (cfb_reader, model/document.rs, parser/mod.rs,
hwpx/mod.rs) 는 #1005 이미 머지 — 재활용.

검증:
- cargo build --release: warning 0
- cargo clippy --release --lib -- -D warnings: clean
- cargo fmt --all -- --check: clean
- cargo test --release --lib: 1319 passed; 0 failed
- cargo test --release --test issue_1035_alignment: 1 passed (회귀 가드)
- sample16-hwp5 페이지 수 64 유지 (PR #1009 over-split 65 회귀 재발 방지)
- alignment 정합률 측정: 24/64 → 60/64 (93.75%)
- 페이지 수 sweep 25 fixture (변환본 9종 + HWP3 + 일반): 모두 무변동
- 작업지시자 한컴 한글 정답지 시각 검증 — p21 영역 alignment 정합

회귀 가드:
- tests/issue_1035_alignment.rs::hwp3_sample16_hwp5_page_count_64 —
  sample16-hwp5 64 유지 단언

잔존 + 후속:
- 잔존 4 미정합 페이지 (p21 등) + p23 overflow 의 본질 = HWP5 변환본
  paragraph height 가 HWP3 의 약 2배 (font/spacing metric 차이).
  Task #1008 격차 D (폰트 매핑) 영역 연장. 별도 issue 등록 예정 —
  "HWP5 변환본 paragraph height 과대 측정 (HWP3 대비 약 2배)".

closes #1035
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants