Skip to content

fix(equation): 파렌 path → 폰트 글리프 전환 (moon 형상 제거) (#283)#285

Merged
edwardkim merged 10 commits into
edwardkim:develfrom
planet6897:local/task283
Apr 24, 2026
Merged

fix(equation): 파렌 path → 폰트 글리프 전환 (moon 형상 제거) (#283)#285
edwardkim merged 10 commits into
edwardkim:develfrom
planet6897:local/task283

Conversation

@planet6897

Copy link
Copy Markdown
Contributor

요약

수식 SVG 렌더링에서 괄호 ( ) 의 path 폭이 폰트 글리프 대비 얇아 moon 모양으로 보이던 문제 해결. 텍스트 높이 파렌은 Times 글리프로 전환하고, 스트레치 파렌은 기존 path 유지.

Closes #283 (Task #280 Phase 2)

배경

samples/equation-lim.hwpf(2+h) 렌더 결과에서 파렌이:

  • 박스 할당 폭 fs * 0.3 = 4.40px
  • 실제 곡선 시각 폭 1.54px (Times bbox 의 31%)
  • 나머지 69% 가 whitespace → 글자와 gap, 얇은 "moon" 형상

조사

  • Times ( 실측 (Chrome headless): advance 4.89px (em 0.333), bbox 5.0, 폭 꽉 참
  • path 튜닝 5안 프로토타입: 단일 제어점 quadratic Bezier 는 수학적으로 대칭 moon 만 생성 — 제어점 이동만으로 Times 의 비대칭
    bowl + 세리프 끝단 재현 불가
  • 글리프 전환안 (B_glyph) 만 폰트와 일관된 타이포그래피 제공

변경

파일 변경
src/renderer/equation/layout.rs paren_w: fs * 0.3 → fs * 0.333 (Times advance 매치)
src/renderer/equation/svg_render.rs LayoutKind::Paren arm 높이 분기 추가
src/renderer/equation/canvas_render.rs 동일 분기 (SVG/Canvas 동기)

분기 로직

let use_glyph = lb.height <= fs * 1.2;
if use_glyph && (bracket == "(" || bracket == ")") {
    // <text>(</text> 또는 ctx.fill_text
} else {
    draw_stretch_bracket(...);  // 기존 path
}

- body.height ≤ fs * 1.2 + 대상 (/) → 폰트 글리프 렌더
-  (분수·sum·행렬 감쌈, 또는 기타 괄호) → 기존 path 유지
- LayoutKind::Matrix arm 은 항상 path (변경 없음)

검증

- cargo test --lib equation: 49/49 (신규 test_paren_stretch_svg 포함)
- cargo test --test svg_snapshot: 3 passed
- cargo clippy --lib --bins --tests: 에러 없음
- cargo check --lib --target wasm32-unknown-unknown: clean

실측 SVG 변화

samples/equation-lim.hwp export:

┌──────────────────┬────────┬───────┐
│    파렌 요소     │ BeforeAfter │
├──────────────────┼────────┼───────┤
│ <path>           │ 4건    │ 0건   │
├──────────────────┼────────┼───────┤
│ <text>(/)</text> │ 0건    │ 4건   │
└──────────────────┴────────┴───────┘

시각 회귀

3비교 (mydocs/working/task_m100_283_stage4/compare.png):
- BEFORE (path): 얇은 moon, 글자와 gap
- AFTER (glyph): Times 글리프, 글자와 자연 밀착
- PDF 레퍼런스: 자연 Times 글리프

→ AFTERPDF.

exam_math.hwp 20페이지 중 4샘플링 (p001/p005/p009/p013):
- 텍스트 높이 파렌 (f(x), P(A|B)) → 글리프 렌더 정상
- 스트레치 파렌 (P(AB), 분수 감쌈) → 기존 path 보전
- 임계치 fs * 1.2 가 텍스트/스트레치 경계 의도대로 분기

범위  (후속 과제 후보)

- 기타 괄호 {·[·| 글리프 전환 (동일 패턴 확장)
- 스트레치 path 품질 개선 (cubic Bezier / 다중 세그먼트 재설계)
- LayoutKind::Matrix arm 동일 임계치 적용 (현재 항상 path)

Test plan

- cargo test --lib equation 통과 (49/49)
- cargo test --test svg_snapshot 통과 (3/3)
- cargo clippy 에러 없음
- WASM lib 빌드 통과
- 시각 회귀 (equation-lim.hwp + exam_math.hwp 4페이지)
- SVG 실측 (<path> 40, <text> 4 신규)

planet6897 and others added 10 commits April 24, 2026 11:31
- samples/equation-lim.{hwp,pdf} 재현 샘플 추가
- 수행계획서 + 구현계획서 + 단계1 보고서
- 변경 전 SVG/PNG 스냅샷 (before.*) + 한컴 PDF 참조 이미지 (pdf.*)
- 단계4 시각 비교용 before_crop.png / pdf_crop.png 확보

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- canvas_render.rs:223 set_font 함수가 폰트 문자열 중복 보유 → svg_render.rs 와 동시 수정 필요 (1줄씩)
- svg.rs:332 폰트 임베딩 로직은 "Latin Modern Math" 키 고정 사용 → 새 스택에서 Latin Modern Math 를 첫 번째로 유지해 파이프라인 호환
- 최종 제안 스택: 'Latin Modern Math', 'STIX Two Text', 'STIX Two Math', 'Times New Roman', 'Times', serif
  (Cambria Math 및 Pretendard 제거)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
svg_render.rs EQ_FONT_FAMILY 와 canvas_render.rs set_font 양쪽에서
Cambria Math + Pretendard 제거, STIX Two Text + Times New Roman 추가.

최종 스택: 'Latin Modern Math', 'STIX Two Text', 'STIX Two Math', 'Times New Roman', 'Times', serif

Windows 기본 환경에서 Cambria Math 가 매칭되어 "볼드 인상" 을 유발하던
문제를 Times New Roman 폴백으로 해소. Latin Modern Math 는 첫 번째로
유지해 svg.rs:332 의 폰트 임베딩 파이프라인 호환성 보존.

회귀:
- cargo test --lib equation: 48 passed / 0 failed
- cargo test --test svg_snapshot: 3 passed
- cargo clippy --lib -- -D warnings: clean
- cargo check --target wasm32-unknown-unknown: clean
- cargo test --lib 전체 14건 실패는 기존 CFB writer Windows path 문제 (무관)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
equation-lim.hwp after 이미지 생성:
- after.svg / after.png / after_crop.png
- before_crop 와 비교: "lim" 및 변수 글자가 두꺼운 Cambria Math → 얇은 Times New Roman 으로 변경 확인
- PDF(HyhwpEQ) 와의 격차가 유의미하게 좁혀짐

exam_math.hwp 5개 페이지 회귀 검증 (PNG):
- p001: f(x)=x^3-8x+7, lim, piecewise, 분수
- p005: prime 기호(f'), sin, sqrt
- p009: 집합 기호 (∩, ∪), 분수
- p013: lim, sin, ln, ∫ - 모두 본문과 동일 크기 확인 (함수명 1.2x 확대 없음)
- p017: 벡터 화살표 (Decoration)

전 케이스 레이아웃 변화 없이 폰트 두께만 완화됨. 특수 기호(∩, ∪, ∫ 등)는
브라우저 글리프 폴백으로 정상 표시.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- mydocs/report/task_m100_280_report.md: 최종 결과 보고서
- mydocs/orders/20260424.md: Task edwardkim#280 섹션 추가 + 종료/대기 이슈 갱신
- GitHub edwardkim#283 등록: 괄호 path 폭 조정 (Phase 2 후속)

이 타스크의 실질적 가치:
- PDF "110 vs 92" 수치로 함수명 1.2x 확대 규칙 의심 → 샘플 bbox 다수 교차
  비교로 규칙 없음 확정 → 잘못된 방향 수정 회피
- Cambria Math 제거 + Times New Roman 폴백의 단순 수정으로 볼드 인상 해소
- exam_math p013 (lim/sin/ln/∫ 동시) 에서 모두 본문 크기 렌더링 재확인

Closes edwardkim#280

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chrome headless (canvas measureText + svg getBBox) 로 Times ( 글리프 측정:
- advance width = 4.89px (fs=14.67 기준, = 0.333em)
- bbox = 5.0 x 14.0px
- ascent/descent = 11/3

현재 rhwp path 파렌 (samples/equation-lim.hwp):
- 박스 할당 paren_w = 4.40 (fs * 0.3)
- 실제 곡선 시각 폭 = 1.54px (할당의 35%)
- 나머지 65% 는 시각적 whitespace → Times 대비 어색

핵심 발견: path 파렌은 "박스 크게 + 곡선 얇게" 구조라 Times 처럼 advance 안에
꽉 찬 곡선을 만들기 어려움. 측정 데이터는 옵션 B (높이-조건부 글리프 전환) 를
지지. 다만 단계 2 에서 A+C 튜닝 대안도 프로토타입 비교 후 최종안 결정.

수행/구현 계획서 + 측정 데이터 + 참고 이미지(current/times_reference) 커밋.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 6개 변형 렌더 (baseline / A_conservative / A_aggressive /
  B_glyph / extra_A / extra_B), 동일 문맥 (f(2+h)-f(2)) 시각 비교
- path 변형(0·1·2·4·5) 모두 moon 형상 극복 불가 — 단일 제어점
  quadratic Bezier 수학적 한계 확인
- B_glyph 만 글자 폰트와 일관된 타이포그래피 제공
- 선정: 옵션 B (글리프 전환 + 임계치 분기)
  - 텍스트 높이 (body.height/fs <= 1.2) → <text>(</text>
  - 스트레치 (> 1.2) → 기존 path 유지
  - 임계치 1.2·fs 는 직관적 구분선
- 구현 범위 확정: layout.rs paren_w → fs*0.333,
  svg_render.rs/canvas_render.rs Paren arm 높이 분기
- 범위 밖: Matrix (항상 스트레치), 기타 괄호 종류

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- layout.rs: paren_w = fs * 0.333 (Times advance 매치, 기존 0.3)
- svg_render.rs / canvas_render.rs Paren arm 높이 분기
  - body.height <= fs * 1.2 + 대상 글리프(`(`/`)`) → <text> 렌더
  - 외: 기존 draw_stretch_bracket (path)
- Matrix arm 은 변경 없음 (항상 스트레치 path)
- test_paren_svg 갱신 (path → text), test_paren_stretch_svg 신규

회귀: equation 49/49, svg_snapshot 3/3, clippy 에러 없음,
WASM lib check 통과. 기존 14 실패(CFB writer)는 본 타스크 무관.

equation-lim.hwp 실측: 4 파렌 모두 <text> 글리프 전환,
<path> 0건 (단계 1 스냅샷은 4건).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- equation-lim.hwp 3면 비교: AFTER ≈ PDF 레퍼런스
  - BEFORE (path): 얇은 moon, 글자와 gap
  - AFTER (glyph, Task edwardkim#283): 본 파렌 형상, 글자와 자연 밀착
- exam_math.hwp 회귀 (p001/p005/p009/p013):
  - 텍스트 높이 파렌 (f(1), f(x), P(A|B)) → 글리프 렌더 정상
  - 스트레치 파렌 (P(A∪B), 분수 감쌈) → 기존 path 보전
  - 분기 임계치(body.height <= fs*1.2) 의도대로 작동
- 실측: <path> 4건 → 0건, <text>(/)</text> 신규 4건

산출물: 3면 compare.png + 회귀 4개 페이지 PNG

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- mydocs/report/task_m100_283_report.md 최종 보고서
  - 조사(Times 실측 4.89px advance) → 프로토타입 6안 → 선정(B_glyph)
  - 변경 범위 3 파일 + 테스트 갱신
  - 검증 결과 (회귀 + 실측 + 시각 비교)
  - 후속 과제 후보 3건 기록 (기타 괄호, path 재설계, matrix 분기)
- mydocs/orders/20260424.md edwardkim#283 섹션 추가 + 종료 표시

range: 괄호 (·) 만. {·[·| 및 스트레치 path 품질은 범위 밖.

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

@edwardkim edwardkim left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. 🎉

검토 결과

@planet6897 님, Phase 2 후속 작업도 훌륭합니다.

특히 인상 깊은 부분

  • 6개 변형 프로토타입 실측 비교 — "path 제어점 튜닝으로 해결 가능한가" 를 추측하지 않고 0/1/2/3/4/5 각 변형을 실제 렌더해 비교. 단일 제어점 quadratic Bezier의 수학적 한계로 대칭 moon 만 생성됨을 확인 → B_glyph 만이 해답임을 근거로 선정.
  • 실측 기반 수치 결정 — Chrome headless로 Times ( advance = 4.89px (em 0.333) 측정 → paren_w = fs * 0.333 정확히 매치.
  • 안전한 범위 한정body.height ≤ fs * 1.2 임계치 + (, ) 글리프 제한으로 스트레치 파렌(분수/행렬)은 기존 path 보전.

메인테이너 검증

항목 결과
cargo test --lib equation ✅ 49 / 0 (신규 test_paren_stretch_svg 포함)
cargo test --test svg_snapshot ✅ 3 / 0
cargo test --lib 전체 ✅ 964 / 0 / 1 ignored
cargo clippy --lib -- -D warnings ✅ clean
cargo check --target wasm32 ✅ clean
WASM Docker 빌드 ✅ 성공
rhwp-studio 브라우저 시각 검증 ✅ 파렌 글리프 렌더링 정상

후속 과제

보고서에 기록된 3건 (기타 괄호 {/[/\| 글리프 전환, 스트레치 path 재설계, Matrix arm 임계치 적용)은 별도 이슈로 추적하시면 됩니다.

Merge 진행합니다.

@edwardkim edwardkim merged commit 54188ef into edwardkim:devel Apr 24, 2026
6 checks passed
planet6897 added a commit to planet6897/rhwp that referenced this pull request Apr 24, 2026
원격 local/task287 이 devel 의 edwardkim#283 (PR edwardkim#285) 머지를 포함해 7 commit 앞섰고
로컬이 edwardkim#288 3 commit 을 앞서던 상태 → 양쪽 합치는 merge.

충돌: mydocs/orders/20260424.md 의 "등록·대기" 섹션
해결:
- Task edwardkim#283 섹션 번호 ## 3 → ## 4 (원격 추가분)
- Task edwardkim#288 섹션 번호 ## 4 → ## 5 (로컬 추가분)
- "종료" 리스트에 edwardkim#283 추가 (실제 devel 에 PR edwardkim#285 로 merge 완료)
- "등록·대기" 비움 (오늘 사이클 내 모든 이슈 종료)

edwardkim#283 소스 변경 (src/renderer/equation/{canvas_render,layout,svg_render}.rs)
및 산출물은 원격에서 이미 commit 되어 그대로 포함.

Refs edwardkim#287 edwardkim#288 edwardkim#283

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit to seanshin/rhwp that referenced this pull request Apr 24, 2026
# Conflicts:
#	mydocs/orders/20260424.md
edwardkim added a commit that referenced this pull request Apr 24, 2026
- 작성자: @planet6897 (Task #283, PR #285)
- Merge commit: 54188ef
- 이슈 #283 closed

검증:
- cargo test --lib: 964 passed / 0 failed (신규 test_paren_stretch_svg 포함)
- cargo clippy + wasm32 check: clean
- WASM Docker 빌드 + rhwp-studio 브라우저 시각 검증 성공

수식 그룹 (#284 + #285) 처리 완료.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request Apr 24, 2026
…r 복구 (closes #275)

- 작성자: @planet6897 (Task #275, PR #278)
- Merge commit: 2a27b36 (admin merge, orders 문서 충돌 직접 해결)
- 이슈 #275 closed
- 커뮤니티 리뷰: @seanshin APPROVED

처리 절차:
- PR 브랜치에 origin/devel 머지 → orders 문서 충돌 해결 (Task #275 섹션 "## 5" 재배치)
- planet6897/local/task275에 push (maintainerCanModify 허용)
- 재승인 + admin merge

검증:
- cargo test --lib svg_fragment: 19 passed / 0 failed
- cargo test --lib: 983 passed / 0 failed / 1 ignored (+19 신규 svg_fragment)
- cargo clippy + wasm32 check: clean
- svg_snapshot: 6 passed

파급 효과: WASM canvas 에서 OLE 네이티브 이미지/EMF/OOXML 차트/Placeholder 모두 복구

===== 오늘 처리 5개 PR 완료 =====
#284 (#280) @planet6897 - 수식 폰트 스택
#285 (#283) @planet6897 - 수식 파렌 글리프
#266 (#157/#103) @seanshin - 비-TAC 표 out-of-flow
#273 (#267) @seanshin - right tab 공백 처리
#277 (#147) @seanshin - MEMO 바탕쪽 오분류
#278 (#275) @planet6897 - WASM OLE RawSvg/Placeholder

별도 추적: 이슈 #291 (KTX.hwp 2단 TAC 표 회귀, 핀셋 처리 예정)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request Apr 24, 2026
 #297)

- 작성자: @planet6897 (Task #297, PR #300, 오늘 6번째 기여)
- Merge commit: 0e3fb02 (admin merge, orders 3구간 충돌 직접 해결)
- 이슈 #297 CLOSED

처리 절차:
- PR 브랜치에 origin/devel 머지 → orders 섹션 3구간 해결 (#295 "## 7", #296 "## 8", #297 "## 9")
- planet6897/task297 에 push
- 재승인 + admin merge

변경 (1파일):
- src/renderer/layout/table_layout.rs +5 -2:
  - VertRelTo::Page => (col_area.y, col_area.height)  [쪽 본문 영역]
  - VertRelTo::Paper => (0, page_h_approx)  [용지 전체, 유지]
  - HWP 스펙 Page=쪽 본문, Paper=용지 전체 반영

성과:
- pi=22 "* 확인 사항" 박스 y: 1371.5 → 1224.07 (PDF 1226.5 ±2 일치)
- 145 샘플 중 본문 Page 표 13건 + 바탕쪽 5건 회귀 스캔 완료 (의도 범위 외 무회귀)

검증:
- cargo test --lib: 992 passed
- svg_snapshot: 6 passed (golden 유지)
- 실제 SVG y 좌표 확인: 1224.07px (PDF 일치)

#295#297 연결 모범 사례: PR #298 리뷰 중 사전 존재 버그로 분리 → 1시간 만에 PR #300 해결.
초기 가설(바탕쪽 Paper) 폐기 → pdftotext 실측으로 근본 원인(enum 미구분) 발견 → 1줄 수정.

===== 오늘 9번째 PR 머지 =====
#284 #285 #266 #273 #277 #278 #289 #292 #298 #300
+ 메인테이너 핀셋 #296

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@planet6897 planet6897 deleted the local/task283 branch April 30, 2026 00:04
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