Skip to content

fix(text_measurement): 좁은 구두점 (U+2018/U+2019/U+2027) 폭 분류 + native/WASM 동기화#1026

Closed
HaimLee-4869 wants to merge 1 commit into
edwardkim:develfrom
HaimLee-4869:pr/narrow-punctuation-native-wasm-sync
Closed

fix(text_measurement): 좁은 구두점 (U+2018/U+2019/U+2027) 폭 분류 + native/WASM 동기화#1026
HaimLee-4869 wants to merge 1 commit into
edwardkim:develfrom
HaimLee-4869:pr/narrow-punctuation-native-wasm-sync

Conversation

@HaimLee-4869

@HaimLee-4869 HaimLee-4869 commented May 20, 2026

Copy link
Copy Markdown
Contributor

(업데이트 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 — 사용자 시각 검증

"선제적‧적극적인 ...." 영역 비교:

  • Before (fix 적용 전): rhwp Canvas 영역 ‧ advance = 10px @ font-size 20px = 0.5em (한컴 대비 과대)


  • After (본 PR 적용 후): rhwp Canvas 영역 ‧ advance = 0.3em (한컴 정합 ✓)

Root cause (3-layer)

Layer 케이스 폴백 결과 한컴 정답
DB fullwidth 잘못 기록 휴먼명조 / HY중고딕 / HY신명조 / HY견명조의 U+2018 / U+2019 → 1.0 em is_halfwidth_punct 분기 em/2 = 0.5 em ~0.3 em
DB 글리프 미수록 휴먼명조 U+2027 영역 폴백 font_size * 0.5 ~0.3 em
WASM path 동기화 누락 3차 JS Canvas 폴백 영역 narrow 분기 부재 휴먼명조 U+2027 → 0.5 em (1차 native fix 후도 NG) ~0.3 em

함초롬바탕 (0.32) / Pretendard (0.22) / 맑은 고딕 — DB narrow 정확 기록, 영향 없음.

Fix (3 hunks, 모두 src/renderer/layout/text_measurement.rs)

H1. is_narrow_punctuation 확장 — DB 미수록 폴백 path 정정

fn is_narrow_punctuation(c: char) -> bool {
    matches!(c,
        ',' | '.' | ':' | ';' | '\'' | '"' | '`' |
        '\u{00B7}' |   // · MIDDLE DOT (기존)
        '\u{2018}' |   // ' LEFT SINGLE QUOTATION MARK (추가)
        '\u{2019}' |   // ' RIGHT SINGLE QUOTATION MARK (추가)
        '\u{2027}'     // ‧ HYPHENATION POINT (추가)
    )
}

H2. measure_char_width_embedded narrow override — DB fullwidth 정정

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  // 0.5em → 0.3em
} else if is_halfwidth_punct && glyph_w >= mm.metric.em_size {
    mm.metric.em_size / 2
} else {
    glyph_w
}

H3. wasm_internals::measure_char_width_hwp narrow 분기 추가 — WASM path 동기화

// 2차: 한글 음절
...
// (추가) narrow punctuation 분기
if super::is_narrow_punctuation(c) {
    return font_size * 0.3;
}
// 3차: JS Canvas 폴백

★ 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 인라인 탭) 에서 검증된 패턴 정합.

회귀 영향 (의도적 분리)

검증 환경

  • Linux + cargo + wasm-pack (Docker 미사용, 로컬 네이티브)
  • 한컴오피스 2024 한글 + Windows + 한컴 폰트 설치 (시각 정합 비교)

회귀 검증

검증 결과
cargo test --lib 1309 passed, 0 failed
cargo clippy --lib --no-deps -- -D warnings 0 warning
wasm-pack build --target web --dev OK
  • svg_snapshot golden 갱신 (issue-157 굴림체 / issue-617 HY신명조): 좁은 구두점(U+2018/U+2019) 폭 0.5em → 0.3em 변화의 의도된 downstream. element 수 동일(좌표만 이동), LAYOUT_OVERFLOW 는 base 커밋부터 존재. 한컴 시각 검증 완료.

