Skip to content

HWPX 수식 직렬화 보존#400

Closed
cskwork wants to merge 2 commits into
edwardkim:develfrom
cskwork:feature/hwpx-equation-serialization
Closed

HWPX 수식 직렬화 보존#400
cskwork wants to merge 2 commits into
edwardkim:develfrom
cskwork:feature/hwpx-equation-serialization

Conversation

@cskwork

@cskwork cskwork commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

요약

  • HWPX section 직렬화에서 Control::Equation<hp:equation>으로 출력하도록 추가
  • 수식 스크립트, 글꼴명, 글자 크기, 색상, 기준선, 기본 개체 크기·위치 속성이 serialize_hwpx 후 재파싱에서도 보존되도록 보강
  • XML 엔티티가 포함된 수식 스크립트(<, & 등)를 HWPX 파서가 원문 의미대로 복원하도록 수정
  • 수식 앞에 다른 컨트롤 슬롯이 있는 경우에도 수식 위치를 잘못 앞당기지 않도록 보수적으로 직렬화

배경

현재 HWP 바이너리 쪽은 eqed / HWPTAG_EQEDIT 기반 수식 파싱·직렬화 경로가 존재하고, HWPX 파서도 <hp:equation><hp:script>Control::Equation으로 읽는다.

하지만 HWPX section 직렬화 경로(src/serializer/hwpx/section.rs)는 문단 본문을 <hp:t> 중심으로만 출력하고 있어, IR에 들어 있는 Control::Equation이 HWPX 저장 결과에 나타나지 않는다. 이 때문에 HWPX 저장/재파싱 과정에서 수식 컨트롤의 스크립트와 개체 속성이 소실될 수 있다.

근본 원인

render_paragraph_parts()가 문단 내용을 만들 때 기존에는 render_hp_t_content(&para.text)만 호출했다.

그 결과:

  • para.controlsControl::Equation이 있어도 <hp:equation> 요소를 출력하지 않음
  • 수식이 문단 텍스트 사이에 있는 경우 제어 슬롯 위치를 HWPX XML에 반영할 방법이 없음
  • <hp:script>에 XML 엔티티가 포함된 경우, 파서가 Event::Text만 처리하고 Event::GeneralRef를 무시해 <, & 같은 문자가 재파싱에서 소실됨

해결책

1. HWPX 수식 XML 출력

src/serializer/hwpx/section.rs에 수식 직렬화 경로를 추가했다.

Control::Equation을 다음 HWPX 구조로 출력한다.

<hp:equation ... version="..." baseLine="..." textColor="..." baseUnit="..." font="...">
  <hp:script>...</hp:script>
  <hp:sz .../>
  <hp:pos .../>
  <hp:outMargin .../>
</hp:equation>

보존 대상:

  • Equation.script
  • Equation.font_size
  • Equation.color
  • Equation.baseline
  • Equation.font_name
  • Equation.version_info
  • CommonObjAttr의 기본 크기·위치·여백·정렬 속성

2. 문단 내 수식 위치 보존

문단의 UTF-16 위치 정보(char_offsets, char_count)를 이용해 수식 컨트롤을 텍스트 사이에 배치한다.

  • 제어 슬롯 수와 컨트롤 수가 일치하는 HWP-origin 형태는 컨트롤 순서를 그대로 사용
  • HWPX-origin 형태는 실제 inline slot을 만드는 컨트롤만 대상으로 사용
  • fieldEnd처럼 텍스트 슬롯은 있으나 Control 엔트리가 없는 모호한 경우에는 수식을 잘못 앞당기지 않도록 텍스트 뒤에 보수적으로 출력

이 PR은 수식 출력만 추가하며, 표/그림/도형 등 다른 컨트롤의 HWPX 직렬화 범위는 확장하지 않는다.

3. 수식 스크립트 XML 엔티티 복원

src/parser/hwpx/section.rs::parse_equation()에서 <hp:script> 내부의 Event::GeneralRef를 처리하도록 보강했다.

지원 대상:

  • &lt;<
  • &gt;>
  • &amp;&
  • &quot;"
  • &apos;'
  • 숫자 character reference

알 수 없는 entity는 원문 형태(&name;)로 보존한다.

검증

신규 회귀 테스트

