Task #409: TopAndBottom Picture vert=Para 레이아웃/페이지네이션 chart 높이 일관화 + atomic TAC top-fit#410
Closed
planet6897 wants to merge 27 commits into
Closed
Task #409: TopAndBottom Picture vert=Para 레이아웃/페이지네이션 chart 높이 일관화 + atomic TAC top-fit#410planet6897 wants to merge 27 commits into
planet6897 wants to merge 27 commits into
Conversation
- 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
21페이지 2x1 표가 차트 아래로 ~400px 밀려나는 결함 분석. TopAndBottom Picture(vert=Para) 다음 문단의 vpos 보정에서 lazy_base 산출 시 차트 높이가 이중 반영되는 문제 진단. prev_has_overlay_shape 가드를 Control::Picture (non-TAC) + TopAndBottom/vert=Para 케이스로 확장하는 3단계 계획 수립.
차트 그림(pi=172, 170×111mm, TopAndBottom, vert=Para) 다음 문단의 vpos 보정에서 lazy_base가 차트 높이(31470 HU)만큼 낮게 산출되어 pi=174 (2x1 표) 가 y=948 → 표 자체 높이 합산 후 1049.7 로 밀려나 LAYOUT_OVERFLOW 21.8px 발생. 후속 17개 빈 문단(pi=175~191)도 연쇄 overflow + pi=192 (10x5 표) overflow 521.7px. 베이스라인: 1023 lib + 6 svg_snapshot 통과, LAYOUT_OVERFLOW 19건.
…opAndBottom/vert=Para) 기존 가드는 Control::Shape + InFrontOfText|BehindText 만 검사하여 Picture (그림) 컨트롤과 TopAndBottom 케이스를 처리하지 못함. Picture (non-TAC) 분기 추가 + TopAndBottom + vert_rel_to=Para 케이스 포함하도록 확장. 한컴이 후속 문단 vpos에 개체 높이를 반영하는 경우 sequential y_offset 이 이미 개체 바닥까지 진행된 상태에서 lazy_base 산출 시 prev_pi 텍스트 vpos_end 만 쓰면 차트 높이만큼 이중 점프 발생. 21페이지 LAYOUT_OVERFLOW 19건 -> 1건 (잔여는 별개 페이지네이션 결함). 2x1 표가 차트 바로 아래로 정상 위치 (PDF 21페이지와 일치). 1023 lib + 6 svg_snapshot 통과, 6개 샘플 무회귀.
cargo test --release 전체 10개 스위트 100% 통과 (1023 lib + 6 svg_snapshot + 통합 테스트, 실패 0). 10개 샘플 LAYOUT_OVERFLOW 비교: 6개 샘플 무회귀, 타겟 샘플 22→4 (-18) 개선. closes edwardkim#409
Task edwardkim#409 — prev_has_overlay_shape 가드를 Control::Picture (non-TAC) + TopAndBottom/vert=Para 케이스로 확장하여 차트 다음 문단/표가 차트 높이만큼 추가 점프하는 결함 해결. LAYOUT_OVERFLOW: 19 -> 1 (21페이지), 22 -> 4 (전체 문서). 1023 lib + 6 svg_snapshot + 통합 테스트 100% 통과. 6개 다른 샘플 무회귀.
prev_has_overlay_shape 가드를 Control::Picture (non-TAC) + TopAndBottom/vert=Para 케이스로 확장. LAYOUT_OVERFLOW: 22 -> 4, 21페이지 19 -> 1. 1023 lib + 6 svg_snapshot + 통합 테스트 100% 통과.
이슈 edwardkim#409 재오픈. v1 (layout 측 수정) 후 잔여: pi=191 헤딩 + pi=192 (10x5 표) 가 21페이지에 묶여 22페이지 SVG 에서 누락되는 결함. 근본 원인: typeset.rs::typeset_section 의 controls 루프가 비-TAC Picture/Shape 의 높이를 current_height 에 누적하지 않아 chart (419.6px) 가 페이지네이션에 미반영. Pagination 추정 used=803.3px (실제 layout y=1275.9px) → 모두 21페이지 packing. Stage 4: 컨트롤 루프 분기 확장 (TopAndBottom + vert=Para). Stage 5: 회귀 검증 + 통합 최종 보고서.
typeset.rs::typeset_section 의 controls 루프에 비-TAC + TopAndBottom + vert=Para 인 Picture/Shape 의 height + margin.bottom 을 current_height 에 누적하는 분기 추가. layout 의 calc_shape_bottom_y 와 동일한 산식. 22페이지에 (4) 헤딩 + 10x5 표 + 연령대별 차트 + 2x1 표가 PDF 와 동일하게 정상 출력. chart 관련 LAYOUT_OVERFLOW 전건 해소 (대상 샘플 4 -> 1, 잔여 1건은 본 변경과 무관한 기존 결함). 1023 lib + 6 svg_snapshot 통과, 6개 다른 샘플 무회귀.
전체 cargo test --release 11개 스위트 100% 통과 (실패 0). 6개 다른 샘플 LAYOUT_OVERFLOW 무회귀. 타겟 샘플 22 -> 1 (chart 관련 전건 해소). PR 초안을 v1 (layout) + v2 (pagination) 통합본으로 갱신. closes edwardkim#409
typeset.rs::typeset_section controls 루프에 비-TAC + TopAndBottom + vert=Para Picture/Shape 의 height + margin.bottom 을 current_height 에 누적. 22페이지 (4) 헤딩 + 10x5 표 정상 표시. LAYOUT_OVERFLOW (대상 샘플): v1 4 -> v2 1 (chart 관련 전건 해소). 1023 lib + 6 svg_snapshot + 9개 통합 = 11개 스위트 100% 통과. 6개 다른 샘플 무회귀.
typeset.rs::typeset_section 의 비-TAC TopAndBottom + vert=Para Picture/Shape 높이를 current_height 에 누적. 22페이지에 (4) 헤딩 + 10x5 표 정상 출력 (PDF 일치). 대상 샘플 LAYOUT_OVERFLOW 22 -> 4 (v1) -> 1 (v2). 11개 테스트 스위트 100% 통과, 6개 다른 샘플 무회귀. closes edwardkim#409
23페이지 차트(pi=208, TAC Picture, lh=23700 HU)가 SVG에서 24페이지로 밀리는 결함. 차트 시작 y=721.37 (vpos 1460593 -> +626.87px) 가 본문 안이지만 끝 1037.37 이 본문 1028 보다 9.37px 초과. PDF/HWP는 atomic (분할 불가 단일-line TAC) 항목에 대해 top-fit 시멘틱 사용 — 시작점이 본문 안이면 현재 페이지 배치 하고 하단 일부는 하단 여백(15mm)으로 흘림 허용. 우리는 strict bottom-fit 으로 판정 -> split -> 1-line 못 쪼갬 -> next page. Stage 6: typeset_paragraph 의 fit 분기에 atomic TAC top-fit 추가 Stage 7: 회귀 검증 + 통합 최종 보고서 v3
typeset_paragraph 의 fit 분기에 atomic TAC top-fit 추가. 단일 라인 + TAC Picture/Shape 항목은 시작점이 본문 안이고 끝이 60px (약 1.6cm) 이내 초과면 현재 페이지에 배치. HWP 의 atomic 항목 top-fit 시멘틱 (시작점이 본문 안이면 배치하고 하단 일부는 하단 여백으로 흘림 허용) 을 구현. 23페이지 차트(pi=208, lh=316px) 가 PDF 와 동일하게 표 아래에 정상 배치. 24페이지는 차트가 빠지고 정상 후속 콘텐츠로 시작. 대상 샘플 LAYOUT_OVERFLOW 동일 1건 유지 (chart 정상 배치 이므로 overflow 미발생). 6개 다른 샘플 무회귀. 1023 lib + 6 svg_snapshot 통과.
cargo test --release 11개 스위트 100% 통과 (실패 0). 6개 다른 샘플 무회귀. 타겟 샘플 LAYOUT_OVERFLOW: 22 -> 4 -> 1 -> 1 (chart 정상 배치). 21~24페이지 PDF 대조 전건 일치: - 21: 2x1 표 차트 직하 - 22: (4) 헤딩 + 10x5 표 - 23: 막대 차트 하단 - 24: 2x1 표 -> (6) 헤딩 -> 파이차트 closes edwardkim#409
edwardkim
added a commit
that referenced
this pull request
Apr 28, 2026
- mydocs/pr/pr_410_review.md (옵션 A: Task #409 본질 3 commits v1+v2+v3 분리 cherry-pick) - mydocs/pr/pr_410_report.md (cherry-pick 머지 결정 + SVG/Canvas 양 경로 시각 판정 통과) - mydocs/orders/20260426.md (PR #410 + 이슈 #426/#429 행 추가) 검증: 1050 passed + svg_snapshot 6/6 + issue_418 1/1 + clippy 0 + WASM 4,116,929 bytes #426 회귀 영향 없음 (aift.hwp 19페이지 byte 단위 동일) 회귀 검증 중 발견 #429 (사전 결함, 별도 등록)
edwardkim
added a commit
that referenced
this pull request
Apr 28, 2026
…hart 정정 + atomic TAC top-fit (cherry-pick @planet6897 3 commits v1+v2+v3)
edwardkim
added a commit
that referenced
this pull request
Apr 28, 2026
Owner
|
@planet6897 님 PR 감사드립니다. 메인테이너가 cherry-pick 으로 devel 에 적용 완료했습니다. 처리본 PR 의 27 commits 분석 — Task #398/#402/#404 commits 는 다른 PR 로 흡수 완료 + 샘플/계획서/Stage 보고서/merge 제외하고, Task #409 본질 3 commits 만 분리 cherry-pick:
devel 머지 commit: 검증
시각 판정 (작업지시자 직접)한컴 hwpx + PDF 정답지 vs rhwp 출력 비교:
회귀 검증이슈 #426 (이미지+캡션+다음 문단 오버래핑) 회귀 영향 확인 — 회귀 검증 중 사전 결함 발견 — 메모리 원칙 부합본 PR 의 변경은 모두 이슈 #409 도 함께 close 됩니다. 감사합니다. |
This was referenced Apr 28, 2026
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.
배경
samples/2025년 기부·답례품 실적 지자체 보고서_양식.hwpx21~22페이지에서 SVG 출력이 PDF 와 다름:21페이지 PDF: 차트(170×111mm) 바로 아래 2x1 빈 표 (y≈540~660)
21페이지 SVG (v0): 차트와 2x1 표 사이 ~400px 빈 공간 → 2x1 표가 페이지 하단(y≈937)으로 밀려 일부 잘림 + pi=192 (10x5 표) 521px overflow
22페이지 PDF: "(4) 연령대별 구매 건수 및 사용 포인트" 헤딩 + 10x5 빈 표 + 연령대별 차트 + 2x1 표
22페이지 SVG (v0): (4) 헤딩 + 10x5 표가 누락된 채 차트로 시작
23페이지 PDF: (5) 헤딩 + 29x5 표 + 막대 차트 (하단)
23페이지 SVG (v0~v2): 차트가 24페이지로 밀림 → 24페이지 시작이 차트로 됨 (PDF 와 다름)
LAYOUT_OVERFLOW22건 (대상 샘플 전체 기준).근본 원인
차트 그림(pi=172) 메타데이터:
Control::Picture(170×111mm = HU 48190×31470)TextWrap::TopAndBottomVertRelTo::Paratreat_as_char = false한컴은 TopAndBottom + vert=Para 그림이 anchor 문단 다음 문단의 vpos 에 그림 높이를 더해 기록:
이 패턴에서 layout 과 pagination 양쪽 모두 chart 높이 처리가 미흡했음.
1. Layout 측 결함 (
src/renderer/layout.rs)prev_has_overlay_shape가드 (line 1366-1370) 가Control::Shape+InFrontOfText|BehindText만 검사 →Control::Picture미처리 +TopAndBottom케이스 미포함.→ 차트 다음 문단(pi=173)에서 vpos 보정이 진입하여 잘못된
lazy_base산출:y_offset = 528.8(= 차트 바닥)vpos_end_172 = 1277445(pi=172 텍스트 라인 끝)lazy_base = 1277445 - 32574 = 1244871← 차트 높이만큼 낮게이후 pi=174 (2x1 표) 보정:
end_y = 948.4→ y_offset 528.8 → 948.4 강제 점프 (차트 높이 이중 반영).3. Atomic TAC fit 시멘틱 결함 (
src/renderer/typeset.rs)23페이지 차트(pi=208) 는 TAC Picture (line_seg.lh=23700 HU = 316px). 페이지네이션 fit 검사:
current_height + height_for_fit = 619.3 + 316 = 935.3 > 933.5(1.8px 초과) → split → 1-line atomic 이라 못 쪼갬 → next pagevpos 기준 차트 시작 y = 721.37 (본문 안), 끝 y = 1037.37 (본문 1028 보다 9.37px 초과). HWP/PDF 는 atomic 항목에 대해 top-fit 시멘틱 사용 — 시작점이 본문 안이면 현재 페이지 배치하고 하단 일부는 하단 여백(15mm = 56.7px)으로 흘림 허용. 우리는 strict bottom-fit 으로 page break 트리거.
2. Pagination 측 결함 (
src/renderer/typeset.rs)typeset_sectioncontrols 루프 (line 622-630) 가 비-TACPicture/Shape의 높이를current_height에 누적하지 않음:format_paragraph도 line_segs 의lh + ls(텍스트 baseline) 만 누적 → 차트 419.6px 가 페이지네이션에 미반영.→ 페이지네이션 추정 21페이지 used = 803.3px (vs 실제 layout y = 1275.9px) → pi=191 헤딩 + pi=192 (10x5 표) 모두 21페이지에 packing → layout overflow 로 잘려 22페이지에 누락.
변경
1.
src/renderer/layout.rs:1365-1390(v1)prev_has_overlay_shape가드 확장 —Control::Picture(non-TAC) 분기 추가 +TopAndBottom + vert_rel_to=Para케이스 포함:2.
src/renderer/typeset.rs:622-672(v2)controls 루프에서 비-TAC + TopAndBottom + vert=Para 인 Picture/Shape 의
height + margin.bottom을current_height에 누적 (layout 의calc_shape_bottom_y와 동일 산식):3.
src/renderer/typeset.rs(v3, fit 분기)typeset_paragraph의 fit 분기에 atomic TAC top-fit 추가. 단일 라인 + TAC Picture/Shape 항목은 시작점이 본문 안이고 끝이 60px (≈1.6cm) 이내 초과면 현재 페이지에 배치 (HWP 시멘틱):검증
시각 결과 (PDF 대조)
LAYOUT_OVERFLOW (대상 샘플 전체)
→ chart 관련 overflow 전건 해소.
회귀 테스트 (cargo test --release)
11개 테스트 스위트 100% 통과:
lib: 1023 passed, 0 failedsvg_snapshot: 6 passed, 0 failed6개 다른 샘플 무회귀
biz_plan.hwpexam_kor.hwpexam_math.hwpaift.hwpk-water-rfp.hwpkps-ai.hwp변경 파일
src/renderer/layout.rs(v1, line 1365-1390)src/renderer/typeset.rs(v2, line 622-672)영향 범위
closes #409