Skip to content

fix: WMF SetTextAlign vertical bits 파싱 정정 + baseline y shift 정합 — WMF 박스 외부 텍스트 표시 해소 (closes #965, ports PR #918 Stage 33-A)#966

Closed
jangster77 wants to merge 5 commits into
edwardkim:develfrom
jangster77:local/task965

Conversation

@jangster77

Copy link
Copy Markdown
Collaborator

요약

samples/hwp3-sample16.hwp page 18 의 WMF 다이어그램 (주전산센터 목표시스템 구성안) 내부 박스의 한글 텍스트 ("PE6450", "기록서버", "Windows 서버군" 등) 가 박스 외부로 벗어남.

PR #918 (closed, 5082 additions) 의 Stage 33-A 핵심 height/baseline 보정 로직만 단독 포팅.

Root cause

`src/wmf/converter/svg/mod.rs:2191-2197` 의 SetTextAlign vertical bits 파싱:

let align_vertical = [VTA_BOTTOM, VTA_TOP /* =0x0000 */]
    .into_iter()
    .find(|a| record.text_alignment_mode & (*a as u16) == *a as u16)
    .unwrap_or(VTA_BASELINE);

`mode & VTA_TOP(=0x0000) == 0x0000` 가 항상 true → BASELINE/BOTTOM 인 mode 도 VTA_TOP 으로 잘못 매핑 → `ext_text_out` 에서 +ascent shift → baseline 이 cell-top 보정만큼 아래로 → 박스 하단 라인 걸침.

WMF spec [MS-WMF] 2.1.2.18:

  • TA_TOP = 0x0000 (default)
  • TA_BOTTOM = 0x0008
  • TA_BASELINE = 0x0018

Fix

`src/wmf/converter/svg/mod.rs` 3 영역 (~60 lines):

  1. `set_text_align` (~2186-2208) — vertical bits (0x0018 mask) 값 기준 BASELINE → BOTTOM → TOP 분기
  2. `ext_text_out` (~813-833) — baseline y shift 정합 (TOP=+ascent, BOTTOM=-descent, BASELINE=0)
  3. `text_out` (~1541-1556) — META_TEXTOUT 동일 보정 (PR Task #902: WMF renderer 근본 개선 + LO emfio 포팅 + WASM RasterPlayer (v2) #918 미포함, 본 PR 추가)

PR #918 와의 차이

PR #918 (closed, 5082+) 본 PR (~60)
LibreOffice emfio 포팅 ❌ 제외
WASM RasterPlayer ❌ 제외
nested SVG inline embed ❌ 제외
woff2 base64 임베드 제거 ❌ 제외
DX byte-aware indexing ❌ 제외
POLYPOLYGON fill-rule ❌ 제외
Stage 33-A set_text_align + ext_text_out ✅ 포팅
text_out baseline (PR #918 미포함) 본 PR 추가

PR #918 close 사유 (다양한 부작용) 회피 + root cause fix 만 도입.

검증

  • cargo test --release --lib: 1288 passed, 0 failed
  • sample16 page 18 WMF 박스 내부 한글 텍스트 정상 위치 ✓ 한컴 viewer 정합
  • WMF sample (sample14 page 0~8, sample4 page 1) PNG diff <1% (정상화 방향, 회귀 없음)

영향

영역 영향
WMF BASELINE/BOTTOM 모드 텍스트 정상 위치 (회귀 fix)
WMF TOP 모드 텍스트 기존 동작 (영향 없음)
비-WMF 영향 없음 (svg/mod.rs WMF converter 만 변경)
WASM 환경 정합 개선 예상 (Canvas2D 가 동일 SVG 사용)

Test plan

  • cargo test --release --lib 통과
  • sample16 page 18 WMF 한컴 viewer 정합
  • WMF 보유 sample 회귀 없음 (sample14 page 0~8, sample4 page 1)

🤖 Generated with Claude Code

jangster77 and others added 5 commits May 17, 2026 23:28
… 박스 외부 텍스트 표시 해소 (closes edwardkim#965, ports PR edwardkim#918 Stage 33-A)

## 본질

`samples/hwp3-sample16.hwp` page 18 (한컴 page 16) 의 WMF 다이어그램 (주전산센터 목표시스템 구성안) 내부 박스의 한글 텍스트 ("PE6450", "기록서버", "Windows 서버군", "Unix 서버군" 등) 가 박스 외부로 벗어남.

## Root cause

`src/wmf/converter/svg/mod.rs:2191-2197` 의 SetTextAlign vertical bits 파싱:

```rust
let align_vertical = [
    VerticalTextAlignmentMode::VTA_BOTTOM,
    VerticalTextAlignmentMode::VTA_TOP,    // VTA_TOP = 0x0000
]
.into_iter()
.find(|a| record.text_alignment_mode & (*a as u16) == *a as u16)
.unwrap_or(VerticalTextAlignmentMode::VTA_BASELINE);
```

`mode & VTA_TOP(=0x0000) == 0x0000` 가 **항상 true** → BASELINE/BOTTOM 인 mode 도 VTA_TOP 으로 잘못 매핑 → `ext_text_out` 에서 +ascent (~em × 0.8) shift → baseline 이 cell-top 보정만큼 아래로 → 박스 하단 라인 걸침.

WMF spec [MS-WMF] 2.1.2.18:
- TA_TOP = 0x0000 (default)
- TA_BOTTOM = 0x0008
- TA_BASELINE = 0x0018

## Fix

`src/wmf/converter/svg/mod.rs` 3 영역 (~60 lines):

### set_text_align (~2186-2208)
vertical bits (0x0018 mask) 값 기준 BASELINE → BOTTOM → TOP 우선순위 분기.

```rust
let v_bits = record.text_alignment_mode & 0x0018;
let align_vertical = if v_bits == 0x0018 {
    VerticalTextAlignmentMode::VTA_BASELINE
} else if v_bits == 0x0008 {
    VerticalTextAlignmentMode::VTA_BOTTOM
} else {
    VerticalTextAlignmentMode::VTA_TOP
};
```

### ext_text_out (~813-833) baseline y shift 정합

```rust
match self.context_current.text_align_vertical {
    VerticalTextAlignmentMode::VTA_TOP => +ascent (em × 0.8)
    VerticalTextAlignmentMode::VTA_BOTTOM => -descent (em × 0.2)
    VerticalTextAlignmentMode::VTA_BASELINE => 0
    _ => 0,
}
```

기존 `font.height < 0 => -font.height` 잘못된 보정 제거.

### text_out (~1541-1556) — PR edwardkim#918 미포함, 본 task 추가
META_TEXTOUT 동일 baseline 보정 (ext_text_out 와 일관성).

## PR edwardkim#918 와의 차이

PR edwardkim#918 (closed, 5082 additions) 의 Stage 33-A 핵심 height/baseline 보정만 단독 포팅. 제외:
- LibreOffice emfio 포팅, WASM RasterPlayer, nested SVG inline embed, woff2 base64 임베드 제거, DX byte-aware indexing, POLYPOLYGON fill-rule

PR edwardkim#918 close 사유 (다양한 부작용) 회피 + root cause fix 만 도입. + text_out baseline 추가.

## 검증

- cargo test --release --lib: 1288 passed, 0 failed
- sample16 page 18 WMF 박스 내부 한글 텍스트 정상 위치 ✓ 한컴 viewer 정합
- WMF sample (sample14 page 0~8, sample4 page 1) PNG diff <1% (정상화 방향, 회귀 없음)

## 영향

| 영역 | 영향 |
|------|------|
| WMF BASELINE/BOTTOM 모드 텍스트 | 정상 위치 (회귀 fix) |
| WMF TOP 모드 텍스트 | 기존 동작 (영향 없음) |
| 비-WMF | 영향 없음 |
| WASM 환경 | 정합 개선 예상 (Canvas2D 가 동일 SVG 사용) |

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 17, 2026
…gn vertical bits 파싱 정정

@jangster77 — Issue #965: sample16 page 18 WMF 다이어그램 박스 내부 한글 텍스트
박스 외부 벗어남.

Root cause (src/wmf/converter/svg/mod.rs:2208 set_text_align): mode & VTA_TOP(=0x0000)
== 0x0000 항상 true → BASELINE/BOTTOM mode 도 VTA_TOP 오매핑 → +ascent shift →
baseline 박스 하단 걸침. WMF [MS-WMF] 2.1.2.18 spec 정합.

본질 (svg/mod.rs 3 영역 ~60 lines):
- set_text_align (:2208): v_bits = mode & 0x0018 마스킹 + 우선순위 BASELINE→BOTTOM→TOP
- ext_text_out (:811): baseline y shift 정합 (VTA_BASELINE=0, BOTTOM=-em*0.2, TOP=+ascent)
  — font.height < 0 잘못된 보정 제거
- text_out (:1545): META_TEXTOUT 동일 보정 (PR #918 미포함, 본 PR 추가)

PR #918 supersede: PR #918 (CLOSED 5/16, +5082/-74 거대 PR, 다양한 부작용
LibreOffice emfio/WASM RasterPlayer 등) → Stage 33-A root cause ~60 lines 만
단독 포팅. feedback_pr_supersede_chain (a) + feedback_small_batch_release_strategy
권위 사례.

본 환경 충돌 수동 해결: orders/20260517.md (--ours + Task #965 작업 일지 갱신).
svg/mod.rs auto-merge — devel PR #860/#864 (5/16, EMF/WMF image 렌더) 변경
보존 + PR #966 정정 양립 확인.

자기 검증: cargo test --release --lib 1288 passed / clippy 통과 /
광범위 sweep 7 fixture / 169 페이지 / 169 same / 0 diff / WASM 4.4 MB 재빌드
시각 판정: 작업지시자 시각 검증 통과 (sample16 p18 WMF 박스 한글 텍스트 정상
+ WMF sample14/4 + devel PR #860/#864 EMF/WMF 회귀 부재)
CI: ✅ Build & Test + CodeQL

@jangster77 연속 5 PR (#956~#964) 완결 후 추가 #966 (#968 후속)
@edwardkim

Copy link
Copy Markdown
Owner

@jangster77 머지 완료 (commit 235e049c).

mode & VTA_TOP(=0x0000) == 0x0000 항상-true 버그 root cause 정확 진단 + WMF [MS-WMF] 2.1.2.18 spec 정합 (v_bits 0x0018 mask). PR #918 (CLOSED, +5082/-74 거대 PR, 다양한 부작용) 의 Stage 33-A root cause ~60 lines 만 단독 포팅 — feedback_pr_supersede_chain (a) + feedback_small_batch_release_strategy 권위 사례. text_out (META_TEXTOUT) baseline 보정 영역 영역 PR #918 미포함분 추가 정합.

본 환경 자기 검증:

본 환경 svg/mod.rs auto-merge — devel PR #860/#864 (EMF/WMF image 렌더) 변경 보존 + PR #966 정정 양립 확인.

수고하셨습니다. 추가 PR #968 이어서 진행하겠습니다.

@edwardkim edwardkim closed this May 17, 2026
edwardkim added a commit that referenced this pull request May 17, 2026
- mydocs/pr/archives/pr_966_review.md (WMF SetTextAlign vertical bits 분석)
- mydocs/pr/archives/pr_966_report.md (옵션 A + PR #918 supersede)
- mydocs/orders/20260517.md PR #966 행 추가

핵심:
- mode & VTA_TOP(=0) == 0 항상-true 버그 root cause
- WMF [MS-WMF] 2.1.2.18 spec 정합 (v_bits 0x0018 mask)
- PR #918 (CLOSED, +5082 거대 PR) → root cause ~60 lines 단독 포팅
- svg/mod.rs auto-merge (devel PR #860/#864 EMF/WMF image 렌더 보존)
- sweep 169/169 same + 작업지시자 시각 판정 통과
- Issue #965 close, 추가 PR #968 후속
edwardkim added a commit that referenced this pull request May 17, 2026
@jangster77 — Issue #967: HWP3 sample18 페이지 수 rhwp 69 vs 한컴 67 (+2 inflate).
빈 paragraph (pi=27/164) 직후 [쪽나누기] (pi=28/165) → 빈 paragraph 별도
page 분기 → 단독 빈 페이지.

Root cause (typeset.rs:555-584): next_force_break (쪽나누기) 시
next_will_vpos_reset 가드 미발동 (hwp-multi-001 회귀 차단 목적) → 빈 paragraph
단독 page 생성.

본질 (src/renderer/typeset.rs:585 별도 분기):
next_force_break && is_curr_empty && empty_h_px > avail (overflow) 시에만
continue (skip → 단독 page 차단). fit 가능 시 정상 emit.

v2 정밀화: v1 (조건 무관 skip) → aift.hwp snapshot 회귀 → v2 (overflow 한정)
→ aift.hwp 18 case 정상 emit + sample18 fix. feedback_hancom_compat_specific_over_general
권위 사례 (일반화 위험 발견 후 케이스별 정밀화).

영역 좁힘: 빈 paragraph + 쪽나누기 + overflow 한정 — fit 가능/비-빈/일반
paragraph/hwp-multi-001 영향 없음.

본 환경 cherry-pick 충돌 0건 — typeset.rs auto-merge (devel Task #836 Endnote
:1064/1091 보존 + PR #968 :585 분기 양립). orders/20260518.md 신규 (5/18).
devel merge commit 2개 cherry-pick 제외 — 본질 3c1ea87 만.

자기 검증: cargo test --release 전체 (lib 1288 + integration svg_snapshot 8) ALL GREEN
+ cargo clippy --release -D warnings 통과 + 광범위 sweep 7 fixture / 169 페이지 /
169 same / 0 diff (aift.hwp 74 same — v1 회귀 해소 입증) + WASM 4.4 MB 재빌드.

⚠️ samples/hwp3-sample18.hwp 영역 영역 PR 미포함 + 본 환경 부재 — 작업지시자 결정
영역 영역 sweep 169/169 same + cargo test 전체 통과 영역 영역 회귀 부재 입증 +
sample18 페이지 수 69→67 정합 영역 영역 컨트리뷰터 PR 본문 신뢰 (작업지시자 승인).

CI: ✅ Build & Test + CodeQL + Canvas visual diff

@jangster77 PR 시리즈 완결 (연속 5 PR #956~#964 + #966 + #968)
edwardkim added a commit that referenced this pull request May 17, 2026
버전 0.7.11 → 0.7.12 (Cargo.toml + rhwp-vscode/npm-editor/rhwp-studio package.json)
CHANGELOG 갱신 (CHANGELOG.md / CHANGELOG_EN.md / rhwp-vscode/CHANGELOG.md)
WASM 재빌드 산출물 동기화 (rhwp-studio/public/rhwp.js)

@jangster77 7-PR 시리즈 (#956~#968) + Issue #952 5-결함 완결 + WMF #966 + HWP3 #968 + LTO #818

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edwardkim edwardkim mentioned this pull request May 17, 2026
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