src/serializer/hwpx/mod.rs에 수식 HWPX 직렬화 회귀 테스트 3건을 추가했다.

  • equation_control_roundtrip_preserves_script
    • <hp:equation> 출력 확인
    • x < y & z 스크립트가 XML escape 후 재파싱에서 원문 의미로 복원되는지 확인
    • 글꼴, 크기, 색상, 기준선, 개체 크기·위치·여백·정렬 속성 보존 확인
  • equation_control_between_text_runs_roundtrips_position
    • ColumnDefTable 컨트롤이 수식 앞에 있을 때 수식이 잘못 앞당겨지지 않는지 확인
  • equation_control_does_not_consume_unmapped_control_gap
    • fieldEnd류처럼 슬롯은 있지만 대응 Control이 없는 모호한 gap에서 수식을 앞쪽 gap에 잘못 배치하지 않는지 확인

테스트 실행 결과

  • cargo test equation_control 통과 (3 passed)
  • cargo test serializer::hwpx 통과 (64 passed)
  • cargo test 통과 (전체 테스트 exit 0)

기존 경고는 본 PR과 무관한 기존 경고다.

의도된 동작 변경

HWPX 저장 시 기존에는 수식 컨트롤이 section XML에 출력되지 않았지만, 이제 Control::Equation<hp:equation>으로 보존된다.

모호한 제어 슬롯이 있는 문단에서는 수식을 임의의 앞쪽 gap에 끼워 넣지 않고 텍스트 뒤에 보수적으로 출력한다. 이는 잘못된 위치 이동을 피하기 위한 안전한 동작이다.

변경 파일

파일 변경
src/serializer/hwpx/section.rs 문단 run 콘텐츠에 수식 컨트롤 출력 추가, 수식 XML 생성, 색상/정렬/위치 속성 매핑
src/parser/hwpx/section.rs <hp:script> 내부 XML entity / character reference 복원
src/serializer/hwpx/mod.rs HWPX 수식 직렬화·재파싱 회귀 테스트 추가

Test plan

  • cargo test equation_control
  • cargo test serializer::hwpx
  • cargo test

메타

@cskwork cskwork force-pushed the feature/hwpx-equation-serialization branch from 95edf3d to 0bfa394 Compare April 28, 2026 01:06
@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
@edwardkim

Copy link
Copy Markdown
Owner

PR 검토 결과 안내드립니다. 코드 자체는 합리적입니다 — `render_paragraph_parts` 가 controls 무시하던 정황을 정확히 짚으셨고, `render_run_content` / `render_equation` 분리 + 모호한 control gap 보수적 처리 + parser 측 XML entity 복원 모두 양호합니다. dry-run merge 도 자동 성공했고 cargo test --lib 1034 passed 검증도 통과했습니다.

다만 본 저장소의 PR 처리 정책상 다음 항목 보강이 필요합니다.

1. 회귀 테스트 보강 — 자기검증 한계

현재 PR 의 회귀 테스트 3개는 모두 자기검증 패턴 입니다:

```rust
let mut doc = Document::default();
para.controls.push(Equation { script: "x < y & z", ... }); // 작성자가 IR 생성
serialize_hwpx(&doc) → bytes;
parse_hwpx(&bytes) → parsed_eq;
assert_eq!(parsed_eq.script, "x < y & z"); // 자기 데이터 검증
```

이 패턴은 rhwp 자체 roundtrip 항등성 만 검증합니다. 다음이 검증 안 됩니다:

  • 한컴 spec 일치성 — `hp:equation` 속성 / 자식 요소 / 네임스페이스가 한컴 spec 과 정확히 일치하는지
  • 한컴 호환성 — 본 직렬화 결과를 한컴 편집기가 정상 열람하는지
  • 실제 한컴 hwpx 의 회귀 — 한컴이 만든 hwpx 의 의미 보존

본 저장소 메모리 (`feedback_self_verification_not_hancom.md`):

자기 라운드트립 통과해도 한컴 거부 가능. HWP 저장 작업은 한컴 수동 검증 게이트 필수

보강 권장 — 실제 한컴 hwpx 샘플 활용

`samples/hwpx/` 에 한컴 생성 hwpx 가 있습니다. 자체 IR 대신 실제 한컴 hwpx 를 활용한 테스트가 더 견고합니다:

```rust
let bytes = std::fs::read("samples/hwpx/<수식 포함 샘플>")?;
let doc = parse_hwpx(&bytes)?;
let serialized = serialize_hwpx(&doc)?;
let reparsed = parse_hwpx(&serialized)?;
// 원본 doc 과 reparsed doc 의 수식 정보가 일치하는지 (rhwp 자체가 만든 데이터 아님)
```

