fix: 빈 paragraph + 다음 [쪽나누기] case 단독 page 차단 — HWP3 sample18 페이지 수 +2 inflate 해소 (closes #967)#968
fix: 빈 paragraph + 다음 [쪽나누기] case 단독 page 차단 — HWP3 sample18 페이지 수 +2 inflate 해소 (closes #967)#968jangster77 wants to merge 4 commits into
Conversation
… inflate 해소 (closes edwardkim#967) ## 본질 `samples/hwp3-sample18.hwp` (HWP3) 페이지 수 rhwp 69 vs 한컴 67 — **+2 inflate**. ## Root cause `dump-pages` 분석: - Page 2: pi=27 "(빈)" 1개만 (24px), vpos=69356 HU - Page 14: pi=164 "(빈)" 1개만 (25.6px), vpos=69836 HU - pi=28, pi=165 (다음 paragraph) 에 [쪽나누기] (column_type::Page) 빈 paragraph 가 이전 page 잔여 공간 초과 (page 1: body 935px, used 934.2px, 빈 24px → over) → 별도 page 분기 → +1 inflate × 2. `src/renderer/typeset.rs:555-584` 의 `next_will_vpos_reset` 가드: ```rust let next_force_break = next_para.column_type == ColumnBreakType::Page || next_para.column_type == ColumnBreakType::Section; if next_force_break { false // ← 다음 force_break 시 false (hwp-multi-001 회귀 차단 목적) } ``` → pi=28/165 의 [쪽나누기] → next_force_break=true → next_will_vpos_reset=false → 단독 빈페이지 차단 가드 미발동 → pi=27/164 단독 page 생성. ## Fix `src/renderer/typeset.rs:584-604`: 기존 next_will_vpos_reset 가드 직후 별도 분기 추가. ```rust } else if !st.current_items.is_empty() && para_idx + 1 < paragraphs.len() { // [Task edwardkim#967] 빈 paragraph 직후 force page break (쪽나누기) case 가드 let next_para = ¶graphs[para_idx + 1]; let next_force_break = next_para.column_type == ColumnBreakType::Page || next_para.column_type == ColumnBreakType::Section; let is_curr_empty = para.text.is_empty() && para.controls.is_empty(); if next_force_break && is_curr_empty { continue; // 빈 paragraph skip — 단독 page 차단 } } ``` 기존 next_will_vpos_reset 의 next_force_break 제외 조건 (hwp-multi-001 회귀 차단) **보존**. ## 검증 - cargo test --release --lib: 1288 passed, 0 failed, 2 ignored - sample18.hwp 페이지 수: 69 → 67 ✓ (한컴 정합) - 다중 sample 회귀 검증 (16 sample): - sample, sample10/11/13/14/16/19/4/5, table_test*, multi-table-001/002, exam_kor/math/eng: 모두 변경 없음 - **hwp-multi-001 (회귀 차단 case): 변경 없음 ✓** (기존 가드 보존 확인) ## 영향 | 영역 | 영향 | |------|------| | 빈 paragraph + 다음 [쪽나누기] | skip → +1 page inflate 제거 (회귀 fix) | | 비-빈 paragraph + 다음 [쪽나누기] | 영향 없음 | | 빈 paragraph + 다음 일반 paragraph | 영향 없음 (기존 가드) | | hwp-multi-001 (회귀 차단) | 영향 없음 | ## 관련 - 닫힌 issue edwardkim#927 — sample16 페이지 수 inflate (본 fix 와 무관) - 잔존: HWPX sample18-hwp5.hwpx +7 inflate (별도 issue, HWPX 특화 pagination) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…gn vertical bits 파싱 정정 @jangster77 — Issue #965: sample16 page 18 WMF 다이어그램 박스 내부 한글 텍스트 박스 외부 벗어남. Root cause (src/wmf/converter/svg/mod.rs:2208 set_text_align): mode & VTA_TOP(=0x0000) == 0x0000 항상 true → BASELINE/BOTTOM mode 도 VTA_TOP 오매핑 → +ascent shift → baseline 박스 하단 걸침. WMF [MS-WMF] 2.1.2.18 spec 정합. 본질 (svg/mod.rs 3 영역 ~60 lines): - set_text_align (:2208): v_bits = mode & 0x0018 마스킹 + 우선순위 BASELINE→BOTTOM→TOP - ext_text_out (:811): baseline y shift 정합 (VTA_BASELINE=0, BOTTOM=-em*0.2, TOP=+ascent) — font.height < 0 잘못된 보정 제거 - text_out (:1545): META_TEXTOUT 동일 보정 (PR #918 미포함, 본 PR 추가) PR #918 supersede: PR #918 (CLOSED 5/16, +5082/-74 거대 PR, 다양한 부작용 LibreOffice emfio/WASM RasterPlayer 등) → Stage 33-A root cause ~60 lines 만 단독 포팅. feedback_pr_supersede_chain (a) + feedback_small_batch_release_strategy 권위 사례. 본 환경 충돌 수동 해결: orders/20260517.md (--ours + Task #965 작업 일지 갱신). svg/mod.rs auto-merge — devel PR #860/#864 (5/16, EMF/WMF image 렌더) 변경 보존 + PR #966 정정 양립 확인. 자기 검증: cargo test --release --lib 1288 passed / clippy 통과 / 광범위 sweep 7 fixture / 169 페이지 / 169 same / 0 diff / WASM 4.4 MB 재빌드 시각 판정: 작업지시자 시각 검증 통과 (sample16 p18 WMF 박스 한글 텍스트 정상 + WMF sample14/4 + devel PR #860/#864 EMF/WMF 회귀 부재) CI: ✅ Build & Test + CodeQL @jangster77 연속 5 PR (#956~#964) 완결 후 추가 #966 (#968 후속)
- mydocs/pr/archives/pr_966_review.md (WMF SetTextAlign vertical bits 분석) - mydocs/pr/archives/pr_966_report.md (옵션 A + PR #918 supersede) - mydocs/orders/20260517.md PR #966 행 추가 핵심: - mode & VTA_TOP(=0) == 0 항상-true 버그 root cause - WMF [MS-WMF] 2.1.2.18 spec 정합 (v_bits 0x0018 mask) - PR #918 (CLOSED, +5082 거대 PR) → root cause ~60 lines 단독 포팅 - svg/mod.rs auto-merge (devel PR #860/#864 EMF/WMF image 렌더 보존) - sweep 169/169 same + 작업지시자 시각 판정 통과 - Issue #965 close, 추가 PR #968 후속
@jangster77 — Issue #967: HWP3 sample18 페이지 수 rhwp 69 vs 한컴 67 (+2 inflate). 빈 paragraph (pi=27/164) 직후 [쪽나누기] (pi=28/165) → 빈 paragraph 별도 page 분기 → 단독 빈 페이지. Root cause (typeset.rs:555-584): next_force_break (쪽나누기) 시 next_will_vpos_reset 가드 미발동 (hwp-multi-001 회귀 차단 목적) → 빈 paragraph 단독 page 생성. 본질 (src/renderer/typeset.rs:585 별도 분기): next_force_break && is_curr_empty && empty_h_px > avail (overflow) 시에만 continue (skip → 단독 page 차단). fit 가능 시 정상 emit. v2 정밀화: v1 (조건 무관 skip) → aift.hwp snapshot 회귀 → v2 (overflow 한정) → aift.hwp 18 case 정상 emit + sample18 fix. feedback_hancom_compat_specific_over_general 권위 사례 (일반화 위험 발견 후 케이스별 정밀화). 영역 좁힘: 빈 paragraph + 쪽나누기 + overflow 한정 — fit 가능/비-빈/일반 paragraph/hwp-multi-001 영향 없음. 본 환경 cherry-pick 충돌 0건 — typeset.rs auto-merge (devel Task #836 Endnote :1064/1091 보존 + PR #968 :585 분기 양립). orders/20260518.md 신규 (5/18). devel merge commit 2개 cherry-pick 제외 — 본질 3c1ea87 만. 자기 검증: cargo test --release 전체 (lib 1288 + integration svg_snapshot 8) ALL GREEN + cargo clippy --release -D warnings 통과 + 광범위 sweep 7 fixture / 169 페이지 / 169 same / 0 diff (aift.hwp 74 same — v1 회귀 해소 입증) + WASM 4.4 MB 재빌드.⚠️ samples/hwp3-sample18.hwp 영역 영역 PR 미포함 + 본 환경 부재 — 작업지시자 결정 영역 영역 sweep 169/169 same + cargo test 전체 통과 영역 영역 회귀 부재 입증 + sample18 페이지 수 69→67 정합 영역 영역 컨트리뷰터 PR 본문 신뢰 (작업지시자 승인). CI: ✅ Build & Test + CodeQL + Canvas visual diff @jangster77 PR 시리즈 완결 (연속 5 PR #956~#964 + #966 + #968)
|
@jangster77 머지 완료 (commit next_force_break (쪽나누기) 시 next_will_vpos_reset 가드 미발동 (hwp-multi-001 회귀 차단 목적) root cause 정확 진단. v2 정밀화 — v1 (조건 무관 skip) → aift.hwp snapshot 회귀 → v2 (overflow 한정, empty_h_px > avail 시에만 skip) → aift.hwp 18 case 정상 emit + sample18 fix. 일반화 위험 발견 후 케이스별 정밀화 (feedback_hancom_compat_specific_over_general 권위 사례). 본 환경 자기 검증:
참고: CI ✅ Build & Test + CodeQL + Canvas visual diff. 잔존: HWPX sample18-hwp5.hwpx +7 inflate (별도 task). @jangster77 PR 시리즈 (연속 5 PR #956~#964 + #966 + #968) 완결. 수고하셨습니다. |
- mydocs/pr/archives/pr_968_review.md (빈 paragraph + 쪽나누기 v2 정밀화 분석) - mydocs/pr/archives/pr_968_report.md (옵션 A + fixture 부재 작업지시자 결정 + PR 시리즈 총평) - mydocs/orders/20260518.md 신규 PR 처리 섹션 + @jangster77 PR 시리즈 총평 핵심: - next_force_break 가드 미발동 root cause + v2 정밀화 (overflow 한정, aift.hwp 회귀 해소) - typeset.rs auto-merge (devel Task #836 Endnote 보존 + PR #968 :585 분기 양립) - cargo test 전체 + sweep 169/169 same + 기존 HWP3 샘플 시각 판정 통과 - samples/hwp3-sample18.hwp 부재 → 작업지시자 결정 회귀 부재 입증 + PR 본문 신뢰 - Issue #967 close, @jangster77 PR 시리즈 (7 PR #956~#968) 완결 - 잔존: HWPX sample18-hwp5 +7 inflate + sample18 fixture 추가 권장
버전 0.7.11 → 0.7.12 (Cargo.toml + rhwp-vscode/npm-editor/rhwp-studio package.json) CHANGELOG 갱신 (CHANGELOG.md / CHANGELOG_EN.md / rhwp-vscode/CHANGELOG.md) WASM 재빌드 산출물 동기화 (rhwp-studio/public/rhwp.js) @jangster77 7-PR 시리즈 (#956~#968) + Issue #952 5-결함 완결 + WMF #966 + HWP3 #968 + LTO #818 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
요약
samples/hwp3-sample18.hwp(HWP3) 페이지 수 rhwp 69 vs 한컴 67 — +2 inflate.빈 paragraph (pi=27, pi=164) 직후 [쪽나누기] (pi=28, pi=165) case 에서 빈 paragraph 가 별도 page 분기 → 단독 빈 페이지 생성.
Root cause
src/renderer/typeset.rs:555-584의next_will_vpos_reset가드:pi=28/165 의 [쪽나누기] → next_force_break=true → 가드 미발동 → pi=27/164 단독 page 생성.
Fix (refined v2 — overflow 시에만 발동)
기존 가드 직후 별도 분기 추가:
핵심 정밀화: 빈 paragraph 가 실제로 fit 안 되는 case 만 skip. fit 가능한 case (aift.hwp 의 18 case) 는 정상 emit.
CI 회귀 (aift snapshot) 발견 후 v2 로 정밀화. v1 (조건 무관 skip) 은 aift.hwp page 3 의 normally-fitting empty paragraph 도 skip 하여 layout 변경 → snapshot 회귀.
검증
영향
잔존 (별도 task)
관련
Test plan
🤖 Generated with Claude Code