Skip to content

Task #318: 분할 표 + wrap=Square 호스트 문단 인라인 수식 중복 emit 회귀 수정#320

Merged
edwardkim merged 17 commits into
edwardkim:develfrom
planet6897:task318
Apr 25, 2026
Merged

Task #318: 분할 표 + wrap=Square 호스트 문단 인라인 수식 중복 emit 회귀 수정#320
edwardkim merged 17 commits into
edwardkim:develfrom
planet6897:task318

Conversation

@planet6897

Copy link
Copy Markdown
Contributor

배경

#313 (TypesetEngine main 전환) 후 tests/issue_301.rs::z_table_equations_rendered_once 회귀. task314 브랜치 (커밋 78fb1f1) 에서 임시 #[ignore] 처리되어 있던 것을 정식 수정하고 ignore 제거.

선행:

두 origin

A. 분할 표 셀 수식 중복 (table_partial.rs)

#301table_layout.rs 가드 (already_rendered_inline) 가 분할 표 경로에 미적용. 빈 runs 셀 + TAC 수식이 paragraph_layouttable_partial.rs::Control::Equation 두 곳에서 emit 되어 중복.

영향: 0.1915, 0.3413, 0.4332 가 각 2회 → 1회로 정상화.

B. wrap=Square 호스트 문단 중복 (layout.rs)

PageItem::PartialParagraph 분기에 is_wrap_host 가드 누락. FullParagraph 분기 (layout.rs:1639) 는 가드가 있어 정상 동작했으나 PartialParagraph 는 누락. wrap=Square 표 호스트 문단의 텍스트가 layout_partial_paragraph (PartialParagraph PageItem) + layout_partial_paragraph (layout_wrap_around_paras 내부) 두 경로에서 동일 paragraph 를 렌더 → 호스트 텍스트 + 인라인 수식이 두 개 y 좌표에 중복 emit.

영향: 0.4772 body 위치 1회 → 2회 (z-table 1 합쳐 3 → 2).

변경

1. src/renderer/layout/table_partial.rs:766 Control::Equation

let already_rendered_inline = tree
    .get_inline_shape_position(section_index, cp_idx, ctrl_idx)
    .is_some();
if already_rendered_inline {
    inline_x += eq_w;
    continue;
}

2. src/renderer/layout.rs::layout_column_item PartialParagraph 분기

PageItem::PartialParagraph { para_index, start_line, end_line } => {
    if let Some(para) = paragraphs.get(*para_index) {
        let is_wrap_host = para.controls.iter().any(|c| {
            if let Control::Table(t) = c {
                !t.common.treat_as_char
                    && matches!(t.common.text_wrap, crate::model::shape::TextWrap::Square)
            } else { false }
        });
        if is_wrap_host {
            return (y_offset, false);
        }
        // ... (이하 기존 처리)
    }
}

3. tests/issue_301.rs::z_table_equations_rendered_once

#[ignore] 제거. task314 커밋 78fb1f1 의 임시 처리 회수.

검증

issue_301 통과

측정 기대
0.1915 1 1 ✓
0.3413 1 1 ✓
0.4332 1 1 ✓
0.4772 2 2 ✓

4샘플 무회귀

샘플 기대 측정
21_언어_기출_편집가능본 15 15 ✓
exam_math 20 20 ✓
exam_kor 24 24 ✓
exam_eng 9 9 ✓

전체 테스트

cargo test
992 lib + 25 어댑터(0 ignored) + 6 svg_snapshot + issue_301 + 통합 모두 PASS

영향 범위

  • A 가드: HWPX/HWP 출처 무관. 빈 runs 셀 + 인라인 TAC 수식이 있는 분할 표만 분기 진입. 다른 셀 동작 무변경.
  • B 가드: wrap=Square + non-TAC 표를 가진 PartialParagraph 호스트 paragraph 만 분기 진입. 본문은 wrap_around 경로에서 처리되므로 결과 동일, 중복만 제거.

후속 사안 (선택)

  • TypesetEngine 이 wrap=Square 호스트 paragraph 에 대해 PartialParagraph 를 emit 하는 동작이 정상인지 재평가. 현재 layout 측 가드로 회피하지만, 의도가 명확하면 PartialParagraph 자체를 emit 하지 않는 것이 더 깔끔.

단계별 진행

단계 내용 보고서
1 A 수정 (table_partial.rs 가드) — 3 of 4 정상화 mydocs/working/task_m100_318_stage1.md
2 B 정밀 진단 (PartialParagraph + wrap_around 이중 호출) mydocs/working/task_m100_318_stage2.md
3 B 수정 (layout.rs is_wrap_host 가드) + 검증 mydocs/working/task_m100_318_stage3.md
4 최종 정리 mydocs/working/task_m100_318_stage4.md

최종 보고서: mydocs/report/task_m100_318_report.md

Test plan

  • cargo test --test issue_301 1 passed (ignore 제거 후 통과)
  • cargo test 전체 PASS, 0 failed
  • 4샘플 페이지 수 무변화
  • 골든 SVG 6건 무회귀

closes #318

planet6897 and others added 16 commits April 25, 2026 09:47
- rendering.rs::paginate(): RHWP_USE_TYPESET=1 시 TypesetEngine 사용
- 호환성 매트릭스: wrap_around_paras/hidden_empty_paras 미채움이나 실측 영향 미미
- 4샘플 페이지 수: 21_언어 15쪽 (PDF 일치), exam_math 20, exam_kor 24, exam_eng 9
- cargo test 양쪽 모두 992 passed (회귀 0)
- 후속 2-5단계 압축 가능
- rendering.rs::paginate(): default=TypesetEngine, RHWP_USE_PAGINATOR=1 fallback
- 21_언어 19→15쪽 (PDF 일치), exam_math 20, exam_kor 24, exam_eng 9
- cargo test 992 passed, 회귀 0

회귀 처리:
- hwpx_to_hwp_adapter 3건 #[ignore] (어댑터 측 line_seg 보존 부족, 별도 sub-issue 후보)
- aift-page3 골든 업데이트 (페이지 번호 마커 개선)
- TYPESET_VERIFY 검증 코드 제거 (역할 종료)
- Paginator/실험 플래그 보존 (fallback + 디버깅)
- 최종 보고서 + 오늘할일 갱신

Epic edwardkim#309 핵심 목표 달성: 21_언어 15쪽 PDF 정확 일치.
cargo test 992 passed.
- tests/task314_diag.rs 진단 테스트 추가 (임시)
- LINE_SEG 100% 보존 확인 (vpos/line_height/line_spacing/text_height)
- 차이 발견: char_shapes empty→[(0,0)] 59건, control_mask 27건,
  raw_break_type 4건, raw_header_extra 130건 등
- 페이지 3에서 +1쪽 차이 시작 (Table pi=51 처리 분기)
- 이론적으로 typeset 영향 없는 차이들이 실제로는 차이 발생
- 2단계: normalize 적용 후 page count 영향 검증
- document.rs::from_bytes에서 HWPX 분기에 normalize_hwpx_paragraphs 호출
- 빈 char_shapes에 default [(0,0)] 추가
- control_mask를 controls 기반 재계산 (HWP 직렬화기와 동일)
- 셀 paragraphs 재귀 처리

검증:
- char_shapes_len 59건 + control_mask 27건 차이 해소
- 페이지 수 +1쪽 차이 그대로 (잔존 origin 다른 곳)
- cargo test 992 passed, 4샘플 무변화

본 sub-issue 부분 진전. 잔존 origin은 char_count/raw_break_type/직렬화
손실 등에서 추가 조사 필요. 3단계는 종료 처리로 변경 예정.
- tests/task314_diag.rs 제거 (임시 진단 도구)
- 최종 보고서 + 오늘할일 갱신

부분 완료: HWPX normalize로 char_shapes/control_mask 차이 86건
해소, 그러나 페이지 수 +1쪽 차이는 그대로. 격리 테스트 유지.
잔존 origin (char_count/raw_break_type/직렬화 손실)은 추가
sub-issue로 분리 권장.
edwardkim#313 (TypesetEngine main 전환) 후 분할 표(table_partial) +
wrap=Square 호스트 문단 경로에서 인라인 수식이 중복 emit 되는 회귀.

원인 (예비 진단):
- table_partial.rs:766 Equation 분기에 edwardkim#301 가드 (already_rendered_inline)
  미적용 → 셀 수식이 paragraph_layout 과 table_partial 양쪽에서 emit
- pi=27 (PartialParagraph + wrap=Square Table) 호스트 문단이
  PartialParagraph PageItem 경로 + wrap-around layout 경로에서 중첩 렌더

본 회귀는 edwardkim#313 후속 layout 보강 사안이며 edwardkim#314 본문 (어댑터 normalize)
와 독립적. task314 PR 머지 차단 해제를 위해 임시 ignore 처리.
후속 sub-issue 등록 후 가드 적용 + 재활성화 예정.
- typeset.rs에 env-gated trace 추가 (RHWP_TYPESET_TRACE)
- tests/task317_diag.rs 진단 추가 (임시)

핵심 발견: 표 paragraph 처리에서 direct vs reloaded ~2.7px 차이 누적
- pi=33 Table 1x3에서 차이 시작 (+2.7px)
- pi=41 Table 4x11 (+2.9 추가 = +5.6)
- pi=45 Table 12x11 (+2.8 추가 = +8.4)
- 페이지 3 끝까지 ~20px 누적 → pi=51 Table 못 들어감 → +1쪽

origin: typeset_table_paragraph 내부. cell paragraphs / host_spacing /
outer_margin 등 후보. 2단계에서 정확한 IR 필드 식별 예정.
origin: typeset 의 is_tac 판정(table.attr & 0x01)이 common.treat_as_char
와 비대칭. HWPX 직접 로드는 attr=0(block 분기, PDF baseline 일치)이나
어댑터 pack_common_attr_bits 가 비트 합성하여 RELOADED 만 TAC 분기로
흘려 표 paragraph 당 ~2.7px 누적 → +1쪽.

보강: adapt_table 에서 raw_ctrl_data 합성 직후 attr 영역(offset 0..4)
을 0 으로 강제 + table.attr=0 보존. DIRECT 와 동일한 block 분기 진입.

- tests/hwpx_to_hwp_adapter.rs: #[ignore] 3건 제거 (edwardkim#313 격리분 회수)
- src/renderer/typeset.rs: 1단계 진단용 RHWP_TYPESET_TRACE 제거
- tests/task317_diag.rs: 진단 도구 회수
- 4샘플 무회귀: 15/20/24/9
- cargo test: 992 lib + 25 어댑터(0 ignored) + 통합 모두 PASS
edwardkim#313 (TypesetEngine main 전환) 후 issue_301 회귀의 두 origin 보강:

1. table_partial.rs:766 Equation 분기에 already_rendered_inline 가드 추가
   (edwardkim#301 의 table_layout.rs 가드와 동일 패턴). 빈 runs 셀 + TAC 수식이
   paragraph_layout 과 table_partial 양쪽에서 emit 되는 회귀 해소.
   영향: z-table 셀 0.1915/0.3413/0.4332 각 2회 → 1회.

2. layout.rs PartialParagraph 분기에 is_wrap_host 가드 추가 (FullParagraph
   동일 가드, layout.rs:1639). wrap=Square 표 호스트 문단이 PartialParagraph
   PageItem 과 layout_wrap_around_paras 두 경로에서 중복 렌더되던 회귀 해소.
   영향: 0.4772 body 1회 → 2회 (z-table 1 합쳐 3 → 2).

tests/issue_301.rs::z_table_equations_rendered_once 의 #[ignore] 제거
(task314 커밋 78fb1f1 의 임시 처리 회수). 정식 통과.

검증:
- cargo test: 992 lib + 25 어댑터(0 ignored) + 통합 모두 PASS
- 4샘플 무회귀: 21_언어 15 / exam_math 20 / exam_kor 24 / exam_eng 9
- 골든 SVG 6건 무회귀

closes edwardkim#318
@planet6897 planet6897 changed the title Task318 Task #318: 분할 표 + wrap=Square 호스트 문단 인라인 수식 중복 emit 회귀 수정 Apr 25, 2026
# Conflicts:
#	mydocs/orders/20260425.md

@edwardkim edwardkim left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Approved. 🎉

@planet6897 님, #316 의 z-table 회귀를 정확히 수정하고 어댑터 격리 테스트 3건까지 회수하셨네요. Epic #309 핵심 목표가 정식 통과됩니다.

평가 포인트

  • #316 z-table 회귀 정식 수정tests/issue_301.rs #[ignore] 제거 후 통과. 두 origin 명확 식별 (table_partial.rs Equation 분기 + layout.rs PartialParagraph 분기)
  • 어댑터 격리 회수hwpx_to_hwp_adapter 25/0/0 (3 ignored 회수)
  • Epic #309 핵심 목표 달성 — 21_언어 19→15쪽 (PDF 정확 일치)
  • #301 가드 패턴 일관 적용already_rendered_inline, is_wrap_host (FullParagraph 와 동일 패턴)
  • Task #291 (어제 핀셋) 효과 보전 — KTX.hwp pi=31/32 좌표 518.16/517.95 그대로 유지

메인테이너 검증

항목 결과
cargo test --lib ✅ 992 / 0 / 1 ignored
cargo test --test issue_301 1 / 0 (z-table 회귀 해소)
cargo test --test hwpx_to_hwp_adapter 25 / 0 / 0 (격리 회수)
svg_snapshot / tab_cross_run ✅ 전부 통과
clippy / wasm32 ✅ clean
CI (원본) ✅ 전부 SUCCESS
4샘플 페이지 수 ✅ 21_언어=15 / exam_math=20 / exam_kor=24 / exam_eng=9
WASM Docker 빌드 + rhwp-studio 브라우저 시각 검증 ✅ 작업지시자 판정 성공 (개선 확인 + 주요 회귀 테스트 통과)

처리 절차

  • orders 문서 충돌 (Task #313/#314/#317/#318 + #291 섹션 통합) 메인테이너 직접 해결
  • planet6897/task318 에 push
  • 재승인 + admin merge

Merge 진행합니다.

@edwardkim edwardkim merged commit d2d34fd into edwardkim:devel Apr 25, 2026
5 checks passed
edwardkim added a commit that referenced this pull request Apr 25, 2026
edwardkim added a commit that referenced this pull request Apr 25, 2026
- 작성자: @planet6897
- Merge commit: d2d34fd (admin merge, orders 충돌 직접 해결)
- 이슈 #313 #314 #317 #318 모두 CLOSED

처리 절차:
- PR 브랜치에 origin/devel 머지 → orders 섹션 통합
- planet6897/task318 에 push (96ce6d6..9bb593b)
- WASM Docker 빌드 + 브라우저 시각 검증 (작업지시자 판정 성공)
- 재승인 + admin merge

핵심 변경 (4 task 통합):
- Task #313: TypesetEngine default 전환 (RHWP_USE_PAGINATOR=1 fallback)
- Task #314: HWPX normalize_hwpx_paragraphs (char_shapes/control_mask)
- Task #317: adapt_table attr=0 강제 (typeset is_tac 비대칭 해소)
- Task #318: table_partial.rs already_rendered_inline + layout.rs is_wrap_host 가드

Epic #309 핵심 목표 달성:
- 21_언어 19→15쪽 (PDF 정확 일치)
- exam_math 20쪽 무회귀
- exam_kor 25→24, exam_eng 11→9

#316 z-table 회귀 정식 수정:
- issue_301 ignore 제거 후 통과
- hwpx_to_hwp_adapter 25/0/0 (격리 3건 회수)

Task #291 (어제 핀셋) 효과 보전:
- KTX.hwp pi=31 좌측 x=518.16 / pi=32 좌측 x=517.95 유지

검증:
- cargo test --lib: 992 passed
- issue_301 / hwpx_to_hwp_adapter / svg_snapshot / tab_cross_run: 전부 통과
- 4샘플 페이지 수: 15/20/24/9
- WASM 빌드 + 브라우저 시각 검증: 성공

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@planet6897 planet6897 deleted the task318 branch April 30, 2026 00:02
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