Skip to content

Task #470 + #471 + #473: cross-column vpos-reset/박스 stroke_sig + 그림 crop scale 정정#472

Closed
planet6897 wants to merge 67 commits into
edwardkim:develfrom
planet6897:local/devel
Closed

Task #470 + #471 + #473: cross-column vpos-reset/박스 stroke_sig + 그림 crop scale 정정#472
planet6897 wants to merge 67 commits into
edwardkim:develfrom
planet6897:local/devel

Conversation

@planet6897

Copy link
Copy Markdown
Contributor

Summary

Task #468 (cross-column 박스 연속 partial 플래그) 후속 정정 두 건. 두 task 는 서로 종속(#471#470 적용 후 노출된 회귀를 정정).

Task #470 — 다단 cross-paragraph vpos-reset 검출 완화

`samples/21_언어_기출_편집가능본.hwp` 페이지 1 좌측 단 하단에 pi=10 ("적합성 검증이란…") 의 처음 2줄이 강제 배치되어 56.2 px overflow. HWP 인코딩상 pi=10 전체가 우측 단 시작 (first vpos=9014, pi=9 last vpos=90426).

원인

Task #321 cross-paragraph vpos-reset 가드가 `cv == 0` 만 인정 → 컬럼 헤더 오프셋 (cv=9014) 인 다단 케이스 미감지.

수정 (`src/renderer/typeset.rs:415, 439`)

다단 / 단일 단 분기:

  • 다단 (col_count > 1): `cv < pv && pv > 5000` (HWPUNIT vpos 단조 증가 가정)
  • 단일 단: 기존 `cv == 0 && pv > 5000` 유지 (issue_418 partial-table split 회귀 차단)

결과

  • 21_언어_기출 OVERFLOW 13 → 10 ✓
  • exam_science 5 → 0
  • exam_social 4 → 1
  • (총 -11 건 추가 해소)
  • hwpspec(단일 단), exam_kor, exam_eng 회귀 0

Task #471 — cross-column 박스 stroke_sig 비교

Task #470 적용 후 21_언어_기출 1p 좌측 단 (가) 박스 하단에 가로선이 그려지는 부작용 발견 (PDF 는 cross-column 으로 우측 단 이어짐).

원인

`src/renderer/layout.rs` paragraph border 머지 (Task #321 v6) 는 stroke_sig (line_type/width/color) 기준이지만, Task #468 cross-column 검출은 bf_id 동등 비교 → 머지 후 그룹의 `g.0` 은 첫 range bf_id 만 보존 → 다른 bf_id 로 묶인 인접 paragraph 에 대해 부정확.

본 케이스: 좌측 단 그룹 [pi=6(bf=7) + pi=7~9(bf=4)] 머지로 g.0=7. composed[10].bf=4 비교 시 4 != 7 → partial_end 미설정 → 4면 stroke 단일 Rectangle → 하단 가로선 발생.

수정 (`src/renderer/layout.rs:1670-1699`)

`bf_id` 비교를 `stroke_sig` 비교로 변경:

```rust
let group_sig = stroke_sig(bf_id);
let prev_sig = stroke_sig(para_bf(first_pi - 1));
let next_sig = stroke_sig(para_bf(last_pi + 1));
if prev_sig.is_some() && prev_sig == group_sig { g.7 = true; }
if next_sig.is_some() && next_sig == group_sig { g.8 = true; }
```

머지와 동일 비교 기준으로 일관성 확보.

결과

  • 좌측 단 (가) 박스 하단 가로선 부재 ✓
  • 우측 단 (가) 박스 (변동 없음, 원래 정상)
  • OVERFLOW 회귀 0

Test plan

참고

  • 한컴독스 PDF 200dpi 비교는 보조 ref. 한컴 2010/2020 정답지 (`samples/2010-exam_kor.pdf`, `2020-exam_kor.pdf`) 와의 환경 차이 정상.
  • 본 PR 의 정정은 레이아웃 알고리즘 본질 (cv < pv 검출 + stroke_sig 일관성).
  • 후속 본질 작업 (`respect_vpos_reset` 일반화) 은 다단/단일 단/표분할/wrap 상호작용으로 회귀 위험 큼 → 별도 PR + 광범위 샘플 검증 필요.

planet6897 and others added 30 commits April 29, 2026 11:54
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
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 모두 통과
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
…로 변경

closes edwardkim#471

Task edwardkim#468 cross-column 박스 인접 검출이 bf_id 동등 비교로 동작했으나,
머지(Task edwardkim#321 v6)는 stroke_sig 기준이라 bf_id 가 다르더라도 visual 동일
하면 한 그룹이 됨. 머지 후 그룹의 g.0 은 첫 range bf_id 만 보존.

본 회귀: 21_언어_기출 1p 좌측 단 (가) 박스가 pi=6(bf=7) + pi=7~9(bf=4)
머지로 g.0=7. composed[10].bf=4. bf_id 비교로 4 != 7 → partial_end 미설정
→ 4면 stroke 단일 Rectangle 로 그려져 하단 가로선 발생 (Task edwardkim#470 적용
후 pi=10 이 col 1 로 이동하면서 노출).

수정: layout.rs:1670-1699 의 cross-column 검출을 stroke_sig 비교로 변경.
머지와 동일한 비교 기준 사용으로 일관성 확보.

검증:
- 신규 통합 테스트 test_471_cross_column_box_no_bottom_line_in_col0
- cargo test 1123 / 1123 PASS
- 21_언어_기출/exam_kor/exam_eng/hwpspec OVERFLOW 회귀 0
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.
closes edwardkim#473

`compute_image_crop_src` 가 `original_size_hu` (= ShapeComponentAttr.
original_width/height, 표시 HU) 를 crop 좌표 변환 scale 기준으로 사용. HWP
crop 은 실제로 이미지 native 픽셀을 96-DPI HU 관행 (75 HU/px) 으로 인코딩
하므로, 사용자가 그림 크기를 변경한 케이스에서 scale 어긋남 발생.

본 회귀: 21_언어_기출 12p `<보기>` 표 내부 그림이 orig=12 HU/px (vs 75)
로 viewBox=13875 산출 → image=2220 px 가 viewBox 의 16% 만 차지 (6.25×
축소).

수정: svg.rs:2385-2404 의 scale 산출을 96-DPI 관행 기반으로 변경.
- original_size_hu/img_px 가 75 ± 5% 안일 때만 orig 사용 (역호환).
- 아니면 75 HU/px fallback.

기존 unit test 갱신: offset_top_left, fallback_when_original_size_missing
의 가상 입력 (10 HU/px 등) 은 96-DPI 관행 외라 새 정책에 따라 75 fallback
적용. exam_kor_header / no_crop_full_image (75 HU/px) 는 영향 없음.

검증:
- 신규 통합 테스트 test_473_picture_crop_viewbox_matches_image_px
- 광범위 샘플 7종 / 415 페이지 / 32 nested SVG / 0 viewBox 과대 회귀
  - exam_kor partial crop 20건 (헤더 로고) 정상 보존
- 21_언어_기출 12p 그림 정상 렌더 (plain `<image 355×265>`)
- cargo test 1078 / 1078 PASS
@planet6897

Copy link
Copy Markdown
Contributor Author

Task #473 추가 (closes #473)

그림 crop 변환의 scale 기준 오류

samples/21_언어_기출_편집가능본.hwp 페이지 12 우측 단 <보기> 표 내부 그림이 16% 비율로 축소 표시.

원인

src/renderer/svg.rs:2385-2404 compute_image_crop_srcoriginal_size_hu (표시 HU) 를 crop 좌표 변환 scale 기준으로 사용. HWP crop 은 이미지 native 픽셀을 96-DPI HU 관행 (75 HU/px) 으로 인코딩하므로 사용자가 그림 크기를 변경한 경우 (orig=12 HU/px vs 기대 75 HU/px) viewBox 가 6.25× 과대.

수정

96-DPI 관행 (75 HU/px) fallback. original_size_hu / img_px 가 75 ± 5% 안일 때만 orig 사용 (exam_kor 헤더 등 native 삽입 케이스 역호환), 아니면 75 fallback.

검증

  • 신규 통합 테스트 test_473_picture_crop_viewbox_matches_image_px
  • 광범위 샘플 7종 / 415 페이지 / 32 nested SVG / 0 viewBox 과대 회귀
    • exam_kor partial crop 20건 (헤더 로고) 정상 보존
  • cargo test 1078 / 1078 PASS

학습: 1차 회귀 검사 시 abs(ratio-1.0) > 0.1 양방향 검출 → exam_kor 20/20 false alarm. Partial crop (viewBox < image) 은 정상 동작이라 검사 기준을 ratio > 1.1 만으로 좁힘. 메모리 feedback_essential_fix_regression_risk 적용.

@planet6897 planet6897 changed the title Task #470 + #471: cross-column vpos-reset 검출 완화 + 박스 stroke_sig 비교 Task #470 + #471 + #473: cross-column vpos-reset/박스 stroke_sig + 그림 crop scale 정정 Apr 30, 2026
paragraph가 페이지에 걸쳐 분할(PartialParagraph)될 때 인라인
treat_as_char Shape의 PageItem::Shape가 무조건 마지막 페이지에 등록되어
박스가 잘못된 페이지의 fallback 위치(예: y=742.45)에 출현하던 문제 해결.

근본 원인: TypesetEngine/Paginator의 process_controls가 Shape 분기에서
무조건 st.current_items.push 함. paginate_text_lines가 paragraph를
이미 다음 페이지로 진행시킨 상태이므로 박스가 line 0의 페이지가 아닌
마지막 페이지에 라우팅됨. 그 결과 박스의 페이지 tree에 inline_pos가 없어
shape_layout의 compute_object_position fallback이 잘못된 위치에 박스를 그림.

수정:
- pagination.rs에 find_inline_control_target_page 공용 함수 추가:
  박스의 char 위치 → line index → 그 line이 라우팅된 (page_idx, col_idx) 반환
- TypesetEngine(메인) / Paginator(fallback) 양쪽의 Shape 분기에서 호출하여
  treat_as_char Shape를 박스가 속한 페이지/단의 column_contents.items에 push
- shape_layout.rs:218에 회귀 안전장치(D): treat_as_char + inline_pos=None 시
  박스 렌더 스킵. RHWP_DEBUG_LAYOUT=1 시 stderr 로깅
- paragraph_layout.rs:2087 빈 paragraph 분기에 Shape의 inline_pos 등록 추가
  (Picture만 처리하던 누락분 보강 — D 차단 false positive 방지)

검증:
- cargo test --release: 1078 + 모든 통합 테스트 통과
- golden SVG 6건 모두 통과
- samples/21_언어_기출_편집가능본.hwp 페이지 11/12 시각 비교 정상

closes edwardkim#476
…m#476)

paragraph 페이지 분할 시 인라인 treat_as_char Shape의 PageItem::Shape가
무조건 마지막 페이지에 등록되어 박스가 잘못된 페이지의 fallback 위치에
출현하던 문제 해결.

closes edwardkim#476
페이지 14 [A] 박스(wrap=Square 표)가 단 1 본문이 아닌 단 0과 단 1
사이의 갭 영역에 그려지던 문제 해결.

근본 원인: layout.rs:2256-2270 Square wrap 분기가 col_area.x 만 사용
하고 paragraph effective_margin(=margin_left + indent)을 미반영. TAC
표 분기는 동일 패턴에서 effective_margin 을 적용했으나 Square wrap
분기는 누락.

수정: area_x = col_area.x + effective_margin / area_w =
col_area.width - effective_margin - margin_right 로 paragraph 영역
기준 정렬. halign=Right/Center 도 동일 기준 적용 (Task edwardkim#295 호환).

검증:
- 페이지 14 [A] 박스: x 580.9 → 605.5 (paragraph 영역 안)
- cargo test --release: 1078 + 모든 통합 통과
- 골든 SVG 6건 통과
- Task edwardkim#295 (halign=Right) 동작 호환

closes edwardkim#480
@edwardkim

Copy link
Copy Markdown
Owner

처리 결과: cherry-pick 머지 (commit e16df68 on local/devel).

PR 의 67 commits 중 본질 신규 3 Task 분석 후 옵션 A 선택 — Task #470 + #471 cherry-pick, Task #473 제외.

Task 처리 비고
Task #470 다단 cross-paragraph vpos-reset 검출 완화 cherry-pick ✅ typeset.rs
Task #471 cross-column 검출 stroke_sig 비교 cherry-pick ✅ layout.rs
Task #473 그림 crop 변환 scale 정정 제외 본 사이클 Task #477 (75 HU/px 단일 룰) 와 충돌

Task #473 제외 사유:

본 작업 사이클의 Task #477 (commit 6057bd3) 가 동일 영역 (compute_image_crop_src) 정정. 두 정정 비교:

정정 방식 Task #477 (이미 머지) Task #473
항상 75 HU/px (단일 룰) 75 ± 5% 안일 때만 orig 사용, 아니면 75 fallback
분기 없음 2 케이스 분기

작업지시자 통찰: "이건 휴리스틱이 아닙니다. 룰입니다." — HWP 표준 명세 룰 (1 inch = 7200 HU = 96 px = 75 HU/px) 단일 적용. Task #473 의 정정 의도는 본 #477 로 이미 해결됨.

위키 페이지: HWP 그림 Crop Scale 룰 — 두 케이스 (PNG = crop 후 / PNG = 원본) 모두 75 HU/px 룰로 자동 정합 처리. 회귀 origin (PR #434 / Task #430) 사례 + 컨트리뷰터 점검 항목 정리.

검증:

  • cargo test --lib: 1080 passed
  • svg_snapshot: 6/6, issue_418: 1/1, clippy: 0건
  • WASM: 4,204,787 bytes

@planet6897 Task #470 / #471 정정 감사합니다. 21_언어_기출_편집가능본 OVERFLOW 13→10 + exam_science 5→0 + exam_social 4→1 같은 광범위 정정 효과 확인되었습니다. Task #473 도 의도는 정합했으나 본 작업 사이클에서 같은 결함을 단일 룰 방식으로 먼저 정정한 상태였습니다.

@edwardkim edwardkim closed this Apr 30, 2026
edwardkim added a commit that referenced this pull request Apr 30, 2026
본 사이클 14번째 PR. 67 commits 중 본질 신규 3 Task.
작업지시자 옵션 A 선택 — Task #470 + #471 cherry-pick, Task #473 제외
(본 작업 사이클 Task #477 (75 HU/px 단일 룰) 와 동일 영역 정정).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request Apr 30, 2026
…스 stroke_sig (cherry-pick @planet6897, Task #473 제외 — 본 #477 룰 충돌)
edwardkim added a commit that referenced this pull request Apr 30, 2026
본 사이클 누적 정정 (5):
- Task #474: RowBreak 표 보호 블록 정책 비적용 (k-water-rfp 5쪽)
- Task #477: 표 셀 안 그림 클램프 + 이미지 Crop Scale 룰 정합화 (PR #434/Task #430 회귀)
- Task #470 + #471: cross-column vpos-reset / 박스 stroke_sig (PR #472 cherry-pick)
- Task #431: 분할 표 셀 내 문단 미출력 (단위 mismatch) + dump-pages 진단 도구
- Task #429: 표 셀 배경 image fill 미구현 (BorderFill image_fill 렌더링)

본 사이클 close 이슈 (10):
- 정정 완료: #474 / #477 / #431 / #429
- 자동 해결 / 정합 점검: #426 / #407 / #231 / #345
- 구현 중지: #497 / #242 (클라우드 HWP 인라인 — 본 사이클 외)

마일스톤 v2.0.0 이관 (2): #272 (HwpCtrl Action) / #307 (이미지 드래그 앤 드롭)

샘플 추가:
- samples/exam_eng-2010.pdf, exam_eng-2020.pdf (이슈 #345 정답지)
- samples/hwpx/issue_241.hwpx + pdf (이슈 #241 본질 분석용)

검증:
- cargo test --lib: 1080 passed
- svg_snapshot: 6/6, issue_418: 1/1, clippy: 0건
- WASM 4,204,760 bytes
- 작업지시자 시각 판정 통과 (Task #474 / #477 / #431 / #429)
jangster77 pushed a commit to jangster77/rhwp that referenced this pull request Apr 30, 2026
본 사이클 14번째 PR. 67 commits 중 본질 신규 3 Task.
작업지시자 옵션 A 선택 — Task edwardkim#470 + edwardkim#471 cherry-pick, Task edwardkim#473 제외
(본 작업 사이클 Task edwardkim#477 (75 HU/px 단일 룰) 와 동일 영역 정정).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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