Task #534 v2 + #537 + #539 + #540 + #544 + #547 + #548: lazy_base / overlay-shape / 빈 paragraph 음수 ls floor / paragraph border 좌표·텍스트 inset / 셀 inline shape margin (21_언어_기출 줄간격 정정)#538
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
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
페이지 4 [7~9] 박스 PDF vs SVG 비교 결과 본질 분석:
(1) 박스 left/width 차이: ParaShape margin (HWP 2배 저장) 이 SVG 박스 outline
에 적용. PDF 는 col_area 전체 폭 (margin 미적용). pi=82 margin_left=1704 HU
→ resolved 852 HU = 11.36 px 가 box_x 에 더해져서 11.5 px 시프트.
(2) 박스 top y 차이: SVG bg_y_start 가 sequential y_offset (Task edwardkim#479 trailing-ls
제외). PDF 는 IR vpos 기반. pi=80 line_spacing 716 HU 가 SVG box top 에서
누락되어 9.4 px 위로 쏠림.
(3) PDF 9개 passage 박스 모두 일관 패턴 (col_width 425.1 px, x=col_area.x).
광범위 회귀 위험 매우 큼 (paragraph border 본질 변경, 모든 샘플 영향).
[feedback_essential_fix_regression_risk] [feedback_pdf_not_authoritative]
[feedback_rule_not_heuristic] 메모리 룰 적용. 작업지시자 검증 입력 대기:
1. 다른 샘플 PDF 비교 (synam-001, 복학원서 등)
2. 한컴 2020 / 한컴독스 환경 비교
3. HWP 표준 명세 paragraph border 좌표 산출 룰
4. fix 범위 (A: 광범위 / B: 빈 paragraph 직후만 / C: heuristic)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… 범위 결정 대기) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
테스트:
- test_544_passage_box_coords_match_pdf_p4 추가 (RED, ignore attribute)
- 페이지 4 [7~9] col 0 박스 top_y/left_x/width PDF 정합 검증
- 1120 기존 테스트 모두 통과
진단 결과:
(1) 박스 top y -9.4 px: layout.rs:1481 가드 (seg.vertical_pos == 0 &&
prev_pi > 0) 가 페이지 시작 paragraph 의 vpos correction 도 skip 시킴
→ trailing-ls 716 HU 누락. 가드 의도 (vpos reset 보호) 가 페이지 시작
case 와 구분 안 됨.
(2) 박스 left x +11.5 px / width -22.6 px: paragraph_layout.rs:2697 의
box_x = col_area.x + box_margin_left 산식. ParaShape margin_left=1704 HU
(2배 저장) → resolved 11.36 px 가 박스 좌표에 적용. PDF 는 미적용.
광범위 사전 평가:
- exam_math (표 outline 우세) / exam_eng (margin=0) / 21_언어_기출 (margin=1704)
- 모든 샘플 동일 산식, margin_left=0 샘플은 우연히 PDF 일치
- 21_언어_기출 만 큰 margin 으로 차이 노출
권장 fix (A안 부분):
- 박스 left/width: (col_area.x, col_area.width) 로 변경 (광범위)
- 박스 top y: bg_y_start 보정 (paragraph border 한정, 회귀 위험 최소)
작업지시자 입력 대기:
1. fix 범위 (A안 부분) 진행 OK?
2. 셀 내부 paragraph border 케이스 샘플
3. wrap=Square 호스트 케이스 샘플
4. 한컴 2020 / 한컴독스 검증
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(1) 박스 left/width — paragraph_layout.rs:2697:
box_x = col_area.x, box_w = col_area.width (margin 미적용)
(2) 박스 top y — paragraph_layout.rs:786 + layout.rs:1492:
LayoutEngine 에 paragraph_border_y_correction_px Cell 추가.
vpos correction 가드 skip 케이스에서 다음 paragraph 가 border 가지면
prev paragraph 의 trailing-ls 를 보정값으로 set. paragraph_layout
진입 시 read 후 reset, bg_y_start 에 더함 (본문 텍스트 위치 보존).
(3) Task edwardkim#540 Stage 4 push skip 가드 revert:
Task edwardkim#540 Stage 4 는 cumulative comp 후 박스 위쪽 여백 회귀의 임시
우회. Task edwardkim#544 의 trailing-ls 보정이 동일 회귀 본질 해결. Stage 4
우회 제거로 페이지 2 [4~6] PDF 정합 + 코드 단순화.
검증:
- test_544_passage_box_coords_match_pdf_p4 RED → GREEN
- 1120 unit tests 통과
- 21_언어_기출 핵심 박스 (페이지 2 [4~6] / 페이지 4 [7~9] / 페이지 10 [16~18])
모두 PDF 일치 (-1.84 px tolerance 내)
- 광범위 회귀: text 음의 시프트 0건, line diff 는 paragraph border 정정의
의도된 결과
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
메인테이너가 회귀 검증을 진행하다가 samples/exam_science.hwp 파일의 2 페이지에서 페이지네이션 회귀 발견을 하였습니다. 0.7.9 에서는 발생되지 않은 케이스여서 어디서 부터 회귀가 발생되었는지 점검 중입니다. |
검증 결과: - 1120 unit tests 통과 (Task edwardkim#537/edwardkim#539/edwardkim#540 무회귀) - 21_언어_기출 핵심 3개 박스 (페이지 2/4/10) PDF 일치 (-1.84 px tolerance) - 광범위 회귀 (6 샘플, 104 페이지): 텍스트 -시프트 0건 - synam-001 (음수 ls 57건): 0 변경 (보존) 페이지 6/8/13/14/15 박스 자동 검출 한계는 paginator 분배 차이 (페이지/컬럼 배치) 로 인한 것으로 본 task 와 무관 (별도 분석). 오늘할일 (orders/20260503.md) Task edwardkim#544 항목 추가. closes edwardkim#544 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…edwardkim#540 후속) paragraph border 좌표 산출 두 본질 정정: (1) box_x/box_w: paragraph margin 미적용, col_area 전체 사용 (2) bg_y_start: vpos correction 가드 skip 시 trailing-ls 보정 (paragraph border 한정, 본문 텍스트 위치 보존) Task edwardkim#540 Stage 4 의 임시 우회 (push skip 가드) 는 Task edwardkim#544 의 본질 정정이 동일 회귀 (passage 박스 안 위쪽 여백) 를 더 본질적으로 해결하므로 revert. 검증: 페이지 4 [7~9] 박스 PDF 일치 (top/left/width 모두 ±2 px), 1120 unit tests 통과, 광범위 회귀 (6 샘플) 텍스트 -시프트 0건. closes edwardkim#544 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dwardkim#540 후속) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
다른쪽 PR 처리하면서 발생된 것으로 확인되어 이번 PR 처리 재개하겠습니다. |
cherry-pick (본질 12 commits 만 — fork devel 누적 170 commits 제외): - e7f1adb~1bca866: Task #534 v1 (4 commits, layout_shape_item TAC Picture inner_pad) - 576aa29~57ccde6: Task #534 v2 (2 commits, LINE_SEG.column_start 정합) - d70599d~58af6c9: Task #537 (3 commits, lazy_base trailing-ls 보정) - f60f580~fc32bd3: Task #539 (3 commits, prev_has_overlay_shape 가드 완화) 검증: - cargo test --lib 1113 passed - cargo test --test issue_530/505/418/501 회귀 0 - cargo test --test svg_snapshot 6/6 passed - cargo clippy --lib 0 건 - cargo build --release 정상 - WASM 빌드 4,461,870 bytes + studio 동기화 시각 판정 (작업지시자 한컴 2010/2020 직접 판정): - 1차 (SVG export-svg) 통과 - 2차 (rhwp-studio web Canvas) 통과 부수 발견 (별도 이슈): - 이슈 #545 — aift.hwp p41 표 위치 (PR #538 와 무관, 이전부터) - 이슈 #546 — exam_science.hwp p2 페이지네이션 회귀 (PR #506 origin, bisect 확정) 컨트리뷰터 안내: 다음 PR 전 fork devel 동기화 부탁. 산출물: - mydocs/pr/pr_538_review.md (검토) - mydocs/pr/pr_538_review_impl.md (cherry-pick 절차) - mydocs/pr/pr_538_report.md (처리 결과) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…— cherry-pick @planet6897 12 commits) — closes #534/#537/#539 본 PR 은 외부 컨트리뷰터 @planet6897 (Jaeuk Ryu) 의 PR. 21_언어_기출_편집가능본.hwp 의 줄간격 716 HU drift + exam_kor 18p 그림 위치 결함의 본질 정정. cherry-pick (본질 12 commits 만 — fork devel 누적 170 commits 제외): - Task #534 v1+v2: layout_shape_item TAC Picture inner_pad + LINE_SEG.column_start · src/renderer/layout.rs (+37 / -3 LOC) - Task #537: lazy_base trailing-ls 보정 (TAC 표 직후 첫 답안 줄간격 716 HU drift, P2/3/5/6/8/9/12/13/14 11곳) · src/renderer/layout.rs (+14 / -2 LOC) - Task #539: prev_has_overlay_shape 가드 완화 (treat_as_char 제외, 글박스 호스트 직후 paragraph 줄간격 P7/P9 2곳) · src/renderer/layout.rs (+9 / -0 LOC) - src/renderer/layout/integration_tests.rs (TDD 통합 테스트 3건) 검증: - cargo test --lib 1113 passed - cargo test --test issue_530/505/418/501 회귀 0 - cargo test --test svg_snapshot 6/6 통과 - cargo clippy --lib 0 건 - cargo build --release 정상 - WASM 4,461,870 bytes + rhwp-studio 동기화 시각 판정 (작업지시자 한컴 2010/2020 직접): - 1차 SVG export-svg 통과 - 2차 rhwp-studio web Canvas 통과 부수 발견 (별도 이슈, PR #538 와 무관): - 이슈 #545: aift.hwp p41 표 위치 (이전부터 발생) - 이슈 #546: exam_science.hwp p2 페이지네이션 회귀 (PR #506 origin, bisect 확정) 컨트리뷰터 안내: 다음 PR 전 fork devel 동기화 부탁.
|
@planet6897 님의 PR을 본질 cherry-pick 으로 devel 에 반영했습니다 (devel 머지된 commits (본질 12 commits, fork devel 누적 170 commits 제외)PR 본문에 명시한 3 task 의 정정만 추출하여 cherry-pick:
author 는 @planet6897 으로 보존되었습니다. 검증
시각 판정 (한컴 2010/2020)
부수 발견 (별도 이슈, 본 PR 와 무관)본 PR 의 시각 판정 중 발견된 결함 — 모두 본 PR 과 별개로 분리 등록:
컨트리뷰터께 부탁드리는 사항본 PR 의 base/head 가 모두 `devel` 로 설정되어 있어 fork 의 devel 브랜치 누적분 (170 commits / 약 40 task) 이 PR 에 모두 포함되었습니다. PR 본문에 명시한 3 task (#534 v2 + #537 + #539) 외의 task 들은 별도 검토 절차 미진행 상태이므로, 본 PR 에서는 본질만 추출하여 cherry-pick 으로 반영했습니다 (CONFLICTING 상태도 함께 해소). 다음 PR 작업 전에 본 환경 `devel` 동기화 부탁드립니다: ```bash 처리 보고서: `mydocs/pr/archives/pr_538_report.md` 이슈 #534/#537/#539 는 PR closes 자동 동작으로 이미 closed 상태이고, 정정이 적용되어 close 유지 정합합니다. milestone v1.0.0 은 사후 처리하겠습니다. |
Task #547 추가 (Task #544 후속)문제: Task #544 가 paragraph border 박스 outline 을 col_area 로 정정한 후, 박스 안 본문 텍스트의 좌측 inset 이 PDF 보다 넓게 보임 (작업지시자 시각 검증: [13~15] 박스 안 본문 inset 우측으로 치우침). 본질: 정정: inner_pad 분기 완전 제거 + 단일 룰 (margin_left = box_margin_left 한 번만) 적용. // 정정
let margin_left = box_margin_left;
let margin_right = box_margin_right;제거된 변수: 핵심 측정값 (페이지 4 [7~9] passage 박스):
[13~15] 박스 직접 검증: 페이지 8 col 1 박스 안 본문 556 건 모두 -11.36 px 좌측 시프트, 좌측 여백 22.66→11.36 px 정상화. 검증:
상세 보고서: |
Task #548 추가 (Task #547 후속)문제: 페이지 8 보기 표 (pi=167) 셀 5 (3-col 병합 본문 셀) 의 첫 줄 [푸코] inline rectangle Shape 가 cell 좌측 가장자리 (x=131.04 px) 에 렌더되어 PDF (한컴 2010, x≈155.6 px) 보다 24.6 px 좌측 시프트 (작업지시자 시각 검증). 본질: 정정: fn effective_margin_left_line(margin_left: f64, indent: f64, line_n: usize) -> f64 {
let line_indent = if indent > 0.0 {
if line_n == 0 { indent } else { 0.0 }
} else if indent < 0.0 {
if line_n == 0 { 0.0 } else { indent.abs() }
} else {
0.0
};
margin_left + line_indent
}
// 3 위치 적용
let line_margin = effective_margin_left_line(para_margin_left_px, para_indent_px, line_n);
match para_alignment {
Alignment::Center | Alignment::Distribute => { ... } // 변경 없음
Alignment::Right => { ... } // 변경 없음
_ => inner_area.x + line_margin, // fix
}핵심 측정값 (페이지 8 셀 5 line 0 [푸코] box):
shape 와 직후 텍스트 정확 일치 (185.83 = 155.60 + 30.23). 검증:
메모리 룰 적용:
상세 보고서: |
회귀 origin: 82e41ba (Task #460 보완5: Square wrap 그림 아래 텍스트 y위치 보정 — layout + typeset) 옵션 C (페이지/단 경계 검사 추가) 시도 결과 0 효과 — 모든 보정값이 col_h 이내라 가드 trigger 안 됨. 결함의 본질이 wrap-around paragraph 들의 누적 height 와 결합 영역이라 작업지시자 결정으로 옵션 A (전체 revert) 진행. 변경: - src/renderer/layout.rs (-58 LOC, wrap_pic_bottom_y + y_offset 보정) - src/renderer/typeset.rs (-36 LOC, wrap_around_pic_bottom_px 처리) - tests/issue_546.rs (신규 회귀 테스트 1건) 검증: - cargo test --lib 1113 passed - cargo test --test issue_546 1 passed - cargo test --test issue_530/505/418/501 회귀 0 - cargo test --test svg_snapshot 6/6 통과 - cargo clippy --lib 0 건 광범위 fixture sweep (PR #538 머지 후 ↔ revert 후 byte 비교): - exam_kor/exam_eng/exam_math/synam-001/복학원서/2010-01-06/21_언어_기출 : 105 페이지 모두 byte-identical (회귀 0) - exam_science: 4 페이지 모두 의도된 정정 (회귀 6→4 + p2 본문 복원) 페이지 수 v0.7.9 정합: - exam_science.hwp 6 페이지 → 4 페이지 (회귀 정정) - 다른 8 fixture v0.7.9 와 동일 페이지 수 보존 Task #460 보완5 의 본 의도 (HWP3 Square wrap 그림 아래 텍스트 y위치 정합) 는 본 revert 로 손실. Stage 5 시각 판정 시 HWP3 fixture 점검 후 재발 시 별도 task 분리. closes #546 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
작업지시자 시각 판정 통과 ("시각 회귀 통과입니다").
WASM 빌드: 4,442,504 bytes (PR #538 시점 4,461,870 -19,366, 94 LOC 제거 반영) +
rhwp-studio 동기화 완료.
산출물:
- mydocs/report/task_m100_546_report.md (최종 보고서)
회귀 origin 분석:
- 82e41ba (Task #460 보완5: Square wrap 그림 아래 텍스트 y위치 보정)
- 본 의도: HWP3 Square wrap 그림 아래 텍스트 y위치 정합
- exam_science 의 부작용: 2단 + 그림이 단 0 끝 + 풍부한 wrap-around 조합에서
current_height = max(current_height, bottom_px) 가 wrap-around paragraph 의
누적 height 와 결합한 double advance 발생 → 페이지/단 강제 분리
광범위 영향 작은 이유: specific 조합에서만 결함 trigger.
PR #506 의 다른 fixture 검증에서도 미검출 (exam_science 가 정밀 fixture).
Task #460 보완5 의 본 의도 손실 — HWP3 fixture 시각 결함 재발견 시 별도 task 로
페이지네이션 안전한 방식 (wrap-around paragraph 누적 height 추적) 재시도 권장.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…revert) — closes #546 회귀 origin: 82e41ba (Task #460 보완5: Square wrap 그림 아래 텍스트 y위치 보정 — layout + typeset) bisect 결과 정확히 식별: - v0.7.9 (main, 0fb3e67): 4 페이지 + p2 본문 37 items ✅ - 0aa7a5e (보완4 직전): 4 페이지 ✅ - ab2f4d0 (보완4 HWP3): 4 페이지 ✅ - 9d9b4ed (보완4 어울림 텍스트): 4 페이지 ✅ - 82e41ba (보완5): 6 페이지 + p2 본문 2 items ❌ ← 회귀 origin 옵션 C (페이지/단 경계 검사) 시도 결과 0 효과 — 모든 보정값이 col_h 이내라 가드 trigger 안 됨. 결함의 본질이 wrap-around paragraph 누적 height 와 결합한 double advance 영역이라 작업지시자 결정으로 옵션 A (전체 revert) 진행. 변경: - src/renderer/layout.rs (-58 LOC, wrap_pic_bottom_y + y_offset 보정) - src/renderer/typeset.rs (-36 LOC, wrap_around_pic_bottom_px 처리) - tests/issue_546.rs (신규 회귀 테스트 1건) 검증: - cargo test --lib 1113 passed - cargo test --test issue_546/505/530/418/501/svg_snapshot 회귀 0 - cargo clippy --lib 0 건 - WASM 4,442,504 bytes + rhwp-studio 동기화 광범위 fixture sweep (PR #538 머지 후 ↔ revert 후 byte 비교): - 8 fixture / 105 페이지 byte-identical (회귀 0) - exam_science 4 페이지 의도된 정정 (회귀 6→4 + p2 본문 복원) 작업지시자 시각 판정 통과 (한컴 2010/2020 직접 비교). Task #460 보완5 의 본 의도 (HWP3 Square wrap 그림 아래 텍스트 y위치 정합) 는 본 revert 로 손실. HWP3 fixture 재발견 시 별도 task 로 페이지네이션 안전한 방식 (wrap-around 누적 height 추적) 재시도 권장.
…e wrap 호스트 텍스트 중복 emit 정정 — cherry-pick @planet6897 6 commits) 본 PR 은 외부 컨트리뷰터 @planet6897 (Jaeuk Ryu) 의 두 번째 PR (PR #538 후속). fork devel 누적 (248 commits / 50+ task) 이라 작업지시자 결정으로 본질 Task #525 만 cherry-pick. 작업지시자도 인지하고 있던 결함의 root cause 정확 식별. cherry-pick (6 commits): - Task #525 수행 계획서 / Stage 1 진단 / 구현 계획서 / Stage 2 정정 / Stage 3 회귀 검증 / Stage 4 보고서 - 코드 변경: src/renderer/layout.rs (+14 / -69 LOC, net -55) 본질: layout_wrap_around_paras 가 비-TAC Picture wrap=Square host 의 호스트 paragraph 자기 텍스트를 PageItem::FullParagraph 정상 경로 외에 두 곳에서 추가 emit (layout.rs:3093 layout_shape_item + :3496 layout_column_shapes_pass) → 같은 줄을 다른 col_w 정렬로 distinct x 위치 emit → 시각 중첩. 정정: Picture Square wrap 의 wrap-around 호출 두 곳 모두 제거. Table Square wrap 호출 (layout.rs:2555) 은 의도된 동작 유지. paragraph_layout.rs:822/973 의 has_picture_shape_square_wrap 분기가 LINE_SEG.cs/sw 기반으로 그림 옆 (좁은) + 그림 아래 (넓은) 모두 처리. 검증: - cargo test --lib 1113 passed - cargo test --test issue_546/530/505/418/501 회귀 0 - cargo test --test svg_snapshot 6/6 통과 - cargo clippy --lib 0 건 - WASM 4,441,878 bytes (-626 from Task #546 시점) + rhwp-studio 동기화 광범위 fixture sweep: - 8 fixture / 113 페이지 byte-identical (회귀 0) - exam_science 2 페이지 의도된 정정 (dup emit 해소) 시각 판정 (작업지시자 한컴 2010/2020 직접): 통과. Task #546 양립 확인 — 두 정정이 다른 영역 (typeset.rs current_height vs layout.rs layout_wrap_around_paras 중복 호출). PR #551 의 다른 영역 (242 commits / ~50 task) 은 별도 처리 결정 — 컨트리뷰터에게 다음 PR 전 fork devel 동기화 + task 별 분리 PR 안내 예정.
…g 좁음 (5+ 케이스) 본 task edwardkim#544 v2 시각 판정 중 작업지시자 발견된 회귀 5 케이스. byte-identical baseline 검증으로 본 task edwardkim#544 v2 와 무관한 사전 회귀 확정. 영역: PR edwardkim#538 의 Task edwardkim#539 그룹 A (지문 시작 [X~Y] 직후 9곳 보류) 일치. 작업지시자 보고 케이스: - p2 [4~6]: pi=46 "15세기 초..." → pi=47 "고진에 따르면..." - p5 [10~12]: pi=112 "살펴보건대..." → pi=113 "형법은 선왕..." - p10 [19~21]: → "성리학의 논의..." - p11 [22~24]: pi=234 "빈곤 퇴치..." → pi=235 "제도의 역할..." - p13 [25~27]: → "세포는 영양분..." 사전 측정 (정상 24.21 px = 1 line spacing 대비): - p2 [4~6]: gap 18.35 (drift -5.86) - p5 [10~12]: gap 19.10 (drift -5.11) - p11 [22~24]: gap 14.66 (drift -9.55, line_height 1100 HU 정확) 본질 가설: 박스 안 sequential paragraph (prev/next 같은 박스 outline) 끝에서 Task edwardkim#479 trailing-ls 제외가 작동 → next_starts_border=false → trailing ls 누락. Task edwardkim#552 가 border 시작 직전만 처리, 박스 안 sequential 은 미처리 영역. 수정 방향: paragraph_layout.rs 의 next_starts_border 가드 확장 또는 layout.rs 에 next_para_continues_visible_border Cell 추가. 4 단계 구성: - Stage 1: 정밀 진단 (p10/13 측정 + p2/5 부분 제외 본질) + TDD RED + 방법 결정 - Stage 2: Phase A 적용 - Stage 3: Phase B (필요 시) + 광범위 회귀 - Stage 4: 최종 보고서 + orders 갱신 브랜치: local/task544_v3 (local/task544_v2 위에 분기) 작업지시자 결정으로 이슈 등록 없이 v3 suffix 사용. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
5개 task 합본 PR. 21_언어_기출_편집가능본.hwp 줄간격 정합 + paragraph border 좌표 정합.
<보기>표 직후 첫 답안 ① ↔ ② 줄간격 716 HU 좁음 →layout.rslazy_base 산출에 trailing-ls 보정 (+14 / -2 LOC). closes 21_언어_기출.hwp TAC 표 직후 첫 문단의 trailing line_spacing 누락으로 줄간격 좁아짐 #537.prev_has_overlay_shape가드에서treat_as_char=trueShape 제외 (+9 / -0 LOC). closes 21_언어_기출.hwp 지문 시작 표시 [X~Y] 직후 + 박스 안 paragraph 줄간격 좁음 (Task #537 후속) #539.layout.rs컬럼 누적 보정vpos_neg_ls_floor_total도입 + lazy_base 누적 보정 가산 (+30 LOC). closes 21_언어_기출.hwp 지문 시작 표시 [X~Y] 직후 줄간격 (Task #539 그룹 A — 한컴 환경 검증 필요) #540.Task #537 — TAC 표 직후 첫 답안
작업지시자 보고 11곳 (P2 q3 등) 모두 IR vpos delta 와 정확 일치.
근본 원인:
prev_tac_seg_applied가드 + Task #479 trailing-ls 제외 + lazy_base sequential drift 동결.Task #539 — 글박스 호스트 직후 (#537 후속)
작업지시자 보고 그룹 B 2곳 (7p 르포르, 9p 수피즘).
근본 원인: pi=145/pi=181 의 controls 에
Shape wrap=InFrontOfText tac=true보유.prev_has_overlay_shape가드가 treat_as_char 무관하게 true → 직후 paragraph 의 vpos correction skipped.Task #540 — 빈 paragraph 음수 ls floor (#539 그룹 A 후속)
가설 H2 채택 — 한컴은 빈 paragraph 의 음수 line_spacing 을 floor (무시) 하여 advance = line_height 만큼만 진행.
근본 원인: HWP IR 의
LINE_SEG.line_spacing은 음수 값 그대로 저장. vpos correction 시 음수 ls 가 반영되어 빈 paragraph advance 가 한컴보다 짧음.정정 (
layout.rs):```rust
let mut vpos_neg_ls_floor_total: i32 = 0;
let prev_neg_ls_floor: i32 = paragraphs.get(prev_pi)
.map(|p| {
if !p.text.is_empty() || !p.controls.is_empty() { return 0; }
p.line_segs.iter()
.map(|s| if s.line_spacing < 0 { -s.line_spacing } else { 0 })
.sum::()
})
.unwrap_or(0);
vpos_neg_ls_floor_total += prev_neg_ls_floor;
let vpos_end = vpos_end_raw + vpos_neg_ls_floor_total;
let lazy_base = prev_vpos_end - y_delta_hu + vpos_neg_ls_floor_total;
```
가드 근거: `text.is_empty() && controls.is_empty()` — synam-001 의 음수 ls 57건 중 일반 paragraph 보존, exam_science section-setup paragraph (cc=57 controls=7 ls=-1348) floor 제외.
Task #544 — paragraph border 좌표 PDF 정합 (#540 후속)
작업지시자 시각 검증 (PDF 비교) 결과 passage 글상자 위치/크기 차이 발견. 두 본질 정정.
근본 원인 (1): ParaShape margin_left/right 가 박스 outline 좌표에 적용 (
box_x = col_area.x + box_margin_left). 21_언어_기출 의 ps_id=11 margin_left=1704 HU → 11.36 px 시프트 노출. PDF 는 col_area 전체.근본 원인 (2): vpos correction 가드 (
seg.vertical_pos == 0 && prev_pi > 0) 가 페이지 시작 paragraph 직후 transition 의 vpos correction 도 skip 시킴 → trailing-ls 716 HU 가 sequential y_offset 에서 누락. PDF 는 IR vpos 기반.정정 (`layout.rs` + `paragraph_layout.rs`):
```rust
// LayoutEngine 에 보정 Cell 추가
paragraph_border_y_correction_px: Cell,
// vpos correction 가드 skip 시 trailing-ls set
if seg.vertical_pos == 0 && prev_pi > 0 {
let trailing_ls_hu = seg.line_spacing.max(0);
let next_has_border = ...; // 다음 paragraph 가 border 가지면
if trailing_ls_hu > 0 && next_has_border {
self.paragraph_border_y_correction_px.set(
hwpunit_to_px(trailing_ls_hu, self.dpi)
);
}
}
// paragraph_layout 에서 bg_y_start 보정 + reset
let bg_y_start = if para_border_fill_id > 0 {
let corrected = y_start + self.paragraph_border_y_correction_px.get();
self.paragraph_border_y_correction_px.set(0.0);
corrected
} else {
self.paragraph_border_y_correction_px.set(0.0);
y
};
// box_x / box_w 산식 정정 (margin 미적용)
let (box_x, box_w) = if let Some((ox, ow)) = self.border_box_override.get() {
(ox, ow)
} else {
(col_area.x, col_area.width)
};
```
Task #540 Stage 4 push skip 가드 revert: Task #544 의 trailing-ls 보정이 동일 회귀 (passage 박스 안 위쪽 여백 증가) 를 더 본질적으로 해결하므로 임시 우회 제거. 페이지 2 [4~6] 박스 PDF 정합 (Stage 4 만으로는 +12.83 px 차이) + 코드 단순화.
박스 좌표 검증 (페이지 2/4/10 모두 PDF 일치, ±2 px tolerance 내).
검증
잔존 / 후속
Test plan
closes #537
closes #539
closes #540
closes #544