Skip to content

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

Merged
edwardkim merged 51 commits intoedwardkim:develfrom
jangster77:devel
May 1, 2026
Merged

Task #460: HWP 3.0 파서 + Square wrap 그림 어울림 렌더링#506
edwardkim merged 51 commits intoedwardkim:develfrom
jangster77:devel

Conversation

@jangster77
Copy link
Copy Markdown
Contributor

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 그림+텍스트 레이아웃 정상 (그림 좌상단, wrap 텍스트 그림 우측, 전체폭 텍스트 그림 아래)
  • HWP5/HWPX 기존 파일 SVG 내보내기 정상 (회귀 없음)

🤖 Generated with Claude Code

jangster77 and others added 30 commits April 28, 2026 19:30
- src/parser/hwp3/ 신규: 9개 파일 (mod, records, paragraph, encoding,
  johab, johab_map, special_char, drawing, ole)
  · parse_hwp3() → Document IR 변환 파이프라인 구현
  · Johab 조합형 → UTF-8 디코딩
  · 표/그림/그리기 객체/머리말꼬리말/각주미주 기본 파싱
  · debug println! 4건 제거 + clippy 오류 3건 수정
- src/parser/mod.rs: FileFormat::Hwp3 → parse_hwp3() 라우팅
- src/bin/dump_pictures.rs: 그림 덤프 유틸 신규
- samples/hwp3-sample.hwp: 테스트용 HWP3 샘플 파일 (87KB)
- mydocs/: 수행/구현계획서, Stage1 완료보고서, 오늘할일 신규

hwp3-sample.hwp export-svg: 20페이지 SVG 생성 성공
cargo test --lib: 1016 passed / cargo clippy: clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- picture_footnote.rs: Para-relative 그림 반환 y를 base_y 기준으로 수정
  (이전: y_offset+total_height → 후속 단락이 그림 위로 겹침)
- paragraph_layout.rs: 캡션 AutoNumber 위치에 U+FFFC 처리 추가
  (HWP3는 AutoNumber 위치를 U+FFFC로 저장, HWP5 "  " 패턴과 별도 처리)
- hwp3/mod.rs: caption.width=0 → pic.common.width로 보정 (캡션 렌더링 복원)
- hwp3/mod.rs + parser/mod.rs: HWP3 그림 카운터 방식 수정
  (HWP3는 Control::Picture 개체 자체가 카운터를 올림 — 캡션 유무 무관)
  꼬리말 로고(tac=true)가 카운터 1을 선점하여 본문 그림은 2부터 시작
  → 그림 번호 한컴과 일치: 그림2/3/4/5 (이전: 그림1/2/3/4)
- pagination/engine.rs: Para-relative vert_offset 처리 주석 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
추가정보블록 #1 TagID 3 (하이퍼텍스트 정보) 파싱 추가 — 스펙 §8.3.
각 항목 617바이트: data[0..256]=파일명(URL, kchar, null종료).
추가정보블록 순서가 본문 하이퍼링크 등장 순서와 일치하므로
순서 매핑으로 Control::Hyperlink.url에 설정.
(hwp3-sample.hwp에는 하이퍼링크 없어 직접 검증 불가)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…기 제거)

- src/parser/hwp3/mod.rs: ch=18(AutoNumber) 시 U+FFFC 대신 ' ' push
  → 캡션 "그림 " + ' ' = "그림  " — HWP5/HWPX "  " 패턴과 일치
- src/renderer/layout/paragraph_layout.rs: '\u{fffc}' 전용 탐색 분기 제거
  → HWP5/HWPX/HWP3 공통 "  " 패턴 단일 경로로 통합
- cargo test --lib: 1068 passed, 0 failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fixup_hwp3_mixed_para_line_segs() 추가 (src/parser/hwp3/mod.rs 하단)
  Para-relative TopAndBottom non-TAC 그림 구역을 탐색하여
  마지막 그림-위쪽 LINE_SEG의 line_height를 그림 하단까지 확장
  (예: seg[5] 36700 HU = 489.3px → 렌더러 y+= 점프 → 겹침 해소)
- text_height=0 설정으로 document.rs advance=lh+ls 공식 보장
- cargo test --lib: 1068 passed, 0 failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cargo test --lib: 1068 passed, 0 failed
- cargo clippy: clean
- export-svg hwp3-sample.hwp: 22페이지, 그림 겹침 없음
- HWP5/HWPX 5종 회귀 없음
- 수행계획서/구현계획서/최종보고서/오늘할일 갱신

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…p 하단 제한)

- HWP3 표 셀 border_fill_id: push 후 len()-1(0-기반) → len()(1-기반)으로 수정
  (렌더러가 border_fill_id-1로 인덱스 접근하므로 1-기반이 정상)
- layout body clip: expand_clip 후 body_bottom+10px 초과 하방 확장 제한
  (대형 부동 그림이 꼬리말 영역까지 body clip을 확장하던 문제 해소)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… 해소)

- percent 줄간격 시 line_spacing = lh - th (600 HU) → 0 으로 수정
- HWP5 IR 모델에서 percent 줄간격은 line_height에 이미 반영되어 있으므로
  line_spacing = 0 이어야 함 (렌더러: y += lh + ls)
- 결과: 줄 이동 2200 HU → 1600 HU, 17×2 bibliography 표 텍스트 겹침 해소
- 전체 페이지 22 → 16으로 감소 (과도한 셀 높이 해소)
- 1068 tests passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
비-TAC TopAndBottom VertRelTo::Para 그림을 가진 anchor 문단이 페이지 하단에서
텍스트+그림 합산 높이로 페이지를 초과할 때 선제적 쪽 나눔을 적용.
anchor 문단과 그림이 동일 페이지에 배치되어 그림 분할 현상 해소.
hwp3-sample.hwp: 16페이지→17페이지 (pi=41 그림이 페이지2에서 페이지3으로 이동).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HWP3 binary의 LINE_SEG break_flag bit 0이 word processor의 레이아웃 결정
(새 페이지 배치)을 인코딩한다. 비-TAC TopAndBottom 그림 문단에서 이 플래그를
column_type = Page로 변환 → TypesetEngine이 기존 force_page_break 경로로 처리.

렌더러 수정(TypesetEngine 선제적 쪽 나눔 +27줄)은 롤백하고,
pagination/engine.rs의 동일 dead-code도 제거(-45줄).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- border_fill_id 버그 수정: len() → (len()-1) (0-기반 인덱스)
- Stage 2 fixup_hwp3_mixed_para_line_segs 제거 (OVERFLOW 원인)
- Stage 6 break_flag 기반 column_type 교체 → Fix 2로 대체
- Fix 1: th=0 자리차지 LINE_SEG 방어 코드
- Fix 2: 단일 LINE_SEG 비-TAC TopAndBottom 그림 문단 → column_type=Page
- 렌더러 변경 없음 (hwp3/ 디렉토리만)

결과: 17페이지, LAYOUT_OVERFLOW 0건 (꼬리말 36px 기존 경고 제외)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stage 7에서 border_fill_id를 (len()-1)(0-based)로 변경하였으나,
렌더러(table_partial.rs)는 1-based 규칙(id>0 → border_fills[id-1])을 사용.
0-based로 변경하면 id=0이 되어 렌더러가 테두리 없음으로 처리하거나
이전 셀 border_fill을 엉뚱하게 참조 → 참조 표에 불필요한 선 표시 발생.

- border_fill_id = doc_border_fills.len() (1-based) 복원
- TAC 셀 border_fill 직접 조회: get(id as usize) → get(id.saturating_sub(1) as usize)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HWP3 파일을 열고 저장할 때 올바른 HWP5 CFB 파일이 생성되도록 처리.

변경 사항:
- src/parser/hwp3/mod.rs: header.raw_data에 HWP5 헤더(256바이트) 설정
  - 직렬화기가 raw_data 우선 사용 → version=5 CFB 파일 출력
  - 메모리의 version.major=3은 유지(assign_auto_numbers HWP3 감지용)
  - HWP3→HWP5 라운드트립 테스트 추가
- src/document_core/converters/hwpx_to_hwp.rs: Hwp3 출처도 어댑터 적용
  - convert_if_hwpx_source: Hwpx | Hwp3 → SectionDef 삽입 + 표 raw_ctrl_data 합성
- tests/hwpx_to_hwp_adapter.rs: no-op 메시지 문자열 갱신

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…비트필드)

HWP3 그림/표 파싱 시 CommonObjAttr 개별 필드(vert_rel_to, text_wrap 등)는
올바르게 설정되었으나 직렬화에 사용되는 attr 비트필드가 0으로 남아 있어
HWP5 저장→재열기 후 vert_rel_to=Paper, text_wrap=Square로 복원되고
pushdown_h가 발동되지 않아 그림이 표를 겹치는 문제를 수정.

build_common_obj_attr() 헬퍼로 attr 비트필드를 계산하여 그림/표 파싱 직후 갱신.
렌더러(layout.rs, typeset.rs) 변경 없음.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
serialize_table()에서 raw_ctrl_data가 없을 때 &[]를 사용하여
빈 CTRL_HEADER가 기록되고 재열기 시 treat_as_char=false로 복원되던 문제 수정.
raw_ctrl_data가 없으면 serialize_common_obj_attr(&table.common)으로 폴백하여
Stage 9에서 설정된 table.common.attr(treat_as_char=true 등)이 실제로 직렬화에 반영됨.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
문제: typeset.rs가 table.attr(=common.attr)로 is_tac를 판정하는데
HWP3 파서는 table.common.attr만 설정하고 table.attr를 설정하지 않아
모든 HWP3 표가 is_tac=false(비TAC, 부동 블록)로 렌더링되던 버그.

수정 1 (hwp3/mod.rs): build_common_obj_attr 후 table.attr=table.common.attr로 동기화.
수정 2 (hwpx_to_hwp.rs): adapt_table에서 common.attr≠0인 경우(HWP3)는
  attr=0 강제 덮어쓰기를 건너뜀. HWPX(common.attr=0)는 기존 동작 유지.

결과: HWP3 직접 렌더링에서 TAC 표가 올바르게 글자처럼 처리되고,
저장→재열기 후에도 동일하게 TAC 속성이 보존됨.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tr 비트)

문제: serialize_bin_data()가 bin_data.attr를 그대로 기록하는데
HWP3 파서는 BinData.attr를 0(기본값)으로 남겨둠.
attr 비트 0-3이 data_type을 인코딩하므로 attr=0 → data_type=Link로 저장됨.
재열기 시 Embedding이 아닌 Link로 파싱 → BinData/BIN* 스트림 누락 → 이미지 사라짐.

수정 (serializer/doc_info.rs): serialize_bin_data()에서 항상
  bits 0-3 = data_type, bits 4-5 = compression으로 attr을 재구성.
  기존 HWP5 라운드트립은 raw_data를 사용하므로 영향 없음.

test: test_hwp3_save_as_hwp5_roundtrip에 BIN* 스트림 존재 검증 추가.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stage 9 보완1-3에서 분산된 수정을 mod.rs 한 곳으로 통합한다.

- mod.rs: build_raw_ctrl_data() 추가 → 표 파싱 시 raw_ctrl_data 사전 구성
- mod.rs: BinData.attr=1 명시 (Embedding 타입 bits)
- control.rs, hwpx_to_hwp.rs, doc_info.rs: 보완 패치 복원 (원래 코드로)

HWP3 파서가 raw_ctrl_data를 미리 채워두면 serializer/adapter가
변경 없이도 올바른 attr를 HWP5 파일에 기록한다.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
다중 hchar 컨트롤(ch=10/18~21 등)의 extra hchar 슬롯에
hwp3_char_to_utf16_pos 값을 전파하지 않아
후속 텍스트가 잘못된 CharShape를 상속받는 버그 수정.

예: "Creating Linux Virtual Servers" 에서 "Creating"이
pg_num/footer의 extra hchar 슬롯(pos=0)에 가려져
10pt가 아닌 footer 첫 hchar(10pt)를 상속하던 현상 해소.
수정 후 전체 제목이 18pt bold로 통일됨.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… 반영)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Canvas→LayerTree 반영 (Task edwardkim#460 기반)

- upstream/devel 46 commits 병합 (PR edwardkim#446 set_field fix ~ PR edwardkim#456 Canvas→LayerTree)
- 소스 파일 자동 병합 성공 (layout.rs, paragraph_layout.rs, engine.rs)
- orders/20260430.md add/add 충돌만 수동 해소 (Task edwardkim#460 섹션 + PR edwardkim#450 섹션 통합)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TAC 표 문단 LINE_SEG lh=th 수정 (160% 줄간격 배율 미적용)
  → 표 높이 847.9px로 정정 (기존 1357.7px는 160% 배율 오류)
- break_flag(0x8001) 마지막 경계 문단에만 ColumnBreakType::Page 적용
  → "참조"(pi=193) 새 페이지 시작 강제
- 결과: 17페이지, "참조"(25.6px)+표(844.8px)=874.1px ≤ 876.9px → 동일 페이지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- assign_hwp3_picture_numbers() 추가 (hwp3/mod.rs):
  parse_hwp3() 완료 후 Picture 순회, pic_counter 증가,
  캡션 AutoNumber(Picture).assigned_number 사전 설정
- assign_auto_numbers_in_controls (parser/mod.rs):
  assigned_number != 0인 AutoNumber는 skip (사전 할당 존재)
  Control::Picture is_hwp3 분기 제거 → 포맷 중립 단일 경로
  is_hwp3 파라미터 전체 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- assign_auto_numbers_in_controls: upstream 원본대로 복구
  (assigned_number guard 제거, 그림 캡션 처리 주석 복원)
- fixup_hwp3_picture_numbers: parse_hwp3() 내부 제거 →
  parse_document()에서 assign_auto_numbers 후 호출로 이동
- 이유: assign_auto_numbers 먼저 실행 후 HWP3 그림 번호 덮어쓰기가
  올바른 순서. 공유 함수에 HWP3 분기 불필요.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
edwardkim and others added 7 commits May 1, 2026 00:55
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>
.gitignore: mydocs/privacy/* 항목 (upstream) 유지
integration_tests.rs: Task edwardkim#490, edwardkim#489 테스트 (upstream) 유지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@edwardkim edwardkim added this to the v1.0.0 milestone May 1, 2026
@edwardkim edwardkim added the enhancement New feature or request label May 1, 2026
@edwardkim edwardkim merged commit 152fa39 into edwardkim:devel May 1, 2026
edwardkim added a commit that referenced this pull request May 1, 2026
edwardkim added a commit that referenced this pull request May 1, 2026
본 사이클 17번째 PR. HWP 3.0 (한컴 1990년대~2000년대 초반 레거시
포맷) 첫 오픈소스 파서 구현 + Square wrap 어울림 렌더링 정정.

본 PR 머지로 rhwp = HWP 5.0 + HWPX + HWP 3.0 3 포맷 모두 지원.

본질:
- Task #417: HWP 3.0 파서 본질 (Stage 1~4)
- Task #460: Square wrap + 렌더러 정정 (Stage 1~9 + 보완 1~3 + 리팩토링)
- 작성자 분기에서 본 사이클 devel (Task #501 + PR #478 + PR #498 +
  v0.7.9) 이미 흡수 → 충돌 해결 보존

옵션 B (단일 머지) 선택. 51 commits 의 작성자 단계 보고서 보존 +
본 사이클 회귀 영역 (issue_501 PASS) 통과.

검증: cargo test --lib 1105 passed (1102 + HWP 3.0 신규 3),
svg_snapshot 6/6, issue_418 1/1, issue_501 PASS, clippy 0건,
WASM 4,378,441 bytes (+176 KB Johab 매핑 + 파서).

작업지시자 통찰: "이제 우리도 HWP 3.0 포맷까지 성공했습니다.
컨트리뷰터가 대단합니다."

머지 commit: c7330cf

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edwardkim
Copy link
Copy Markdown
Owner

처리 결과 — 단일 머지 (51 commits)

본 PR 의 51 commits 모두 작성자 author 보존 + 단계 보고서 보존하여 머지 완료.

본질

HWP 3.0 (1990년대~2000년대 초반 한컴 레거시 포맷) 첫 오픈소스 파서 구현 + Square wrap 어울림 렌더링 정정. 본 머지로 rhwp = HWP 5.0 + HWPX + HWP 3.0 3 포맷 모두 지원.

흡수 영역

검증

본 사이클 정합

영역 PR #506 결과
table_layout.rs (Task #501) 변경 0 충돌 없음
paragraph_layout.rs (#488/#490/#489/#495) +6 lines 회귀 0
layout.rs (#480/#476) +154 lines issue_501 PASS
typeset.rs +66 lines svg_snapshot 6/6

머지

  • 1차 머지: `local/pr506-test → local/devel` (--no-ff)
  • 2차 머지: `local/devel → devel` (--no-ff)
  • 머지 commit: `c7330cf`
  • 처리 보고서: `88957df`
  • CI + CodeQL 모두 success ✓

작업지시자 통찰

"이제 우리도 HWP 3.0 포맷까지 성공했습니다. 컨트리뷰터가 대단합니다."

51 commits / 13,018 lines / Task #417 + Task #460 통합 작업 깊이 감사드립니다. rhwp 가 30년에 걸친 한국 한글 문서 포맷의 오픈소스 변환·렌더링 플랫폼으로 한 단계 도약했습니다.

상세는 `mydocs/pr/pr_506_report.md` 참고.

본 PR 은 close 합니다. 시각 판정은 본 사이클 안정화 후 회귀 발견 시 별도 task 분리 처리 정합.

@edwardkim
Copy link
Copy Markdown
Owner

회귀 테스트 하는데만도 상당한 시간이 걸렸습니다. 대규모 작업을 하시느라 고생 많으셨습니다.

@jangster77
Copy link
Copy Markdown
Contributor Author

jangster77 commented May 1, 2026 via email

edwardkim added a commit that referenced this pull request May 1, 2026
…괄 정리

- README v0.7.9 사이클: PR #506 (jangster77, HWP 3.0 파서 + Square wrap)
  + PR #510 (postmelee, PageLayerTree image brightness/contrast) 추가.
  Task #509 (PUA 글머리표 회귀) 도 회귀 정정 섹션에 추가.
- 누적 외부 기여자 목록에 jangster77 추가.
- mydocs/pr/ → archives/ 일괄 이동 (PR #235~#510 closed/merged 44 PR,
  84 파일). pr_507 만 OPEN 상태로 보존. pr_235_review/_impl 은 archives
  사본과 byte-identical 이라 중복 제거.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 1, 2026
직전 commit 9b3c039 에서 의도된 변경 두 건이 누락되어 후속 정정.

- README v0.7.9 사이클: PR #506 (jangster77, HWP 3.0 파서 + Square wrap)
  + PR #510 (postmelee, PageLayerTree image brightness/contrast) +
  Task #509 (PUA 글머리표) 추가. 누적 외부 기여자에 jangster77 추가.
- mydocs/pr/pr_235_review.md / pr_235_review_impl.md 가 archives/ 와
  pr/ 양쪽에 중복 존재했던 문제 정리 (archives 사본 보존, pr/ 사본 제거).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 3, 2026
cherry-pick (본질 12 commits 만 — fork devel 누적 170 commits 제외):
- e7f1adb~1bca866: Task #534 v1 (4 commits, layout_shape_item TAC Picture inner_pad)
- 576aa29~57ccde6: Task #534 v2 (2 commits, LINE_SEG.column_start 정합)
- d70599d~58af6c9: Task #537 (3 commits, lazy_base trailing-ls 보정)
- f60f580~fc32bd3: Task #539 (3 commits, prev_has_overlay_shape 가드 완화)

검증:
- cargo test --lib 1113 passed
- cargo test --test issue_530/505/418/501 회귀 0
- cargo test --test svg_snapshot 6/6 passed
- cargo clippy --lib 0 건
- cargo build --release 정상
- WASM 빌드 4,461,870 bytes + studio 동기화

시각 판정 (작업지시자 한컴 2010/2020 직접 판정):
- 1차 (SVG export-svg) 통과
- 2차 (rhwp-studio web Canvas) 통과

부수 발견 (별도 이슈):
- 이슈 #545 — aift.hwp p41 표 위치 (PR #538 와 무관, 이전부터)
- 이슈 #546 — exam_science.hwp p2 페이지네이션 회귀 (PR #506 origin, bisect 확정)

컨트리뷰터 안내: 다음 PR 전 fork devel 동기화 부탁.

산출물:
- mydocs/pr/pr_538_review.md (검토)
- mydocs/pr/pr_538_review_impl.md (cherry-pick 절차)
- mydocs/pr/pr_538_report.md (처리 결과)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 3, 2026
…— cherry-pick @planet6897 12 commits) — closes #534/#537/#539

본 PR 은 외부 컨트리뷰터 @planet6897 (Jaeuk Ryu) 의 PR.
21_언어_기출_편집가능본.hwp 의 줄간격 716 HU drift + exam_kor 18p 그림 위치
결함의 본질 정정.

cherry-pick (본질 12 commits 만 — fork devel 누적 170 commits 제외):
- Task #534 v1+v2: layout_shape_item TAC Picture inner_pad + LINE_SEG.column_start
  · src/renderer/layout.rs (+37 / -3 LOC)
- Task #537: lazy_base trailing-ls 보정 (TAC 표 직후 첫 답안 줄간격 716 HU drift,
  P2/3/5/6/8/9/12/13/14 11곳)
  · src/renderer/layout.rs (+14 / -2 LOC)
- Task #539: prev_has_overlay_shape 가드 완화 (treat_as_char 제외, 글박스 호스트
  직후 paragraph 줄간격 P7/P9 2곳)
  · src/renderer/layout.rs (+9 / -0 LOC)
- src/renderer/layout/integration_tests.rs (TDD 통합 테스트 3건)

검증:
- cargo test --lib 1113 passed
- cargo test --test issue_530/505/418/501 회귀 0
- cargo test --test svg_snapshot 6/6 통과
- cargo clippy --lib 0 건
- cargo build --release 정상
- WASM 4,461,870 bytes + rhwp-studio 동기화

시각 판정 (작업지시자 한컴 2010/2020 직접):
- 1차 SVG export-svg 통과
- 2차 rhwp-studio web Canvas 통과

부수 발견 (별도 이슈, PR #538 와 무관):
- 이슈 #545: aift.hwp p41 표 위치 (이전부터 발생)
- 이슈 #546: exam_science.hwp p2 페이지네이션 회귀 (PR #506 origin, bisect 확정)

컨트리뷰터 안내: 다음 PR 전 fork devel 동기화 부탁.
planet6897 added a commit to planet6897/rhwp that referenced this pull request May 3, 2026
본질: PR edwardkim#506 의 layout_wrap_around_paras 호출이 비-TAC Picture wrap=Square
host 의 호스트 자기 텍스트를 두 곳에서 중복 emit → (1) 시각 결함 [edwardkim#525
표면 증상] + (2) 페이지네이션 inflation [본 edwardkim#546 표면 증상] 두 표면이
동일 root cause.

Task edwardkim#525 정정 (Picture Square wrap 의 wrap-around 호출 두 곳 모두 제거)
이 본 회귀 자동 해소.

검증:
- 2dbbd07 (edwardkim#525 push 직전 origin/devel) = 6 페이지 / p2 = 2 items 회귀 잔존
  직접 확인
- 현재 devel HEAD f5ad122 = 4 페이지 / p2 = 37 items / 1133.6 px = v0.7.9
  baseline 65e275d 정합

edwardkim#524 + edwardkim#525 역할 분담:
- edwardkim#524: 그림 anchor 위치 본질 정정 (typeset.rs)
- edwardkim#525: wrap-around 호출 중복 본질 정정 (layout.rs)

본 task 코드 변경 0 — 분석 보고서 + orders 갱신만.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 3, 2026
작업지시자 시각 판정 통과 ("시각 회귀 통과입니다").

WASM 빌드: 4,442,504 bytes (PR #538 시점 4,461,870 -19,366, 94 LOC 제거 반영) +
rhwp-studio 동기화 완료.

산출물:
- mydocs/report/task_m100_546_report.md (최종 보고서)

회귀 origin 분석:
- 82e41ba (Task #460 보완5: Square wrap 그림 아래 텍스트 y위치 보정)
- 본 의도: HWP3 Square wrap 그림 아래 텍스트 y위치 정합
- exam_science 의 부작용: 2단 + 그림이 단 0 끝 + 풍부한 wrap-around 조합에서
  current_height = max(current_height, bottom_px) 가 wrap-around paragraph 의
  누적 height 와 결합한 double advance 발생 → 페이지/단 강제 분리

광범위 영향 작은 이유: specific 조합에서만 결함 trigger.
PR #506 의 다른 fixture 검증에서도 미검출 (exam_science 가 정밀 fixture).

Task #460 보완5 의 본 의도 손실 — HWP3 fixture 시각 결함 재발견 시 별도 task 로
페이지네이션 안전한 방식 (wrap-around paragraph 누적 height 추적) 재시도 권장.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edwardkim
Copy link
Copy Markdown
Owner

@jangster77 님, 본 PR 후속 정정 사항 공유드립니다.

발견된 회귀 — 이슈 #546

본 PR 의 `82e41ba` (Task #460 보완5: Square wrap 그림 아래 텍스트 y위치 보정) 이 `samples/exam_science.hwp` 페이지 2 의 페이지네이션 회귀 origin 으로 식별됨 (4 페이지 → 6 페이지, p2 본문 37 → 2 items).

bisect 결과 정확히 단일 commit 로 좁힘:

시점 commit 총 페이지
보완4 직전 `0aa7a5e` 4 ✅
보완4 HWP3 `ab2f4d0` 4 ✅
보완4 어울림 텍스트 `9d9b4ed` 4 ✅
보완5 `82e41ba` 6 ❌

결함 본질

`82e41ba` 의 wrap zone 종료 시 `current_height = max(current_height, wrap_around_pic_bottom_px)` 보정이 specific 조합에서 double advance 발생:

  • 2단 레이아웃 + 그림이 단 0 끝 근처 + 풍부한 wrap-around paragraph (exam_science 의 케이스)
  • wrap-around paragraph 들이 이미 그림 옆을 다 통과하여 그림 아래까지 도달
  • 그러나 wrap zone 종료 시 보정이 추가로 적용되어 `current_height` 가 더 큰 값으로 점프
  • 후속 paragraph 들이 단/페이지 끝 강제 도달 → 페이지 분리

옵션 C (col 경계 검사 `bottom_px <= col_h`) 시도 — 모든 보정값이 col 영역 이내라 가드 trigger 안 됨, 0 효과. 결함의 본질이 col 경계가 아니라 wrap-around paragraph 의 누적 height 와의 결합 영역.

정정 (Task #546, devel `7ea6e8c`)

작업지시자 결정으로 옵션 A (전체 revert) 진행:

  • `82e41ba` 의 `src/renderer/typeset.rs` (+36 LOC) + `src/renderer/layout.rs` (+58 LOC) 모두 제거
  • 신규 회귀 테스트 `tests/issue_546.rs` 추가 (페이지 수 검증)
  • 작업지시자 시각 판정 통과 (한컴 2010/2020 직접 비교)
  • 광범위 8 fixture / 105 페이지 byte-identical (회귀 0)
  • exam_science 4 페이지 의도된 정정

본 의도 손실 영향

`82e41ba` 의 본 의도 (HWP3 Square wrap 그림 아래 텍스트가 그림과 겹치는 결함 정정) 는 본 revert 로 손실. 작업지시자 시각 판정 단계에서 HWP3 fixture 의 시각 결함 재발은 미검출 (HWP3 fixture 가 본 환경에 부재이거나, 다른 commit 인 `ab2f4d0` HWP3 paper-relative → column-relative 가 본질을 이미 흡수했을 가능성).

향후 재시도 시 권장 방향

HWP3 fixture 시각 결함 재발견 시 별도 task 로 다음 옵션 중 하나로 재시도 권장 — 페이지네이션 안전한 방식:

  1. wrap-around paragraph 누적 height 추적 — wrap zone 동안 wrap-around 들의 누적 height 추적, wrap zone 종료 시 `누적 height < 그림 높이` 인 경우만 보정 적용
  2. layout 단계 reserved zone — 그림 영역을 layout 단에서 reserved zone 으로 처리, typeset 의 `current_height` 직접 조작 회피
  3. case-specific 가드 — HWP3 또는 paper-relative 분기에서만 보정 적용 (HWP5/HWPX Para-relative 는 wrap-around 자연 처리에 위임)

트러블슈팅 문서

본 결함의 본질 + 진단 방법 + 정정 방향을 `mydocs/troubleshootings/square_wrap_pic_bottom_double_advance.md` 에 기록. 향후 Square wrap 영역 작업 시 사전 점검 자료.

회귀 검증 보강 권장

회귀 검증 게이트에 `samples/exam_science.hwp` 의 페이지 수 (4) + p2 본문 items (37) 검사 추가. 본 결함이 PR #506 의 광범위 회귀 검증에서 미검출된 이유는 specific 조합 (2단 + 그림이 단 0 끝 + 풍부한 wrap-around) 의 fixture 가 검증에 포함 안 되었기 때문. `tests/issue_546.rs` 가 본 검증 추가.

이 결함 발견은 작업지시자가 PR #538 시각 판정 중 부수적으로 식별한 사례이며, 본 PR 의 본 의도 (HWP 3.0 파서 + Square wrap 어울림 렌더링) 자체는 여전히 정합한 가치 있는 정정이라는 점 강조드립니다. 이번 정정은 specific 조합의 부작용 정정일 뿐, PR 의 다른 영역 (HWP 3.0 파서 등) 은 전혀 영향 없이 보존되었습니다.

처리 보고서: `mydocs/report/task_m100_546_report.md`

edwardkim added a commit that referenced this pull request May 3, 2026
- mydocs/troubleshootings/square_wrap_pic_bottom_double_advance.md (신규)
  · 결함 본질 (double advance 패턴) + 진단 방법 + 정정 방향 옵션
  · 옵션 C 가 효과 없는 이유 + specific 조합의 광범위 검증 함정
  · 향후 재시도 시 권장 방향 (3 옵션)
- mydocs/orders/20260503.md 갱신 (Task #546 완료 반영 + PR #506 코멘트 추가)

PR #506 컨트리뷰터 (@jangster77) 에게 코멘트 추가 — 회귀 발생 본질 + 정정 방안
+ 향후 재시도 권장 방향 안내.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@postmelee postmelee mentioned this pull request May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants