Skip to content

Task #825/#826 + #824: sample11.hwp 머리말 영역 결함 3건 통합 정정#832

Open
jangster77 wants to merge 29 commits into
edwardkim:develfrom
jangster77:local/task825_826
Open

Task #825/#826 + #824: sample11.hwp 머리말 영역 결함 3건 통합 정정#832
jangster77 wants to merge 29 commits into
edwardkim:develfrom
jangster77:local/task825_826

Conversation

@jangster77
Copy link
Copy Markdown
Contributor

Summary

samples/hwp3-sample11.hwp 시각 검증 중 발견된 머리말 영역 결함 3건 통합 정정. PR #827 (Task #824 단독) 은 close 되었고 본 PR 에 commits 흡수.

결함별 정정

#824 — HWP3 임베디드 그림 external_path 오표시

src/parser/hwp3/mod.rs:935-952 Task #741pic_type 분기 누락 (외부파일/OLE/Embedded 동일 처리) → 임베디드 그림 (pic_type==2) 도 external_path = Some("E$$00000.jpg") 설정 → rhwp-studio 그림 속성 dialog 가 외부 파일로 오표시. 정정: if pic_type == 0 가드 1줄.

#825 — 머리말/꼬리말 picture 선택 + dialog

다층 결함:

  1. layout_picture 가 머리말 호출 시 indices None 전달 → ImageNode 메타 부재
  2. TAC 인라인 picture (sample11 머리말 ) 는 layout_paragraph 경로 (para_index = usize::MAX-i 인코딩)
  3. getPictureProperties 가 본문 lookup 만 → 머리말 도달 불가
  4. cursor 의 selectedPictureRef 에 headerFooter 정보 부재
  5. 머리말 편집 모드 click handler 가 picture hit-test 우선 미지원

정정 (Stage 3a~3d, 다중 보강):

  • ImageNode.header_footer_ref 필드 + HeaderFooterImageRef / HeaderFooterKind 신규 타입
  • layout_picture_full() (header_footer_ref 파라미터 + 메타 전달)
  • propagate_header_footer_ref() 후처리 (TAC 인라인 picture 의 ImageNode 통합 정합 + para_index 정규화)
  • get_header_footer_picture_properties_native + set_header_footer_picture_properties_native + WASM 바인딩
  • getPageControlLayout JSON 에 headerFooter 필드 출력
  • rhwp-studio: findPictureAtClick / cursor.selectedPictureRef.headerFooter 전파 (line/shape/image 3 path 모두) + 머리말 모드 click handler picture 우선 + picture-props-dialog 분기

#826 — HWP3 PUA U+F080F / U+F0827 글리프 부재

