Task #639: 한컴 호환 — cover-style 페이지 자동 쪽번호 미표시 (closes #639)#641
Open
planet6897 wants to merge 10 commits intoedwardkim:develfrom
Open
Task #639: 한컴 호환 — cover-style 페이지 자동 쪽번호 미표시 (closes #639)#641planet6897 wants to merge 10 commits intoedwardkim:develfrom
planet6897 wants to merge 10 commits intoedwardkim:develfrom
Conversation
aift.hwp 페이지 2, 3 (큰 표만 있는 cover-style) 쪽번호 미표시 메커니즘 분석. Task edwardkim#634 종료 후 분리된 후속 issue. 5가지 가설 (cover-style 휴리스틱 / 셀 내부 PageHide / paragraph header 비트 / 표 attr 비트 / 한컴 자체 휴리스틱) 데이터 기반 검증 + 권고안 결정. 본 task 는 read-only 분석. 결정적 룰 발견 시에만 별도 fix issue 분리.
aift.hwp 페이지 2, 3 (큰 표만 있는 cover-style) 미표시 메커니즘 분석 사전 단계. 데이터 수집: - dump-pages 페이지 1~6 구조 (items, body 사용량, 표 비율) - dump 페이지 1/2/3/6 host paragraph (cc, ps_id, controls) - 문서 전체 PageHide 컨트롤 (정확히 2개: 페이지 4 para 2.34, 페이지 5 para 2.54) - 한컴 PDF 측정 (pypdf): 페이지 1, 6, 7 표시 / 페이지 2, 3, 4, 5 미표시 확정 - 표 attr 매트릭스 (페이지 1: 0x04000006, 2: 0x0600000e, 3: 0x0400000e, 6: 0x0600000e) Stage 0 사전 판정: - H1 (cover-style 휴리스틱): 유력 — 페이지 2/3 만 items=1 + 표 89~98% - H2 (셀 내부 PageHide): 기각 — 문서 전체 PageHide 가 정확히 2개 - H3 (paragraph header 비트): 기각 — 페이지 2 (미표시) host 와 페이지 6 (표시) host 거의 동일 - H4 (표 attr 비트): 기각 — 페이지 6 표 attr = 페이지 2 표 attr (0x0600000e) - H5 (한컴 휴리스틱): H1 변형, Stage 2 교차 검증 후 결정 본 단계는 read-only 분석. 코드 변경 없음.
도구: examples/inspect_637.rs (paragraph header raw + cover-candidate enumeration).
검증 결과:
- H1 cover-style 휴리스틱 → 결정적 룰로 정형화: 페이지가 items=1 인 단일
완전한 Table (PartialTable 아님) 을 포함하고 tac=false 일 때 한컴은
쪽번호 미표시.
- 174 샘플 전수 조사: aift.hwp p2, p3 만 매칭 (전체의 0.06%, 한컴 미표시 일치)
- aift.hwp 의 tac=true items=1 페이지 (p74, p75) 는 한컴 표시 — tac=false
가 결정적 분리자임을 검증
- H2, H3, H4 최종 기각:
* H2: 문서 전체 PageHide 정확히 2개 (페이지 4 para 2.34, 페이지 5 para 2.54)
* H3: 페이지 2 host (미표시) 와 페이지 6 host (표시) 의 raw paragraph
header 가 ps_id 외 byte-for-byte 동일 (cc=9, mask=0x00000800,
break_raw=0x04, raw_header_extra 12B 완전 동일)
* H4: 페이지 6 (표시) 표 attr = 0x0600000e (페이지 2 attr 와 동일)
- H5 (한컴 휴리스틱): H1 의 정확형 등가
회귀 위험: 매우 낮음 (174 샘플 중 2 페이지만 영향, 모두 정합성 개선).
Stage 3 사전 권고: 시나리오 (a) 별도 fix issue 분리 후 본 issue close-as-analysis.
본 단계는 read-only 분석. 코드 변경 없음 (example 추가만).
분석 완료 (Stage 0 → Stage 1 → Stage 3, Stage 2 α 결정으로 생략). 결과: - H1 cover-style 휴리스틱이 결정적 룰로 확정: 페이지 items=1 + 완전한 Table (PartialTable 아님) + tac=false → 한컴 미표시 - 174 샘플 전수 조사: aift p2, p3 만 매칭 (전체의 0.06%) - H2/H3/H4 최종 기각 - 회귀 위험 매우 낮음 (영향 페이지 2건, 모두 정합성 개선) 권고: 시나리오 (a) — 별도 fix issue 분리 후 본 issue close-as-analysis-complete. 코드 변경 0 (examples/inspect_637.rs 분석 도구 추가만). orders/20260506.md 갱신 (edwardkim#637 완료 상태 반영).
GitHub Issue 처리: - edwardkim#637 close-as-analysis-complete (분석 완료 코멘트 포함) - edwardkim#639 등록 (fix issue: cover-style 페이지 자동 쪽번호 미표시) orders 갱신: - edwardkim#637 상태: "완료 (분석 close, fix issue edwardkim#639 분리)" - edwardkim#639 신규 등록 entry 추가 (등록만, 진행 대기) milestone v1.0.0 설정은 planet6897 권한 부족으로 실패 — 메인테이너 수동 설정 필요.
한컴 호환 — cover-style 페이지 (items=1 + 완전한 Table + tac=false) 자동 쪽번호 미표시 구현 task. Task edwardkim#637 분석에서 도출된 결정적 룰의 코드 구현. 본질: src/renderer/pagination/engine.rs:finalize_pages 의 page_hide 결정 직후 cover-style 룰 추가 (예상 +15~25 LOC). 단계: 0a 수행계획서 → 0b 구현계획서 → Stage 1 TDD RED → Stage 2 fix → Stage 3 회귀 검증. 회귀 위험 매우 낮음 (Task edwardkim#637 174 샘플 전수 조사로 영향 페이지 2건 확인). orders 갱신 (edwardkim#639 진행중).
정확한 코드 변경 시그니처/위치/edge case 명시.
핵심 변경:
- src/renderer/pagination/engine.rs (+30 LOC):
* finalize_pages 시그니처 확장 (paragraphs &[Paragraph] 추가)
* 신규 헬퍼 is_cover_style_page (단일 단 + items=1 + 완전 Table + tac=false)
* page.page_hide.is_none() 가드로 기존 PageHide 우선
* 룰 매칭 시 PageHide { hide_page_num: true, ..Default::default() } 설정
- src/renderer/layout/integration_tests.rs (+150 LOC):
* test_639_aift_page2_cover_style_no_page_number (RED→GREEN)
* test_639_aift_page3_cover_style_no_page_number (RED→GREEN)
* test_639_aift_page1_shows_page_number (회귀 가드)
* test_639_aift_page6_shows_page_number (회귀 가드)
* test_639_aift_page74_tac_true_table_shows_page_number (회귀 가드, tac=true 결정적 분리자)
Edge case 9개 검토 (None 처리, 다단, PartialTable, Shape, 기존 PageHide 우선 등).
PageNumberAssigner 와의 상호작용: page.page_hide 만 설정, page_number 는
단조 증가 그대로 유지 (Task edwardkim#634 의 페이지 4, 5 PageHide 동작과 일치).
회귀 검증 전략: cargo test sweep + 174 샘플 page_hide diff sweep.
aift.hwp 페이지 2, 3 cover-style 미표시 룰 검증 통합 테스트 추가. fix 미적용 상태에서 RED 시나리오 정확히 재현. 검출 패턴: - render_page_svg_native 의 footer 쪽번호 "- N -" 는 글자별 분리된 <text> 요소로 출력 (y="1079.16", font-size="10") - 헬퍼 count_footer_page_number_glyphs 로 마커 글리프 수 카운트 - 0 = 미표시, 3+ = 표시 5건 테스트: - test_639_aift_page2_cover_style_no_page_number (RED, FAIL 글리프 3) - test_639_aift_page3_cover_style_no_page_number (RED, FAIL 글리프 3) - test_639_aift_page1_shows_page_number (회귀 가드, PASS) - test_639_aift_page6_shows_page_number (회귀 가드, PASS) - test_639_aift_page74_tac_true_table_shows_page_number (회귀 가드, PASS) Stage 2 fix 적용 시 페이지 2, 3 도 PASS 전환 예상. 검증: $ cargo test --release --lib test_639 test result: FAILED. 3 passed; 2 failed; 0 ignored (2 fail 는 페이지 2, 3 RED 의도 — Stage 2 fix 후 GREEN 전환) probe_637.rs: SVG footer 검출 패턴 사전 조사용 일회성 도구 (Stage 3 정리).
룰 구현: 페이지 items=1 + 단일 단 + 완전한 Table (PartialTable 아님) + tac=false → page.page_hide.hide_page_num=true 자동 설정. Task edwardkim#637 분석에서 도출된 결정적 룰. 174 샘플 중 aift.hwp 페이지 2, 3 만 매칭 (전체의 0.06%, 한컴 PDF 미표시와 일치). 구현 발견: rhwp 의 페이지네이션은 두 경로 존재. - TypesetEngine::typeset_section (기본 main path, src/renderer/typeset.rs) - Paginator::paginate_with_measured (RHWP_USE_PAGINATOR=1 fallback, src/renderer/pagination/engine.rs) 두 경로 모두 동일 fix 적용: - finalize_pages 시그니처에 paragraphs: &[Paragraph] 추가 - 기존 PageHide 적용 직후 cover-style 룰 분기 (page.page_hide.is_none() 가드) - 신규 헬퍼 is_cover_style_page (단일 단 + items=1 + Table + tac=false) 검증: - Stage 1 RED 5건 모두 GREEN 전환: test_639_aift_page2_cover_style_no_page_number .. ok test_639_aift_page3_cover_style_no_page_number .. ok test_639_aift_page1_shows_page_number .. ok (회귀 가드) test_639_aift_page6_shows_page_number .. ok (회귀 가드) test_639_aift_page74_tac_true_table_shows_page_number .. ok (tac=true 분리자) - 전체 cargo test sweep: 1139 + 76+ 통합 PASS, 0 failed - clippy warning 0 코드 변경: - src/renderer/pagination/engine.rs: +29 / -2 - src/renderer/typeset.rs: +29 / -2 본 변경은 렌더 시점 derived state 만 영향 (라운드트립/HWPX 호환성 0 영향).
회귀 검증: - 174 샘플 룰 매칭 재확인: aift.hwp p2, p3 만 (Task edwardkim#637 일치) - cargo test --release: 1139 + 76+ 통합 테스트 PASS, 0 failed - clippy: warning 0 - aift.hwp 페이지 카운트 77 무변화 - 다른 173 샘플 0 영향 (회귀 위험 0 확정) aift.hwp 페이지별 footer 글리프 카운트 검증: - p1 (표시): 3 ✓ - p2 (cover-style fix): 0 ✓ - p3 (cover-style fix): 0 ✓ - p4, p5 (PageHide 기반): 0 유지 ✓ - p6 (정상 표시): 3 ✓ - p74 (tac=true items=1): 4 ✓ (tac=true 분리자) 산출물: - mydocs/working/task_m100_639_stage3.md (Stage 3 보고서) - mydocs/report/task_m100_639_report.md (최종 보고서) - orders/20260506.md 갱신 (edwardkim#639 완료) - examples/probe_637.rs 정리 (Stage 1 일회성 도구) 본 task 완료. close-as-fixed 대기.
6 tasks
Contributor
Author
|
PR #640 supersede 확정 작업지시자 결정 (α — 분석+fix 함께 머지) 에 따라 PR #640 (Task #637 분석) 을 close 하고
10 커밋 (5 task637 + 5 task639) 단일 머지로 한컴 호환 cover-style fix 완료. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
핵심 발견 — 두 페이지네이션 경로 존재
rhwp 의 페이지네이션은 두 경로 양분:
`render_page_svg_native` → `build_page_tree` → `find_page` 경로는 TypesetEngine 사용.
페이지 단위 derived state (page_hide 등) 변경 시 양쪽 동일하게 적용 필수.
구현 룰
```rust
// finalize_pages 내부, 기존 PageHide 적용 직후
if page.page_hide.is_none() && Self::is_cover_style_page(page, paragraphs) {
page.page_hide = Some(crate::model::control::PageHide {
hide_page_num: true,
..Default::default()
});
}
fn is_cover_style_page(page: &PageContent, paragraphs: &[Paragraph]) -> bool {
if page.column_contents.len() != 1 { return false; }
let items = &page.column_contents[0].items;
if items.len() != 1 { return false; }
let (para_idx, ctrl_idx) = match &items[0] {
PageItem::Table { para_index, control_index } => (*para_index, *control_index),
_ => return false,
};
let Some(para) = paragraphs.get(para_idx) else { return false; };
let Some(Control::Table(t)) = para.controls.get(ctrl_idx) else { return false; };
!t.common.treat_as_char
}
```
`page.page_hide.is_none()` 가드로 기존 PageHide 컨트롤 우선. 매칭 시 `hide_page_num=true` 만 설정.
검증
신규 통합 테스트 (5건)
검출 패턴: SVG 의 `<text y="1079.16" font-size="10">` 글리프 카운트.
174 샘플 룰 매칭 재확인
```
Total pages matching rule (items=1 + Table + tac=false): 2
aift.hwp page 2
aift.hwp page 3
```
Task #637 사전 조사 결과와 정확 일치. 174 샘플 중 영향 페이지 2 만.
전체 회귀 검증
회귀 위험 평가
메모리 룰 준수
Test plan
산출물 (mydocs/)
🤖 Generated with Claude Code