Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions mydocs/plans/task_m100_702.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Task #702: shortcut.hwp 다단 정의 후속 갱신 누락 — 수행계획서

## 결함 개요

`samples/basic/shortcut.hwp` (한글 2010 단축키 일람표, A4 가로) 의 rhwp SVG 출력이 한글 2022 편집기 PDF 정답지(`pdf/basic/shortcut-2022.pdf`, 7쪽) 와 시각 정합 결함.

- **rhwp 출력**: 10쪽 (+43% 페이지 수 증가)
- **정답지 (한글 2022 PDF)**: 7쪽

## 진단 결과

### 본질 1 — 다단 정의(`SectionColumnDef`) 후속 갱신 누락

문서가 `다단나누기 + 단정의:1단` (1열 헤더) ↔ `다단나누기 + 단정의:2단 배분` (2열 본문) 패턴으로 약 15회 alternating. **첫 번째 사이클은 정상, 두 번째 사이클부터 단정의 갱신이 동작하지 않음**.

검증 사례 (`rhwp dump-pages` 페이지 1):
- 단 0(제목 1단) → 단 1(`커서 이동` 헤더) → 단 2+3(2단 배분 14+13 정상) → 단 4(`지우기` 헤더) → **단 5 (items=6, 1단으로 적층)** ← PDF는 2단 3+3 분배 기대

### 본질 2 — zone height 측정 결함

`dump-pages` 거의 모든 zone에서 `hwp_used > used` (e.g. 페이지 2 단 0 used=27.3 hwp_used=47.1 diff=−19.8px). 컬럼 `유형=배분` 균등분배 기준이 짧게 측정되어 컬럼 균형 / 페이지 분기 누적 오류.

### 페이지별 이탈

- **p2**: SVG는 `파일` 헤더 + 5항목만. PDF는 `파일` 2단 5+5 + `<미리보기>` 2단 5+5 + `편집` 2단 14+12 모두 동일 페이지 (컨텐츠 90%+ 손실)
- **p7**: SVG는 폰트 단축키. PDF는 `표` 후반 + `기타` + `<그림 그리기에서>` (페이지 인덱스 완전 어긋남)
- **p8**: `LAYOUT_OVERFLOW` 다수 (40~60px) 발생

## 부수 시각 결함 (본 사이클 범위 외)

본 사이클은 본질 1/2 정정에 한정. 부수 결함은 별도 이슈로 분리:

- 제목 "한글 2010" → "ㅎ글2010" 표시 (PUA 글리프 `\u{f53a}` + char_shape 적용 정합 결함)
- 탭 leader (점선 가이드) 미렌더링
- 바탕쪽 글상자 자동번호(Page) 데코 (각 쪽 우하단 회색 1~7) 미렌더

## 환경

- 브랜치: `local/task702` (`upstream/devel @ 2fe386c4` 기준 분기)
- 정답 PDF: 한컴 2022 12.0.0.4426 출력 (A4 landscape 841×595 pts, 7쪽)
- 작업 디렉터리: `/Users/planet/rhwp`

## 단계 구성 (4단계)

### Stage 1 — 본질 진단 (read-only)

- **본질 1 코드 위치 식별**: `src/parser/hwp5/` 의 `SectionColumnDef` (단정의) 파싱, `src/renderer/layout.rs` 의 column zone 전환 로직 추적. "첫 번째 단정의는 동작, 두 번째 이후 무시" 의 실제 코드 위치 식별.
- **본질 2 코드 위치 식별**: `dump-pages` 의 `hwp_used` 와 `used` 산출 경로. ParaShape spacing/line spacing 적용 대비 hwp_used 정의 (왜 hwp_used 가 더 큰가) 확인.
- 산출: 코드 위치 + 결함 가설 + 구현 계획서 (`task_m100_702_impl.md`).

### Stage 2 — 본질 1 (다단 정의 후속 갱신) 정정

- column def 갱신 로직 정정. 회귀 가드 (단일 다단 케이스 미회귀).
- 검증: `dump-pages` 로 페이지 1 단 5가 2단으로 분할되는지, 페이지 수가 7쪽에 근접하는지.
- 산출: `task_m100_702_stage1.md` (단계별 보고서).

### Stage 3 — 본질 2 (zone height 측정) 정정

- `hwp_used` 기준 정합. 컬럼 균등분배 시 PDF 와 동일하게 균형 잡히는지.
- 검증: 페이지 2 가 PDF 와 동일하게 `파일` + `<미리보기>` + `편집` 3섹션 모두 채워지는지.
- 산출: `task_m100_702_stage2.md`.

