Skip to content

Task #631: HWP 권위값 더블체크로 vpos-reset 인접 line 보존#632

Closed
planet6897 wants to merge 6 commits into
edwardkim:develfrom
planet6897:pr-task631
Closed

Task #631: HWP 권위값 더블체크로 vpos-reset 인접 line 보존#632
planet6897 wants to merge 6 commits into
edwardkim:develfrom
planet6897:pr-task631

Conversation

@planet6897

Copy link
Copy Markdown
Contributor

Summary

aift.hwp page 18 (PDF page 12) 의 pi=222 단락에서 PDF 정답은 첫 2줄을 page 18 에 담지만, 우리 렌더러는 1줄만 담고 line 1~3 을 page 19 로 밀던 회귀를 수정.

typeset.rs::typeset_paragraph 줄 단위 분할 루프에 HWP 권위값 더블체크 를 추가하여 한컴 엔진이 LINE_SEG vpos-reset 으로 명시한 페이지 경계를 존중하도록 함.

이는 Task #332 stage4b 커밋(0211e57)에서 명시적으로 알려진 회귀 — aift pi=222, 21_언어 pi=10 line 1, hwp-multi-002 pi=68 — 가운데 마지막 미해결분.

원인

typeset.rs:1046typeset.rs:1074 두 곳에서 LAYOUT_DRIFT_SAFETY_PX = 10 이 차감되어 합계 20px 보수 마진.

pi=222 진입 시점:

  • current_height = 912.2px, base_available_height = 971.4px
  • avail_for_lines = 961.4 - 912.2 - 10 = 39.2
  • li=1: `25.6 + 16 = 41.6 > 39.2` → break (line 1 탈락)

20px 보수 마진이 본문 잔여 59.2px → 39.2px 로 축소시켜 2줄 합(41.6) 을 넣지 못함.

수정 내용

`src/renderer/typeset.rs:1078~1098` — 줄 단위 분할 루프 break 직전 HWP 권위값 우회:

```rust
for li in cursor_line..line_count {
let content_h = fmt.line_heights[li];
if cumulative + content_h > avail_for_lines && li > cursor_line {
// [Task #631] HWP 권위값 더블체크
let hwp_authoritative = para.line_segs.get(li + 1)
.map(|next| next.vertical_pos == 0)
.unwrap_or(false)
&& para.line_segs.get(li).map(|cur| {
let bottom_px = crate::renderer::hwpunit_to_px(
cur.vertical_pos + cur.line_height, self.dpi);
bottom_px <= st.base_available_height()
}).unwrap_or(false);
if !hwp_authoritative {
break;
}
}
cumulative += fmt.line_advance(li);
end_line = li + 1;
}
```

조건 (모두 AND):

  1. typeset 누적 추정으로 fit 실패 (기존 break 조건)
  2. 다음 줄의 vpos == 0 — HWP 가 명시적으로 페이지 경계 신호 인코딩
  3. 현재 줄의 vpos+lh ≤ body_available_height — HWP 좌표상 본문 안

조건 2~3 이 모두 참일 때만 break 우회. 조건 2가 매우 좁아 회귀 위험 최소.

검증 결과

단위 테스트

  • `cargo test --lib`: 1125 passed; 0 failed; 2 ignored

광범위 회귀 검증 (155 샘플 / 1255 페이지)

byte-level SVG 비교 (HEAD~5 vs HEAD):

카테고리 샘플 효과
의도된 수정 aift, 20250130-hongbo(-no), hwp3-sample4/5 page 끝 1~수 줄 페이지 내 복원
회복 — 페이지 통합 loading-fail-01 17→16 페이지, drift 잉여 페이지 통합
회복 — 누락 콘텐츠 복구 hwpctl_API_v2.4 +2 페이지, +460 text elements
회복 — 누락 콘텐츠 복구 hwpspec +1 페이지, +855 text elements
변화 없음 (147개 샘플) exam_kor/eng/math/science, synam-001, hwp-multi-002, 21_언어, 2010-01-06 등 byte 동일

부정적 회귀 0건. 1300+ 누락 콘텐츠 복구.

타겟 케이스 (aift page 18)

```
Before:
페이지 18 단 0 (items=15, used=937.8px, hwp_used≈922.4px, diff=+15.4px)
PartialParagraph pi=222 lines=0..1 vpos=67980..69900 ← 1줄만

After:
페이지 18 단 0 (items=15, used=963.4px, hwp_used≈948.0px, diff=+15.4px)
PartialParagraph pi=222 lines=0..2 vpos=67980..0 [vpos-reset@line2] ← 2줄, PDF 일치
```

Test plan

산출물

  • `mydocs/plans/task_m100_631.md` — 수행계획서
  • `mydocs/plans/task_m100_631_impl.md` — 구현계획서
  • `mydocs/working/task_m100_631_stage{1,2,3}.md` — 단계별 보고서
  • `mydocs/report/task_m100_631_report.md` — 최종 보고서
  • `mydocs/orders/20260506.md` — 오늘할일

후속 검토 권고

  • 본 권위값 더블체크는 `vpos-reset` 신호가 인접한 경우만 적용. HWP 파일이 신호 없이 단순히 페이지 끝까지 채울 수 있는 케이스는 여전히 보수 마진(20px) 적용. 추가 회귀 발견 시 조건 완화 검토.
  • `RHWP_USE_PAGINATOR=1` 경로의 `engine.rs::paginate_text_lines` 도 동일 마진을 가질 가능성. 본 task 범위 외.

closes #631

planet6897 added 6 commits May 6, 2026 12:34
- typeset.rs::typeset_paragraph 줄 단위 분할 루프에 HWP 권위값 우회 추가
- 다음 줄 vpos==0 + 현재 줄 vpos+lh<=body_h 일 때만 LAYOUT_DRIFT_SAFETY_PX 마진 우회
- aift.hwp 18페이지 pi=222: 1줄 → 2줄 (PDF 일치)
- cargo test --lib 1125 passed
- 주요 9개 샘플 SVG byte 비교: aift 2페이지 외 회귀 0건

refs edwardkim#631
- aift, hongbo, hwp3-sample4/5, loading-fail-01: 페이지 끝 1줄 복원 패턴
- hwpctl_API_v2.4: +2페이지/+460 text — 누락 콘텐츠 복구
- hwpspec: +1페이지/+855 text — 누락 콘텐츠 복구
- exam_kor/eng/math/science, synam-001, hwp-multi-002, 21_언어, 2010-01-06: byte 동일
- cargo test --lib 1125 passed

refs edwardkim#631
- 원인: typeset.rs::typeset_paragraph LAYOUT_DRIFT_SAFETY_PX 이중 차감(20px)
- 수정: HWP 권위값 더블체크 (다음 줄 vpos==0 + 현재 줄 vpos+lh<=body_h)
- 결과: aift page 18 = 2줄 (PDF 일치)
- 회귀 검증: 155 샘플 / 1255 페이지 / 부정 회귀 0건
- 부가 효과: 6개 샘플 1300+ 누락 콘텐츠 복구
- Task edwardkim#332 stage4b 알려진 회귀 (aift pi=222) 종결

closes edwardkim#631
@edwardkim

Copy link
Copy Markdown
Owner

PR #632 처리 결과

cherry-pick 머지 완료 (51c22a6 Stage 1 + 7127ded Stage 2 구현계획 + 5fdc096 Stage 2 정정 + e415f62 Stage 3 + e098562 Stage 4 보고서). PR #632 close + Issue #631 close 처리.

처리 옵션

옵션 B — 5 commits cherry-pick (samples/aift.pdf 영역 제외, 본 환경 명명 규약 영역 정합).

본 환경 영역에 pdf/aift-2022.pdf (PR #670 영구 보존 영역, 한글 2022 PDF) 영역이 이미 존재 — PR 의 samples/aift.pdf 영역 (다른 SHA) 영역과 명명 규약 영역 충돌 영역으로 제외.

본 환경 결정적 검증 (모두 통과)

항목 결과
cargo test --lib --release 1156 passed (회귀 0)
cargo test --test svg_snapshot --release 7/7
cargo test --test issue_546 / issue_554 / issue_418 / issue_501 1/1, 12/12, 1/1, 1/1
cargo clippy --lib -- -D warnings 0
cargo build --release 통과
Docker WASM 빌드 4,577,370 bytes (PR #627 baseline 4,578,751 -1,381 bytes)
rhwp-studio/public/{rhwp_bg.wasm, rhwp.js} 갱신 (vite dev server web 영역)

권위 영역 100% 일치 (본 환경 직접 측정)

항목 PR 본문 본 환경 측정
페이지 18 pi=222 lines=0..2 lines=0..2 vpos=67980..0 [vpos-reset@line2]
페이지 19 pi=222 lines=2..4 lines=2..4 vpos=0..1920 [vpos-reset@line2]

PR 본문 명세와 정확 일치. dump-pages 출력의 [vpos-reset@line2] 마커 영역 본 환경 직접 재현.

본질 정정의 가치

HWP 권위값 더블체크 (구조적 가드, 측정 의존 없음):

  1. 다음 줄 vertical_pos == 0 (HWP 페이지 경계 신호)
  2. 현재 줄 vpos+lh ≤ body_available_height (HWP 좌표상 본문 안)
  3. 두 조건 모두 참일 때만 break 우회

→ 조건 1 영역이 매우 좁아 회귀 위험 영역 최소.

feedback_hancom_compat_specific_over_general 권위 사례 강화 누적

본 사이클의 vpos-reset 영역 권위 사례 누적:

모두 구조적 판단 영역 (측정 의존 없음) + HWP 인코딩 신호 영역 존중.

컨트리뷰터의 광범위 회귀 검증 (PR 본문)

155 샘플 / 1,255 페이지 / 부정적 회귀 0건 / 1,300+ 누락 콘텐츠 복구 (hwpctl_API_v2.4 +460 text + hwpspec +855 text 등).

Task #332 stage4b 영역 종결

PR #632 영역으로 Task #332 stage4b 영역의 알려진 회귀 (aift pi=222) 영역 종결. 21_언어 pi=10 line 1 + hwp-multi-002 pi=68 영역은 본 환경 영역에서 이미 정합 (issue_554 통과 영역).

거버넌스 영역 본 환경 명명 규약 정합

task_m100_631* 영역 — 본 환경 명명 규약 정합 영역 (PR #622/#627 패턴).

충돌 처리

mydocs/orders/20260506.md add/add 충돌 (Stage 4) → git checkout --ours 본 환경 영역 보존 정합 (PR #622/#627 패턴).

cherry-pick 결과

  • Stage 1 commit 51c22a6 — 정밀 진단
  • Stage 2 commit 7127ded — 구현계획서
  • Stage 2 정정 commit 5fdc096 — HWP 권위값 더블체크 (+18/-1)
  • Stage 3 commit e415f62 — 광범위 회귀 검증
  • Stage 4 commit e098562 — 최종 보고서

본 PR 의 정확한 진단 + HWP 권위값 더블체크 (구조적 가드) + 광범위 회귀 검증 + 본 사이클의 vpos-reset 영역 권위 사례 누적 영역 정합.

@edwardkim edwardkim closed this May 7, 2026
edwardkim added a commit that referenced this pull request May 7, 2026
PR #632 (Task #631 HWP 권위값 더블체크로 vpos-reset 인접 line 보존) 처리 완료 후속 영역:
- mydocs/pr/archives/pr_632_review.md (1차 검토 + 옵션 분석 + cherry-pick simulation + 권위 영역 직접 측정)
- mydocs/pr/archives/pr_632_report.md (처리 결과 + 결정적 검증 + 권위 영역 100% 일치 + Task #332 stage4b 종결)
- mydocs/orders/20260507.md PR #632 entry 추가

처리 결과 요약:
- 옵션 B: 5 commits cherry-pick (samples/aift.pdf 영역 제외, 본 환경 PR #670 pdf/aift-2022.pdf 영역 정합)
- mydocs/orders/20260506.md add/add 충돌 → ours 영역 본 환경 보존 (PR #622/#627 패턴)
- devel commits: 51c22a6 Stage 1 + 7127ded Stage 2 구현계획 + 5fdc096 Stage 2 정정 + e415f62 Stage 3 + e098562 Stage 4 (author Jaeook Ryu 보존)
- cargo test --lib 1156 passed / svg_snapshot 7/7 / issue_546/554/418/501 통과 / clippy 0
- WASM 4,577,370 bytes (PR #627 baseline -1,381)
- 권위 영역 100% 일치: page 18 pi=222 lines=0..2 + page 19 pi=222 lines=2..4 + [vpos-reset@line2] 마커
- 시각 판정 영역 스킵 (작업지시자 결정) — 결정적 검증 + 권위 영역 100% 일치 + 광범위 sweep 회귀 0 통과
- PR #632 close + Issue #631 수동 close + 한글 댓글

본질 정정의 가치:
- HWP 권위값 더블체크 (다음 줄 vertical_pos==0 + 현재 줄 vpos+lh ≤ body_h) — 구조적 가드 영역 (측정 의존 없음)
- feedback_hancom_compat_specific_over_general 권위 사례 강화 누적 (PR #621 다중 줄 가드 + PR #622 다단 vpos-reset + 본 PR vpos-reset 인접 line 보존 = 본 사이클의 vpos-reset 영역 권위 사례 누적)
- Task #332 stage4b 알려진 회귀 (aift pi=222) 종결

본 사이클 (5/7) PR 처리 누적: 13건
edwardkim added a commit that referenced this pull request May 7, 2026
PR #636 (Task #630 aift p4 목차 `·` 포함 라인 8.67px 좌측 이탈 정정 + Issue #635 흡수) 처리 완료 후속 영역:
- mydocs/pr/archives/pr_636_review.md (1차 검토 + 옵션 분석 + cherry-pick simulation + 권위 영역 직접 측정)
- mydocs/pr/archives/pr_636_report.md (처리 결과 + TDD 5 단계 + Stage 6 + Stage 6 보강 + 시각 판정 통과)
- mydocs/orders/20260507.md PR #636 entry 추가

처리 결과 요약:
- 옵션 A: 8 commits 단계별 cherry-pick (TDD 5 단계 + Stage 6 + Stage 6 보강)
- b534dc9 (orders only commit) skip + mydocs/orders/20260506.md add/add 충돌 → ours 영역 보존
- devel commits: 1a31c5a Stage 1 + e7d80fd Stage 2 + 393d5f7 Stage 3 + 069de4e Stage 4 + 27e7dd9 Stage 5 + e75c974 최종 보고서 + cb78efc Stage 6 + 1aa48fc Stage 6 보강 (author Jaeook Ryu + Co-Authored-By Claude Opus 4.7 보존)
- cargo test --lib 1157 (test_630 신규 정합) / svg_snapshot 7/7 / issue_546/554/418/501/630 통과 / clippy 0
- WASM 4,572,439 bytes (PR #632 baseline -4,931)
- 권위 영역 100% 일치: aift p4 목차 (페이지 표기) 모든 라인 x=605.4533 동일 위치 + 본문 우측 끝까지 정렬
- 메인테이너 시각 판정 ★ 통과
- PR #636 close + Issue #630 + Issue #635 (Stage 6 흡수) 수동 close + 한글 댓글

본질 정정의 가치:
- TDD 5 단계 + Stage 6 + Stage 6 보강 영역의 권위 패턴
- Stage 4 회귀 발견 → 철회 (학습 영역의 권위 사례)
- Stage 6 Issue #635 흡수 (RIGHT + leader 본문 우측 끝 클램프, 3 곳)
- Stage 6 보강 WasmTextMeasurer 정합 (Embedded + Wasm 양쪽 경로)
- feedback_hancom_compat_specific_over_general + feedback_image_renderer_paths_separate 권위 사례 강화 누적

본 사이클 (5/7) PR 처리 누적: 14건
edwardkim added a commit that referenced this pull request May 11, 2026
- mydocs/pr/archives/pr_813_review.md (5 정책 위반 + 회귀 위험 분석)
- mydocs/pr/archives/pr_813_report.md (옵션 C 처리 결과 + 분리 PR 가이드)
- mydocs/orders/20260511.md PR #813 행 추가

핵심 문제점:
1. base=main 정책 위반 (devel 재제출 필수)
2. 폰트 라이선스 정책 어긋남 (Noto/Pretendard 미포함)
3. typeset.rs LINE_SEG 가드 회귀 위험 (PR #621/#622/#627/#632/#636 누적 정정 우회)
4. Issue 연결 부재
5. wasm_api.rs pub(crate) core 가시성 변경

분리 PR 3 가이드 (재제출 대기):
- 분리 PR 1: pdf.rs SVG BBox (안전, PR #798 close 후속, 우선)
- 분리 PR 2: 폰트 fallback (Noto/Pretendard 우선)
- 분리 PR 3: typeset.rs LINE_SEG + HWPX adapter (광범위 sweep 필수)
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.

2 participants