수식 포함 한컴 hwpx 를 본 저장소에서 찾을 수 없으면 메인테이너에게 요청해 주세요.

2. 한컴 호환 검증 자료

본 PR 의 직렬화 결과 hwpx 를 한컴 편집기 (한컴 2010 또는 2022) 에서 정상 열람하는 증빙 (스크린샷 / PDF) 을 PR 본문 또는 댓글에 첨부해 주세요. 이는 본 저장소의 외부 컨트리뷰터 PR 머지 게이트입니다.

테스트 시나리오:

  1. 수식 포함 hwpx 샘플 로드
  2. 본 PR 의 `serialize_hwpx` 호출 → 새 hwpx 생성
  3. 결과 hwpx 를 한컴 편집기에서 열람 → 수식이 정상 보이는지
  4. 결과 캡처 첨부

3. devel 기반 rebase 필요

현재 PR 의 base 가 `94d9347` 입니다. 그 이후 devel 에 PR #395 (그림 밝기/대비) + PR #396 (수식 렌더링) 이 머지되어 BEHIND 상태입니다 (다행히 직접 충돌은 없으나 자동 머지 후 rebase 필요).

```bash
git fetch upstream # 또는 origin
git rebase upstream/devel
git push --force-with-lease origin feature/hwpx-equation-serialization
```

4. CI 실행 확인

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

5. 이슈 #286 milestone

이슈 #286 의 milestone 을 v1.0.0 으로 갱신했습니다. 작성자가 보고하신 내용대로 본 PR 은 v1.0.0 범위에 포함됩니다 — 시기상 적합한 작업입니다.

정리

단계 상태
코드 품질 ✅ 합리적
dry-run merge ✅ 자동 성공
cargo test --lib ✅ 1034 passed
회귀 테스트 보강 (한컴 hwpx 활용) ⚠️ 필요
한컴 호환 검증 (편집기 열람 증빙) ⚠️ 필요
devel 기반 rebase ⚠️ 필요
CI 실행 ⚠️ rebase + push 후 자동 트리거 예상

본 PR 은 OPEN 유지합니다. 위 항목 보강 후 알려주시면 다시 검토 후 머지 진행하겠습니다.

좋은 기여 감사합니다.

edwardkim added a commit that referenced this pull request Apr 28, 2026
- PR #400 (HWPX 수식 직렬화, @cskwork) 검토 — 옵션 A (작성자 보강 요청)
- 이슈 #286 milestone v1.0.0 으로 갱신 (작업지시자 결정)
- 자기검증 테스트 한계 — 한컴 hwpx 샘플 활용 + 한컴 호환 검증 자료 + devel rebase + CI 통과 요청

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request Apr 28, 2026
cskwork added 2 commits April 29, 2026 00:39
HWPX 저장 시 수식 컨트롤의 스크립트와 기본 개체 속성을 보존하고, XML 엔티티가 포함된 수식 스크립트도 재파싱에서 유지되도록 한다.

Made-with: Cursor
PR edwardkim#400 검토 코멘트 대응 — 자기검증 한계 보강.

기존 회귀 3건은 모두 `Document::default()` + 수동 `controls.push()` 형태로
rhwp가 만든 IR이 rhwp의 직렬화·재파서 사이에서만 항등성을 갖는지 검증한다.

본 커밋은 `samples/equation-lim.hwp`(한컴 편집기 origin)의 Equation IR을
입력으로 사용해 다음을 검증한다.

- parse_hwp(eq-lim.hwp) → Document IR (한컴 origin 데이터)
- serialize_hwpx(IR) → bytes
- parse_hwpx(bytes) → IR'
- IR.Equation 과 IR'.Equation 의 script/font_size/baseline/font_name/color/
  common.{width,height,treat_as_char} 필드가 모두 동일한지 확인

자기검증 패턴을 회피하지만, 한컴 hwpx → rhwp serialize → 한컴 편집기 열람
시퀀스의 호환성은 별도 검증 자료(스크린샷)로 보강 예정이다.

samples/hwpx 폴더에는 `<hp:equation>` 포함 hwpx가 부재하여 hwp 샘플로 대체.
@cskwork cskwork force-pushed the feature/hwpx-equation-serialization branch from 6d5f1f4 to ecd7d9a Compare April 28, 2026 15:58
@cskwork

cskwork commented Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

검토 의견 감사합니다. 5개 항목 처리 결과를 보고합니다.

1. 회귀 테스트 보강 — 한컴 origin 데이터 활용