### Stage 4 — 광범위 회귀 + 시각 판정

- `cargo test` 통과 + 다른 샘플 (KTX, aift, hwp-multi, kps-ai, hwpx/aift) export-svg 회귀 점검.
- shortcut-2022.pdf 7쪽 vs SVG 7쪽 시각 정합 (`qlmanage` 비교 이미지) 보고.
- 부수 결함 (탭 leader / PUA 글자 / 바탕쪽 자동번호) 은 별도 이슈 후보로 식별.
- 산출: `task_m100_702_stage3.md` (광범위 검증) + `task_m100_702_report.md` (최종 보고서).

## 회귀 위험

다단 정의 본질 정정은 메모리 룰 "essential_fix_regression_risk" 에 해당. 다단/단일/표분할 상호작용 회귀 가능성 있음. Stage 4 에서 광범위 샘플 + 한컴 2010/2020 정답지 비교 필수.

## 승인 요청

본 수행계획대로 Stage 1 (read-only 진단 + 구현계획서 작성) 진입 가능 여부 승인 부탁드립니다.
166 changes: 166 additions & 0 deletions mydocs/plans/task_m100_702_impl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Task #702: 구현 계획서

## Stage 1 진단 결과 요약

### 본질 1 정정 (수행계획서 본질 1 재진단)

**최초 가설 (수행계획서)**: `SectionColumnDef` 후속 갱신 누락 — 첫 번째 단정의는 동작, 두 번째 이후 무시.

**실제 원인 (Stage 1 진단)**: 단정의 갱신은 정상 동작. 결함은 **inter-paragraph vpos-reset 검출 임계값**이 보수적이라 짧은 컬럼에서 column-advance 가 발동하지 않는 것.

`src/renderer/typeset.rs:430-434`:
```rust
let trigger = if st.col_count > 1 {
cv < pv && pv > 5000 // ← 다단 케이스 임계값
} else {
cv == 0 && pv > 5000 // ← 단단 케이스 임계값
};
```

**문제 케이스 (지우기 섹션, 6항목 2단 배분)**:
- pi=32 ("한 단어 지우기") last vpos = 3000 (< 5000 임계값)
- pi=33 ("앞 단어 지우기") first vpos = 0
- → 임계값 미달로 trigger=false → column-advance 미발동 → pi=33~35 가 col 0 에 적층

PDF 정답: 3+3 (col 0: pi=30~32, col 1: pi=33~35)
rhwp 출력: 6+0 (col 0 에 모두 적층)

**왜 5000 이 도입됐는가**:
- 단단(`col_count==1`): partial-table split 의 LAYOUT 잔재로 vpos가 high→low 변동 발생 (Issue #418, hwpspec pi=78→pi=79). 이걸 column-break 로 오인 방지.
- 다단(`col_count>1`): Task #470 에서 `cv != 0` 도 column-break 인정하되 동일 임계값 유지 (안전 보수).

### 본질 2 진단 — 시각 영향 미미

`src/renderer/typeset.rs:1023`:
```rust
st.current_height += if st.col_count > 1 { fmt.height_for_fit } else { fmt.total_height };
```

다단 누적은 `height_for_fit` (trailing_ls 제외) 사용. Task #359 + exam_eng 8p 정합으로 도입.

`hwp_used > used` 차이 (예: 단 2 used=186.7 vs hwp_used=273.3)는 trailing_ls 누적 분만큼 발생. 그러나 **본질 1 정정 후에도 이 측정 차이가 페이지 분기에 추가 영향을 주지 않을 가능성**이 높음 (col_count>1 분기는 fit 판정도 height_for_fit 사용해 balance 유지). 따라서 **본질 2는 본 사이클 범위 외**로 분리.

## 정정 방안 (본질 1)

### 옵션 비교

| 옵션 | 변경 범위 | 회귀 위험 | 본질 정합 |
|------|----------|----------|----------|
| A. `pv > 5000` 임계값 완화 (`pv > 0`) | 1줄 | 🔴 높음 (Task #321/#418/#470 회귀 가능) | 🟡 헤유리스틱 |
| B. `MultiColumn` 직후 zone 만 임계값 완화 | 가드 추가 | 🟡 중간 | 🟡 휴리스틱 |
| C. `ColumnType::Distribute` 한정 임계값 완화 | 가드 추가 + ColumnDef 전달 | 🟢 낮음 | 🟢 본질 (스펙 정합) |

**채택: 옵션 C**

이유:
- HWPX 명세에 `BalancedNewspaper` (=ColumnType::Distribute) 는 컨텐츠 균등 분배가 본질. 짧은 컬럼이라도 vpos-reset 가 column-break 신호.
- `Normal` (`NEWSPAPER`) 단은 기존 임계값 유지 → 회귀 차단.
- 메모리 룰 "essential_fix_regression_risk" 정합 — 범위 한정.

### 구현 상세

#### 변경 1: `current_zone_layout` 에 ColumnType 보존

`src/renderer/page_layout.rs` 의 `PageLayoutInfo` 또는 별도 zone state 에 `column_type: ColumnType` 추가.

`process_multicolumn_break` (typeset.rs) 에서 새 ColumnDef 매칭 시 column_type 전파.

#### 변경 2: vpos-reset 임계값 완화 가드

`src/renderer/typeset.rs:430-434` 수정:
```rust
let column_type = st.current_zone_column_type(); // 신규 메서드
let is_distribute = matches!(column_type, ColumnType::Distribute);

let trigger = if st.col_count > 1 {
if is_distribute {
cv < pv && pv > 0 // 배분: 임계값 완화 (짧은 컬럼 허용)
} else {
cv < pv && pv > 5000 // 일반 다단: 기존 유지
}
} else {
cv == 0 && pv > 5000 // 단단: 기존 유지
};
```

#### 변경 3 (회귀 가드 테스트)

`src/renderer/pagination/tests.rs` 또는 별도 `tests/shortcut_distribute.rs`:
- 짧은 Distribute 컬럼 (3+3 분배) 정합 테스트
- Normal 다단 (긴 컬럼) 회귀 차단 테스트

## 검증 절차

### Stage 2 검증 (본질 1 정정)

1. `cargo build --release` 빌드 통과
2. `rhwp dump-pages samples/basic/shortcut.hwp` 출력에서:
- 페이지 1 단 5 가 `(items=3) + 단 6 (items=3)` 로 분할
- 페이지 수가 7쪽에 근접 (현재 10쪽 → 7쪽 목표)
3. `rhwp export-svg samples/basic/shortcut.hwp` 후 SVG 시각 정합 확인 (`qlmanage` 비교)

### Stage 3 회귀 검증

1. **Distribute 다단 정합**:
- shortcut.hwp 7쪽 ≈ PDF 7쪽
- 페이지별 컬럼 분배 정합
2. **Normal 다단 회귀 차단**:
- exam_eng 류 다단 샘플 8p 정합 유지 (Task #470)
- hwpspec 다단 컬럼 회귀 차단 (Issue #418)
3. **단단 회귀 차단**:
- kps-ai pi=317 단독 페이지 차단 유지 (Task #359)
- hwp-multi-001 force_page_break 회귀 차단
4. **광범위 샘플**:
- KTX, aift, hwp-multi-001, kps-ai, hwpx/aift export-svg 무회귀
- `cargo test` 전체 통과

### Stage 4 본질 2 영향 평가 (선택)

- 본질 1 정정 후에도 hwp_used vs used diff 가 시각 결함을 만드는지 평가
- 만들면 별도 task 로 분리, 안 만들면 보고서에만 기록

## 단계 구성

### Stage 2 — 본질 1 정정 (옵션 C)

작업 항목:
1. `PageLayoutInfo` 또는 `TypesetState` 에 `current_zone_column_type` 필드/메서드 추가
2. `process_multicolumn_break` 에서 ColumnDef 매칭 시 column_type 전파
3. `typeset.rs:430-434` vpos-reset 임계값 분기 추가 (Distribute 한정 완화)
4. 회귀 가드 테스트 작성
5. shortcut.hwp dump-pages + export-svg 시각 검증

산출: `mydocs/working/task_m100_702_stage1.md` (Stage 2 단계별 보고서 — 명명 규약상 stage1 지만 실제 구현 단계 1)

### Stage 3 — 광범위 회귀 검증 + 시각 판정

작업 항목:
1. `cargo test --release` 전체 통과
2. 광범위 샘플 export-svg 회귀 (KTX, aift, hwp-multi-001, kps-ai, hwpx/aift, exam_eng 류)
3. shortcut.hwp 7쪽 vs PDF 7쪽 페이지별 시각 정합 (qlmanage 이미지 비교)
4. 한컴 2010/2020 정답지 비교 (가능한 경우)

산출: `mydocs/working/task_m100_702_stage2.md`

### Stage 4 — 최종 보고 + 부수 결함 분리

작업 항목:
1. 본질 2 평가 + 부수 결함 (탭 leader, PUA 글자, 바탕쪽 자동번호) 별도 이슈 등록
2. 최종 결과 보고서 작성
3. 작업지시자 승인 후 commit

산출:
- `mydocs/working/task_m100_702_stage3.md` (회귀 + 시각 판정 보고)
- `mydocs/report/task_m100_702_report.md` (최종 보고서)

## 회귀 위험 평가

🔴 **High**: 옵션 C 는 ColumnType::Distribute 한정으로 범위가 좁지만, 다단 정의 type 정보 전파 경로 추가는 zone 상태 관리에 영향. PageLayoutInfo / TypesetState 변경은 광범위 영향 가능.

🟡 **Medium**: Distribute 임계값 완화로 wrap_around 표/그림 + Distribute 다단 조합에서 잠재 회귀 가능.

🟢 **Low (회귀 가드)**: Normal 다단 + 단단 분기는 기존 임계값 유지 → 핵심 회귀 차단.

## 승인 요청

본 구현계획대로 Stage 2 (본질 1 정정) 진입 가능 여부 승인 부탁드립니다.
144 changes: 144 additions & 0 deletions mydocs/report/task_m100_702_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Task #702: 최종 결과 보고서 — shortcut.hwp 다단 정의 후속 갱신 누락

## Issue / 브랜치

- GitHub Issue: [#702](https://github.com/edwardkim/rhwp/issues/702)
- 브랜치: `local/task702` (`upstream/devel @ 2fe386c4` 기준)
- 작업 기간: 2026-05-08

## 결함 개요

`samples/basic/shortcut.hwp` (한글 2010 단축키 일람표, A4 가로) 의 rhwp SVG 출력이 한글 2022 편집기 PDF (`pdf/basic/shortcut-2022.pdf`, 7쪽) 와 시각 정합 결함.

- **rhwp 출력 (수정 전)**: 10쪽 (+43% 폭주)
- **PDF 정답지**: 7쪽
- **rhwp 출력 (수정 후)**: 8쪽 (PDF +1쪽, 잔여 결함 별도 이슈로 분리)

## 본질 정정 (2건)

### 본질 1A — Distribute 다단의 짧은 컬럼 vpos-reset 검출 임계값

`src/renderer/typeset.rs:430-446` (수정 전 410-417):

기존 임계값 `pv > 5000` 은 짧은 Distribute (배분) 컬럼 (예: 지우기 3+3 분배) 에서 마지막 paragraph vpos=3000 < 5000 임계값 미달로 column-advance 미발동 → 6항목 1단 적층.

**정정**: `ColumnType::Distribute` (HWPX BalancedNewspaper) 한정 임계값 `pv > 0` 으로 완화. Normal/단단 분기는 기존 `pv > 5000` 유지 → Task #321/#418/#470 회귀 차단.

### 본질 1B — Page/Column break + 새 ColumnDef 미적용

`src/renderer/typeset.rs:396-441`:

shortcut.hwp p2 의 파일/미리보기/편집 sections 는 `[쪽나누기] + 단정의:1단 + 표(header)` → `[단나누기] + 단정의:2단 배분` 패턴 사용. 기존 코드는 `MultiColumn` break 만 ColumnDef 적용 → Page/Column break 동반 ColumnDef 무시 → col_count 가 이전 zone 값 유지 → 페이지 분기 폭주.

**정정**: Page/Column break 처리 시 새 ColumnDef 검출 후 zone 재정의 적용:
- `Column + has_diff_col_def`: `process_multicolumn_break` 호출
- `Column + 동일 ColumnDef`: 기존 `advance_column_or_new_page`
- `Page/Section + has_diff_col_def`: `force_new_page` 후 새 ColumnDef 적용 (col_count, layout, column_type 갱신)

### 보조 변경

- `TypesetState` 에 `current_zone_column_type: ColumnType` 필드 추가
- `TypesetState::new` 시그니처에 `column_type: ColumnType` 인자 추가
- `process_multicolumn_break` 에서 ColumnDef 적용 시 `current_zone_column_type` 갱신
- `typeset_section` 에서 초기 `column_def.column_type` 전달

## 변경 파일

### src/

- `src/renderer/typeset.rs`: +50 / -6
- `ColumnType` import 추가
- `TypesetState.current_zone_column_type` 필드 + 초기화
- `TypesetState::new` 시그니처 변경
- 메인 루프 `vpos-reset trigger` 분기 (Distribute 완화)
- 메인 루프 Page/Column break 핸들러 + 새 ColumnDef 적용
- `process_multicolumn_break` 에서 column_type 전파

### tests/

- `tests/issue_702.rs` (신규):
- `shortcut_distribute_short_column_split`: 페이지 수 ≤ 8 검증
- `shortcut_page2_has_three_sections`: 페이지 2 SVG 에 파일/편집 헤더 모두 존재 검증

### mydocs/

- `mydocs/plans/task_m100_702.md`: 수행계획서
- `mydocs/plans/task_m100_702_impl.md`: 구현계획서
- `mydocs/working/task_m100_702_stage2.md`: Stage 2 단계별 보고서
- `mydocs/working/task_m100_702_stage3.md`: Stage 3 단계별 보고서
- `mydocs/report/task_m100_702_report.md`: 본 최종 보고서

## 검증 결과

### `cargo test --release`

```
1248+ tests run
- 단위 테스트 (lib): 1157 passed, 0 failed, 2 ignored
- 통합 테스트 (총 18 그룹, 0 failures)
- exam_eng_multicolumn: 14 passed (Task #470 회귀 차단)
- issue_418: 1 passed (단단 partial-table split 잔재)
- svg_snapshot: 7 passed (golden snapshots 시각 회귀)
- issue_702 (신규): 2 passed
- 기타 모두 0 failures
```

### dump-pages 정합

| 항목 | 수정 전 | 수정 후 |
|------|--------|--------|
| 페이지 수 | 10 | 8 |
| LAYOUT_OVERFLOW | 다수 (40~60px) | 1건 (페이지 8 마지막) |
| 페이지 1 지우기 | 1단 6항목 | 2단 3+3 ✓ |
| 페이지 2 섹션 | 파일 header 만 | 파일+미리보기+편집 ✓ |

### 시각 검증 (qlmanage 비교)

- **페이지 1**: 제목 + 커서이동 (2단 14+13) + 지우기 (2단 3+3) ✓ PDF 정합
- **페이지 2**: 파일 (2단 5+5) + 미리보기 (2단 5+4) + 편집 (2단 12+11) ✓ PDF 정합
- **페이지 3 이후**: 컨텐츠 1쪽 시프트 (pi=94 케이스, 별도 이슈 #708)

### 광범위 샘플 회귀

| 샘플 | 페이지 수 | LAYOUT_OVERFLOW | 회귀 |
|------|----------|----------------|------|
| KTX | 1 | 1 | ✓ 무회귀 |
| aift | 77 | 8 | ✓ 무회귀 |
| hwp-multi-001 | 10 | 1 | ✓ 무회귀 |
| kps-ai | 80 | 10 | ✓ 무회귀 |
| exam_eng | 8 | 12 | ✓ 무회귀 |

## 잔여 결함 → 별도 이슈 분리

### Issue #708 — pi=94 bare `[단나누기]` 마지막 col 시프트

shortcut.hwp pi=94 (`<편집 화면 분할에서>`) 의 bare `[단나누기]` (no ColumnDef) at last col 케이스. 1쪽 시프트의 직접 원인.

시도한 정정 (Column+last_col+no_def → process_multicolumn_break) 은 3개 기존 테스트 회귀 발생 → rollback. 회귀 가드 더 정밀하게 분석 후 후속 task 에서 처리.

### Issue #709 — 부수 시각 결함 4건

본질 1 외 잔존 시각 결함:
1. 제목 PUA 글자 (`\u{f53a}`) "한글 2010" → "ㅎ글2010" 표시
2. 탭 leader (점선 가이드) 미렌더링
3. 바탕쪽 자동번호 (페이지 번호 데코) 미렌더
4. 페이지 1 커서이동 right col 단축키 우측 정렬 누락

## 회귀 위험 평가

🟢 **Low**: 정정 범위가 본질 1 (Distribute 한정 + Page/Column break + ColumnDef 검출) 로 한정. Normal/단단 분기 영향 없음. 광범위 회귀 테스트 0 failures.

🟢 **회귀 가드 추가**: tests/issue_702.rs 2건. 향후 회귀 시 조기 검출.

## 정정 효과

- 핵심 결함 정정: 페이지 분기 폭주 정정 (10→8쪽), 지우기 분배 정합, 파일/미리보기/편집 통합 페이지 정합
- 별도 이슈 분리: 잔여 결함 (#708, #709) 후속 처리 명확화
- 광범위 회귀 0 failures, 시각 회귀 0건

## 다음 단계

작업지시자 승인 시:
1. commit (memory rule "내부 task commit 금지" — 명시 요청 시에만)
2. local/devel 머지 (수동 처리)
3. 작업지시자가 mydocs/orders 일일 할일 갱신
Loading
Loading