HWP3 char 0x301C / 0x303D → 한컴 PUA U+F080F / U+F0827 매핑 (PR #753 본문에 명시된 후속 항목). 한컴 함초롬 폰트는 PUA glyph 보유, rhwp-studio 번들 폰트 (오픈 라이선스) 부재.

정정: paragraph_layout.rs:3052 기존 0xF00D0~0xF09FF match 에 2 케이스 추가:

  • U+F080FU+2501 (━ HEAVY HORIZONTAL)
  • U+F0827U+25A0 (■ BLACK SQUARE, 잠정)

측정/렌더링 양쪽 자동 적용. IR/parser 무변경 → HWP5 cross-ref 정합 유지.

검증

  • cargo test --release: 1360 passed, 0 failed (신규 9 테스트 통합)
  • cargo test --test issue_824 / issue_825 / issue_826: 모두 PASS
  • cargo clippy --release -- -D warnings: clean
  • npx tsc --noEmit: clean
  • ✅ SVG 회귀 (6 sample × 886 page 비교): 회귀 0 (sample10 272 diff = 의도 PUA 변환만, 1:1 정확 변환 입증)
    • sample10: U+F080F 24,695개 → U+2501 (━) 24,695개
    • sample11: U+F080F 27,376개 → U+2501 (━) 27,376개
  • ✅ WASM 재빌드 26.07s wasm-opt 완료
  • ✅ 작업지시자 시각 판정 통과 (4회 iteration: 머리말 모드 click → 본문 모드 click → TAC 후처리 → Task HWP3 임베디드 그림이 외부 파일 참조로 잘못 표시됨 (그림 속성 dialog 파일 이름 오표시) #824 merge)

Fixture 추가

  • samples/hwp3-sample11.hwp (391 KB)
  • samples/hwp3-sample11-hwp5.hwp + -hwpx.hwpx
  • pdf/hwp3-sample11-hwpx-2022.pdf (27 MB, LFS 임계 50 MB 미만)

후속 (별도 이슈)

PR #827 close 사유

Task #824 (PR #827) 의 4 commits 가 본 브랜치에 통합되어 close. 본 PR 에서 일괄 머지.

closes #824
closes #825
closes #826

Test plan

🤖 Generated with Claude Code

jangster77 added 18 commits May 11, 2026 15:59
본질: HWP3 임베디드 그림(pic_type=2)에 external_path 가 잘못 설정되는
결함을 객관 테스트로 입증. 한컴오피스 2022 는 "문서에 포함" 으로 표시하나
rhwp-studio 그림 속성 dialog 는 "E$$00000.jpg" 를 외부 파일로 표시.

산출물:
- tests/issue_824.rs — 2개 테스트
  - issue_824_embedded_picture_no_external_path → FAIL (RED)
  - issue_824_external_picture_keeps_external_path → PASS (회귀 가드)
- fixture: hwp3-sample11.hwp + 변환본 + 한컴 2022 PDF
- mydocs/plans/task_m100_824.md (수행계획서)
- mydocs/plans/task_m100_824_impl.md (구현계획서)
- mydocs/working/task_m100_824_stage1.md (본 단계 보고서)

다음 단계: Stage 2 (GREEN) — pic_type 분기 수정.
본질: HWP3 그림 종류 (offset 74) 분기 누락 정정. pic_type == 0 (외부 파일)
만 external_path 설정, pic_type == 1 (OLE) / 2 (Embedded) 는 pic_name 이
내부 참조명이므로 미설정 (한컴오피스 2022 정합).

수정: src/parser/hwp3/mod.rs +1줄 if (bin_data_id 매핑은 type 무관 유지).

검증: cargo test --test issue_824 → 2 PASS
- issue_824_embedded_picture_no_external_path → ok (Stage 1 RED → GREEN)
- issue_824_external_picture_keeps_external_path → ok (회귀 가드 유지)

다음 단계: Stage 3 (회귀) — 전체 cargo test + HWP3 sample 5개 SVG 회귀.
…e 1031 page bit-identical

본질: pic_type 분기 수정의 회귀 영향 검증.

검증 결과:
- cargo test --release: 1353 passed, 0 failed
- cargo test --test issue_824: 2 PASS (embedded + external 가드)
- cargo clippy --release -- -D warnings: clean
- SVG 회귀: 5 sample (sample/sample4/sample5/sample10/sample11) × 1031 page
  baseline vs patched bit-identical (diff 0)

해석: external_path 는 SVG 출력 layout/렌더링에 영향 없음 (placeholder 분기는
web_canvas.rs 만 존재, native SVG 경로에 placeholder 코드 없음). 의도 효과
(Canvas placeholder + 그림 속성 dialog) 는 Stage 4 시각 검증으로 확인.

다음 단계: Stage 4 (시각 검증) — WASM 재빌드 + rhwp-studio 작업지시자 시각 판정.
본질: 작업지시자 시각 판정 통과 — sample11 임베디드 그림 그림 속성 dialog
"파일 이름 빈 값 + 문서에 포함 체크" 한컴오피스 2022 정합 + sample10 외부
file path 그림 회귀 부재.

산출물:
- mydocs/report/task_m100_824_report.md (최종 보고서)
- mydocs/orders/20260511.md (Task edwardkim#824 entry 추가)

추가 발견 결함 (별도 이슈 분리 예정): 머리말 영역 그림 우클릭 → "개체 속성"
dialog 미표시. rhwp-studio context-menu / picture-props-dialog UI 결함으로
Task edwardkim#824 (HWP3 parser) 본질과 다름.

closes edwardkim#824
본질: 두 결함의 정확한 구현 위치 확정 + RED 테스트로 결함 객관 입증.

산출물:
- tests/issue_826.rs (4 테스트) — PUA U+F080F/U+F0827 substitution
  - issue_826_pua_f080f_substitution → FAIL (RED)
  - issue_826_pua_f0827_substitution → FAIL (RED)
  - issue_826_non_pua_passthrough → PASS (회귀 가드)
  - issue_826_other_pua_existing_unchanged → PASS (기존 PUA 정합 가드)
- tests/issue_825.rs (2 테스트) — 머리말 그림 lookup
  - issue_825_sample11_has_header_picture → PASS (fixture sanity)
  - issue_825_header_picture_lookup_via_existing_api_fails → PASS
    (현행 API 한계 문서화 — Stage 3 후 신규 API 테스트 추가)
- src/renderer/layout.rs / paragraph_layout.rs:
  map_pua_bullet_char pub(crate) → pub (통합 테스트 직접 검증)
- samples/hwp3-sample11.hwp — fixture 추가 (Task edwardkim#824 PR edwardkim#827 머지 시 정합)
- mydocs/plans/task_m100_825_826.md (수행계획서)
- mydocs/plans/task_m100_825_826_impl.md (구현계획서)
- mydocs/working/task_m100_825_826_stage1.md (본 단계 보고서)

조사 결과:
- edwardkim#826 위치: paragraph_layout.rs:3052 0xF00D0~0xF09FF match 에 2 케이스 추가
- edwardkim#825 정정 방향: WASM API 에 optional header/footer path 파라미터 추가

다음 단계: Stage 2 (GREEN edwardkim#826) — PUA substitution 2 케이스 추가.
…ubstitution

본질: HWP3 한컴 PUA → 표준 Unicode render-time substitution 추가
(PR edwardkim#753 후속 — johab.rs:65,67 의 PUA 매핑 폰트 fallback).

수정: src/renderer/layout/paragraph_layout.rs:3052 기존 0xF00D0~0xF09FF
match 에 2 케이스 추가 (+6/-0, 코멘트 4 + match arm 2):
  0xF080F => U+2501 (━ HEAVY HORIZONTAL) — 한컴 굵은 가로선
  0xF0827 => U+25A0 (■ BLACK SQUARE) — 잠정, 시각 판정 후 조정

기존 호출처 (web_canvas.rs:1597 / paragraph_layout.rs:223 / 2933) 자동
적용 — 측정/렌더링 양쪽 정합. IR 보존 (parser/HWP5 cross-ref 무영향).

검증: cargo test --test issue_826 → 4 PASS
- issue_826_pua_f080f_substitution → ok (RED → GREEN)
- issue_826_pua_f0827_substitution → ok (RED → GREEN)
- issue_826_non_pua_passthrough → ok (회귀 가드)
- issue_826_other_pua_existing_unchanged → ok (기존 PUA 정합)

다음 단계: Stage 3 (GREEN edwardkim#825) — WASM API header/footer path 확장.
…ture 도달 가능

본질: 머리말/꼬리말 안 picture 가 ImageNode 인덱스 정보 None 으로 렌더링되어
rhwp-studio findPictureAtClick 가 필터링 → 선택/dialog 불가. 본 commit 은
Rust 측 render chain + WASM API 추가 (UI 측 Stage 3c 별도).

수정:
- render_tree.rs: ImageNode 에 header_footer_ref 필드 + HeaderFooterImageRef
  / HeaderFooterKind 신규 타입.
- layout/picture_footnote.rs: layout_picture_full() 신규 (header_footer_ref
  파라미터). 기존 layout_picture() 는 None 으로 래퍼.
- layout.rs: layout_header_footer_paragraphs 시그니처 확장
  (outer_section_index + outer_hf_ref). build_header / build_footer 호출처
  에서 HeaderFooterImageRef 생성하여 전달.
- queries/rendering.rs: getPageControlLayout JSON 에 headerFooter 필드 출력
  (kind / outerParaIdx / outerControlIdx).
- commands/object_ops.rs: get_header_footer_picture_properties_native 신규
  + format_picture_properties_json helper 분리 (코드 재사용).
- wasm_api.rs: getHeaderFooterPictureProperties WASM 바인딩 추가.
- tests/issue_825.rs: 신규 GREEN 테스트 추가
  (issue_825_header_picture_lookup_via_new_api_succeeds).

검증: cargo test --test issue_825 → 3 PASS
- issue_825_sample11_has_header_picture (sanity)
- issue_825_header_picture_lookup_via_existing_api_fails (회귀 가드)
- issue_825_header_picture_lookup_via_new_api_succeeds (GREEN, 신규 API)

다음 단계: Stage 3c — rhwp-studio findPictureAtClick / ref / dialog dispatch.
…alog (view-only)

본질: render chain + WASM API (Stage 3a+3b) 위에 UI 레이어 통합 — 머리말/꼬리말
picture 클릭 hit-test → context-menu → "개체 속성" dialog 표시까지 view-only chain
완성. Stage 3 (3a+3b+3c) 마무리.

수정:
- input-handler-picture.ts: findPictureAtClick 반환 타입에 headerFooter 추가
  + 모든 return path 에 ctrl.headerFooter 전파.
- wasm-bridge.ts: getHeaderFooterPictureProperties WASM 호출 메서드 신규.
- picture-props-dialog.ts: open() 시그니처에 optional headerFooter 추가
  → 신규 API 사용 분기 (본문은 기존 API 그대로).
- insert.ts: insert:picture-props 커맨드 dispatch 시 ref.headerFooter 전달.

검증:
- npx tsc --noEmit: clean
- wasm-pack build: 18.56s + wasm-opt 25.51s 완료

알려진 제약 (별도 follow-up):
- setPictureProperties (저장) 경로는 본문 lookup 만 → 머리말 그림 속성 저장
  미지원. setHeaderFooterPictureProperties 신규 API + dialog 분기 필요.
- 머리말 진입 모드 / 머리말 picture 신규 삽입 기능 부재 (별도 task).

다음 단계: Stage 4 (회귀) — cargo test + clippy + SVG 회귀 + WASM 재빌드.
… SVG 회귀 의도 변화만

본질: 두 결함 정정 (edwardkim#826 PUA substitution + edwardkim#825 header/footer picture lookup)
의 회귀 영향 객관 검증.

검증 결과:
- cargo test --release: 1358 passed, 0 failed (신규 7 테스트 포함)
- cargo clippy --release -- -D warnings: clean
- npx tsc --noEmit: clean
- WASM 재빌드: 25.51s wasm-opt 완료
- SVG 회귀 (6 sample × 1038 page):
  · 4 sample (sample/sample4/sample5/pic-in-head-02): bit-identical (회귀 0)
  · sample10 (763 page) / sample11 (152 page): 423 diff = 의도 PUA 변환

PUA 변환 1:1 입증:
- sample10: U+F080F 24,695개 → U+2501 (━) 24,695개 (1:1)
- sample11: U+F080F 27,376개 → U+2501 (━) 27,376개 (1:1)
- 합계 52,071개 PUA 정확 변환, 다른 곳 영향 0

해석: Stage 3 (render chain + WASM API + UI) 는 메타데이터/신규 경로 추가
이므로 기존 SVG 출력 무영향. 모든 diff 는 Stage 2 PUA substitution 의도 효과.

다음 단계: Stage 5 (시각 검증) — rhwp-studio dev 서버 + 작업지시자 시각 판정.
…aderFooter 보존

본질: Stage 3c 시각 검증 결과 — 머리말 편집 모드 안에서 picture 클릭이 텍스트
hit-test 로 우선 처리되어 picture selection 활성화 안 됨 → context menu 가
TEXT 메뉴만 표시 (개체 속성 항목 부재).

수정:
- input-handler-mouse.ts onMouseDown: 머리말/꼬리말 모드 클릭 시 hitTestInHeaderFooter
  앞에 findPictureAtClick 우선 호출. picHit 가 있으면 picture object selection
  으로 진입 (headerFooter marker 보존하여 enterPictureObjectSelectionDirect 전달).
- cursor.ts: selectedPictureRef 타입에 headerFooter 추가
  + enterPictureObjectSelectionDirect 시그니처 확장 (headerFooter 파라미터)
  + getSelectedPictureRef 반환 타입 확장.

이로써 머리말 편집 모드 → picture 클릭 → object selection → 우클릭 → "개체
속성" 메뉴 → dialog 표시 chain 정상 동작.

검증: cargo test --test issue_825 / issue_826 → 모두 PASS, tsc --noEmit clean.

다음 단계: WASM/Vite 재기동 후 시각 재검증.
…리말 그림 속성 저장 지원

본질: Stage 3 view-only 한계 해소 — set 경로 추가로 머리말/꼬리말 그림 속성
변경 후 [설정] 버튼으로 정상 저장 가능. 데이터 손상 위험 제거.

수정:
- src/document_core/commands/object_ops.rs:
  · apply_picture_props_inner(pic, props_json) -> bool 헬퍼 분리 (mutation only,
    후처리 없음). caption_created 반환.
  · set_picture_properties_native: 헬퍼 사용 + 기존 후처리 (AutoNumber + recompose
    + paginate + event_log) 호출.
  · set_header_footer_picture_properties_native 신규: 5-tuple lookup → 헬퍼
    호출 → 후처리 (AutoNumber 신규 캡션은 NotSupported 에러로 명시 거부).
- src/wasm_api.rs: setHeaderFooterPictureProperties WASM 바인딩 추가.
- rhwp-studio/src/core/wasm-bridge.ts: setHeaderFooterPictureProperties 메서드.
- rhwp-studio/src/ui/picture-props-dialog.ts:
  · headerFooter 필드 신설 + open() 시 저장.
  · 저장 시 this.headerFooter 분기 → setHeaderFooterPictureProperties 호출.

알려진 제약:
- 머리말/꼬리말 그림 캡션 신규 생성은 NotSupported 에러 (현행 dialog UI 가
  머리말 picture 캡션 신규 생성을 노출하지 않으므로 실용 영향 없음).

검증:
- cargo build --release: clean
- cargo test --release: 1358 passed, 0 failed
- npx tsc --noEmit: clean
- wasm-pack build: 24.53s wasm-opt 완료

다음 단계: Stage 4 회귀 재검증 (set 추가 반영) + Stage 5 시각 + PR.
…ct 호출에 headerFooter 전파

본질: Stage 3c 보강 (commit 36f9d86) 은 머리말 편집 모드 안 클릭 path 만
headerFooter 전달. 본문 모드에서 머리말 picture 클릭 → 다른 enterPicture
ObjectSelectionDirect 호출 path 들 (line 696/710/740) 에서 headerFooter
누락 → cursor.selectedPictureRef.headerFooter 가 undefined → dialog.open
이 본문 API 호출 → 실패 → dialog 미표시.

수정:
- input-handler-mouse.ts 의 line/shape/image 3개 enterPictureObjectSelectionDirect
  호출 모두 picHit.headerFooter 전파.
- input-handler.ts getSelectedPictureRef 반환 타입에 headerFooter 추가
  (TypeScript 타입 정합 — 런타임 동작은 이전부터 정상이었으나 (ref as any)
  캐스팅으로 우회).
- insert.ts: (ref as any).headerFooter → ref.headerFooter (타입 정합 후 cast 제거).

검증: tsc --noEmit clean.

이로써 본문 모드에서 머리말 picture 클릭 → 우클릭 → 개체 속성 → dialog 표시
chain 완성.
…처리 + 인덱스 정규화

본질: Stage 3a 가 비-TAC picture path (layout_picture_full) 만 처리. TAC 인라인
picture (예: hwp3-sample11.hwp 머리말 DCT 로고  마커) 는 layout_paragraph
경로에서 ImageNode 생성되어 header_footer_ref 미설정 + para_index = usize::MAX-i
인코딩으로 저장. 결과: getPageControlLayout JSON 에 headerFooter 부재 +
paraIdx = u32::MAX → dialog open 시 "문단 인덱스 4294967295 범위 초과" 에러.

수정: src/renderer/layout.rs
- propagate_header_footer_ref(node, outer_ref, section_index) helper 신규.
  재귀적으로 모든 ImageNode 에 header_footer_ref 부여 + para_index 정규화
  (usize::MAX - i → i).
- build_header / build_footer 마지막에 호출 (expand_bbox + cell_clip 직후).

검증:
- cargo test --release --test issue_825 / issue_826: 모두 PASS
- getPageControlLayout JSON 확인:
  Before: paraIdx:18446744073709551615, headerFooter 부재
  After : paraIdx:0, headerFooter:{kind:"header",outerParaIdx:23,outerControlIdx:0}
- WASM 재빌드 25.15s 완료

이제 머리말/꼬리말 picture click → ref.headerFooter 정상 동반 → dialog
getHeaderFooterPictureProperties 호출 → 정상 표시 chain 완성.
…ic_type 분기) 통합

Task edwardkim#824 PR edwardkim#827 머지 대기 중이나, 본 브랜치 시각 검증에서 Task edwardkim#824 결함
(임베디드 그림 external_path 오표시) 이 노출되어 통합 적용. PR edwardkim#827 머지 후
upstream/devel 사이클에서 동일 commit SHA 로 정합 (no conflict 예상).
… clippy 정정 + SVG 회귀 의도 변화만

본질: Task edwardkim#824 merge 포함 통합 회귀 검증 + clippy lint 정리.

검증 결과:
- cargo test --release: 1360 passed, 0 failed (신규 9 테스트 통합)
- cargo clippy --release -- -D warnings: clean (1건 정정 후)
  · src/document_core/commands/object_ops.rs:243-245
    set_picture_properties_native 의 불필요한 return → if/else 표현식
- npx tsc --noEmit: clean
- WASM 재빌드: 26.07s wasm-opt 완료
- SVG 회귀 (6 sample, baseline 비교 가능 5 sample × 886 page):
  · 4 sample (sample/sample4/sample5/pic-in-head-02): bit-identical (회귀 0)
  · sample10 (763 page): 272 diff = 의도 PUA 변환 (Stage 2)
  · sample11: 본 task 신규 fixture (baseline 부재)

Stage 3 진행 중 발견 + 정합된 4건 결함 (Stage 3c 보강 + 추가 보강 + Stage 3a
보강 + Task edwardkim#824 merge) 모두 회귀 zero 확인.

추가 발견 edwardkim#831 (picture 회전/대칭 미지원) 별도 이슈 등록 — 본 task scope 외.

다음 단계: Stage 5 — 작업지시자 시각 검증 + 최종 보고서 + 통합 PR.
…edwardkim#824 + edwardkim#825 + edwardkim#826

본질: sample11.hwp 머리말 영역 결함 3건 (Task edwardkim#824 임베디드 그림 +
Task edwardkim#825 머리말 picture 선택+dialog + Task edwardkim#826 PUA glyph fallback) 통합
정정 완료. 작업지시자 시각 판정 4회 iteration 통과.

산출물:
- mydocs/report/task_m100_825_826_report.md (최종 보고서)
- mydocs/orders/20260511.md (Task edwardkim#825/edwardkim#826 entry 추가)

시각 판정 통과 확인:
- ✅ sample11 머리말 가로선 패턴: ━━━ 정상 (edwardkim#826)
- ✅ 머리말 DCT 그림 클릭 → 선택 핸들 (edwardkim#825 hit-test)
- ✅ 우클릭 → "개체 속성" 메뉴 (edwardkim#825 context menu)
- ✅ 개체 속성 dialog 표시 (edwardkim#825 view)
- ✅ 파일 이름 빈 값 + 문서에 포함 체크 (edwardkim#824 한컴 정합)
- ✅ 속성 변경 + 저장 작동 (edwardkim#825 set 경로)

알려진 제약 (별도 follow-up):
- 머리말/꼬리말 picture 캡션 신규 생성 (NotSupported 에러 유지)
- 머리말 picture 신규 삽입/삭제 (별도 task)
- picture (이미지) 회전/대칭 — Issue edwardkim#831 별도 등록

closes edwardkim#824
closes edwardkim#825
closes edwardkim#826
@jangster77 jangster77 force-pushed the local/task825_826 branch from a571edf to 53a170e Compare May 11, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant