Skip to content

Task #402 inline TAC 그림 페이지네이션 + #404 heading-orphan 보정#408

Closed
planet6897 wants to merge 14 commits into
edwardkim:develfrom
planet6897:local/task404
Closed

Task #402 inline TAC 그림 페이지네이션 + #404 heading-orphan 보정#408
planet6897 wants to merge 14 commits into
edwardkim:develfrom
planet6897:local/task404

Conversation

@planet6897

Copy link
Copy Markdown
Contributor

Summary

#402: inline TAC 그림 페이지네이션 수정

원인

  • layout.rs::layout_shape_item 이 표 옆에 그림이 있을 때 para_start_y 를 갱신하지 않아 그림 y 가 표 영역과 겹침
  • typeset.rs::typeset_table_paragraph 가 선행 TAC 그림의 line_seg lhcurrent_height 에 누적하지 않아 페이지 초과 시 분할 미발동

수정

  • layout.rs::layout_shape_item: 선행 TAC 컨트롤 존재 시 para_start_y = y_offset 로 갱신
  • typeset.rs::typeset_table_paragraph: 선행 TAC 그림의 lhcurrent_height 에 누적 + 페이지 초과 시 분할

#404: heading-orphan 보정 (vpos 기반)

원인

  • HWP 원본은 paragraph 단위 vpos (LineSeg.vertical_pos) 가 페이지 본문 영역을 넘으면 다음 페이지로 push
  • 우리 엔진은 누적 height 기반 fit 결정 → vpos 미세 초과(886 HU = 0.31mm) 케이스에서 누적 height 는 fit 으로 판정 → 헤딩만 페이지 끝에 잔류, 후속 표는 다음 페이지로 → orphan

수정 (typeset.rs::typeset_section 메인 루프)

heading-orphan trigger 5 조건 AND:

# 조건 의미
A !current_items.is_empty() 페이지 첫 item 자기참조 회피
B wrap_around_cs < 0 && col_count == 1 단일 단 + non-wrap
C current_height + para_h_px <= avail 현재 paragraph 누적 height 로 fit
D vpos_end > page_bottom_vpos + 283 vpos 기준 1mm 초과
E next_h_px > 30 && current+para+next > avail 다음 paragraph substantial AND 잔여 영역에 fit 안 함

발동 시 st.advance_column_or_new_page() 로 push.

설계 노트

page_top_vposTypesetState 필드 추적 대신 current_items 첫 item 의
para_index 로 매 iteration 즉시 계산. (typeset_paragraph 내부 페이지 flush 와
필드 setter 가 동기 안 되는 문제 회피.)

False Positive 차단

Stage 1 진단 로그에서 vpos overflow paragraph 41건 중 1건만 진짜 orphan.
조건 E (next_substantial && next_doesnt_fit) 가 핵심 필터:

  • pi=62~76 (페이지 8 wrap-around 압축): next_h ≈ 14.7px → 차단 ✓
  • pi=171 (text-only 페이지): next_h 14.7px → 차단 ✓
  • pi=83 (타겟): next_h 207px (pi=84 표) → 발동 ✓

Test plan

Commits

비범위 (별도 후속)

closes #402, closes #404

- row_block_start/end 필드 + compute_row_blocks 헬퍼
- row_block_for / snap_to_block_boundary 메서드
- 신규 단위 테스트 7개 (rowspan 단일/겹침/비인접 + 폴백 + 스냅)

회귀 검증용 샘플 hwpx/pdf 동반 커밋.
- pagination/engine.rs::split_table_rows: pre-loop first_block_h, snap_to_block_boundary, cur/next 블록 단일성 가드
- typeset.rs::paginate_table: 동일 패턴 적용 (실제 SVG 내보내기 경로)
- 다중 행 블록이 페이지에 들어가지 않으면 블록 전체를 한 단위로 배치

본 샘플 검증: 1쪽에서 표 분할 사라지고 2쪽에 표 전체 시작.
cargo test --lib: 1023 passed.
- cargo test --tests: 1073 passed (lib 1023 + integration 50)
- svg_snapshot 골든 6건 통과 (table-text, issue-147/157/267, form-002, deterministic)
- cargo build --release 성공
- 본 샘플 외 다른 표 샘플 (table-vpos-01, 표-텍스트) 정상

closes edwardkim#398
같은 paragraph 안에 TAC 컨트롤이 2개 이상 있을 때 두 번째 이후
그림의 pic_y가 paragraph 시작 y로 고정되어 표와 겹침.

- pi=51 ci=0 (단독 그림): pic_y=94.49 (정상, 선행 TAC 없음)
- pi=57 ci=1 (Table 뒤 그림): pic_y=578.09 (버그, y_offset=919.40 사용해야)

선행 TAC 존재 여부가 핵심 판별 조건임을 확인. Stage 2 구현 방향 확정.
같은 paragraph에 TAC 컨트롤(표/그림/도형) 2개 이상이 서로 다른 line_seg에
배치된 경우, 두 번째 이후 inline 그림이 첫 번째와 같은 y 좌표에 그려져
겹침/오버플로 발생하던 문제 수정.

- layout.rs::layout_shape_item: 선행 TAC 컨트롤이 있으면 para_start_y를
  진행된 y_offset으로 갱신하여 그림 y 좌표를 표 아래로 정확히 배치.
- typeset.rs::typeset_table_paragraph: 선행 TAC 그림의 line_seg 높이를
  current_height에 누적하고, 페이지 초과 시 다음 페이지로 분할.

기본 페이지네이션 엔진은 typeset.rs(TypesetEngine). engine.rs는 현재
RHWP_USE_PAGINATOR=1 fallback 경로이므로 typeset.rs만 수정.

회귀 테스트: 1023 passed, 0 failed.
샘플 비교: 7쪽 표 + 파이 차트 겹침 해소, 파이 차트가 8쪽 정상 배치.
closes edwardkim#402

검증 결과:
- 7쪽: 표 + 파이 차트 겹침 해소 (PDF 일치)
- 8쪽: 파이 차트 정상 배치
- cargo test 1023 passed, 0 failed
- 10개 대표 샘플 LAYOUT_OVERFLOW 카운트 회귀 없음
- 페이지 수 27→30 (분할로 인한 정상 증가)
기존 task_404.md(영문 줄바꿈 역공학 미실행 초안)는 archives로 이동.
신규 task_404 = orphan heading vpos 기반 분할 추가.
TypesetState.page_first_vpos 필드 추가 + typeset_section 메인 루프에
진단 로그 삽입. 타겟 샘플 분석 결과:

- pi=83 가설 확정: vpos overflow=886 HU, curr_h=906.6/avail=933.5
  (cumulative fit) + next pi=84 표 190.9px fit 불가 → orphan
- False positive 41건 중 40건은 wrap-around 페이지에서 vpos↔px 비율
  어긋남 (페이지 8 pi=57 TAC 그림 + 빈 문단 19개)

Stage 2 전략 재정의:
heading-orphan 패턴 (current fit + next block 못 fit + vpos overflow
+ single column) 4조건 모두 만족 시 push. 단순 vpos overflow check은
다수 회귀 위험.
typeset_section 메인 루프에 vpos 기반 보정 추가. 5개 조건 AND
(current fits + vpos overflow + next substantial + next doesn't
fit + single column non-wrap) 으로 false positive 차단.

설계 변경: page_top_vpos 는 TypesetState 필드 대신 current_items
첫 item 의 para_index 로 즉시 계산 (typeset_paragraph 내부 페이지
flush 와 동기 안 되는 문제 회피).

검증:
- pi=83 heading 이 페이지 9 → 페이지 10 으로 이동 (pi=84/85 표와
  함께 배치)
- 1073개 테스트 모두 통과
- 10개 샘플 LAYOUT_OVERFLOW: 회귀 없음 + 타겟 -15, kps-ai -1 개선
SVG 시각 검증으로 pi=83 "(7) 다수 기부자 현황" 이 페이지 10 첫
본문 라인으로 이동하고 후속 표 pi=84/85 와 함께 배치됨을 확인.

검증 기준 충족:
1. 페이지 9 SVG 에 pi=83 heading 미표시
2. 페이지 10 SVG 가 pi=83 + pi=84/85 표 함께 표시
3. 회귀 테스트 1073개 모두 통과
4. 10개 샘플 LAYOUT_OVERFLOW 회귀 없음 + 2개 샘플 개선

closes edwardkim#404
edwardkim added a commit that referenced this pull request Apr 28, 2026
- mydocs/pr/pr_408_review.md (옵션 A: Task #404 핵심 4 commits 분리 cherry-pick)
- mydocs/pr/pr_408_report.md (cherry-pick 머지 결정 + SVG/Canvas 양 경로 시각 판정 통과)
- mydocs/orders/20260426.md (PR #408 완료 행 추가)

검증: 1050 passed + svg_snapshot 6/6 + issue_418 1/1 + clippy 0 + WASM 4,124,155 bytes
edwardkim added a commit that referenced this pull request Apr 28, 2026
@edwardkim

Copy link
Copy Markdown
Owner

@planet6897 님 PR 감사드립니다. 메인테이너가 cherry-pick 으로 devel 에 적용 완료했습니다.

처리

본 PR 의 14 commits 분석:

작성자 attribution 보존 4 commits:

devel 머지 commit: 2636d70

검증

시각 판정 (작업지시자 직접)

한컴 hwpx + PDF 정답지 vs rhwp 출력 비교:

devel baseline PR #408 적용
9쪽 pi=83 헤딩 잔류 (orphan) pi=83 미표시 (push)
10쪽 후속 표만 시작 pi=83 헤딩 + pi=84/85 표 함께 (한컴 일치)
  • SVG 내보내기: 통과 ✅
  • Canvas (rhwp-studio): 통과 ✅

메모리 원칙 부합 — 5 조건 AND trigger

vpos overflow 41건 중 1건만 진짜 orphan 인 정황에서, 단순 vpos check 가 아닌 5 조건 AND (current fit + vpos overflow + next substantial + next 못 fit + single column non-wrap) 로 false positive 차단한 점이 본 저장소의 메모리 feedback_hancom_compat_specific_over_general 원칙과 정확히 부합합니다. 좋은 설계입니다.

이슈 #404 도 함께 close 됩니다. 감사합니다.

비범위 (별도 후속)

  • 21쪽 pi=192 표 위치 차이 — TopAndBottom wrap 그림 + 빈 문단 line-height 압축 누적. 사전 존재 이슈, 별도 등록 예정
  • engine.rs::paginate_with_measured fallback 경로 동일 보정 미적용 — 별도 후속

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