본가 sample 결정적 검증 (samples/2022년 국립국어원 업무계획.hwp)

  • HY중고딕 16.67pt, U+2018 → '1' advance: 8.00 px (0.48em) → 5.00 px (0.30em) — 정확히 0.5em → 0.3em
  • 35 페이지 중 33 페이지에서 동일 패턴 일관 적용
  • HY중고딕 / HY신명조 / HY헤드라인M 모두 정상 작동

사용자 시각 검증 (공직기강 page 1)

  • ✅ "선제적‧적극적", "갑질‧비위" 가운뎃점 (U+2027) — 한컴 정합
  • ✅ 여는 따옴표 (U+2018) "‘집행부 지휘부-도의회 의장단’" — 유지
  • ✅ 다른 punctuation (콤마, 마침표, 괄호) — 변경 없음
  • ✅ 다른 폰트 (맑은 고딕, 함초롬바탕) — 변경 없음

관련 영역

별도 PR 권장 영역 (본 PR 영역 외)

@edwardkim edwardkim self-requested a review May 20, 2026 03:13
@edwardkim edwardkim added the enhancement New feature or request label May 20, 2026
@edwardkim edwardkim added this to the v1.0.0 milestone May 20, 2026
@edwardkim

Copy link
Copy Markdown
Owner

PR 레이블에서 First-time contributor 를 빼고 PR 해주세요 ^^

@HaimLee-4869 HaimLee-4869 removed their assignment May 20, 2026
@HaimLee-4869 HaimLee-4869 force-pushed the pr/narrow-punctuation-native-wasm-sync branch from 5777736 to eee99c2 Compare May 20, 2026 03:30
@HaimLee-4869

Copy link
Copy Markdown
Contributor Author

PR 레이블에서 First-time contributor 를 빼고 PR 해주세요 ^^

@edwardkim 님, 제가 잘 몰라서 찾아봤는데 "first-time contributor" 는 GitHub 가 메인테이너 화면에만 자동으로 띄우는 배지라 제 권한으로는 보이지 않는 것 같네요ㅎㅎ.. cherry-pick 머지라 GitHub 가 아직 머지 이력을 인식 못 하는 것 같습니다. CI fmt 는 force-push 로 정리했습니다! 감사합니다.

@edwardkim

Copy link
Copy Markdown
Owner

PR 레이블에서 First-time contributor 를 빼고 PR 해주세요 ^^

@edwardkim 님, 제가 잘 몰라서 찾아봤는데 "first-time contributor" 는 GitHub 가 메인테이너 화면에만 자동으로 띄우는 배지라 제 권한으로는 보이지 않는 것 같네요ㅎㅎ.. cherry-pick 머지라 GitHub 가 아직 머지 이력을 인식 못 하는 것 같습니다. CI fmt 는 force-push 로 정리했습니다! 감사합니다.

github 에서 일정 시간이 지나야 사라지는군요!

@HaimLee-4869 HaimLee-4869 force-pushed the pr/narrow-punctuation-native-wasm-sync branch from eee99c2 to 85e84d4 Compare May 20, 2026 04:02
@edwardkim edwardkim removed their request for review May 20, 2026 04:03
@edwardkim edwardkim self-requested a review May 20, 2026 04:04
@edwardkim

Copy link
Copy Markdown
Owner

