Task #459: 다단 후속 페이지 LINE_SEG vpos-reset 단 경계 미인식 수정#461
Conversation
exam_kor.hwp 24페이지 → 20페이지 정합 작업 1단계. - 페이지별 단별 used/hwp_used/diff CSV (output/debug/task435/) - 회귀 대상 5문서 페이지 수 (exam_kor 24, exam_eng 8, k-water-rfp 28, hwpspec 177, synam-001 35) - RHWP_TYPESET_DRIFT 출력 캡처 (pi=0.30, pi=1.25 split 진단) - compute_body_wide_top_reserve_for_para 산정 경로 추적 핵심 진단: col 1 reserve 306.1px (HWP 실제 94.5px 대비 +211.6px 과대). 원인: Paper-rel 좌표를 body-rel 변환 없이 그대로 reserve 에 누적. Stage 2 에서 typeset.rs:2127-2172 의 VertRelTo::Paper 분기 정정. 수행계획서/구현계획서/Stage 1 보고서 포함.
compute_body_wide_top_reserve_for_para 의 VertRelTo::Paper 분기에서 body-rel 변환 누락 정정. body 와 일부만 겹치는 (header→body 침범) 케이스에서 Paper-rel 좌표를 그대로 reserve 에 누적하던 버그 수정. 수정 전 reserve = shape_y_offset(paper) + h + outer_bottom = 306.1 px 수정 후 reserve = max(0, shape_top_abs - body_top) ... = 94.4 px 결과: - exam_kor.hwp: 24 → 22 페이지 (page 2, 15 orphan 해소) - pi=0.30, pi=1.25 split → FullParagraph - 회귀: exam_eng 8, k-water-rfp 28, hwpspec 177, synam-001 35 유지 - cargo test: 1062 passed Stage 3 에서 일반 페이지 누적 -100~-300px 정정 (22 → 20).
원래 가설 ("표/도형 후 컬럼 잔여 공간 산정 부족") 재검토.
RHWP_TYPESET_DRIFT 분석 결과 diff 메트릭은 typeset cur_h 누적
(height_for_fit, trail_ls 제외) vs hwp_used (last line vpos+lh)
의 좌표계 차이를 측정하는 것일 뿐, "rhwp 가 채울 수 있는데 못 채운
잔여 공간" 이 아님을 확인.
실제 22→20 페이지 단축 장애물:
1. 섹션 1 페이지 14: Square wrap 표 + col 0 over-fill (1225>1211)
→ col 1 under-use (64px)
2. 섹션 1 페이지 15: 단일 컬럼 출력 (단정의는 2단인데 단 1 누락)
3. 섹션 2 페이지 18: pi=11 split + pi=13 [단나누기] orphan-like
3가지 전부 해결 시 22→19 가능 (목표 20 도달).
옵션 A/B/C 결정 필요 (현 상태 종료 / Stage 4 확장 / Stage 4 부분).
옵션 A 종료: 24→22 페이지 (Stage 2 col 1 reserve 정정). 잔여 22→20 미달성, 3가지 별도 메커니즘 (Square wrap over-fill, 단일 컬럼 출력 버그, col 0 cur_h over-advance) 별도 task 분리 권고. edwardkim#393 (옵션 A) 본 task Stage 2 로 적용 완료, close 가능.
수행계획서(task_m100_439.md) + 단계1 진단 보고서. 핵심 발견: - 이슈 가설의 engine.rs:702-711 는 fallback 경로 (RHWP_USE_PAGINATOR=1). 기본 활성 엔진은 typeset.rs::TypesetEngine. - 실제 버그 위치: typeset.rs::place_table_with_text (1400-1467). engine.rs 의 !is_wrap_around_table 가드 (engine.rs:1349, 1422) 누락. - 페이지 14 col 0 used=1225.8 (본문 1211.3 초과 +14.5) 정확히 재현. 코드 변경 없음 — 임시 디버그 코드는 모두 revert.
place_table_with_text 의 정확한 cur_h 누적 추적 (디버그 후 revert): - Square wrap 4 표가 pre_height + table_total 합산 → +244.88 px 과다 - HWP 의도: max(호스트 텍스트, 표 + v_offset) 만 누적 수정안: typeset.rs::place_table_with_text 에서 current_height += max(pre_height, v_off + table_total) (Square wrap 시) 코드 변경 없음 — 임시 디버그 println 모두 revert.
typeset.rs::place_table_with_text 에서 Square wrap (어울림) 표일 때 current_height 누적을 pre_height + table_total → max(pre_height, v_off + table_total) 로 변경. 호스트 문단 텍스트와 어울림 표는 같은 수직 영역을 공유하므로 더 큰 쪽만 한 번 누적해야 HWP layout 의도와 일치. PageItem 자체는 PartialParagraph + Table 모두 push (layout 렌더링 보존). 검증: - 페이지 14 col 0 used 1225.8 → 1036.1 px (≤ 1211.3 충족) - exam_kor.hwp 22 → 20 페이지 (목표 ≤ 21 충족) - 회귀 샘플 6 종 (exam_eng/math, 21언어, aift, 2010-01-06, biz_plan) 페이지 수 동일 - cargo test 1066 개 모두 통과 - SVG 렌더링 정상 closes edwardkim#439
Stage 4 회귀 검증 결과: - 149 개 sample HWP 전수 페이지 수 비교: exam_kor 만 22→20 변경, 148개 동일 - cargo test 1066 passed - DoD 전부 충족 본 task 완료. closes edwardkim#439.
typeset.rs::place_table_with_text 의 Square wrap 표 누적 정책을 pre_height + table_total → max(pre_height, v_off + table_total) 로 변경. 효과: - exam_kor.hwp 페이지 14 col 0 used 1225.8 → 1036.1 px (over-fill 해소) - exam_kor.hwp 22 → 20 페이지 - 149 개 sample 중 exam_kor 만 변화, 148 개 동일 (회귀 0건) - cargo test 1066 passed closes edwardkim#439
…문제 수정 - 수행/구현 계획서 + Stage 1·2 보고서 작성 - src/renderer/layout.rs: paragraph border merge 그룹을 col_area 바닥/꼭대기로 클램프 - exam_kor p2/5/8/15 의 세로 구분선이 PDF 와 일치하는 길이로 정상화 (p8: 1671 → 1425, 246px 단축, 페이지 바깥 침범 해소) - vpos-reset 미존중으로 인한 텍스트 자체의 overflow 는 별도 이슈로 분리 - snapshot 갱신: tests/golden_svg/issue-267/ktx-toc-page.svg (invisible 구조 rect 의 height 5.34px 변화, 가시 변화 없음)
페이지 번호 박스가 column divider line 과 붙어 보이는 문제 해결. 원인: 꼬리말 paragraph 의 vert=Para + wrap=TopAndBottom 표가 paragraph top 에 배치되어 본문 바닥과 같은 y 에 위치. HWP 의 실제 동작은 첫 라인의 line_height/2 만큼 아래에 anchor 되어 본문과 시각적 갭 형성. 수정: layout_header_footer_paragraphs 에서 해당 조건의 첫 paragraph 표는 y_offset 에 line_height/2 (px) 를 더하여 배치. 검증 (exam_kor.hwp 20p): - 박스 top y: 1422.93 → 1439.47 (PDF 380.6mm 와 일치) - column line - 박스 갭: 0px → 16.3-17.0px (PDF 16.0px 와 일치) - column line 길이/위치는 PDF 자연 그대로 유지 (p1 1131px, p2+ 1226px) - cargo test --release: 1117 passed, 0 failed
이슈 edwardkim#445 의 두 시각적 결함 (paragraph border 페이지 바깥 침범 + 페이지 번호 박스가 column line 에 붙음) 모두 PDF 와 일치하도록 수정 완료. 승인 후 local/devel 머지 + edwardkim#445 close 예정.
- 수행계획서 (mydocs/plans/task_m100_452.md): 옵션 A 선언, 4단계 분해 - 구현 계획서 (mydocs/plans/task_m100_452_impl.md): paragraph_layout.rs:2511-2520 is_para_last_line 분기 제거, is_cell_last_line 만 보존 - Stage 1 보고서 (mydocs/working/task_m100_452_stage1.md): - exam_kor pi=1.line9↔pi=2.line0 step = 15.34px (버그) ↔ 단락내 24.51px - 10종 샘플 페이지 수 baseline 캡처 (/tmp/task_452_baseline/) - 21_언어 p1 col 1 pi=26+보기①②③ fit 확인 (edwardkim#332 회귀 baseline) - orders 갱신: 버그 섹션 edwardkim#452 항목 추가
… + golden 갱신 - src/renderer/layout/paragraph_layout.rs:2511-2519: is_para_last_line 분기 제거. is_cell_last_line(셀 내) 만 trailing 제외 보존, 본문 단락은 모든 줄에서 y += lh + ls 통일. pagination/engine.rs 의 current_height 누적과 정합. - 검증: exam_kor 1페이지 pi=1.line9↔pi=2.line0 step = 15.34 → 24.50 px (단락내 step 과 동일). cargo test --lib 1066 passed. - golden SVG 2건 baseline 갱신 (Task edwardkim#332 에서 갱신된 것을 본 정합으로 재갱신): - tests/golden_svg/issue-147/aift-page3.svg - tests/golden_svg/issue-157/page-1.svg - LAYOUT_OVERFLOW 메시지: 페이지 마지막 단락의 trailing ls (~10.9 px) 가 col_bottom 을 살짝 넘으나 빈 공간이므로 시각 무영향. pagination engine 의 effective_trailing 처리로 페이지 분배는 유지.
paragraph_layout.rs:2511-2520 의 is_para_last_line 분기 제거. 본문 단락 모든 줄에서 y += lh + ls 통일 → pagination/engine.rs 의 current_height 누적과 정합. is_cell_last_line(셀 내) 만 trailing 제외 보존. 효과: exam_kor pi=1.line9↔pi=2.line0 step 15.34→24.50 px (단락내 step 과 동일, PDF 정합). 10종 샘플 페이지 수 변동 0. Task edwardkim#332 회귀 0 (21_언어 p1 col 1 pi=26+보기①②③ fit 유지). cargo test --lib 1066 passed + svg_snapshot 6/6 (issue-147/157 baseline 재갱신). closes edwardkim#452
paragraph_layout 의 skip_text_for_inline_shape 분기 제거. 인라인 글상자가 있는 줄에서 외부 문단 본문이 통째로 스킵되던 버그를 수정. 원인: skip_text_for_inline_shape 가 글상자 내부 텍스트와 외부 문단 본문 텍스트를 혼동하여, 외부 본문(글상자 좌·우의 일반 텍스트)도 함께 스킵하고 있었음. 글상자 자체와 내부 텍스트는 shape_layout 의 inline_shape_position 경로로 별도 렌더되므로 외부 본문을 항상 렌더해도 중복되지 않는다. 검증: samples/exam_kor.hwp 페이지 2 좌측 단 line 2 의 본문 39자(척사파의 ... 거스를) 가 글상자 좌·우로 분리 렌더되어 복원됨. 페이지 수 20 유지, 전체 테스트 1117 passed.
Stage 3 회귀 검증 결과 (1117 tests passed, 페이지 수 회귀 0). Stage 4 최종 결과보고서 및 PR 메시지(`mydocs/report/task_m100_455_pr.md`) 작성. 오늘할일에 Task edwardkim#455 항목 추가.
`on_first_multicolumn_page` 가드는 새 페이지 시작 시 false 로 리셋되어 다단 구역이 여러 페이지에 걸칠 때 후속 페이지에서 `detect_column_breaks_in_paragraph` 호출이 차단됨. HWP 원본의 LINE_SEG vpos 리셋 위치가 무시되어 좌측 단 하단이 col_bottom 을 초과해 그려지던 버그. 수정: `typeset.rs:856` (기본 경로) + `pagination/engine.rs:607` (RHWP_USE_PAGINATOR=1 fallback) 의 가드에서 `on_first_multicolumn_page` 조건 제거. 검증: - exam_kor p2 pi=39 0..4/4..7 → 0..2/2..7 (HWP 원본 정합) - exam_kor LAYOUT_OVERFLOW 36 → 16 (페이지 5/8 의 동일 메커니즘 자동 해소) - 다단 샘플 4종 (exam_eng/math/science/social) 페이지 수 + SVG byte 동일 - cargo test --release 1120 passed, 0 failed closes edwardkim#459
학생3 wrap host paragraph 의 텍스트가 학생1 등 일반 paragraph 보다 margin_left (11.33px) 만큼 더 들여쓰기 되던 문제 해결. 원인: layout_wrap_around_paras 가 wrap_area.x = col_area.x + wrap_cs 로 설정. wrap_cs (LINE_SEG.column_start) 는 paragraph margin_left 를 이미 포함한 값이지만, 이후 layout_composed_paragraph 가 col_area.x 에 다시 margin_left 를 추가하여 결과적으로 margin_left 가 이중 적용됨. 수정: wrap_area 의 x 를 host paragraph 의 margin_left 만큼 좌측으로 보정하고 width 도 그만큼 확장 (margin_right 포함). 이렇게 하면 후속 margin_left 추가가 wrap_cs 를 이미 보정한 값에 적용되어 정상 위치 산출. inner_pad 는 stroke 가 있을 때 paragraph border 안쪽 여백이므로 wrap_cs 와 무관 — 보정 대상 아님 (이중 보정 시 wrap host 가 오히려 margin_left 만큼 좌측으로 튀어나옴, 첫 시도에서 확인). 검증: pi=33 학생3 wrap host x 좌표 151.17→139.84 (pi=30 학생1 과 일치). 1069 단위테스트 통과, 3종 샘플 회귀 0. 박스 우측 영향 없음. Refs edwardkim#463
학생3 wrap host paragraph 가 일반 paragraph 보다 margin_left 만큼 더 들여쓰기 되던 문제 해결. wrap_area.x 를 host paragraph margin_left 만큼 좌측 보정 (wrap_cs 가 이미 margin_left 를 포함하므로 layout_composed_ paragraph 의 추가 margin_left 가 이중 적용되던 부작용 차단).
- paragraph_layout.rs 의 3개 ImageNode emit 사이트에 crop / original_size_hu 추출 추가 - exam_kor.hwp 15p 헤더 "국어 영역(A형)" → "국어 영역" 으로 정상 잘림 - HWP 스펙의 picture crop 정보는 picture_footnote.rs::layout_picture 에서만 처리되어 인라인 TAC 그림 (예: 바탕쪽 셀 그림) 에서는 누락되어 비트맵 전체가 표시됨
- overlap=true 확장 master 가 active master 와 같은 apply_to 인 경우 active 대체 - exam_kor.hwp 16p/20p (sec 마지막 짝수) 좌측 헤더 "2"+"4" 겹침 해소 - 한컴 PDF 출력 동작 (마지막 쪽 전용 헤더로 대체) 과 일치
Task edwardkim#463 Stage 2 의 cell_ctx.is_none() 게이팅으로 셀 paragraph 의 invisible border rect 가 본문 큐에서 제거됨. 4개 snapshot golden 이 이전 구조 (셀 paragraph 포함) 로 작성되어 있어 mismatch 발생. 시각 비교 결과 4건 모두 PNG 동일 (table-text 만 1px 폭 anti-aliasing 4/255 차이) 이라 golden 갱신이 적절. - form-002/page-0.svg - issue-157/page-1.svg - issue-267/ktx-toc-page.svg - table-text/page-0.svg
- para_border_ranges 튜플에 para_index 추가 (9 → 10 필드) - build_single_column merge 그룹의 첫/마지막 paragraph index 추적 - sequential 인접 paragraph bf_id 검사로 partial_start/partial_end 보정 - 그룹 첫 paragraph 의 prev paragraph 가 같은 bf_id 면 partial_start - 그룹 마지막 paragraph 의 next paragraph 가 같은 bf_id 면 partial_end - 결과: cross-column / cross-page 박스 연속 시 inner 경계 stroke 미렌더 검증: - exam_kor.hwp 6p 좌측 단 [18~21] 박스 하단 stroke 미렌더 (PDF 일치) - exam_kor.hwp 6p 우측 단 박스 상하단 모두 미렌더 (양방향 연속) - exam_kor.hwp 7p 좌측 단 박스 상단 미렌더 (페이지 경계 연속) - exam_kor.hwp 14p 단일 박스 정상 닫힘 유지 (Task edwardkim#463 회귀 0) - cargo test 1069 + svg_snapshot 6 통과 - 5종 샘플 회귀 0
- mydocs/report/task_m100_468_report.md 작성 - mydocs/orders/20260430.md 신설 (edwardkim#468 완료, edwardkim#464/edwardkim#465/edwardkim#466/edwardkim#467 대기)
…ot 경계 침범 수정 closes edwardkim#469 src/renderer/layout.rs paragraph border 그룹 렌더링에서 partial_start/ partial_end 박스가 col_top 위(헤더선) 또는 col_bot 아래(꼬리말선) 까지 침범하던 현상을 차단. 원인: L1740 `rect_y = y_start - top_pad` 가 cross-column 으로 이어진 partial 박스의 후속 부분에도 적용되어, y_start 가 col_top 으로 클램프된 이후 top_pad (border_spacing.top) 만큼 다시 위로 확장. exam_kor p2 우측 단 (나) 박스 좌·우 세로선이 헤더 가로선(y=196.55) 과 맞닿음. 수정: `effective_top_pad`/`effective_bot_pad` 도입. - is_partial_start → effective_top_pad = 0 - is_partial_end → effective_bot_pad = 0 이전/다음 컬럼에서 이미 inset 처리된 후속 부분은 col_top/col_bot 경계 안쪽에서만 그려지도록 한다. 검증: - 신규 통합 테스트 test_469_partial_start_box_does_not_cross_col_top - exam_kor p2 우측 단 (나) 박스 세로선 y1: 196.55 → 211.65 ✓ - 좌측 단 (가) 박스 영향 없음 (y1 = 242.41 유지) - cargo test 1121 + svg_snapshot 6 모두 통과
- mydocs/pr/pr_454_review.md (Task #452 cherry-pick 검토, 광범위 영향 87% 정황) 검증: 1069 passed + svg_snapshot 6/6 (golden 2건 갱신) + issue_418 1/1 + clippy 0 광범위 byte 비교: 305 중 266 차이 (87% 영향, paragraph_layout 변경) 시각 판정: 후속 PR (#457, #461) 처리 후 통합 검증 (작업지시자 결정) 본질: paragraph_layout::is_para_last_line 분기 제거 (셀 마지막 줄만 trailing 제외 보존)
closes edwardkim#470 Task edwardkim#321 cross-paragraph vpos-reset 가드가 `cv == 0` (정확히 0) 만 인정해 컬럼 헤더 오프셋 (cv=9014 등) 으로 시작하는 다단의 새 컬럼 reset 을 놓침. 21_언어_기출_편집가능본 1p 의 pi=10 ("적합성 검증이란…") 이 col 0 에 강제 삽입되어 56.2px overflow 발생. 수정: typeset.rs:415, 439 에 다단/단일 단 분기. - 다단(col_count > 1): cv < pv && pv > 5000 (Task edwardkim#470, 컬럼 헤더 오프셋 인정) - 단일 단: cv == 0 && pv > 5000 (Task edwardkim#321 보수적 기준 유지, hwpspec partial-table split 회귀 차단 / issue_418) 검증: - 신규 통합 테스트 test_470_cross_paragraph_vpos_reset_with_column_header_offset - 다단 샘플 OVERFLOW 11건 추가 해소 21_언어_기출 13→10, exam_science 5→0, exam_social 4→1 - hwpspec(단일 단) OVERFLOW 45→45, issue_418 PASS (회귀 0) - cargo test 1122 / 1122 PASS
- mydocs/pr/pr_457_review.md (Task #455 cherry-pick 검토, 영향 범위 한정 4.3%) 검증: 1069 passed + svg_snapshot 6/6 + issue_418 1/1 + clippy 0 광범위 byte 비교: 305 중 13 차이 (4.3% 영향, exam_* 11 + synam-001 1 + k-water-rfp 1) 시각 판정: PR #461 처리 후 통합 검증 (작업지시자 결정) 본질: paragraph_layout::skip_text_for_inline_shape 가드 제거
- mydocs/pr/pr_461_review.md (5 Tasks 분리 cherry-pick 검토, 광범위 영향 86% 정황) 검증: 1070 passed + svg_snapshot 6/6 (golden 4건 갱신) + issue_418 1/1 + clippy 0 광범위 byte 비교: 305 중 263 차이 (86% 영향, Task #463 깊은 누적 정정) 시각 판정: PR #454 + #457 + #461 통합 검증 (작업지시자 결정) 본질 5 Tasks: - Task #459: 다단 후속 페이지 vpos-reset (PR #450 잔여 본질) - Task #462: TAC Picture 인라인 line advance - Task #463: 셀 leakage + 박스 geometry + 들여쓰기 + TAC crop + 바탕쪽 (Stage 1~8) - Task #468: cross-column 박스 partial 플래그 - Task #469: cross-column partial 박스 col_top/col_bot 침범
…ick @planet6897 15 commits, 5 Tasks)
|
@planet6897 님 PR 감사드립니다. 메인테이너가 5 Tasks 분리 cherry-pick 으로 devel 에 적용 완료했습니다. 본 사이클 16번째 PR 입니다. PR 제목은 Task #459 만 명시되었으나 실제로는 5 Tasks 누적 (Task #459 + #462 + #463 + #468 + #469). 작업지시자 결정 (옵션 A) 으로 5 Tasks 모두 분리 cherry-pick 진행했습니다. 처리 — 15 commits 분리 cherry-pick작성자 attribution 보존:
devel 머지 commit: 검증
광범위 byte 비교10 샘플 / 305 페이지 SVG: 42 동일, 263 차이 (86% 영향). 차이 분포: kps-ai 66, aift 61, exam_* 45, synam-001 33, 2025년 기부 30, k-water-rfp 23, biz_plan 5. → Task #463 의 본문 외곽선 / 박스 geometry / wrap host 들여쓰기 / 바탕쪽 정정이 매우 광범위 영향. 통합 시각 검증 정책PR #454 + #457 + #461 (3 PR / 7 Tasks) 모두 머지 후 작업지시자가 통합 시각 검증 진행 예정. 누적 정정 (paragraph_layout 통일 + 글상자 외부 본문 + 다단 vpos-reset + TAC line advance + 셀 leakage + 박스 geometry + 들여쓰기 + 바탕쪽 + cross-column) 모두 적용된 상태에서 한컴 정답지 비교가 작업 효율 + 위험 분산 양쪽에 부합. 후속 PR 작성 권장 사항본 사이클 누적 정황을 보고 향후 PR 작성 시 권장:
이 권장은 작성자에게 부담 안내가 아닌, 본 사이클 패턴 학습 자료입니다 — @planet6897 님은 이미 본 사이클 16 PR 처리하신 신뢰 컨트리뷰터로 패턴 인지 + 향후 PR 효율성 향상 가능. 이슈 #459 + Task #462/#463/#468/#469 모두 close 됩니다. 5 Tasks 의 본질 누적 정정 + PR #450 의 잔여 본질 ( |
- mydocs/pr/pr_456_review.md (P2 cherry-pick 검토, SVG 100% byte 동일) 검증: 1075 passed (+5 Canvas parity test) + svg_snapshot 6/6 + issue_418 1/1 + clippy 0 WASM: 4,206,022 bytes (+19,741, paint 모듈 Canvas replay 추가) 광범위 byte 비교: 305/305 byte 동일 (SVG legacy 경로 0 영향) ✅ 본질 (PR #419 의 P2): - Canvas 렌더 경로를 PageLayerTree replay 로 전환 - legacy 경로는 renderPageCanvasLegacy 로 보존 (fallback) - LayerBuilder leaf children 보존 정정 - Canvas parity test 추가 (CI 통합) 시각 판정: 통합 검증 (PR #454 + #457 + #461 + #456 머지 후 작업지시자 직접)
devel 의 PR edwardkim#461 (Task edwardkim#459+edwardkim#462+edwardkim#463+edwardkim#468+edwardkim#469) 통합본과 local/devel 의 Task edwardkim#469+edwardkim#470+edwardkim#471 작업 충돌 해결. 해결 내용: - src/renderer/layout.rs: ours (Task edwardkim#471 stroke_sig 비교 채택). devel 의 bf_id 비교는 Task edwardkim#471 에서 정정한 결함이므로 ours 가 superset. - src/renderer/layout/integration_tests.rs: ours (Task edwardkim#469/edwardkim#470/edwardkim#471 통합 테스트 3건 모두 보존). devel 은 edwardkim#469 테스트만 포함. - mydocs/orders/20260430.md: 양측 섹션 병합 (M100 박스 정합 + 잔존 이슈 + PR 처리 PR edwardkim#450). 검증: - cargo test 1072 + svg_snapshot 6 + issue_418 1 + 기타 PASS, FAIL 0.
…Canvas→LayerTree 반영 (Task edwardkim#460 기반) - upstream/devel 46 commits 병합 (PR edwardkim#446 set_field fix ~ PR edwardkim#456 Canvas→LayerTree) - 소스 파일 자동 병합 성공 (layout.rs, paragraph_layout.rs, engine.rs) - orders/20260430.md add/add 충돌만 수동 해소 (Task edwardkim#460 섹션 + PR edwardkim#450 섹션 통합) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
요약
다단 구역이 여러 페이지에 걸칠 때, 첫 페이지가 아닌 후속 페이지의 좌측 단 → 우측 단 단 경계 인코딩(
LINE_SEG.vertical_pos리셋)이 무시되어 좌측 단 하단이 col_bottom 을 초과해 그려지던 버그를 수정.samples/exam_kor.hwp페이지 2 좌측 단의pi=39문단 4줄 중 마지막 2줄이 col_bottom 을 39px 초과해 그려지던 문제를 해결. HWP 원본의 LINE_SEG vpos 리셋 위치(ls[2])대로 좌측 단 2줄, 우측 단 5줄로 정확히 분할됨.원인
on_first_multicolumn_page가드는state.rs:218의reset_for_new_page()에서 새 페이지 시작 시 false 로 리셋됨. 다단 구역이 페이지를 넘어가면 후속 페이지에서 flag = false →detect_column_breaks_in_paragraph호출이 차단됨. 결과: HWP 가 LINE_SEG vpos 리셋으로 인코딩한 단 경계가 무시되고, 높이 기반 폴백이 사용되어 col_bottom 초과.git log -G "on_first_multicolumn_page"결과 최초 커밋 한 번만 나와 가드 도입 이유는 코드 주석/문서에 기록 없음. 초기 보수적 설계의 흔적으로 판단.변경 파일
src/renderer/typeset.rs:856(기본 페이지네이션 경로, TypesetEngine)src/renderer/pagination/engine.rs:607(RHWP_USE_PAGINATOR=1 fallback)두 엔진 모두 동일 가드를 가지고 있어 양쪽 일관성 유지.
안전성 분석
detect_column_breaks_in_paragraph는 vpos 가 감소하는 경우에만 단 경계로 감지 → 단일 컬럼 문단이나 vpos-reset 이 없는 다단 문단에는 영향 없음 (col_breaks=[0]→ 분기 진입하지 않음)current_column == 0조건은 유지 — 이미 우측 단에 있는 paragraph 는 단 경계 의미 없음paginate_multicolumn_paragraph/typeset_multicolumn_paragraph는advance_column_or_new_page()사용으로 후속 페이지에서도 단 진행 가능 (특별한 first-page 가정 없음)검증 결과
exam_kor 페이지 2 (목표 케이스)
수정 후 HWP 원본의 LINE_SEG 인코딩과 정확히 일치.
자동 해소된 추가 케이스
회귀 검증 (다단 샘플 5종)
단위/통합 테스트
cargo test --release: 1120 passed, 0 failed후속 과제
관련 문서
mydocs/plans/task_m100_459.mdmydocs/working/task_m100_459_stage1.mdmydocs/report/task_m100_459_report.mdcloses #459