Skip to content

fix: HWP5 multi-TAC paragraph z-order 정합 — composer marker synthesis (closes #991)#995

Closed
jangster77 wants to merge 4 commits into
edwardkim:develfrom
jangster77:local/task991-fix
Closed

fix: HWP5 multi-TAC paragraph z-order 정합 — composer marker synthesis (closes #991)#995
jangster77 wants to merge 4 commits into
edwardkim:develfrom
jangster77:local/task991-fix

Conversation

@jangster77

Copy link
Copy Markdown
Collaborator

Summary

Root cause (KS X 6101 표 6 분석)

확장 컨트롤 (ch=1-3, 11-12, 14-18, 21-23) 은 stream 에 8 wchar 차지, visual 로 1 char position (placeholder).

HWP3 parser HWP5 parser
text 마커 push `\u{FFFC}` 1개 / 컨트롤 마커 미푸시
char_offsets entry 마커마다 1 entry 컨트롤 skip

→ HWP5 가 sparse text + char_offsets 으로 composer 가 line 별 text 범위 계산 시 어긋남.

pi=394 (HWP5 sample16) 예

```
3 TAC controls + 3 line_segs:
ls[0] ts=0 contains 가. 라벨
ls[1] ts=8 contains 다이어그램
ls[2] ts=18 contains 나. 라벨

HWP5 IR (sparse):
text=" " (2 chars), char_offsets=[16, 17]
composer.utf16_range_to_text_range(0, 8): text [0..0) — 빈!
→ ls[0] 빈 라인 처리 → 가. 라벨 어긋남
```

Fix (F2-narrow)

src/renderer/composer.rs 의 `compose_paragraph` 진입 시 marker synthesis layer 추가:

```rust
fn synthesize_marker_paragraph(para: &Paragraph) -> Option {
// inline-visible extended ctrl count
let inline_ctrl_count = ...; // Header/Footer/Footnote/Endnote/HiddenComment 제외

let existing_markers = para.text.chars().filter(|c| *c == '\\u{FFFC}').count();
if existing_markers >= inline_ctrl_count { return None; }  // HWP3 path

// [Task #991 좁힘] pi=394 패턴만 catch
let n_leading = (para.char_offsets.first().copied().unwrap_or(0) as usize) / 8;
if n_leading < 2 || inline_ctrl_count < 3 { return None; }

// char_offsets gap (8 wchar) 분석으로 \\u{FFFC} 마커 합성

}

pub fn compose_paragraph(para: &Paragraph) -> ComposedParagraph {
let synth = synthesize_marker_paragraph(para);
let para = synth.as_ref().unwrap_or(para); // shadow
// ... 기존 logic ...
}
```

좁힘 조건

  • `inline_ctrl_count >= 3` (pi=394 = 3 TAC pattern)
  • `n_leading >= 2` (leading char_offsets gap 에 2+ extended ctrl)
  • `existing_markers >= inline_ctrl_count` (HWP3 자동 차단)

Verification

  • ✅ cargo test --release --lib: 1297 passed, 0 failed
  • ✅ cargo fmt --check: 통과
  • ✅ 240 sample 페이지 수 회귀: 0 건
  • ✅ HWP5 sample16 page 18 시각: PDF 정합 (가. 위, 다이어그램, 나. 아래)
  • ✅ Editor pipeline (parser 미변경): 영향 없음

시도 비교

시도 cargo test fail
F1 (parser 광범위) 9 (editor 영향)
F1-narrow (ch=11/14) 5
F2-wide (composer synth) 5
F2-narrow (본 PR) 0

Residual / 후속

Test plan

  • cargo test --release 통과 확인
  • cargo fmt --check 통과 확인
  • samples/hwp3-sample16-hwp5.hwp page 18 시각 정합 확인 (가. 위, 다이어그램, 나. 아래)
  • 240 sample 페이지 수 변동 0 확인
  • Editor 기능 (insert_text/save/cursor) 동작 확인

🤖 Generated with Claude Code

jangster77 and others added 3 commits May 18, 2026 19:25
…C paragraph z-order 정합

KS X 6101 / HWP5 spec 표 6 분석 결과:
- 확장 컨트롤 (ch=1-3, 11-12, 14-18, 21-23) 은 stream 에 8 wchar,
  visual 로 1 char position (placeholder).
- HWP3 parser 는 \u{FFFC} 마커 push (spec 정합).
- HWP5 parser 는 마커 미푸시 → composer 의 line-to-control 매핑 어긋남.

증상 (samples/hwp3-sample16-hwp5.hwp 페이지 18):
- pi=394 의 3 TAC controls (가. 라벨, 다이어그램, 나. 라벨) 가
  ls[0]/[1]/[2] 에 분포되어야 함.
- char_offsets=[16,17] (sparse) 로 composer 가 ls[0] 빈 라인 처리.
- 결과: 가. 라벨이 다이어그램 아래 (PDF 정답: 위).

Fix (F2-narrow):
src/renderer/composer.rs 의 compose_paragraph 진입 시 marker synthesis.
좁힘 조건:
  - inline_ctrl_count >= 3 (pi=394 패턴)
  - n_leading >= 2 (leading char_offsets gap 에 2+ extended ctrl)
  - existing_markers < inline_ctrl_count (HWP3 자동 차단)

영향 범위:
- Composer 내부만 (rendering pipeline)
- Editor pipeline (insert_text/save/cursor) 영향 없음 (parser 미변경)

검증:
- cargo test --release --lib: 1297 passed, 0 failed
- 240 sample 페이지 수: 변동 0 건
- HWP5 sample16 p18 시각: PDF 정합 확인 (가. 위, 나. 아래)
- HWP5 page count: 62 (변동 없음)

이전 시도 vs 본 fix:
- F1 (parser 광범위): 9 test fail (editor 영향)
- F1-narrow (ch=11/14): 5 test fail
- F2-wide (composer synth, 조건 없음): 5 test fail
- F2-narrow (3+ TAC + 2+ leading): 0 test fail ✓

잔존:
- HWPX 변종 (sample16-hwp5.hwpx) 은 parser path 다름 — 본 fix 미적용.
  edwardkim#942/edwardkim#988 close 영역 (HWPX 포맷 fundamental 한계).

closes edwardkim#991

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
하이퍼-워터폴 절차 보완 — 직전 commit (ad1f20d) 에서 누락한 문서 추가:

- mydocs/plans/task_m100_991_impl.md (구현 계획서)
- mydocs/working/task_m100_991_stage2.md (Stage 2: F2 후보 결정)
- mydocs/working/task_m100_991_stage3.md (Stage 3: 회귀 검증)
- mydocs/working/task_m100_991_stage4.md (Stage 4: 시각 검증)

소스 변경 없음 (docs 만).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@edwardkim

Copy link
Copy Markdown
Owner

검토 완료 — merge 수용. devel 에 반영했습니다.

검증 결과

  • CI 전체 pass (Build & Test / Analyze / Canvas visual diff / CodeQL)
  • cargo test --release --lib: 1303 passed, 0 failed (회귀 0)
  • cargo fmt --check 통과
  • 240 sample 페이지 수 회귀 0
  • sample16-hwp5 p18 시각 정합 확인 (가. 위, 다이어그램, 나. 아래)

처리

  • PR 본질 commit 2개 author 보존 cherry-pick (857b0007, fe5066d5)
  • 검토에서 지적한 코드 clean-up 2건은 후속 commit 으로 분리 정정 (b39b59b1):
    • synthesize_marker_paragraphfirst_off/n_leading 중복 계산 제거
    • 좁힘 가드(n_leading < 2)로 도달 불가했던 빈 paragraph 분기 제거
    • 동작 불변 (좁힘 3조건·합성 로직 그대로), 테스트 통과 수 동일로 확인

F2-narrow 의 3중 좁힘 가드 설계가 적절했습니다 (F1/F2-wide 5~9 fail → F2-narrow 0 fail). composer-only 격리로 editor/HWP3/HWPX path 무영향인 점도 확인했습니다.

이슈 #991 은 이미 CLOSED 상태이므로 closes #991 은 no-op 입니다 (reopen 하지 않음). 잔존 영역(HWPX 변종 #942/#988, sample16 p22 #994)은 PR 에 명시된 분리 그대로 둡니다.

기여 감사합니다.

@edwardkim

Copy link
Copy Markdown
Owner

devel 반영 완료 (cherry-pick). PR close 합니다.

@edwardkim edwardkim closed this May 19, 2026
edwardkim pushed a commit that referenced this pull request May 19, 2026
PR #995 검토 §3.3 지적 2건 정정. 동작 완전 불변 (좁힘 3조건·합성
로직 그대로), 중복/죽은 코드만 제거.

- (a) first_off/n_leading 중복 계산 제거: offsets 바인딩을 좁힘
  가드 앞으로 이동 → 1회 계산 후 본문 재사용 (shadowing 제거)
- (b) 빈 paragraph 분기 제거: 좁힘 가드 n_leading<2 가 빈 offsets
  (n_leading=0) 를 항상 차단 → 도달 불가 죽은 코드. 근거 주석 명시

검증: cargo fmt --check ✅ / cargo test --release --lib 1303 passed
0 failed ✅ (clean-up 전후 통과 동일 → 동작 불변 확인)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim pushed a commit that referenced this pull request May 19, 2026
HWP5 multi-TAC paragraph z-order 정합 (composer marker synthesis).
검증 게이트 전부 통과 + 작업지시자 시각 판정 통과 → merge 수용.
이슈 #991 은 이미 CLOSED 확정 (closes #991 no-op, reopen 안 함).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim pushed a commit that referenced this pull request May 19, 2026
- PR #995: HWP5 multi-TAC paragraph z-order 정합 (composer marker
  synthesis, F2-narrow). 작업지시자 시각 판정 통과. 이슈 #991 은
  이미 CLOSED (closes #991 no-op).
- composer.rs clean-up (검토 §3.3 a/b, 동작 불변)
- 세션 메모리 ↔ 백업 메모리 동기화 (51개 정합)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim pushed a commit that referenced this pull request May 19, 2026
다른 환경의 devel 작업 통합:
- Task #993: HWPX→HWP cell contracts + top-aligned cell line positioning
- fix(fmt): task 993 diagnostics 포맷 정정 (CI Format check 복구)
- fix(clippy): nested format 회피
- test: issue 617 SVG snapshot 갱신

로컬 PR #995 (HWP5 multi-TAC z-order) + 메모리 동기화 분기와 통합.

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