검토 결과 수정 요청드립니다 (보류, 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 목차 정합이 회귀됨을 발견했습니다.

회귀 정량 (메인테이너 시각 판정)

라인 devel (PR #1021 적용 상태) 본 PR 적용 후 정답
페이지번호 "8" (y=394) x="689.76" (cell right inner) x="699.76" (+10px 우측) PR #1021 (689.76)
페이지번호 "16/20/24" x="679.76" x="689.76" (+10px) PR #1021 (679.76)

작업지시자 판정: tests/golden_svg/issue-267/ktx-toc-page.svg UPDATE_GOLDEN 결과의 페이지번호 우측 끝 좌표 변경이 틀린 것 (PR #1021 정합이 정답).

회귀 원인 추정

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의 cell_right_run_rel - seg_w_full 계산이 영향받은 것으로 추정됩니다.

요청 사항

  1. PR fix: 단일-run RIGHT + leader 인라인 탭 cell right inner 정렬 (Task #874 후속) #1021 KTX 정합 보존 필수 — 페이지번호 우측 끝 x="689.76" (또는 "679.76") 유지

    • 옵션 1: H2 가드를 더 좁게(예: 특정 폰트 fingerprint 한정)
    • 옵션 2: 분기 순서 변경 — is_halfwidth_punct 먼저, is_narrow_unicode_punct fallthrough
    • 옵션 3: seg_w_full 계산 영향 분석 + 회귀 차단 가드 추가
  2. 회귀 가드 fixture 추가 권고 — KTX 목차 페이지번호 cell right inner 정합을 tests/issue_*.rs 영구 회귀 가드로 등록 (PR #1003의 issue_table_vpos_01_page5_cell_hit_test 패턴 정합). 본 PR이 추가하시면 향후 동일 회귀 자동 차단.

  3. 본 PR의 의도(좁은 구두점 정합) 자체는 정합 — 설계 우수, PR fix: 단일-run RIGHT + leader 인라인 탭 cell right inner 정렬 (Task #874 후속) #1021 회귀만 차단하시면 머지 가능합니다.

처리 내역

세 번째 기여도 매우 충실한 PR이었습니다. PR #1021은 같은 컨트리뷰터(@HaimLee-4869)의 직전 PR이므로 회귀 가드 보강과 함께 정확한 fix를 기대합니다.

edwardkim added a commit that referenced this pull request May 20, 2026
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>
@HaimLee-4869 HaimLee-4869 force-pushed the pr/narrow-punctuation-native-wasm-sync branch from 85e84d4 to ef4f6a3 Compare May 20, 2026 15:27
@HaimLee-4869

Copy link
Copy Markdown
Contributor Author

@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 코드가 보존됩니다. 확인한 점:

  • rebase 후 UPDATE_GOLDEN으로 golden 전체 재생성 시 issue-267(KTX) golden은 byte-동일 — 본 PR이 KTX에 영향을 주지 않음.
  • C8+C9는 U+2018/U+2019/U+2027 세 글자의 측정폭만 바꾸는데, KTX 목차·표지 페이지에는 이 글자가 없습니다(렌더 SVG 확인). 본문 9개 페이지만 해당 글자의 advance가 0.5em→0.3em으로 좁아집니다.
  • C8+C9 소스 로직은 변경하지 않았습니다.

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 모두 통과.

edwardkim added a commit that referenced this pull request May 20, 2026
…류 + 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>
@edwardkim

Copy link
Copy Markdown
Owner

Merged via cherry-pick (commit cb53dd9, author @HaimLee-4869 보존) into devel 3d9bf994.

본 환경 검증 통과: 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 소스 로직 변경 없음'):

  1. 최신 devel rebase (bbd38e8, PR Task #1025: 페이지보다 큰 단일 표 셀 내부 분할 (intra-cell line split) (closes #1025) #1033 머지 후)
  2. KTX 회귀 가드 신규 (98 lines, max_x < 695.0 단언)
  3. golden 갱신 (issue-157 + issue-617, issue-267 보존)

본 PR @HaimLee-4869 모범 사례 — 1차 보류 사유 "이게 틀린겁니다" 작업지시자 시각 판정 권위 (feedback_visual_judgment_authority) 를 회귀 가드 영구화로 정정.

Refs PR #1021

@edwardkim edwardkim closed this May 20, 2026
edwardkim added a commit that referenced this pull request May 20, 2026
…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>
edwardkim added a commit that referenced this pull request May 20, 2026
…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>
planet6897 added a commit to planet6897/rhwp that referenced this pull request May 24, 2026
…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>
planet6897 added a commit to planet6897/rhwp that referenced this pull request May 25, 2026
…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>
planet6897 added a commit to planet6897/rhwp that referenced this pull request May 25, 2026
…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>
edwardkim added a commit that referenced this pull request May 25, 2026
- 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants