Skip to content

Task #460: HWP 3.0 파서 + Square wrap 그림 어울림 렌더링#1

Closed
jangster77 wants to merge 316 commits into
mainfrom
devel
Closed

Task #460: HWP 3.0 파서 + Square wrap 그림 어울림 렌더링#1
jangster77 wants to merge 316 commits into
mainfrom
devel

Conversation

@jangster77

Copy link
Copy Markdown
Owner

Summary

  • HWP 3.0 바이너리 포맷 파서 신규 구현 (src/parser/hwp3/)
    • 문단·표·그림·하이퍼링크·페이지 경계 처리
    • 조합형(Johab) 인코딩 변환 포함
    • OLE 컨테이너 파싱
  • Square wrap(어울림) 그림 렌더링 수정
    • layout.rs: wrap_pic_bottom_y 계산 + 앵커 Shape 처리 후 y_offset 전진 → 텍스트가 그림에 겹치지 않도록
    • typeset.rs: wrap zone 종료 시 current_height를 그림 하단까지 보정
    • HWP3 Square wrap 위치 계산: paper-relative → column-relative 변환 수정
  • LinesegTextRunReflow 검증을 HWPX 전용으로 제한 (HWP3/HWP5 오탐 방지)
  • 기존 렌더러(HWP5/HWPX)와 공통 IR(Document) 경유, 렌더러 분기 없음

Test plan

  • cargo test — 전체 테스트 통과 확인
  • cargo clippy -- -D warnings — 경고 없음
  • rhwp export-svg samples/hwp3-sample5.hwp -p 3 — page 4 그림+텍스트 레이아웃 정상
  • HWP5/HWPX 기존 파일 SVG 내보내기 정상 (회귀 없음)

🤖 Generated with Claude Code

oksure and others added 30 commits April 28, 2026 22:49
1. test_cases_korean_no_overlap: if-let silent pass → match + panic!
   으로 구조 변경 시 테스트가 명시적으로 실패하도록 개선.
   실제 구조가 Row[..., Paren{cases}]임을 확인하고 테스트 수정.

2. canvas_render.rs: CJK 수식 텍스트 이탤릭 제거를 SVG 렌더러와
   동일하게 Canvas 렌더러에도 적용 (백엔드 일관성).
PR edwardkim#396 (@oksure) cherry-pick 후 메인테이너 후속 정정:

- 결함: Canvas 경로 (rhwp-studio 웹 에디터) 의 LayoutKind::Fraction 처리에서
  분수선 y = baseline 으로 그려져 분모와 겹치는 정황. SVG 경로 (svg_render.rs)
  는 baseline - fs * AXIS_HEIGHT 로 정상 처리됐으나 canvas_render.rs 가 누락.
- 정정: line_y 계산을 SVG 와 동일하게 변경 (- fs * super::layout::AXIS_HEIGHT)
- 작업지시자가 web 에디터 시각 판정 중 발견 — exam_math.hwp 의 분수 분모와
  가로선 겹침 결함

검증:
- cargo test --lib: 1031 passed (무회귀)
- cargo test --test svg_snapshot: 6/6
- cargo test --test issue_418: 1/1 (Task edwardkim#418 보존)
- cargo clippy --lib -- -D warnings: warning 0건

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR edwardkim#396 의 svg.rs Y축 스케일링 정정 (scale_x, 1) → (scale_x, scale_y) 가
SVG 경로에만 적용되어, Canvas 경로 (rhwp-studio web 에디터) 에서는 수식이
SVG 와 다르게 그려지는 정황.

정정:
- src/renderer/web_canvas.rs:397 의 Equation 분기에 scale_x / scale_y 적용
- HWP 저장 영역(bbox) 과 레이아웃 산출 크기(layout_box) 비율로 ctx.scale 호출
- SVG 경로 (svg.rs:328-348) 와 동일한 동작

검증:
- cargo build --lib + cargo check --target wasm32-unknown-unknown --lib 통과
- cargo test --lib: 1031 passed (무회귀)
- cargo test --test svg_snapshot: 6/6
- cargo test --test issue_418: 1/1
- cargo clippy --lib -- -D warnings: warning 0건

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
증상:
- web 에디터에서 lim (극한 함수) 글자가 다른 글자보다 약 1.5배 크게 표시
- SVG 경로는 정상 — Canvas 만 결함

원인:
- canvas_render.rs::Limit 의 fi = font_size_from_box(lb, fs)
- Limit 의 LayoutBox.height 는 "lim 텍스트 + sub 첨자" 를 포함한 wrapper 높이
- font_size_from_box 가 lb.height 를 반환하므로 base_fs 의 1.5~2 배 → lim 글자 확대
- SVG 경로 (svg_render.rs::Limit) 는 fi = fs 로 정상

정정:
- canvas_render.rs::Limit 의 fi = fs 로 SVG 와 일치화
- 다른 텍스트 분기 (Text/Number/Symbol/MathSymbol/Function) 는 LayoutBox 가
  본인 텍스트만 포함해서 lb.height ≈ fs 라 font_size_from_box 정상 동작 — 변경 없음

검증:
- cargo test --lib: 1031 passed (무회귀)
- cargo test --test svg_snapshot: 6/6
- cargo test --test issue_418: 1/1
- cargo clippy --lib -- -D warnings: warning 0건

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PR edwardkim#396 (수식 렌더링 개선, @oksure) 옵션 B cherry-pick + 메인테이너 후속 정정 (3건) 완료
- 검토 / 처리 보고서 추가
- 오늘할일 갱신

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PR edwardkim#397 (수식 ATOP, @cskwork) 검토 — 옵션 B (작성자 재제출 대기)
- 신규 컨트리뷰터 첫 PR — devel rebase + 시각 검증 자료 + CI 통과 요청 댓글
- 작업지시자 git 정보 확인 결과 실제 사람 컨트리뷰터 (AI 에이전트 아님)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PR edwardkim#400 (HWPX 수식 직렬화, @cskwork) 검토 — 옵션 A (작성자 보강 요청)
- 이슈 edwardkim#286 milestone v1.0.0 으로 갱신 (작업지시자 결정)
- 자기검증 테스트 한계 — 한컴 hwpx 샘플 활용 + 한컴 호환 검증 자료 + devel rebase + CI 통과 요청

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- samples/synam-001.hwp 추가 — Task edwardkim#398 정정의 회귀 검증용 표 샘플
- PR edwardkim#401 검토 문서 (옵션 A — 작성자 재정정 요청)
- 회귀 정황: pi=69 표의 셀[4] (r=2, rs=5) — 큰 rowspan 셀이 PR edwardkim#401 의
  rowspan 묶음 단위 분할 정책으로 페이지 5 잔여 공간에 안 들어감 → 35→37 페이지 증가
- 작성자가 본 샘플을 가지지 못해 회귀 검증 못한 정황

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PR edwardkim#401 의 본 샘플은 통과했지만 samples/synam-001.hwp 5페이지에서 회귀
- pi=69 셀[4] (rs=5) 큰 rowspan 셀에서 PR 의 rowspan 블록 분할 정책이 너무 보수적
- 작성자에게 회귀 보고 + synam-001 샘플 공유 (commit b1c97fe) + 재정정 요청

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…elpers wrapper 전환

- src/model/paragraph.rs: impl Paragraph 의 char_shape_id_at 다음에 pub fn control_text_positions(&self) -> Vec<usize> 신설. 알고리즘 본체 이식 (char_offsets 갭 분석).
- src/document_core/helpers.rs: find_control_text_positions 본체 → para.control_text_positions() thin wrapper. pub(crate) 가시성 유지로 26 caller 호환.
- 의존성 방향 model ← parser ← document_core 보존을 위해 알고리즘 본체를 model::Paragraph 로 이동.

mydocs/plans/task_m100_390.md, _impl.md 동봉.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- test_control_text_positions_empty: controls.is_empty() 분기
- test_control_text_positions_no_offsets_inline_sequential: fallback 인라인 컨트롤 (Table) 순차 배치
- test_control_text_positions_no_offsets_non_inline_skipped: fallback else 분기 (비인라인 Bookmark 는 pos 미증가)
- test_control_text_positions_gap_between_chars: 일반 분기 'AB' 사이 갭 분석
- test_control_text_positions_gap_before: 첫 문자 이전 갭
- test_control_text_positions_surrogate_pair_char_width: surrogate pair (U+1F389) UTF-16 width=2 분기 boundary

cargo test --lib 1022 passed (baseline upstream/devel @ 4828937 = 1016, +6 신규).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cargo test 1072 / cargo clippy -- -D warnings / samples/2010-01-06.hwp SVG 내보내기 6페이지 검증 통과.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PR edwardkim#405 (Paragraph::control_text_positions, @DanMeon) 옵션 A cherry-pick 머지 완료
- 작업지시자 사전 PR 권유 사안 — 이슈 작성자 본인 정정
- 알고리즘 위치 이동 리팩토링 (외부 binding PyO3/napi/JNI 등 노출 위함)
- 검토 / 처리 보고서 추가
- 오늘할일 갱신

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PR edwardkim#411 (editor.exportHwp API, @ggoban) 옵션 A cherry-pick 머지 완료
- iframe wrapper @rhwp/editor 에 exportHwp() 노출 — WASM core 변경 없음
- 4 files / +30 / -0, Rust 영향 없음
- 검토 / 처리 보고서 추가
- 오늘할일 갱신

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add vite-plugin-pwa with autoUpdate strategy
- Generate manifest.json (name, icons 128/256/512, theme_color)
- Precache JS/CSS/HTML/WASM/PNG/fonts via workbox
- Add theme-color meta + apple-touch-icon to index.html
- Pin .npmrc legacy-peer-deps=true (vite-plugin-pwa peer is vite<=7)
- start_url/scope: '.' → '/rhwp/' (GitHub Pages subpath hardened)
- icon-192.png added (PWA standard 192×192 size)
- WASM removed from precache globPatterns → runtimeCaching CacheFirst
  (avoids 12 MB blocking SW installation; offline after first load)
- .npmrc: add comment noting legacy-peer-deps is temporary

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PR edwardkim#413 (rhwp-studio PWA support, @dyjung150605) 옵션 A cherry-pick 머지 완료
- 이슈 edwardkim#383 메인테이너 안내 7항목 100% 대응
- 4 환경 + fork 배포 사전 검증
- 검토 / 처리 보고서 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jangster77 and others added 20 commits April 30, 2026 14:43
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- parse_hwp3()에서 assign_auto_numbers + fixup_hwp3_picture_numbers 직접 호출
- Hwp3Parser struct + DocumentParser impl 추가
- parse_document: Hwp3Parser.parse(data) 단일 라인으로 정리
- fixup_hwp3_picture_numbers: pub(crate) → private

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HwpxParser: assign_auto_numbers 제거 (upstream과 동일하게 복원)
- src/bin/dump_pictures.rs: 개발 중 임시 도구 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hwp3LineInfo.pgy 필드 추출 (bytes 6-7, 한글97 줄 Y좌표 hunit 단위)
- 페이지 경계: first_pgy < prev_last_pgy 시 Page break 설정
- margin_bottom 1600 HWPUNIT 축소: 한글97 last-line 허용 동작 근사
- 디버그 eprintln! 3개 제거 (DOC_INFO, PAGE BREAK, DEBUG pi=)
- hwp3-sample.hwp: 21→16 페이지 (한컴 뷰어·LibreOffice와 일치)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HWP3/HWP5에서 1 line_info → 1 lineseg는 정상 동작이므로
validate_linesegs에 check_textrun_reflow 플래그를 추가하여
HWPX 파일에서만 R3 규칙을 적용한다.
hwp3-sample.hwp 열기 시 HWPX 비표준 경고 5건이 사라진다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
본질: HWP TablePageBreak::RowBreak (행 단위 분할 허용) 정책이 Task edwardkim#398
의 BLOCK_UNIT_MAX_ROWS=3 rowspan 보호 블록 정책과 모순. 표 정책 우선.

회귀 origin: PR edwardkim#401 v2 (Task edwardkim#398 Stage 2, commit 81db203)
  - samples/k-water-rfp.hwp 5쪽 표 pi=52 (4행, rs=2): rows=0..2 만 배치,
    used=196.3px (21%) — 잔여 482px 비어있음

정정:
  - MeasuredTable::allows_row_break_split() 메서드 추가
  - snap_to_block_boundary 에 RowBreak 조기반환
  - paginate_table (typeset.rs) 의 first/cur/next_block_protected 가드
  - split_table_rows (engine.rs) 동일 3개소 가드

회귀 정정 확인:
  - k-water-rfp 5쪽: rows=0..2 → rows=0..4 (4행 모두 배치)
  - synam-001 5쪽/6쪽: rows=0..5 / 4..8 (PR edwardkim#401 v2 정정 보존)

검증:
  - cargo test --lib: 1077 passed (1075 + 단위 테스트 2 추가)
  - svg_snapshot: 6/6, issue_418: 1/1, clippy: 0건
  - 광범위 회귀 점검 10 샘플 / 232 페이지 — 영향 영역 k-water-rfp 5/6 만
  - 작업지시자 시각 판정: synam-001 정상 + k-water-rfp 정정 후 승인

closes edwardkim#474

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
작업 시작점: k-water-rfp 16쪽 두 번째 표 셀 안 그림 셀 경계 초과.
시각 검증 중 1쪽 K-water 로고 결함도 발견 → 회귀 origin: PR edwardkim#434 /
Task edwardkim#430 (commit a5541f9, @planet6897). 작업 범위 확장 통합.

본질:
  HWP 의 image bin 두 가지 케이스:
  - A: PNG = crop 적용 후 image (k-water-rfp pi=31 등)
  - B: PNG = 원본 image (exam_kor 헤더 등)

  Task edwardkim#430 의 scale_x = orig_w / img_w_px 계산식이 케이스 B 만 정합,
  케이스 A 회귀 (image 좌측 89.66% 만 보이고 1.069배 stretch = 확대).

정정:
  - compute_image_crop_src: HWP 표준 75 HU/px 룰 단일 계산식
    (1 inch = 7200 HU = 96 px → 75 HU/px = DPI 96)
  - 호출부 셀 폭 클램프 3개소 (table_layout.rs / table_partial.rs /
    shape_layout.rs) — TAC 표 셀 / 도형 안 그림이 셀 폭 초과 시 비율
    유지 클램프

검증:
  - cargo test --lib: 1078 passed (1075 + 단위 테스트 3 추가)
  - svg_snapshot: 6/6, issue_418: 1/1, clippy: 0건
  - 작업지시자 시각 판정: 1쪽 + 16쪽 모두 정상

문서:
  - 트러블슈팅: mydocs/troubleshootings/image_crop_scale_rule.md
  - 위키: HWP 그림 Crop Scale 룰

작업지시자 통찰: "이건 휴리스틱이 아닙니다. 룰입니다."

closes edwardkim#477

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
closes edwardkim#470

Task edwardkim#321 cross-paragraph vpos-reset 가드가 `cv == 0` (정확히 0) 만 인정해
컬럼 헤더 오프셋 (cv=9014 등) 으로 시작하는 다단의 새 컬럼 reset 을 놓침.
21_언어_기출_편집가능본 1p 의 pi=10 ("적합성 검증이란…") 이 col 0 에 강제
삽입되어 56.2px overflow 발생.

수정: typeset.rs:415, 439 에 다단/단일 단 분기.
- 다단(col_count > 1): cv < pv && pv > 5000 (Task edwardkim#470, 컬럼 헤더 오프셋 인정)
- 단일 단:           cv == 0 && pv > 5000 (Task edwardkim#321 보수적 기준 유지,
                     hwpspec partial-table split 회귀 차단 / issue_418)

검증:
- 신규 통합 테스트 test_470_cross_paragraph_vpos_reset_with_column_header_offset
- 다단 샘플 OVERFLOW 11건 추가 해소
  21_언어_기출 13→10, exam_science 5→0, exam_social 4→1
- hwpspec(단일 단) OVERFLOW 45→45, issue_418 PASS (회귀 0)
- cargo test 1122 / 1122 PASS
…로 변경

closes edwardkim#471

Task edwardkim#468 cross-column 박스 인접 검출이 bf_id 동등 비교로 동작했으나,
머지(Task edwardkim#321 v6)는 stroke_sig 기준이라 bf_id 가 다르더라도 visual 동일
하면 한 그룹이 됨. 머지 후 그룹의 g.0 은 첫 range bf_id 만 보존.

본 회귀: 21_언어_기출 1p 좌측 단 (가) 박스가 pi=6(bf=7) + pi=7~9(bf=4)
머지로 g.0=7. composed[10].bf=4. bf_id 비교로 4 != 7 → partial_end 미설정
→ 4면 stroke 단일 Rectangle 로 그려져 하단 가로선 발생 (Task edwardkim#470 적용
후 pi=10 이 col 1 로 이동하면서 노출).

수정: layout.rs:1670-1699 의 cross-column 검출을 stroke_sig 비교로 변경.
머지와 동일한 비교 기준 사용으로 일관성 확보.

검증:
- 신규 통합 테스트 test_471_cross_column_box_no_bottom_line_in_col0
- cargo test 1123 / 1123 PASS
- 21_언어_기출/exam_kor/exam_eng/hwpspec OVERFLOW 회귀 0
본 사이클 14번째 PR. 67 commits 중 본질 신규 3 Task.
작업지시자 옵션 A 선택 — Task edwardkim#470 + edwardkim#471 cherry-pick, Task edwardkim#473 제외
(본 작업 사이클 Task edwardkim#477 (75 HU/px 단일 룰) 와 동일 영역 정정).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… 도구 보강

bisect 회귀 origin 확정: Task edwardkim#362 commit a5541f9 (메인테이너 직접 정정,
2026-04-27).

본질:
  compute_cell_line_ranges 의 content_limit 비교 단위 mismatch
  - line_end_pos = cum + line_h = 절대 좌표 (셀 시작부터의 누적 px)
  - content_limit = split_end_limit = avail_content = 상대 길이
  - content_offset > content_limit 케이스 (synam-001 페이지 15:
    split_start=1280.6, split_end=965.4) 에서 즉시 break → 빈 페이지

정정:
  - abs_limit = content_offset + content_limit 으로 절대 좌표 변환
  - atomic 분기 + line 분기 모두 abs_limit 사용

부수 작업:
  - dump-pages 의 PartialTable 출력에 split_start/split_end 정보 추가
  - 본 결함 진단의 결정적 도구, 향후 분할 표 결함 진단에 활용

검증:
  - cargo test --lib: 1080 passed
  - svg_snapshot: 6/6, issue_418: 1/1, clippy: 0건
  - WASM 4,204,778 bytes
  - synam-001 페이지 15: 빈 페이지 → 정상 출력 (text 6 → 1438)
  - kps-ai 정합 영역 (p56/p67-68/p73) 보존 — Task edwardkim#362 의도 보존
  - 작업지시자 시각 검증 통과

작업지시자 통찰:
  VSCode/Chrome 확장 (4/13~4/21 빌드) 정상, 현재 (4/30) 결함 → bisect 영역 확정
  "분할된 표의 셀내 문단이 출력되지 않는 문제로 접근해야 합니다"
  "이번 기회에 dump-pages 에 분할 표에 대한 정보도 추가해서 진행하는게 좋을 것 같습니다"

잔여: 페이지 15 마지막 줄 시각 클립핑은 이슈 edwardkim#485 로 분리.

closes edwardkim#431

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
본질: 표 셀 안 그림이 아닌 BorderFill 의 fill_type=Image (셀 배경 그림).
render_cell_background 가 border_style.image_fill 필드 무시.
회귀가 아닌 미구현 기능 추가.

작업지시자 통찰:
  "이건 셀의 배경으로 그림을 넣은 것입니다. 우리가 미구현한 것 같습니다."

정정:
  - render_cell_background 시그니처 확장 (bin_data_content 인자 추가)
  - image_fill 분기 추가 (zone 처리와 동일 패턴)
  - 호출부 6개소 정합화 (table_layout / table_partial / table_cell_content)
  - zone 의 별도 image_fill 처리 제거 (render_cell_background 가 통합 처리)

검증:
  - cargo test --lib: 1080 passed
  - svg_snapshot: 6/6, issue_418: 1/1, clippy: 0건
  - WASM 4,204,760 bytes
  - aift 41쪽 <image> 갯수 0 → 1
  - aift 43쪽 <image> 갯수 1 → 3 (다른 셀 배경 image fill 케이스)
  - 광범위 회귀 점검: aift 77 페이지 중 2 변화 (모두 정정 의도 영역)
  - 작업지시자 시각 판정 통과

closes edwardkim#429

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
이슈 edwardkim#345 (exam_eng 9→8 페이지 회귀):
  - exam_eng-2010.pdf (한컴 2010, 14쪽 — 부정확)
  - exam_eng-2020.pdf (한컴 2020, 8쪽 — 정답지)
  - 결론: rhwp 8쪽 = 한컴 2020 정합 = 정정 효과 (회귀 아님). 이슈 close

이슈 edwardkim#241 (HWPX 첨부 이미지 공백):
  - issue_241.hwpx (테스트 샘플)
  - issue_241.pdf (한컴 정답지)
  - 본 task 시도 후 layout 누적 drift 본질로 작업 일시 중단

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mydocs/feedback/:
  - manual_currency_audit.md (매뉴얼 현행화 감사 결정 요청 — 2026-04-23)
  - open_issues_priority.md (열린 이슈 37건 우선순위 — 2026-04-22)
  - pr165_merge_decisions.md (PR edwardkim#165 Skia + Layered Renderer 머지 충돌 결정 — 2026-04-21)
  - self_censor_audit.md (외부 공개 문서 자기검열 감사 — 2026-04-22)

mydocs/pr/:
  - archives/pr_256_review.md + review_impl.md (PR edwardkim#256 검토 + 구현 계획)
  - pr_273_review.md (PR edwardkim#273 Task edwardkim#267 right tab 처리 검토)

이전 사이클 작성 후 미커밋 보관 — devel 머지 + push 진행 시 함께 커밋.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/parser/hwp3/: HWP 3.0 바이너리 포맷 파서 신규 구현
  - mod.rs: 메인 파서 (문단·표·그림·하이퍼링크·페이지 경계 처리)
  - paragraph.rs: LINE_SEG 기반 줄 정보(pgy) 파싱
  - drawing.rs: 그림 개체 파싱
  - encoding.rs / johab.rs / johab_map.rs: 조합형 인코딩 변환
  - ole.rs: OLE 컨테이너 파싱
  - records.rs: 레코드 타입 정의
  - special_char.rs: 특수 문자 처리
- src/parser/mod.rs: HWP 3.0 포맷 자동 감지 + parse_hwp3 라우팅
- samples/hwp3-sample*.hwp: HWP 3.0 테스트 파일 3종

HWP5 IR(Document)로 변환하여 기존 렌더러에서 바로 표시 가능.
Square wrap(어울림) 그림: pgy 기반 줄별 column_start/segment_width 계산.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
layout.rs:
- VertRelTo::Para + TextWrap::Square 그림의 result_y 오류 수정
  layout_body_picture가 반환하는 y가 para_start_y로 되돌아가는 문제 →
  y_offset(누적 컬럼 y)으로 덮어써서 다음 문단이 밀리지 않도록 함

document.rs:
- HWPX 전용 TextRunReflow 자동 보정 추가 (reflow_textrun_paragraphs)
  lineseg 1개 + 긴 텍스트 패턴을 문서 로드 시 자동 보정하여
  "HWPX 비표준 감지" 경고 대화상자 미표시
- reflow 결과가 여전히 1 lineseg면 원본 보존 (부동소수점 누적 방지)
- validate_linesegs 호출을 reflow 이후로 이동

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n-relative 수정

parse_paragraph_list에 body_left_hu, column_width_hu 파라미터 추가.
그림의 horz_rel_to(Paper/Para/Page)에 따라 column_start/segment_width를
컬럼 기준 좌표로 올바르게 변환:
- 그림이 왼쪽 절반에 있으면 텍스트가 오른쪽으로 흐름 (cs=pic_right, sw=나머지)
- 그림이 오른쪽 절반에 있으면 텍스트가 왼쪽으로 흐름 (cs=0, sw=pic_left)
vert_rel_to에 따라 pgy_start 계산도 분기 처리 (Para-relative vs 절대 좌표).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[layout.rs]
- layout_shape_item: non-paper-based Control::Picture Square wrap에서
  wrap_around_paras(ColumnItemCtx)를 사용해 layout_wrap_around_paras 호출 추가
- layout_wrap_around_paras: end_line 1줄 제한 제거 → 전체 줄 렌더링
  (표 어울림은 WrapAroundPara 1개=1줄, 그림 어울림은 1개에 여러 줄 포함 가능)
- layout_column_shapes_pass: Control::Picture와 Control::Shape(Picture) 통합 처리.
  typeset.rs 경로에서 PaginationResult.wrap_around_paras가 항상 비어있으므로
  col_content.wrap_around_paras를 직접 참조하여 어울림 텍스트 렌더링.
  page-relative 그림도 어울림 텍스트는 body 기준 좌표로 렌더링.

[typeset.rs / engine.rs]
- wrap_around_any_seg 필드 추가: Control::Picture/Shape Square wrap 앵커에서
  후속 문단의 첫 LINE_SEG(cs=0)도 어울림 문단으로 흡수 가능하도록 허용
- has_non_tac_pic_square 감지 블록 추가: 비-TAC Square wrap 그림/도형 앵커에서
  wrap_around_cs/sw/table_para/any_seg 설정 (표 어울림과 동일 시멘틱)

[CLAUDE.md]
- 파일 포맷별 파서 구조 표 추가
- HWP3 파서 규칙 명시 (공통 모듈에 HWP3 전용 분기 금지)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
layout.rs: wrap_pic_bottom_y 계산 후 앵커 Shape 처리 완료 시점부터
y_offset을 그림 하단으로 전진시켜 텍스트가 그림에 겹치지 않도록 수정.
typeset.rs: wrap zone 종료 시 current_height를 그림 하단까지 보정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jangster77

Copy link
Copy Markdown
Owner Author

잘못된 대상(fork 내부)으로 생성. edwardkim/rhwp으로 재생성합니다.

@jangster77 jangster77 closed this May 1, 2026
jangster77 pushed a commit that referenced this pull request May 8, 2026
closes edwardkim#671 (자동보정 회귀 영역 정정 영역 통합)

본질:
PR edwardkim#673 (Task edwardkim#671) 영역의 layout 단계 영역 정정 영역의 가드 #1 (line_segs.is_empty())
영역이 자동보정 영역 영역의 LINE_SEG 영역 채움 영역 후 영역 거짓 영역으로 영역 →
PR edwardkim#673 영역의 정정 영역 미적용 영역 → 자동보정 영역에서 한 줄 겹침 회귀.

작업지시자 시각 판정 영역에서 발견:
"그대로 보기 하면 2줄로 처리되지만 오히려 자동보정 선택하면 한줄로 겹쳐집니다."

자동보정 영역의 결함 본질:
src/document_core/commands/document.rs:270 + :425 영역 영역 두 곳 영역 모두
reflow_line_segs(cell_para, col_width, ...) 영역 — column 폭 영역 사용.
주석 영역 의도: "셀 너비가 아직 불확정이므로 컬럼 너비를 근사값으로 사용. ...
실제 셀 내 줄바꿈은 테이블 레이아웃이 재수행한다." 그러나 본 환경 영역의 layout
영역 (PR edwardkim#673) 영역의 가드 #1 영역으로 인해 재수행 영역 미작동 영역.

정정 (옵션 A1):
- col_width 영역 → cell_inner_width 영역 (cell.width - cell.padding.left - cell.padding.right)
- 두 곳 영역 (line 270 자동 + line 425 사용자 명시) 영역 모두 영역 동일 본질 영역 정정
- 자동보정 영역 자체 영역에서 셀 폭 영역으로 LINE_SEG 영역 채움 영역
  → recompose_for_cell_width 영역의 가드 #1 (line_segs.is_empty()) 영역 우회 영역
  → 본 환경 영역의 layout 영역의 multi-line 영역 분할 영역 정합

본 환경 검증:
- cargo test --release ALL PASS (1165 lib + 회귀 0)
- cargo clippy --release clean

작업지시자 결정:
"A1 으로 진행해봅니다. 검증 할 수 있게 준비해주세요"

PR: edwardkim#673
컨트리뷰터: @jangster77 (Taesup Jang)
jangster77 pushed a commit that referenced this pull request May 9, 2026
- test_705_aift_page2_cell_pagehide_collected
- test_705_aift_page2_cell_pagehide_six_fields (메인테이너 권위 측정 6 필드)
- test_705_aift_page3_cell_pagehide_collected
- test_705_aift_cell_pagehides_total_count (실제=2 vs 기대>=4 정량 측정)

모두 RED — 결함 #1 (engine.rs 셀 안 paragraph 미순회) 정량 검증.

Refs edwardkim#705
jangster77 pushed a commit that referenced this pull request May 9, 2026
- engine.rs + typeset.rs 두 경로 모두에 collect_pagehide_in_table 헬퍼 추가
  - 결함 #1 본질: TypesetEngine::typeset_section 가 main path,
    Paginator::paginate_with_measured 는 RHWP_USE_PAGINATOR=1 fallback
  - 두 경로 동시 정정 필요 (PR edwardkim#641 description 의 두 경로 양분 정합)
- 외부 paragraph index pi 그대로 사용 → 페이지 매핑 정합성 유지
- 중첩 표 (depth 2+) 재귀 보존 (실측 0건이지만 미래 케이스 대비)

회귀 sweep: 1123 passed, 0 failed (RED 4건 → GREEN, 기존 1119건 유지)

Refs edwardkim#705
jangster77 pushed a commit that referenced this pull request May 31, 2026
- mydocs/plans/task_m100_1151_v3.md: 수행계획서. Scope 재정의 후 v3 의
  paragraph + sibling 표 + inline picture 시각 layout 정합 작업.
- mydocs/plans/task_m100_1151_v3_impl.md: 구현계획서. Stage 3 + Stage 4 + Stage 5.
- mydocs/tech/topandbottom_table_inline_picture_layout.md: v3 root cause 분석.
  paragraph_layout / shape_layout / layout.rs 의 picture 렌더 path 추적. Fix edwardkim#2
  (paragraph_layout sibling 표 누적) 권장 + Fix #1/edwardkim#3 위험 분석.
- mydocs/working/task_m100_1151_v3_stage3.md: Stage 3 완료 보고서.
  진입점 진단 (layout.rs:4514 Task edwardkim#347 fallback), 변경 내용, SVG 좌표 시각 검증
  (4 시나리오 한컴 정합), 자동 검증 결과 합산.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
jangster77 pushed a commit that referenced this pull request May 31, 2026
- mydocs/working/task_m100_1151_v3_stage4.md: WASM 재빌드 + 자동 SVG 좌표 검증 +
  사용자 직접 시각 시연 (4 시나리오 한컴 편집기 1:1 정합 확인). v1+v2 회귀 0,
  v3 helper 6 + v2 통합 4 + 전수 1442/0/6.
- mydocs/plans/task_m100_1151_v4.md: 수행계획서. tac-img-02.hwp 의 셀 안 inline
  picture 클릭 hit-test 정합. 3-layer fault (ImageNode cell_index 누락 +
  rendering.rs JSON 직렬화 누락 + cursor_rect.rs 셀 안 picture skip).
- mydocs/plans/task_m100_1151_v4_impl.md: 구현계획서. Stage 6 (Fix #1/edwardkim#2/edwardkim#3 +
  단위 테스트) + Stage 7 (WASM 재빌드 + dev server 클릭 시연) + Stage 8 (통합 PR).
- 이슈 edwardkim#1151 description: v4 scope 섹션 추가 (별도 gh issue edit).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
jangster77 pushed a commit that referenced this pull request May 31, 2026
…_indices() helper

audit 결과 (anti-pattern #1): paragraph_layout.rs 3 곳 + picture_footnote.rs 1 곳에서
동일한 cell_ctx → ImageNode 3 필드 (cell_index / cell_para_index /
outer_table_control_index) 매핑 boilerplate 가 반복.

Fix: CellContext 에 last_image_indices() 메서드 추가. 4 곳 호출 측을 helper 호출로
변경하여 boilerplate 제거. 동작 변화 없음 (model 동일).

cargo test --lib 1445 passed (회귀 0), clippy/fmt clean.
jangster77 pushed a commit that referenced this pull request May 31, 2026
audit (Explore agent 2 회) 9 항목 평가 결과:
- Fix (Stage 12~14): #1 (CellContext::last_image_indices) + edwardkim#4 (parse_cell_path_json
  / resolve_cell_paragraph_mut) + edwardkim#7 (make_picture_image_node)
- 의도된 분리 분석 명시 (Stage 15 / 본 commit): edwardkim#2 (Table dual maintenance —
  serializer source-of-truth 차이) + edwardkim#3 (invalidate_page_tree_cache 분산 —
  setter 별 책임 명확화)
- Skip / OK: edwardkim#5, edwardkim#6, edwardkim#8, edwardkim#9

별도 이슈 발급 불필요 — edwardkim#2 / edwardkim#3 모두 의도된 구조 확정.

v6 최종 보고서 → v7 통합 보고서로 갱신 (v1~v7 누적 산출물 정리).
오늘 할일 (orders/20260530.md) 갱신.

cargo test --lib 1445 passed (회귀 0).
jangster77 pushed a commit that referenced this pull request May 31, 2026
## 증상

samples/한셀OLE.hwp 등 OLE 미리보기 페이지를 rhwp-studio 에서 첫 로드 시 백지로
렌더되고, 두 번째 로드부터는 정상. 모듈 static IMAGE_CACHE 가 첫 로드 중 디코드한
HtmlImageElement 를 유지해 두 번째 호출에서만 img.complete() 분기로 즉시 그려지는
패턴.

## 원인

OLE/차트 미리보기는 PaintOp::RawSvg 로 emit (src/paint/json.rs:814) 되며 web_canvas
RawSvg 핸들러는 단일 <image data:...> 또는 SVG 조각을 draw_image() 로 그려서
IMAGE_CACHE 비동기 디코드 경로를 그대로 탄다.

그런데 edwardkim#1154 v2 (PR edwardkim#1164) 의 디코드 안전망은 PaintOp::Image 만 image_count 에
포함시켜 RawSvg 케이스에서는:

- rendering.rs collect() : image_count == 0
- page-renderer.ts scheduleReRender : if (imageCount <= 0) return 으로 200/600/1500ms
  재시도 미발화
- prefetchFlowImages 정규식 ("type":"image") 도 rawSvg 미매칭

결과적으로 첫 렌더 후 캔버스가 백지로 남는다.

## 수정

1. src/document_core/queries/rendering.rs : collect() 가 PaintOp::RawSvg 도
   image_count += 1 처리. scheduleReRender 재시도 발화 트리거.
2. rhwp-studio/src/view/page-renderer.ts : prefetchFlowImages 가 전체 JSON 의
   data:image/MIME;base64,... 패턴을 직접 스캔하여 rawSvg 내장 데이터 URL 도
   prefetch (중복 dedupe). 기존 image 항목 regex 는 유지.

## 검증

Puppeteer 측정 (samples/한셀OLE.hwp):

| 시점 | 수정 전 load #1 | 수정 후 load #1 | 수정 후 load edwardkim#2 |
|------|-----------------|-----------------|-----------------|
| t+0ms | 6 (백지) | 1276 | 1276 |
| t+1500ms | 6 (백지) | 1276 | 1276 |
| t+3000ms | 6 (백지) | 1276 | 1276 |

cargo test --lib document_core::queries::rendering : 6 passed.

Closes edwardkim#1181
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.

10 participants