Skip to content

수식 ATOP 파싱 및 렌더링 보정#397

Closed
cskwork wants to merge 1 commit into
edwardkim:develfrom
cskwork:fix/atop-equation-parser
Closed

수식 ATOP 파싱 및 렌더링 보정#397
cskwork wants to merge 1 commit into
edwardkim:develfrom
cskwork:fix/atop-equation-parser

Conversation

@cskwork
Copy link
Copy Markdown
Contributor

@cskwork cskwork commented Apr 28, 2026

요약

  • 한컴 수식 a ATOP bEqNode::Atop으로 파싱하도록 보정
  • Atop 레이아웃을 Fraction과 분리해 분수선 없이 위/아래 항만 배치하도록 구현
  • SVG/Canvas 렌더러가 LayoutKind::Atop을 별도 처리하도록 추가
  • 파서 단위 테스트와 SVG 렌더링 회귀 테스트를 추가

배경

한컴 수식 문법에서 ATOPOVER와 비슷하게 두 항을 위아래로 배치하지만, OVER와 달리 가운데 분수선을 그리지 않는다.

예:

a ATOP b

기대 렌더링은 a가 위, b가 아래에 배치되는 형태이며, a OVER b처럼 분수선이 들어가면 안 된다.

현상

기존 코드에는 이미 EqNode::Atop { top, bottom } AST 타입이 있었지만 실제 파싱·레이아웃·렌더링 흐름이 완성되어 있지 않았다.

그 결과 a ATOP b 입력 시:

  • 파서가 ATOP 명령을 독립 명령으로 만나면 빈 노드로 처리
  • 중위 연산자 형태의 a ATOP b를 위/아래 구조로 결합하지 못함
  • EqNode::Atop이 만들어져도 레이아웃 단계에서 Fraction 레이아웃으로 대체되어 분수선이 생길 수 있음
  • SVG/Canvas 렌더러에는 LayoutKind::Atop 처리 분기가 없었음

근본 원인

OVERparse_expression()과 그룹 파싱 내부에서 중위 연산자로 처리되어 직전 요소를 분자, 직후 요소를 분모로 결합한다.

반면 ATOP은 문법적으로 같은 중위 연산자 계열임에도 해당 처리 흐름에 포함되어 있지 않았다.

또한 레이아웃 단계에서 EqNode::Atoplayout_fraction()으로 넘기고 있었기 때문에, Atop의 의미인 “분수선 없는 위/아래 배치”가 유지되지 않았다.

해결책

1. ATOP 중위 연산자 파싱

src/renderer/equation/parser.rs에서 OVER 처리 경로에 ATOP을 함께 포함했다.

  • a OVER bEqNode::Fraction { numer, denom }
  • a ATOP bEqNode::Atop { top, bottom }

동일한 처리를 최상위 표현식과 { ... } 그룹 내부 파싱 양쪽에 적용했다.

2. Atop 전용 레이아웃 추가

src/renderer/equation/layout.rsLayoutKind::Atoplayout_atop()을 추가했다.

layout_atop()layout_fraction()과 유사하게 위/아래 항을 중앙 정렬하지만, 분수선 영역을 만들거나 렌더러에 선을 요구하지 않는다.

핵심 차이:

  • Fraction: 위/아래 항 + 분수선
  • Atop: 위/아래 항만 배치, 분수선 없음

3. SVG/Canvas 렌더링 분기 추가

src/renderer/equation/svg_render.rssrc/renderer/equation/canvas_render.rsLayoutKind::Atop 분기를 추가했다.

렌더러는 이미 계산된 top, bottom 위치에 각각 텍스트/수식 박스를 렌더링하며, 별도의 <line> 또는 Canvas line stroke를 만들지 않는다.

검증

신규 회귀 테스트

src/renderer/equation/parser.rs:

  • test_atop
    • a atop bEqNode::Atop으로 파싱되는지 확인
    • 위 항이 a, 아래 항이 b로 보존되는지 확인

src/renderer/equation/svg_render.rs:

  • test_atop_svg_has_no_fraction_line
    • a atop b SVG에 <text>가 출력되는지 확인
    • SVG에 <line>이 없어 분수선이 그려지지 않는지 확인
    • 두 텍스트의 y 좌표가 달라 위/아래 배치가 되었는지 확인

기존 회귀 확인:

  • test_fraction_svg
    • OVER 수식은 기존처럼 분수선을 유지하는지 확인

테스트 실행 결과

  • cargo test renderer::equation::parser::tests::test_atop 통과
  • cargo test renderer::equation::svg_render::tests::test_atop_svg_has_no_fraction_line 통과
  • cargo test renderer::equation::svg_render::tests::test_fraction_svg 통과
  • cargo test 통과
    • lib 1018 passed, 1 ignored
    • 통합 테스트 및 문서 테스트 통과
  • git diff --check 통과
  • cargo clippy -- -D warnings는 기존 코드의 clippy::uninlined_format_args 716건으로 실패
  • 기존 lint만 제외한 cargo clippy -- -D warnings -A clippy::uninlined_format_args 통과

의도된 동작 변경

ATOP 수식이 더 이상 빈 노드로 사라지거나 분수선 있는 분수처럼 렌더링되지 않는다.

앞으로:

  • a OVER b는 분수선 있는 분수로 렌더링
  • a ATOP b는 분수선 없는 위/아래 배치로 렌더링

이는 한컴 수식 문법의 의미에 맞는 동작 변경이다.

변경 파일

파일 변경
src/renderer/equation/parser.rs ATOPOVER와 같은 중위 연산자 흐름으로 파싱, EqNode::Atop 생성, 파서 테스트 추가
src/renderer/equation/layout.rs LayoutKind::Atop 추가, 분수선 없는 layout_atop() 구현
src/renderer/equation/svg_render.rs LayoutKind::Atop SVG 렌더링 분기 추가, 분수선 없음 회귀 테스트 추가
src/renderer/equation/canvas_render.rs LayoutKind::Atop Canvas 렌더링 분기 추가

Test plan

  • cargo test renderer::equation::parser::tests::test_atop
  • cargo test renderer::equation::svg_render::tests::test_atop_svg_has_no_fraction_line
  • cargo test renderer::equation::svg_render::tests::test_fraction_svg
  • cargo test
  • git diff --check
  • cargo clippy -- -D warnings -A clippy::uninlined_format_args

메타

  • Base 브랜치: devel
  • Head 브랜치: cskwork:fix/atop-equation-parser
  • 커밋: d212857fix: 수식 ATOP 파싱 및 렌더링 보정

@cskwork cskwork force-pushed the fix/atop-equation-parser branch from c8ea466 to d212857 Compare April 28, 2026 00:18
@cskwork cskwork marked this pull request as ready for review April 28, 2026 01:04
@edwardkim
Copy link
Copy Markdown
Owner

PR 검토 결과 안내드립니다. 코드 자체는 ATOP 의 의미 (분수선 없이 위/아래 배치) 를 정확히 분리한 합리적인 정정입니다 — Fraction / Atop 의 LayoutKind 분리 + 파서/레이아웃/렌더 일관성 모두 양호합니다. 본 검토에서 dry-run merge 도 자동 성공했고 cargo test --lib 1033 passed 검증도 통과했습니다.

다만 본 저장소의 PR 처리 절차상 다음 항목이 추가로 필요합니다. 첫 PR 이시라 향후 지속적인 기여를 위해 절차 안내드리니 양해 부탁드립니다.

1. devel 기반 rebase 필요

현재 PR 의 base 가 `94d9347` 입니다. 그 이후 devel 에 PR #395 (그림 밝기/대비) + PR #396 (수식 렌더링 — TAC 높이 + 한글 이탤릭) 이 머지되어 BEHIND 상태입니다.

특히 PR #396 이 본 PR 과 같은 파일 (`equation/{layout,svg_render,canvas_render}.rs`) 을 변경했으니, devel 위로 rebase 후 충돌 확인 + 재제출 부탁드립니다:

```bash
git fetch upstream # 또는 origin (rhwp 본 저장소)
git rebase upstream/devel

충돌 발생 시 해결 후

git push --force-with-lease origin fix/atop-equation-parser
```

2. 시각 검증 자료 첨부 요청

본 저장소의 PR 처리 정책상 외부 컨트리뷰터 PR 은 한컴 출력 (정답지) 과 rhwp 출력의 시각 비교 를 PR 본문 또는 댓글에 첨부해야 머지 가능합니다.

ATOP 수식이 포함된 hwp 샘플 (예: 한컴 편집기에서 `a atop b` 수식 입력 후 저장) 로 다음을 비교:

  • 한컴 편집기 출력 (PDF 또는 스크린샷 — 한컴 2010 또는 2022 편집기)
  • rhwp SVG 출력 — `cargo run --release --bin rhwp -- export-svg <sample.hwp> -o output/svg/pr397-visual/`

두 출력의 분수선 유무 / 텍스트 위치 일치 여부 시각 비교 자료를 첨부해 주세요. 작성자 환경에 한컴 편집기가 없으면 알려주세요 — 메인테이너가 시각 판정 보조 가능합니다.

3. CI 실행 확인

현재 PR 의 CI status 가 비어있습니다. rebase 후 push 시 CI 가 자동으로 실행될 것입니다. 머지 전 모든 check 가 SUCCESS 인지 확인 부탁드립니다.

4. clippy 정황

PR 본문에 명시된 `clippy::uninlined_format_args` 716건 정황은 clippy 버전 차이로 추정됩니다. 본 저장소의 CI 환경 (Rust stable) 에서는 `cargo clippy --lib -- -D warnings` 가 통과합니다. rebase 후 CI 가 정상 실행되면 작성자 환경의 clippy 정황은 별도 처리 불필요합니다.

정리

단계 상태
코드 품질 ✅ 합리적
dry-run merge ✅ 자동 성공
cargo test --lib ✅ 1033 passed
devel 기반 rebase ⚠️ 필요
시각 검증 자료 ⚠️ 필요
CI 실행 ⚠️ rebase + push 후 자동 트리거 예상

본 PR 은 OPEN 유지합니다. 위 3가지 (rebase + 시각 자료 + CI 통과) 완료 후 알려주시면 다시 검토 후 머지 진행하겠습니다.

좋은 기여 감사합니다.

edwardkim added a commit that referenced this pull request Apr 28, 2026
- PR #397 (수식 ATOP, @cskwork) 검토 — 옵션 B (작성자 재제출 대기)
- 신규 컨트리뷰터 첫 PR — devel rebase + 시각 검증 자료 + CI 통과 요청 댓글
- 작업지시자 git 정보 확인 결과 실제 사람 컨트리뷰터 (AI 에이전트 아님)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request Apr 28, 2026
@edwardkim edwardkim added this to the v1.0.0 milestone Apr 28, 2026
@edwardkim edwardkim added the enhancement New feature or request label Apr 28, 2026
@cskwork cskwork force-pushed the fix/atop-equation-parser branch from 139d539 to a26a181 Compare April 28, 2026 15:06
@cskwork cskwork force-pushed the fix/atop-equation-parser branch from a26a181 to 8be4940 Compare April 28, 2026 15:07
@cskwork
Copy link
Copy Markdown
Contributor Author

cskwork commented Apr 28, 2026

검토 감사합니다. 지시 항목을 처리하여 보고드립니다.

1. devel 기반 rebase 완료

origin/devel 최신 (d81865f Merge local/devel: PR #400 검토 문서) 위에 rebase 했습니다.

항목
이전 head d212857 + merge 139d539
신규 head 8be4940 fix: 수식 ATOP 파싱 및 렌더링 보정 (단일 커밋)
PR #396 (동일 파일 수정) 충돌 없음
Merge 커밋 처리 제거 후 fix 커밋만 재적용

2. 시각 검증 자료 (export-svg 풀 파이프라인)

본 환경에 한컴 정품 편집기 (HWP 2010/2022) 가 없어 한컴 정답지 첨부는 메인테이너 보조 옵션을 부탁드립니다.

대신 본 저장소의 rhwp CLI 빌드 결과물을 사용하여 실제 HWP 파일에서 export-svg 풀 파이프라인을 거친 SVG 출력을 첨부합니다. 베이스는 기존 samples/equation-lim.hwp 의 수식 컨트롤이며, Equation.script 필드만 ATOP / OVER / 그룹 케이스로 교체한 후 HWP 직렬화 → export-svg 파이프라인을 거쳤습니다.

산출물은 cskwork fork 의 pr397-visuals 브랜치에 별도 푸시 완료. 본 PR (fix/atop-equation-parser) 에는 포함하지 않아 fix 커밋이 단일 상태 유지됩니다.

결과 요약

케이스 script SVG 크기 <text> <line> (분수선)
ATOP a atop b 1040 byte 2 0
OVER (회귀 확인) a over b 1128 byte 2 1
GROUP {x+y} atop {u-v} 1806 byte 6 0

2-1. ATOP — a atop b

분수선 미생성, 위 / 아래 배치만 유지 (의도된 동작):

atop a b

직접 보기: docs/pr397-visuals/atop.svg

2-2. OVER — a over b (회귀 확인)

분수선 유지 (기존 동작 보존):

over a b

직접 보기: docs/pr397-visuals/over.svg

2-3. GROUP — {x+y} atop {u-v}

ATOP 이 그룹 ({...}) 내부에서도 정상 동작. 분수선 미생성:

atop group

직접 보기: docs/pr397-visuals/atop_group.svg

재현 명령

example 소스 (atop_visual_demo.rs) 를 examples/atop_visual_demo.rs 에 두고:

# 1. 베이스 HWP 의 equation script 만 ATOP / OVER / 그룹으로 교체하여 HWP 저장
cargo run --release --example atop_visual_demo
# → output/svg/pr397-visual/atop.hwp, over.hwp, atop_group.hwp

# 2. export-svg 풀 파이프라인 실행
./target/release/rhwp.exe export-svg output/svg/pr397-visual/atop.hwp       -o output/svg/pr397-visual/atop_out/
./target/release/rhwp.exe export-svg output/svg/pr397-visual/over.hwp       -o output/svg/pr397-visual/over_out/
./target/release/rhwp.exe export-svg output/svg/pr397-visual/atop_group.hwp -o output/svg/pr397-visual/atop_group_out/

회귀 테스트 (이미 PR 에 포함)

  • test_atop_svg_has_no_fraction_line — ATOP 의 <line> 부재 + 두 텍스트의 y 좌표 차이 검증
  • test_fraction_svg — OVER 의 <line> 유지 검증

3. CI 실행 상태

push 후 gh run list 결과, CI / CodeQL workflow 모두 action_required 상태입니다.

completed  action_required  CI       fix/atop-equation-parser  pull_request
completed  action_required  CodeQL   fix/atop-equation-parser  pull_request

첫 외부 컨트리뷰터 PR 이라 GitHub Actions 가 메인테이너 승인 대기 중입니다. PR 페이지의 "Approve and run" 승인을 요청드립니다. 승인 후 CI 결과 확인 후 필요 시 후속 정정하겠습니다.

4. 로컬 테스트 결과

명령 결과
cargo test --lib 1033 passed, 0 failed, 1 ignored
cargo test renderer::equation 52 passed
cargo test test_atop_svg_has_no_fraction_line passed
cargo test test_fraction_svg (회귀) passed
rhwp export-svg (3 케이스) ATOP 0줄, OVER 1줄, GROUP 0줄 확인

정리

단계 상태
devel 기반 rebase 완료 (8be4940, PR #395 + #396 위)
시각 검증 자료 export-svg 풀 파이프라인 SVG 3종 첨부 (pr397-visuals 브랜치)
CI 실행 "Approve and run" 승인 대기
로컬 테스트 1033 passed

추가 검토 요청드립니다.

edwardkim added a commit that referenced this pull request Apr 28, 2026
- PR #397 (수식 ATOP, @cskwork) 본 저장소 첫 외부 컨트리뷰터 PR cherry-pick 머지 완료
- 작성자 절차 안내 모두 대응 (rebase + 시각 자료 + CI 승인)
- 메인테이너 GitHub Actions API 로 첫 외부 PR CI 승인 → SUCCESS
- samples/atop-equation-01.hwp 작업지시자 추가 (ATOP / OVER / GROUP 시각 판정용)
- 1044→1046 passed + svg_snapshot 6/6 + clippy 0 + WASM 빌드 + 작업지시자 시각 판정 통과

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

완료. `devel` 에 cherry-pick (작성자 attribution 보존) 으로 머지됨 (commit `76a97e9`).

@cskwork 님은 본 저장소의 첫 번째 외부 컨트리뷰터 이십니다. 의미 있는 첫 기여를 환영하고, 깊이 감사드립니다.

ATOP / OVER 의 의미를 정확히 분리한 정정 (`EqNode::Atop`, `LayoutKind::Atop`, `layout_atop()`) 도 합리적이었고, 1차 검토 후 절차 안내에 대해서도 모든 항목 (devel 기반 rebase, 시각 검증 자료, CI 정황) 을 정확하게 대응해 주셨습니다. 본 저장소의 처리 흐름을 빠르게 익혀주신 점이 양호합니다.

시각 판정 — 작업지시자 전용 샘플 추가

작업지시자가 ATOP 회귀 검증을 위해 `samples/atop-equation-01.hwp` 전용 샘플을 본 저장소에 추가했습니다 (3 가지 케이스 포함):

케이스 text line 평가
ATOP `a atop b` a, b 0 ✅ 분수선 없음
OVER `a over b` (회귀 확인) a, b 1 ✅ 분수선 유지
ATOP 그룹 `{x+y} atop {u-v}` x+y, u-v 0 ✅ 분수선 없음

작업지시자가 직접 확인하여 시각 판정 통과했습니다 — 정정 의도가 정확히 반영됐다는 의미입니다.

머지 commit

작성자 attribution 보존:

  • `a9eb13c` (cherry-pick from `8be4940`, @cskwork) — fix: 수식 ATOP 파싱 및 렌더링 보정

검증

  • `cargo test --lib`: 1046 passed (1044 → +2 신규 ATOP 테스트)
  • `cargo test --test svg_snapshot`: 6/6 passed
  • `cargo test --lib renderer::equation`: 52 passed
  • `cargo clippy --lib -- -D warnings`: warning 0건
  • WASM 빌드 (Docker): 4,134,499 bytes
  • GitHub CI Build & Test SUCCESS (메인테이너 승인 후 자동 실행)
  • `samples/atop-equation-01.hwp` 작업지시자 시각 판정 통과

향후 PR 작성 안내

향후 PR 의 CI 도 자동 실행될 것입니다 (첫 PR 의 "action_required" 정황은 GitHub 의 정책으로, 첫 머지 후 신뢰 컨트리뷰터로 인식됩니다).

저장소 자체 이슈로 발견하신 정황 외에도 외부 binding / RAG 작업에서 발견한 정황을 이슈로 자유롭게 등록해 주세요. 본 저장소의 정황은 `mydocs/manual/dev_environment_guide.md` 에 정리되어 있고, 단계별 절차는 `CLAUDE.md` 와 `CONTRIBUTING.md` 에 있습니다.

다시 한 번 첫 기여 감사드립니다. 앞으로 좋은 협업이 이어지기를 기대합니다.

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