자기검증 한계 지적에 동의합니다. samples/equation-lim.hwp(한컴 편집기 origin) 의 수식 IR을 입력으로 사용하는 회귀 테스트를 추가했습니다.

parse_hwp(samples/equation-lim.hwp) → Document IR  (한컴 origin 데이터)
  → serialize_hwpx → bytes
  → parse_hwpx → IR'
  → Equation.{script, font_size, baseline, font_name, color,
              common.{width, height, treat_as_char}}
    모든 필드가 origin 과 일치하는지 단언

자체 IR 생성 패턴(Document::default() + 수동 controls.push()) 을 회피하고, 한컴 편집기가 만든 hwp 파일에서 추출한 Equation 데이터로 직렬화 항등성을 검증합니다.

추가 커밋: ecd7d9a test: 한컴 origin hwp 기반 수식 HWPX 라운드트립 회귀 추가

다만 samples/hwpx/ 폴더에는 <hp:equation> 포함 hwpx 가 부재하여 hwp 샘플로 대체했습니다. 한컴 origin hwpx 가 별도 확보되면 동일 패턴으로 회귀를 한 건 더 추가하겠습니다.

2. 한컴 호환 검증 자료

한컴 한글 2020 (Office 2020 / HOffice110) 에서 본 PR 의 직렬화 결과 hwpx 를 정상 열람 + PDF 내보내기 검증을 수행했습니다.

  • 입력: samples/equation-lim.hwp (한컴 편집기 origin)
  • 처리: parse_hwpserialize_hwpx (본 PR 변경사항 반영)
  • 산출물: output/pr400_equation_roundtrip.hwpx (7,145 bytes)
  • 한컴 한글 2020 열람 후 PDF 내보내기: 11,662 bytes
  • 수식 렌더링: lim_{h→0} {f(2+h) - f(2)} / h (한컴 origin 과 시각적으로 일치)

pr400_equation_roundtrip.pdf

3. devel 기반 rebase

origin/devel 위로 깔끔하게 linear rebase 완료.

ecd7d9a  test: 한컴 origin hwp 기반 수식 HWPX 라운드트립 회귀 추가
f25d986  feat: HWPX 수식 직렬화 보존
d81865f  Merge local/devel: PR #400 검토 문서   ← origin/devel

4. CI 실행

rebase + force-with-lease push 직후 CI 자동 트리거됩니다. 결과 공유드리겠습니다.

5. 이슈 #286 milestone v1.0.0

갱신 감사합니다. 시기상 적합하다는 판단에 동의합니다.

정리

단계 상태
한컴 origin 회귀 테스트 (#1) ✅ 추가 (ecd7d9a)
한컴 호환 PDF 증빙 (#2) ✅ 첨부
devel rebase (#3) ✅ 완료
CI 실행 (#4) ⏳ push 직후 자동 트리거
이슈 milestone (#5) ✅ 메인테이너 처리 완료

재검토 부탁드립니다.

edwardkim pushed a commit that referenced this pull request Apr 28, 2026
PR #400 검토 코멘트 대응 — 자기검증 한계 보강.

기존 회귀 3건은 모두 `Document::default()` + 수동 `controls.push()` 형태로
rhwp가 만든 IR이 rhwp의 직렬화·재파서 사이에서만 항등성을 갖는지 검증한다.

본 커밋은 `samples/equation-lim.hwp`(한컴 편집기 origin)의 Equation IR을
입력으로 사용해 다음을 검증한다.

- parse_hwp(eq-lim.hwp) → Document IR (한컴 origin 데이터)
- serialize_hwpx(IR) → bytes
- parse_hwpx(bytes) → IR'
- IR.Equation 과 IR'.Equation 의 script/font_size/baseline/font_name/color/
  common.{width,height,treat_as_char} 필드가 모두 동일한지 확인

자기검증 패턴을 회피하지만, 한컴 hwpx → rhwp serialize → 한컴 편집기 열람
시퀀스의 호환성은 별도 검증 자료(스크린샷)로 보강 예정이다.

samples/hwpx 폴더에는 `<hp:equation>` 포함 hwpx가 부재하여 hwp 샘플로 대체.
edwardkim added a commit that referenced this pull request Apr 28, 2026
- PR #400 (HWPX 수식 직렬화, @cskwork) 옵션 A cherry-pick 머지 완료
- 작성자가 1차 검토 5 항목 모두 정확 대응 (한컴 origin 회귀 + 한컴 한글 2020 PDF 검증)
- 자기검증 한계 정확히 보강 (메모리 feedback_self_verification_not_hancom 원칙 부합)
- cherry-pick 2 commits + 1046→1050 passed + WASM 빌드

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

Copy link
Copy Markdown
Owner

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

이슈 #286 close 됩니다.

머지 commit

작성자 attribution 보존:

  • `28d6eba` (cherry-pick, @cskwork) — feat: HWPX 수식 직렬화 보존
  • `43da8f9` (cherry-pick, @cskwork) — test: 한컴 origin hwp 기반 수식 HWPX 라운드트립 회귀 추가

1차 검토 5 항목 모두 정확 대응

항목 작성자 대응
1. 자기검증 한계 → 한컴 origin 회귀 ✅ `equation_roundtrip_from_hancom_origin_hwp_sample` 신설
2. 한컴 호환 검증 (편집기 열람) ✅ 한컴 한글 2020 (Office 2020) PDF 검증 자료 첨부
3. devel 기반 rebase ✅ linear 2 commits
4. CI 실행 ✅ 메인테이너 승인 후 실행
5. 이슈 #286 milestone ✅ 메인테이너 v1.0.0 갱신 완료

특히 자기검증 한계 보강이 모범적이었습니다. `samples/equation-lim.hwp` (한컴 편집기 origin) 의 수식 IR 을 입력으로 `parse_hwp → serialize_hwpx → parse_hwpx` 의 모든 Equation 필드 라운드트립을 검증하는 패턴은 본 저장소의 메모리 원칙 (`feedback_self_verification_not_hancom.md`) 을 정확히 인지한 결과로 보입니다.

한컴 호환 검증

한컴 한글 2020 (Office 2020 / HOffice110) 에서:

  • 입력: `samples/equation-lim.hwp` (한컴 origin)
  • rhwp serialize 결과 hwpx (7,145 bytes) 정상 열람
  • PDF 내보내기 (11,662 bytes) 의 수식 (`lim_{h→0} {f(2+h) - f(2)} / h`) 가 한컴 origin 과 시각적 일치

→ HWPX 직렬화의 한컴 호환성 입증.

검증

  • `cargo test --lib`: 1050 passed (1046 → +4 신규 — 한컴 origin 회귀 + 기타 3건)
  • `cargo test --test svg_snapshot`: 6/6 passed
  • `cargo test --test issue_418`: 1/1 (Task samples/hwpspec.hwp 20 페이지 이미지 이중 출력 (PR 처리 후 회귀) #418 보존)
  • `cargo test --lib serializer::hwpx`: 65 passed (한컴 origin 라운드트립 통과 ✅)
  • `cargo clippy --lib -- -D warnings`: warning 0건
  • WASM 빌드 (Docker): 4,114,661 bytes
  • HWPX 직렬화 (저장 경로) 정정이라 시각 판정 불필요

향후 개선 제안

PR 본문에 명시하신 `samples/hwpx/` 의 수식 포함 hwpx 부재 정황은 별도 task 후보입니다. 한컴 origin hwpx (직접) 회귀 테스트도 가능해지면 본 PR 의 회귀 검증이 한 단계 더 강해질 것입니다.

평가

@cskwork 님은 본 저장소의 첫 외부 컨트리뷰터이시고, PR #397 (수식 ATOP) 에 이어 본 PR (HWPX 수식 직렬화) 까지 두 건 모두 깊이 있는 분석 + 정확한 절차 대응으로 처리됐습니다. 한컴 호환 게이트 (rhwp 자체 라운드트립 ≠ 한컴 호환) 정황을 명확히 인지하고 한컴 한글 2020 으로 직접 검증해 주신 점이 특히 양호합니다.

좋은 기여 감사드립니다. 앞으로의 협업 기대합니다.

@edwardkim edwardkim closed this Apr 28, 2026
edwardkim added a commit that referenced this pull request Apr 29, 2026
- mydocs/pr/pr_428_review.md (그룹 내 Picture 직렬화 정정, 트러블슈팅 사전 검토 + 호출 함수 정정 적용 확인 + 작성자 보강 요청 항목 정리)
- mydocs/orders/20260426.md (PR #428 작성자 보강 대기 행 추가)

핵심: 저장 경로 PR 이라 메모리 feedback_self_verification_not_hancom 원칙 적용
- 자기 라운드트립 ≠ 한컴 호환
- 한컴 origin 기반 회귀 테스트 (PR #400 패턴) + 한컴 편집기 호환 검증 자료 요청
- PR #428 OPEN 유지, 작성자 재제출 대기
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