Skip to content

기능: hwpx 수식 방출 arm + hp:script 엔티티 해석 (GE-14·GE-15) - #23

Merged
entelecheia merged 3 commits into
mainfrom
feat/hwpx-equation-write
Jul 26, 2026
Merged

기능: hwpx 수식 방출 arm + hp:script 엔티티 해석 (GE-14·GE-15)#23
entelecheia merged 3 commits into
mainfrom
feat/hwpx-equation-write

Conversation

@entelecheia

Copy link
Copy Markdown
Member

hwpx writer에 eqed arm이 없어 수식이 generic fallback으로 DROP됐다. hwpx를 편집·변환만 해도 수식이 통째로 사라지는 데이터 손실이다.

변경

  • write_equation — run 직속 <hp:equation> + hp:sz/hp:pos/hp:script 방출. 개체 공통 스캐폴드 순서(sz→pos→outMargin)는 write_shape_element·hp:pic과 동일하고, 부유/인라인의 flowWithText·allowOverlap 조합은 gso_pos_xml의 실측 규칙을 따른다. hwpx 출신(hp:script)·hwp5 출신(EQEDIT, parse_eqed) 모두 IR Equation을 채우므로 한 경로로 방출된다 — hwp→hwpx 수식 변환도 함께 열린다.
  • resolve_entity — 엔티티 참조 해석을 parse_textread_element_text가 공유. read_element_textGeneralRef/CData를 무시해 x &lt; y 같은 스크립트의 특수문자가 읽기에서 유실되던 결함 해소(writer esc()의 역).
  • 수식_hwpx_왕복 — 인라인/부유 2종 + XML 특수문자 케이스.
  • docs/design/12-feature-gaps.md GE-14·GE-15 등재(해소 표기).

검증

  • scripts/check.sh 통과 (fmt + clippy -D warnings + test 전체).
  • 기존 방출 경로 무변경 — 추가된 arm은 equation.is_some() 가드로 기존 DROP fallback 앞에만 들어간다.

남은 것 (머지 전 확인)

수식 전용 속성(version="Equation Version 60"·baseLine="85"·baseUnit="1000"·font="HYhwpEQ")은 정답지 미확보 상태의 표준 추정값이다 — 로컬 코퍼스에 수식 든 hwpx가 없다. 한글 실기에서 수식이 깨져 보이면 정품 저장본의 <hp:equation> 속성으로 교체해야 한다.

외부 기여 #7(@maenjh)의 진단에서 출발했다. 각주/미주는 e433462 실측판으로 선행 해소돼 제외하고, 남은 수식 방출 + 엔티티 해석만 구현했다.

hwpx writer에 eqed arm이 없어 수식이 generic fallback으로 DROP됐다. hwpx를
편집·변환만 해도 수식이 통째로 사라지는 데이터 손실이었다.

- write_equation: run 직속 <hp:equation> + hp:sz/hp:pos/hp:script.
  개체 공통 스캐폴드 순서(sz→pos→outMargin)는 write_shape_element·hp:pic과
  동일. 부유/인라인 flowWithText·allowOverlap 조합은 gso_pos_xml의 실측 규칙.
  hwpx 출신(hp:script)·hwp5 출신(EQEDIT, parse_eqed) 모두 IR Equation을
  채우므로 한 경로로 방출된다.
- resolve_entity: 엔티티 참조 해석을 parse_text와 read_element_text가 공유.
  read_element_text가 GeneralRef/CData를 무시해 `x &lt; y` 같은 스크립트의
  특수문자가 읽기에서 유실되던 결함 해소(writer esc()의 역).
- 수식_hwpx_왕복: 인라인/부유 2종 + XML 특수문자 케이스로 왕복 고정.

수식 전용 속성(version·baseLine·baseUnit·font)은 정답지 미확보 표준 추정값이라
한글 실기 확정이 남았다(12-feature-gaps.md GE-14).

외부 기여 PR #7(maenjh)의 진단에서 출발 — 각주/미주는 e433462 실측판으로 선행
해소돼 제외하고, 남은 수식·엔티티만 구현.
Copilot AI review requested due to automatic review settings July 26, 2026 19:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7aeec3c1d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/hwpx/src/write/section.rs Outdated
let id = ids.next();
let _ = write!(
out,
r##"<hp:equation id="{id}" zOrder="0" numberingType="EQUATION" textWrap="{wrap}" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" version="Equation Version 60" baseLine="85" textColor="#000000" baseUnit="1000" lineMode="0" font="HYhwpEQ"><hp:sz width="{}" widthRelTo="ABSOLUTE" height="{}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="{treat}" affectLSpacing="0" flowWithText="{flow}" allowOverlap="{overlap}" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="{}" horzOffset="{}"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:script>{}</hp:script></hp:equation>"##,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the equation anchor coordinate system

For floating equations sourced with PAPER, PAGE, or COLUMN anchoring, hardcoding vertRelTo="PARA" and horzRelTo="PARA" reinterprets the stored offsets relative to the anchor paragraph. This moves formulas during HWPX round trips and HWP5-to-HWPX conversion even though the numeric x/y values survive; preserve the source relation/alignment fields in the IR or emit the coordinate system represented by Equation::x and y instead.

Useful? React with 👍 / 👎.

Comment thread crates/hwpx/src/write/section.rs Outdated
Comment on lines +1011 to +1012
eq.width.max(0),
eq.height.max(0),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Synthesize dimensions for zero-sized equations

When an Equation has width or height 0, these expressions emit an absolute zero-sized hp:sz. Zero is the model's documented sentinel meaning that the renderer should estimate the extent, and it can occur in synthesized or JSON-edited IR, so the resulting HWPX formula has no usable layout box. Derive positive dimensions from the script/typesetter, as the renderer does, rather than serializing the sentinel.

Useful? React with 👍 / 👎.

적대적 점검(자체) 결과 반영.

- count_shape_tags에 <hp:equation> 추가 + equation arm에 shape_break!/카운트.
  다른 개체 arm은 run당 12개에서 run을 가르는데(한글 ~21개 한도, 실기 확정) 수식만
  빠져 있었다. 한글 한도가 수식까지 포함하는지는 미확정이나, 과다 계상은 run이 더
  일찍 갈라질 뿐이고 과소 계상은 개체 유실이라 안전한 쪽을 택한다.
- tools/gen_effects_cases.py L1_수식: JSON IR에 eqed 컨트롤을 심어 hwpx로 방출하고
  재읽기로 스크립트 원문(특수문자 포함) 3종을 단언 + validate. 실기 파일 생성 경로가
  이제 재현 가능하다.
- 실기검증-체크리스트에 L 시리즈 추가 — 정답지 미확보 상태라 판정 기준을 "열리는가"가
  아니라 "수식이 조판돼 보이는가"로 두고, 증상별로 어느 속성이 틀렸는지 회수한다.
한컴 공식 OWPML 모델(hancom-io/hwpx-owpml-model) 대조로 확인한 결함들.

1. lineMode="0"은 스키마 밖 값이었다. enumdef.h g_EquationLineList는 LINE|CHAR
   열거이고 기본값은 CHAR다(EquationType.cpp). 합성 경로를 CHAR로 고쳤다.
2. hwpx 출신 수식의 비기본 속성이 왕복에서 전부 기본값으로 재작성됐다(zOrder·
   textWrap·lock·baseLine·글자색·baseUnit·수식 글꼴·PAGE 기준 배치·outMargin).
   Equation에 raw_attrs·raw_props를 두고 원문 pass-through(secpr_raw_children과
   같은 규약). writer는 원문이 있으면 그대로 되쓰고 없으면 표준값을 합성한다.
3. hwp5 출신 부유 수식의 배치가 PARA/PARA·zOrder 0 상수로 뭉개졌다. eqed의 data는
   gso 공통 헤더라 parse_gso_header + gso_pos_xml을 그대로 쓴다(그림 GE-9와 동형).
4. GE-16 attr() 엔티티 미해석 → 이중 이스케이프. 책갈피·필드·스타일 이름과 수식
   script 속성 전부에 영향. 읽는 즉시 unescape(해석 불가 참조는 원문 유지).
5. GE-17 esc()가 XML 비문자(U+FFFE·U+FFFF)를 흘려 well-formed하지 않은 패키지를
   만들 수 있었다. C0와 동일하게 제거.

테스트 3종 추가: 수식_속성_원문_보존(비기본 속성 11종 + 합성 lineMode),
수식_hwp5출신_배치_복원(gso 헤더 → PAGE/오프셋/z-order), 속성값_엔티티_해석,
esc_금지문자_제거. 게이트 통과(fmt/clippy/test), L1 실기 케이스 재생성 확인.
@entelecheia

Copy link
Copy Markdown
Member Author

적대적 점검(codex) 결과 반영

codex exec 로 반증 중심 리뷰를 돌렸다. 한컴 공식 OWPML 모델 소스(hancom-io/hwpx-owpml-model)를 근거로 실제 결함 4건이 나왔고 전부 수정했다(원문 링크 확인 완료).

# 지적 조치
1 lineMode="0" 은 스키마 밖 값 — enumdef.h g_EquationLineListLINE|CHAR 열거, 기본 CHAR 합성 경로를 CHAR 로 수정
2 hwpx 출신 수식의 비기본 속성이 왕복에서 기본값으로 재작성(zOrder·textWrap·lock·baseLine·글자색·baseUnit·수식 글꼴·PAGE 기준 배치·outMargin) Equation::raw_attrs·raw_props 원문 pass-through (secpr_raw_children 과 같은 규약)
3 hwp5 출신 부유 수식 배치가 PARA/PARA·zOrder=0 상수로 뭉개짐 eqeddata 는 gso 공통 헤더 → parse_gso_header + gso_pos_xml 재사용(그림 GE-9와 동형)
4 attr() 이 속성값 엔티티를 안 풀어 esc() 와 이중 이스케이프(책갈피·필드·스타일 이름 전부 영향) 읽는 즉시 unescape, 해석 불가 참조는 원문 유지 (GE-16)
5 esc() 가 XML 비문자(U+FFFE·U+FFFF)를 흘려 well-formed 하지 않은 패키지 생성 가능 C0와 동일하게 제거 (GE-17)
6 수식이 run당 개체 한도 카운트에서 제외(자체 발견과 동일) count_shape_tags<hp:equation> 추가 + shape_break!()

반증 실패(= 안 깨짐)로 판정된 것: arm 순서·가드에 hwp5 회귀 입력 없음, ExtCtrl wchar 폭 정합, 음수 pos 오프셋 대칭, ]]>·apostrophe·CDATA 분할 처리.

미해결로 남긴 것(문서화): hp:scripttrim()(앞뒤 공백 비보존 — 조판 영향 없음), 합성 경로의 수식 전용 상수 정답지 대조.

검증

  • scripts/check.sh 통과, 테스트 4종 추가(수식_속성_원문_보존·수식_hwp5출신_배치_복원·속성값_엔티티_해석·esc_금지문자_제거).
  • 실기 케이스 상설화: tools/gen_effects_cases.pyL1_수식 추가(JSON IR → hwpx → 재읽기 단언 + validate), 체크리스트 §L 등재. 정답지 미확보 상태라 판정 기준을 "열리는가"가 아니라 "수식이 조판돼 보이는가"로 뒀다.

@entelecheia
entelecheia merged commit 486a78e into main Jul 26, 2026
3 checks passed
@entelecheia
entelecheia deleted the feat/hwpx-equation-write branch July 26, 2026 19:55
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