fix(text_measurement): 좁은 구두점 (U+2018/U+2019/U+2027) 폭 분류 + native/WASM 동기화#1026
fix(text_measurement): 좁은 구두점 (U+2018/U+2019/U+2027) 폭 분류 + native/WASM 동기화#1026HaimLee-4869 wants to merge 1 commit into
Conversation
|
PR 레이블에서 |
5777736 to
eee99c2
Compare
@edwardkim 님, 제가 잘 몰라서 찾아봤는데 "first-time contributor" 는 GitHub 가 메인테이너 화면에만 자동으로 띄우는 배지라 제 권한으로는 보이지 않는 것 같네요ㅎㅎ.. cherry-pick 머지라 GitHub 가 아직 머지 이력을 인식 못 하는 것 같습니다. CI fmt 는 force-push 로 정리했습니다! 감사합니다. |
github 에서 일정 시간이 지나야 사라지는군요! |
eee99c2 to
85e84d4
Compare
|
검토 결과 수정 요청드립니다 (보류, PR OPEN 유지). 핵심 — PR #1021 KTX 정합 회귀 부수효과 발견본 PR의 좁은 구두점(U+2018/U+2019/U+2027) advance 0.3em 정합 + native/WASM 동시 fix 설계 자체는 매우 우수합니다 — Task #257/#630 보완 + case-specific 가드 + 결정적 검증(35페이지 중 33). 그러나 광범위 sweep 검증 중 직전에 머지된 PR #1021 (단일-run RIGHT + leader cell right inner 정렬)의 KTX 목차 정합이 회귀됨을 발견했습니다. 회귀 정량 (메인테이너 시각 판정)
작업지시자 판정: 회귀 원인 추정KTX 목차에 좁은 구두점(U+2018/U+2019/U+2027) 사용 안 됨을 확인했습니다. 본 PR H2의 신규 분기: let is_narrow_unicode_punct = matches!(c, '\u{2018}' | '\u{2019}' | '\u{2027}');
if is_narrow_unicode_punct && glyph_w >= mm.metric.em_size {
(mm.metric.em_size as f64 * 0.3) as u16 // 신규 분기 (is_halfwidth_punct 앞)
} else if is_halfwidth_punct && glyph_w >= mm.metric.em_size {
...분기 순서 변경이 다른 punctuation/space 측정에 영향을 미쳐 PR #1021의 요청 사항
처리 내역
세 번째 기여도 매우 충실한 PR이었습니다. PR #1021은 같은 컨트리뷰터(@HaimLee-4869)의 직전 PR이므로 회귀 가드 보강과 함께 정확한 fix를 기대합니다. |
PR #1026 (좁은 구두점 polished by @HaimLee-4869, 세 번째 기여) 는 머지하지 않음. 본 PR H2 분기 순서 변경이 PR #1021 (단일-run RIGHT + leader) 의 KTX 목차 cell right inner 정합을 회귀 — 페이지번호 x="689.76" → "699.76" (+10px). 컨트리뷰터에게 PR #1021 KTX 동작 보존 + 회귀 가드 fixture 추가 요청. PR OPEN 유지. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…SM 동기화
증상:
- HY중고딕/HY신명조/휴먼명조 등 본가 sample 영역 폰트 문단의 가운뎃점
(U+2027) · 여는/닫는 따옴표 (U+2018/U+2019) 뒤 advance 가 한컴 대비
~3 px 과대.
- 본가 `samples/2022년 국립국어원 업무계획.hwp` page 3, HY중고딕 16.67pt
컨텍스트에서 U+2018 advance 측정:
Before: 8.00 px (= 0.48 em, em/2)
After : 5.00 px (= 0.30 em)
본질:
1. 메트릭 DB: 휴먼명조/HY중고딕/HY신명조/HY견명조 가 U+2018/U+2019 를
fullwidth (1.000 em) 로 잘못 기록 → `is_halfwidth_punct` 분기
(U+2018..=U+2027 range) 가 em/2 = 0.5 em 강제.
2. DB 미수록 글리프: 휴먼명조 U+2027 → 폴백 `font_size * 0.5` → 동일 0.5 em.
3. WASM path 누락: `wasm_internals::measure_char_width_hwp` 의
2차 한글 → 3차 JS Canvas 폴백 사이에 `is_narrow_punctuation` 분기 부재
→ 휴먼명조 U+2027 (DB 미수록) 케이스에서 JS 측정값이 그대로 ~0.5 em.
(Native SVG 는 1·2 정정 만으로 정상, Canvas/WASM 은 별도 동기화 필요)
함초롬바탕(0.32 em) / Pretendard(0.22 em) / 맑은 고딕 — DB narrow 정확
영역 조건 미충족으로 영향 없음.
Fix (text_measurement.rs, 3 hunks):
1. `is_narrow_punctuation`: U+2018/U+2019/U+2027 추가. DB 미수록 폰트
폴백 path 영역 `font_size * 0.3` 적용 (Task edwardkim#257 헬퍼 확장).
2. `measure_char_width_embedded`: `is_halfwidth_punct` 분기 앞에
narrow override 추가 — `is_narrow_unicode_punct && glyph_w >= em_size`
조건 (DB 가 fullwidth 잘못 기록한 경우에만) → 0.3 em 강제.
3. `wasm_internals::measure_char_width_hwp`: 2차 한글 분기 다음, 3차 JS
Canvas 폴백 직전 영역 `super::is_narrow_punctuation(c) → font_size * 0.3`
추가. native EmbeddedTextMeasurer 와 WASM WasmTextMeasurer 두 path
동기화 (Issue edwardkim#900 canvas/WASM 동기화 패턴 정합).
검증:
- `cargo test --lib`: 1309/1309 ✓
- `cargo clippy --lib --no-deps`: clean
- 본가 sample `2022년 국립국어원 업무계획.hwp` (35페이지, U+2018 72개,
U+2019 86개, U+2027 52개, HY신명조/HY중고딕/HY헤드라인M 사용):
page 3 cell `'18년` HY중고딕 16.67pt → U+2018 advance 8.00→5.00 px,
'1' 이후 chars 모두 약 1.5 px 좌측 영역 영역 일관 시프트. 35페이지 中
33페이지 영역 영역 영역 영역 영역 동일 패턴 일관 적용. 함초롬바탕/
돋움체 영역 사용 영역 영역 영역 영역 영역 영역 (samples/20250130-hongbo.hwp) —
좁은 구두점 케이스 없음, 회귀 없음.
본가 인용:
- Task edwardkim#257 (선행 `is_narrow_punctuation` 헬퍼 — 콤마/중점 등 ASCII +
U+00B7 narrow 폴백) — 본 변경은 General Punctuation block 으로 확장.
- Issue edwardkim#885 (HY family fallback 메트릭 보정 — planet6897, 5/14) — 같은
도메인 (HY 계열 폰트 폴백 메트릭 정합) 영역 인접 영역 영역.
- Issue edwardkim#900 (canvas/WASM 수식 path 동기화) — 같은 framing
(native + WASM 두 path 동시 점검 정책).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85e84d4 to
ef4f6a3
Compare
|
@edwardkim 검토 감사합니다. 피드백 반영해 재제출했습니다. devel 위로 rebase — 본 브랜치가 #1020/#1021 머지 이전 시점에서 분기돼 있었습니다. 최신 devel 위로 rebase하니 base에 #1020/#1021이 포함되고 mergeable CONFLICTING도 해소됐습니다. KTX 페이지번호 회귀 — 재검증 결과, KTX +10px 이탈은 text_measurement.rs를 본 브랜치 버전으로 통째 반영할 때 #1021의 단일-run RIGHT+leader 분기가 함께 들어오지 못해 나타나는 것으로 보입니다. rebase(diff 적용)로 통합하면 #1021 코드가 보존됩니다. 확인한 점:
KTX 회귀 가드 추가 (검토 의견) — tests/issue_874_ktx_toc_page_number_right_align.rs. KTX 목차 페이지번호의 cell right inner 정렬(x≈689.76)을 단언하며, #1021 코드가 빠지면 x≈699.76으로 FAIL합니다. 검증: svg_snapshot 8/8, KTX 가드 PASS, cargo test --lib, clippy -D, fmt --check 모두 통과. |
…류 + native/WASM 동기화 @HaimLee-4869 4번째 기여 (1차 옵션 B 보류 후 KTX 회귀 가드 + rebase 후 재제출). 휴먼명조/HY중고딕/HY신명조/HY견명조 폰트의 좁은 구두점 (U+2018 ', U+2019 ', U+2027 ‧) 폭이 한컴 대비 ~4px 과대 문제 fix. 폰트 메트릭 DB fullwidth 잘못 기록 + WASM path 동기화 누락이 본질. 본질 (3 hunks, 모두 src/renderer/layout/text_measurement.rs): - H1. is_narrow_punctuation 확장 — DB 미수록 폴백 path 정정 (U+2018, U+2019, U+2027 추가). - H2. measure_char_width_embedded narrow override — DB fullwidth 정정 (em/2 = 0.5em → 0.3em, glyph_w >= em_size 조건으로 정상 DB 값 영향 0). - H3. wasm_internals::measure_char_width_hwp narrow 분기 추가 — WASM path 동기화 (native EmbeddedTextMeasurer 와 일관). ★ native + WASM 두 path 정책 (본가 PR #900 패턴 정합, PR #1021 영역 확정): rhwp 의 문자 폭 측정은 native CLI (EmbeddedTextMeasurer) 와 rhwp-studio Canvas (WasmTextMeasurer 영역 wasm_internals::measure_char_width_hwp) 두 구현으로 병존. char/layout fix 는 두 path 동시 점검 필수. 재제출 변경 (PR 본문 "C8+C9 소스 로직 변경 없음"): - 최신 devel rebase (bbd38e8, PR #1033 머지 후 base) - **KTX 회귀 가드 신규** tests/issue_874_ktx_toc_page_number_right_align.rs (98 lines) — render_page_svg_native SVG 의 페이지번호 digit max_x < 695.0 단언. 정상 ≤690.8 / 회귀 시 ≈699.76. PR #1021 정합 영구화. - golden 갱신 (issue-157 굴림체 + issue-617 HY신명조). issue-267 KTX golden 보존 (1차 검토 시 변경됐던 영역, 본 재제출에서 정정). 검증 (본 환경): - cargo test --release --lib 1319 passed + 통합 모두 passed - cargo test --release --test issue_874_ktx_toc_page_number_right_align **1 passed** (페이지번호 정합 정량 입증) - cargo test --release --test svg_snapshot **8/8 passed** (issue_267_ktx_toc_page 포함) — KTX golden 무회귀 - cargo fmt --all --check clean - sweep 7 fixtures (550 SVGs): 32 diff = 모두 본 PR 본질 (KTX 9 휴먼명조 / hwp3-sample16-hwp5 1 + exam_kor 17 + aift 5 HY신명조 narrow 정합) / 페이지 수 회귀 부재 / 함초롬바탕 / Pretendard / 맑은 고딕 무영향 - KTX page 002 (목차) 페이지번호 좌표 BEFORE = AFTER (1차 회귀 부재) - WASM Docker 빌드 4.89MB + rhwp-studio 동기화 - 작업지시자 시각 판정 통과 (KTX 페이지번호 정합 유지 + 휴먼명조 narrow 정합 한컴 정답지 정합) 1차 검토 회귀 원인 재정정: - 1차 보고서 (mydocs/pr/archives/pr_1026_report.md) 의 "H2 분기 순서 변경" 추정이 잘못된 분석으로 판명. 진짜 원인은 1차 cherry-pick + UPDATE_GOLDEN 일괄 갱신 (issue-267 KTX golden 도 함께 변경) 또는 통째-파일 교체로 PR #1021 코드 유실 (KTX 회귀 가드 코드 본문 명시). - 본 PR 재제출에서 KTX 회귀 가드 (issue_874) 영구화 + issue-267 golden 보존으로 1차 결함 정정. 회귀 영향 (의도적 분리): - 함초롬바탕 / Pretendard / 맑은 고딕: DB narrow 정확 기록, 영향 0 - U+00B7 ('·' Middle Dot): 본가 Task #257 + #630 영역 이미 처리. Task #630 "DB 전각 측정 유지 결정" trade-off 충돌 없음 - 다른 punctuation (콤마, 마침표, 콜론 등): 분기 조건 영향 0 별도 PR 권장 영역 (PR 본문 명시): - U+00B7 본문 영역 시각 NG: Task #630 context-aware 처리 후속 - U+2014 / U+2016 등 다른 General Punctuation block - HY family fallback 메트릭 보정 (별도 트랙) Refs PR #1021 Co-Authored-By: HaimLee-4869 <j_haim4869@naver.com>
|
Merged via cherry-pick (commit cb53dd9, author @HaimLee-4869 보존) into devel 본 환경 검증 통과: cargo test 1319 + 통합 + issue_874 KTX 가드 1 + svg_snapshot 8/8 (issue_267 KTX 보존) + sweep 32 diff (모두 본 PR 본질, 페이지 수 회귀 부재) + WASM 4.89MB + 작업지시자 시각 판정. 1차 검토 (5/20) 옵션 B 보류 정정: 1차 보고서 추정 'H2 분기 순서 변경' 원인 분석이 잘못된 분석으로 판명. 진짜 원인은 1차 cherry-pick + UPDATE_GOLDEN 일괄 갱신 (issue-267 KTX golden 도 함께 변경) 또는 통째-파일 교체로 PR #1021 코드 유실. 본 PR 재제출에서 KTX 회귀 가드 (issue_874) 영구화 + issue-267 golden 보존으로 1차 결함 정정 (회귀 가드 코드 본문 명시). 본 PR 재제출 변경 (PR 본문 'C8+C9 소스 로직 변경 없음'):
본 PR @HaimLee-4869 모범 사례 — 1차 보류 사유 "이게 틀린겁니다" 작업지시자 시각 판정 권위 ( Refs PR #1021 |
…osed) PR #1026 (재제출, Refs PR #1021) 머지 후속 처리: - mydocs/pr/archives/pr_1026_review_v2.md (재검토 문서 archives 이동) - mydocs/orders/20260521.md (2026-05-21 PR #1026 entry) - rhwp-studio/public/{rhwp.d.ts,rhwp.js,rhwp_bg.wasm} (WASM 4.89MB 동기화) @HaimLee-4869 1차 보류 PR 재제출 모범 사례 — KTX 회귀 가드 영구화로 1차 검토 결함 정정 + native/WASM 두 path 동기화. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ffset 해석 통일 — PR #913 revert @HaimLee-4869 5번째 기여. CharShape.start_pos 해석을 UTF-16 stream offset (해석 A) 로 통일 — PR #913 (closes #884, @planet6897, commit 9f81351) 의 visible char index (해석 B) 해석을 한컴 2024 글자모양 패널 실측 4 케이스 권위로 명시적 revert. 본질: - src/renderer/composer.rs split_by_char_shapes 의 start_pos lookup 을 char_offsets.iter().position(|&off| off >= cs.start_pos) 로 변경 — char_offsets[i] 가 가시문자 i 의 stream offset 이므로 start_pos 이상인 첫 가시문자가 char_shape 적용 시작점. - fallback active-shape 조회: find_active_char_shape_visible → find_active_char_shape (line_stream_start = char_offsets[text_start]). - 결과: composer ↔ paragraph_layout.rs / line_breaking.rs 의 4 경로가 모두 해석 A 로 일관. 한컴 2024 글자모양 패널 실측 4 케이스 (해석 A 정답): | 위치 | 한컴 패널 | 해석 A (본 PR) | 해석 B (PR #913) | |------|----------|----------------|------------------| | table-in-tbox p1 footer "충남중부권지사장" | HY수평선B 16pt | ✅ | 26pt ❌ | | table-in-tbox p2 "충남중부권지사" | 정상 | ✅ | 휴먼명조 1pt ❌ #915 | | KTX 목차 "Ⅰ" | 휴먼명조 16pt | ✅ | 18.7pt ❌ | | KTX 목차 "사업 개요" | HY헤드라인M 15pt | ✅ | 18.7pt ❌ | PR #913 의 오진 원인: - footer "충남중부권지사장" 의 "26pt" 판정이 HY수평선B 굵은 글꼴 + 로고 옆 배치로 인한 육안 착시 — 한컴 패널 실측은 16pt. - composer 만 해석 B 로 바꿔서 paragraph_layout/line_breaking 와 불일치 상태 (path 불일치). - 인라인 제어자가 문단 *중간* 에 있는 문단에서 start_pos 가 char_offsets gap 만큼 부풀려져 char_shape 통째 누락 → #915 (table-in-tbox p2 "충남중부권지사" 1.33px). 회귀 가드 (4 가드 영구화): - tests/issue_884_charshape_diagnostic.rs 갱신 — 단언 반전 (footer "충" HY수평선B 검증, 한컴 실측 기반). - tests/issue_915_charshape_cell_font_size.rs 신규 — 한글 음절 < 5px 부재 휴리스틱 (정상 ≥5px, 결함 ≈1.33px). - tests/issue_874_ktx_toc_page_number_right_align.rs (PR #1026) 양립 — KTX 페이지번호 정합 유지. - tests/svg_snapshot.rs 8/8 (issue-157/267/617/677 골든 4 갱신, table_text/form_002/aift/render_deterministic 보존). 검증 (본 환경): - cargo test --release --lib **1319 passed** - cargo test --release --tests 모든 통합 passed (0 FAILED) - cargo test --release --test issue_884_charshape_diagnostic **2/2 passed** - cargo test --release --test issue_915_charshape_cell_font_size **1/1 passed** - cargo test --release --test issue_874_ktx_toc_page_number_right_align **1/1 passed** - cargo test --release --test svg_snapshot **8/8 passed** - cargo fmt --all --check clean - sweep 8 fixtures (277 SVGs): 53 diff (광범위 stream offset 정합, table-in-tbox/KTX/hwp3-sample16/exam_kor/exam_math/aift) / 페이지 수 회귀 부재 / biz_plan 무영향 - **table-in-tbox p2 정량 입증**: font-size="1.3333" → font-size="16" - WASM Docker 빌드 4.89MB + rhwp-studio 동기화 - 작업지시자 시각 판정 통과 (한컴 2024 패널 실측 4 케이스 권위) `feedback_visual_judgment_authority` 모범 사례 — PR #913 의 "★ 시각 판정 통과" 도 정량적 한컴 패널 실측 권위로 정정 가능 (측정 우위). `reference_authoritative_hancom` — Windows 한컴 편집기 글자모양 패널이 1차 권위. `feedback_pr_supersede_chain` — PR #913 (오진 정합) → PR #1047 (실측 정정) 의 supersede 패턴. PR #913 머지 commit `9f81351a` 의 4 경로 중 composer 만 해석 B 였음 → path 일관성 복원. closes #915 Co-Authored-By: HaimLee-4869 <j_haim4869@naver.com>
…loses edwardkim#977) WASM `WasmTextMeasurer` 의 미등록 폰트 폭 폴백 두 경로를 native heuristic 으로 통일: 1. `measure_char_width_hwp` (일반 문자) — JS Canvas 폴백 제거, native compute_char_positions 와 동일 휴리스틱 (CJK/fullwidth=font_size, 일반=0.5em, narrow_punct=0.3em). 2. `measure_hangul_width_hwp` (한글 '가' 대리 측정) — JS cached_js_measure('가') 폴백 제거, 미등록 폰트도 font_size (1.0 em) 휴리스틱. 본질: 한컴은 LEFT tab + tab_extended[0] 에 "tab_pos - 한컴_선행텍스트폭" 으로 행별 tab 폭 저장. 미등록 폰트(예: 나눔바른고딕)에서 우리 한글 폭이 한컴 metric 과 다르면 (= 브라우저 fallback 폰트의 '가' 폭), 같은 ext[0] 더해도 디지트 x 좌표가 행별 한글 개수 차이 × 폭차 만큼 누적 어긋남. PR edwardkim#1026 (narrow_punct 분기) 흡수 + Stage 2 공백 분기 통일 후 잔존 본질. 영향: - 등록 폰트(맑은 고딕, HCR Batang 등): embedded 메트릭 우선 → 본 분기 미진입, 무회귀. - 미등록 폰트(나눔바른고딕 등): native 와 동일 폭 산출 → SVG 와 WASM 일관 정렬. 검증: - issue_874_ktx_toc_page_number_right_align (PR edwardkim#1026 회귀 가드): 1/1 PASS - svg_snapshot (golden SVG 8 종): 8/8 PASS - cargo check --lib --target wasm32-unknown-unknown: OK - Docker WASM 빌드 성공 - 작업지시자 시각 재현 확인 (TOC 페이지 디지트 정렬) closes edwardkim#977 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…loses edwardkim#977) WASM `WasmTextMeasurer` 의 미등록 폰트 폭 폴백 두 경로를 native heuristic 으로 통일: 1. `measure_char_width_hwp` (일반 문자) — JS Canvas 폴백 제거, native compute_char_positions 와 동일 휴리스틱 (CJK/fullwidth=font_size, 일반=0.5em, narrow_punct=0.3em). 2. `measure_hangul_width_hwp` (한글 '가' 대리 측정) — JS cached_js_measure('가') 폴백 제거, 미등록 폰트도 font_size (1.0 em) 휴리스틱. 본질: 한컴은 LEFT tab + tab_extended[0] 에 "tab_pos - 한컴_선행텍스트폭" 으로 행별 tab 폭 저장. 미등록 폰트(예: 나눔바른고딕)에서 우리 한글 폭이 한컴 metric 과 다르면 (= 브라우저 fallback 폰트의 '가' 폭), 같은 ext[0] 더해도 디지트 x 좌표가 행별 한글 개수 차이 × 폭차 만큼 누적 어긋남. PR edwardkim#1026 (narrow_punct 분기) 흡수 + Stage 2 공백 분기 통일 후 잔존 본질. 영향: - 등록 폰트(맑은 고딕, HCR Batang 등): embedded 메트릭 우선 → 본 분기 미진입, 무회귀. - 미등록 폰트(나눔바른고딕 등): native 와 동일 폭 산출 → SVG 와 WASM 일관 정렬. 검증: - issue_874_ktx_toc_page_number_right_align (PR edwardkim#1026 회귀 가드): 1/1 PASS - svg_snapshot (golden SVG 8 종): 8/8 PASS - cargo check --lib --target wasm32-unknown-unknown: OK - Docker WASM 빌드 성공 - 작업지시자 시각 재현 확인 (TOC 페이지 디지트 정렬) closes edwardkim#977 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…loses edwardkim#977) WASM `WasmTextMeasurer` 의 미등록 폰트 폭 폴백 두 경로를 native heuristic 으로 통일: 1. `measure_char_width_hwp` (일반 문자) — JS Canvas 폴백 제거, native compute_char_positions 와 동일 휴리스틱 (CJK/fullwidth=font_size, 일반=0.5em, narrow_punct=0.3em). 2. `measure_hangul_width_hwp` (한글 '가' 대리 측정) — JS cached_js_measure('가') 폴백 제거, 미등록 폰트도 font_size (1.0 em) 휴리스틱. 본질: 한컴은 LEFT tab + tab_extended[0] 에 "tab_pos - 한컴_선행텍스트폭" 으로 행별 tab 폭 저장. 미등록 폰트(예: 나눔바른고딕)에서 우리 한글 폭이 한컴 metric 과 다르면 (= 브라우저 fallback 폰트의 '가' 폭), 같은 ext[0] 더해도 디지트 x 좌표가 행별 한글 개수 차이 × 폭차 만큼 누적 어긋남. PR edwardkim#1026 (narrow_punct 분기) 흡수 + Stage 2 공백 분기 통일 후 잔존 본질. 영향: - 등록 폰트(맑은 고딕, HCR Batang 등): embedded 메트릭 우선 → 본 분기 미진입, 무회귀. - 미등록 폰트(나눔바른고딕 등): native 와 동일 폭 산출 → SVG 와 WASM 일관 정렬. 검증: - issue_874_ktx_toc_page_number_right_align (PR edwardkim#1026 회귀 가드): 1/1 PASS - svg_snapshot (golden SVG 8 종): 8/8 PASS - cargo check --lib --target wasm32-unknown-unknown: OK - Docker WASM 빌드 성공 - 작업지시자 시각 재현 확인 (TOC 페이지 디지트 정렬) closes edwardkim#977 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- pr_1095_review.md: 검토 (v3 본질 분리 + 코드 정독 + 메인테이너 검증 계획) - pr_1095_report.md: 최종 결정 — merge 수용 (검증 7/7 + CI 전부 pass) 검증: - 회귀 가드 #874: 1/1 PASS - svg_snapshot: 8/8 PASS - lib 1382 passed / 0 failed - WASM check / clippy / fmt clean - CI 전부 pass 본 PR 의 본질: - v2 #1045 (PR #1026 흡수로 close) 후 잔존 본질 - measure_hangul_width_hwp 도 native heuristic 동기화 (PR #1026 의 measure_char_width_hwp 영역 외 추가) - 영향 영역: 미등록 폰트만 (등록 폰트는 _embedded 가 Some 반환 → 무회귀) merge: 471d5bf (GitHub merge commit), 이슈 #977 close. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
(업데이트 2026-05-20) 최신 devel 위로 rebase — #1020/#1021 포함, mergeable CONFLICTING 해소. KTX 목차 페이지번호 회귀 가드 tests/issue_874_ktx_toc_page_number_right_align.rs 추가. C8+C9 소스 로직 변경 없음.
Summary
휴먼명조 / HY중고딕 / HY신명조 / HY견명조 폰트 영역에서 좁은 구두점 (U+2018
', U+2019', U+2027‧) 의 폭이 한컴 대비 ~4px 과대하게 표시되는 문제 fix. 폰트 메트릭 DB fullwidth 잘못 기록 + WASM path 동기화 누락이 본질. native (EmbeddedTextMeasurer) + WASM (WasmTextMeasurer) 두 path 동시 fix (본가 PR #900 패턴 정합, PR #1021 영역 확정 패턴).Symptom
휴먼명조 / HY 시리즈 폰트의 문단에서 가운뎃점 (
‧) 뒤 공백이 한컴 대비 ~4px 더 넓음. 여는/닫는 작은 따옴표 동일.Before / After — 사용자 시각 검증
"선제적‧적극적인 ...." 영역 비교:
Root cause (3-layer)
is_halfwidth_punct분기 em/2 = 0.5 emfont_size * 0.5함초롬바탕 (0.32) / Pretendard (0.22) / 맑은 고딕 — DB narrow 정확 기록, 영향 없음.
Fix (3 hunks, 모두
src/renderer/layout/text_measurement.rs)H1.
is_narrow_punctuation확장 — DB 미수록 폴백 path 정정H2.
measure_char_width_embeddednarrow override — DB fullwidth 정정H3.
wasm_internals::measure_char_width_hwpnarrow 분기 추가 — WASM path 동기화★ native + WASM 두 path 정책 (본가 PR #900 패턴 정합, PR #1021 영역 확정)
rhwp 의 문자 폭 측정은 native CLI (
EmbeddedTextMeasurer) 와 rhwp-studio Canvas (WasmTextMeasurer영역wasm_internals::measure_char_width_hwp) 두 구현으로 병존. char/layout fix 는 두 path 동시 점검 필수 — native 만 fix 시 SVG 검증 OK / WASM Canvas NG.본 PR 도 H1+H2 (native) + H3 (WASM) 양쪽 동시 적용. 직전 PR #1021 (단일-run RIGHT + leader 인라인 탭) 에서 검증된 패턴 정합.
회귀 영향 (의도적 분리)
·Middle Dot): 본가 Task narrow glyph(콤마·중점 등) 뒤 문자 advance 과다로 숫자·중점 뒤 공백 벌어짐 #257 (is_narrow_punctuation) + Task aift.hwp TOC '·' 포함 문단 right-tab 8.67px 좌측 이탈 — middle-dot 측정/tab_type 해석 불일치 #630 (is_halfwidth_punct range U+2018..=U+2027) 영역 이미 처리. Task aift.hwp TOC '·' 포함 문단 right-tab 8.67px 좌측 이탈 — middle-dot 측정/tab_type 해석 불일치 #630 영역 "DB 전각 측정 유지 결정 (right-tab 정렬 영향)" trade-off 영역 충돌 없음.is_narrow_unicode_punct영역 U+2018/U+2019/U+2027 한정) 영역 영향 0검증 환경
회귀 검증
cargo test --libcargo clippy --lib --no-deps -- -D warningswasm-pack build --target web --dev본가 sample 결정적 검증 (samples/2022년 국립국어원 업무계획.hwp)
사용자 시각 검증 (공직기강 page 1)
관련 영역
is_narrow_punctuation헬퍼 도입 + base_w 분기. 본 PR 영역 확장 정합.is_halfwidth_punctrange (U+2018..=U+2027) 도입 + DB 전각 측정 유지 결정 (right-tab 정렬 영향). 본 PR 영역 U+2018/U+2019/U+2027 narrow override 분기 영역 충돌 없음.별도 PR 권장 영역 (본 PR 영역 외)
·Middle Dot) 본문 영역 시각 NG: 본가 Task aift.hwp TOC '·' 포함 문단 right-tab 8.67px 좌측 이탈 — middle-dot 측정/tab_type 해석 불일치 #630 영역 "DB 전각 측정 유지 결정 (right-tab 정렬 영향)" 알려진 trade-off. 본문 영역에서 휴먼명조 / HY 시리즈 영역 점 뒤 공백 영역 한컴 대비 과대 (samples/2022년 국립국어원 업무계획.hwp 페이지 4 "시·청각장애인" 영역에서 확인). 본 PR 영역 외 — Task aift.hwp TOC '·' 포함 문단 right-tab 8.67px 좌측 이탈 — middle-dot 측정/tab_type 해석 불일치 #630 영역 context-aware 처리 (본문 narrow vs 표 안 fullwidth 구분) 후속 토론 가치 있음.—em dash) / U+2016 (‖) 등 다른 General Punctuation block: 본 PR narrow 매칭 영역 안 됨. 한컴 정합 검증 후 별도 PR 가능.