Skip to content

Task #1016: Resolve image payloads in PaintOp::Image#1018

Closed
postmelee wants to merge 1 commit into
edwardkim:develfrom
postmelee:local/task1016
Closed

Task #1016: Resolve image payloads in PaintOp::Image#1018
postmelee wants to merge 1 commit into
edwardkim:develfrom
postmelee:local/task1016

Conversation

@postmelee

@postmelee postmelee commented May 19, 2026

Copy link
Copy Markdown
Collaborator

왜 변경했나

#976은 워터마크성 JPEG를 한컴 참고 톤의 baked PNG로 바꾸는 문제를 해결했지만, 해당 처리가 renderer별 경로에 남아 있으면 PageLayerTree / native Skia / browser CanvasKit 전환 과정에서 같은 이미지가 서로 다른 payload로 replay될 수 있습니다.

이 PR은 이미지 포맷 변환과 워터마크 bake 판단을 renderer 내부가 아니라 PaintOp::Image 생성 단계로 올립니다. renderer들은 더 이상 각자 원본 ImageNode를 재판정하지 않고, 이미 resolved 된 visual payload를 공유해 replay합니다.

무엇을 변경했나

  • src/renderer/image_resolver.rs 추가
    • BMP / PCX -> PNG 변환 payload
    • 워터마크성 JPEG -> 한컴 참고 톤 baked PNG payload
    • resolved payload를 임시 ImageNode에 반영하는 helper
  • PaintOp::Imageresolved: Option<Box<ResolvedImagePayload>> 추가
    • ResolvedImageKind::{FormatConverted, BakedWatermark}
    • ResolvedImagePayload { data, mime, kind, suppress_effects }
  • LayerBuilder에서 RenderNodeType::Image를 낮출 때 resolver 호출
  • PageLayerTree JSON에서 resolved payload를 우선 사용
    • baked watermark이면 bakedWatermark:true
    • schema minor 12 -> 13
  • Studio overlay JSON이 별도 JPEG 재판정 없이 PaintOp::Image.resolved를 사용하도록 정리
  • native Skia / Canvas / WebCanvas / SVG layer replay가 resolved bytes를 보존하도록 연결
  • legacy SVG helper 구현을 공통 resolver 쪽으로 이동하고 기존 helper 이름은 re-export로 유지
  • tests/issue_938.rs에 PageLayerTree resolved watermark 회귀 테스트 추가

범위 제외

작업 중 native Skia PNG export에서 wrap=behindText 워터마크가 글 내용 위에 보이는 문제가 확인됐습니다.

이는 이 PR의 image payload 문제가 아니라 PageLayerTree replay z-order / compositor 정책 문제입니다. #976의 baked watermark PNG는 transparent PNG가 아니라 opaque PNG이므로, 사각형이 보이지 않으려면 background -> behindText image -> flow text -> inFrontOfText image 순서로 합성되어야 합니다.

따라서 #1016의 완료 조건은 "renderer들이 같은 resolved image payload를 공유한다"는 contract로 좁혔고, z-order replay 일반화는 #1017로 분리했습니다.

관련 이슈

Refs #1016
Follows #976
Related #1017

테스트

  • cargo fmt --all -- --check 통과
  • cargo test 통과
  • cargo clippy -- -D warnings 통과
  • 관련 샘플 파일로 native Skia PNG 내보내기 확인
  • 관련 PageLayerTree / SVG / Studio 회귀 테스트 확인
  • 웹(WASM) production build 확인

통과한 추가 검증:

  • cargo check
  • cargo test --test issue_938
  • cargo test --test issue_516
  • cargo test --test issue_514
  • cargo test --test svg_snapshot
  • npm --prefix rhwp-studio test
  • cargo check --features native-skia
  • cargo test --features native-skia --no-run
  • cargo test --features native-skia skia --lib
  • cargo run --features native-skia --bin rhwp -- export-png samples/복학원서.hwp -p 0 -o output/task1016-stage4
  • docker-compose --env-file .env.docker run --rm wasm
  • npm --prefix rhwp-studio run build
  • git diff --check

확인 결과:

  • PageLayerTree 진단: image ops: 2, wrap=behindText: 2, mime png: 2, mime jpg: 0
  • native Skia PNG 산출: output/task1016-stage4/복학원서.png, 794 x 1123, 175080 bytes
  • Studio production build 산출: rhwp-studio/dist/ 생성, rhwp_bg-*.wasm 포함

참고:

  • npm ci 중 기존 의존성에서 deprecated package 경고와 moderate vulnerability 1건이 보고됐습니다. lockfile 기준 설치는 성공했고 build도 통과했습니다.
  • vite build 중 CanvasKit의 fs / path browser externalization 경고와 500 kB 초과 chunk 경고가 출력됐습니다. build는 성공했습니다.

스크린샷

별도 첨부 없음.

문서

  • mydocs/plans/task_m050_1016.md
  • mydocs/plans/task_m050_1016_impl.md
  • mydocs/working/task_m050_1016_stage1.md
  • mydocs/working/task_m050_1016_stage3.md
  • mydocs/working/task_m050_1016_stage4.md
  • mydocs/report/task_m050_1016_report.md

@postmelee postmelee changed the title Resolve baked watermark image payloads Task #1016: Resolve baked watermark image payloads May 19, 2026
@postmelee postmelee changed the title Task #1016: Resolve baked watermark image payloads Task #1016: Resolve image payloads in PaintOp::Image May 19, 2026
@postmelee postmelee marked this pull request as ready for review May 19, 2026 16:21
@edwardkim edwardkim self-requested a review May 19, 2026 23:20
@edwardkim edwardkim added this to the v1.0.0 milestone May 19, 2026
@edwardkim edwardkim added the enhancement New feature or request label May 19, 2026
edwardkim added a commit that referenced this pull request May 19, 2026
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim added a commit that referenced this pull request May 19, 2026
…PaintOp::Image

@postmelee — 이미지 변환(BMP/PCX→PNG) + 워터마크 JPEG→baked PNG 판정이
5+ renderer 별 사본(svg/canvas/web_canvas/skia/paint/json)에 분산되어
있던 것을 LayerBuilder 단계 단일 image_resolver::resolve_image_payload()
진입점으로 통합. 결과를 ResolvedImagePayload 로 패키지하여
PaintOp::Image.resolved: Option<Box<...>> 옵션 필드에 부착 (schema minor
12→13, 하위호환). 모든 renderer 는 resolved payload 소비만 — 재판정 없음.

옵션 A: 본질 커밋 3aeaa5b cherry-pick (작성자 postmelee 보존,
orders/20260520.md --ours 1건 충돌 해소). 검증: cargo test 1307 +
cargo test --test issue_938 3건(overlay/svg/PageLayerTree resolved
watermark contract) + clippy -D + fmt 0 + WASM 4.83MB.

광범위 sweep 10 fixture (BEFORE devel ↔ AFTER): 복학원서(워터마크),
sample16-hwp5/hwp3, hy-001 HWPX/HWP5, exam_kor/math, aift, biz_plan,
test-image — **전부 diff=0** (SVG 출력 100% 동일). 광범위 표면(24파일,
7개 renderer 경로)에도 시각 결과 완전 보존 — feedback_image_renderer_
paths_separate 본질적 해소 + 시각 호환성 100% 모범 사례. 작업지시자
시각 판정 생략 통과 (sweep diff=0 정량 입증).

PR scope 좁힘: z-order replay 일반화는 #1017로 분리 (PR 본문 명시).
@postmelee 후속 #1019 (Task #975 PageBackground fill + RealPic
watermark tone) OPEN 시리즈 연속.
@edwardkim

Copy link
Copy Markdown
Owner

옵션 A로 devel에 반영했습니다 (본질 커밋 3aeaa5b cherry-pick, 작성자 메타데이터 보존, orders/20260520.md --ours 1건 충돌 해소).

검증:

  • cargo test --release --lib 1307 passed / cargo test --test issue_938 3 passed (overlay/svg/PageLayerTree resolved watermark contract) / clippy -D / fmt 0 / WASM 4.83MB
  • 광범위 sweep 10 fixture (BEFORE devel ↔ AFTER): 전부 diff=0 (복학원서 워터마크, sample16-hwp5/hwp3, hy-001 HWPX/HWP5, exam_kor/math, aift, biz_plan, test-image) — SVG 출력 100% 동일. 24파일/7개 renderer 경로 광범위 표면에도 시각 결과 완전 보존
  • 작업지시자 시각 판정 생략 통과 (sweep diff=0 정량 입증으로 대체)

feedback_image_renderer_paths_separate 메모리 룰 도입(Task #514/#516) 이래 가장 직접적 아키텍처 정정입니다. 5+ renderer 별 사본을 단일 image_resolver 진입점으로 통합 + PaintOp::Image.resolved 옵션 필드 + schema minor 12→13 하위호환 — 아키텍처 정정 + 시각 호환성 100% 유지 모범 사례. scope 좁힘(z-order는 #1017 분리)도 명확합니다.

이슈 #1016은 Refs 관계로 OPEN 유지합니다 (workflow 일반화 진행 상황 추적용). #1019 후속 검토 대기합니다.

@edwardkim edwardkim closed this May 19, 2026
edwardkim added a commit that referenced this pull request May 20, 2026
… + LAYOUT_OVERFLOW 42→12

@planet6897 분할 표 시리즈 마무리 (#1003 + #1004 머지 후 발전형, devel 최신
기준 재구성으로 MERGEABLE). RowCut 이산 모델(cell_units + advance_row_cut
단일 권위 함수)로 페이지네이터·렌더러 측정 통합 — PR #1004 휴리스틱
정정 일반화. closes #1022 — LAYOUT_OVERFLOW 42→12 (71% 감소) 정량 측정.

v2 trailing-ls 조건부 복원 (issue_598 회귀 자정) — lazy_base_corrected >= 0
조건부 가드로 vpos≠0 시작 컬럼 보정 + IR 정확 추적 케이스 비보정.
복학원서 본문 시작 band 196→214=PDF 일치 PR 본문 명시 (본 환경 미발동,
후속 관찰).

옵션 A: 본질 squash commit bc1cd4d cherry-pick (작성자 Jaeook Ryu 보존,
충돌 없음). 검증: cargo test 1308(lib) + 전체 통합 + pagination 15 +
wasm_api 160 + clippy -D 전체(--lib 미한정 CI 패턴) + fmt --all --check
(feedback_push_full_test_required + CI 패턴 정합). sweep 10 fixture:
hy-001/table-vpos-01/복학원서 diff=0, sample16-hwp5 13/64 + sample16-hwp3
2/64 + aift 27/74 + exam_kor 16/20 + biz_plan 3/6 광범위 변동 (분할 표
영역 의도된 효과). WASM 4.84MB.

작업지시자 결정 "그대로 A로" — 광범위 변동 시각 판정 생략 머지. 정량
측정(LAYOUT_OVERFLOW 71% 감소) + 회귀 가드(issue_598/svg_snapshot) +
CI 검증 항목 통과로 게이트 대체. golden 갱신: issue-617 + form-002
(PR 본문 명시 issue-677은 PR 적용 후 출력=현재 golden binary identical
로 갱신 불필요).

feedback_pr_supersede_chain 권위 사례 — 작은 단위(#1003) + 부분(#1004) +
발전형(#1024) 순차 적층 → 단일 권위 모델 일반화. feedback_image_renderer_
paths_separate 본질 정합 (PR #1018 image_resolver 패턴).
edwardkim pushed a commit that referenced this pull request May 20, 2026
… 4 hunk 복원

PR #1003 (closes #990) 의 cherry-pick `--theirs` 충돌 해소가 PR #1011
(closes #1006) 의 PageBorderBasis 통합 contract 를 PR #987 시절 attr 비트
해석으로 무심코 revert. HWP3 native (attr=0) 만 paper_based=false 로 판정되어
외곽선이 body-edge 로 좁아진 시각 회귀 발생.

Bisect 단언 (border_top y):
- 850cfb5 (PR #1011): 17.88 (paper-edge, 기준선)
- 71aedda (PR #1015): 17.88
- 84246b2 (PR #1018): 17.88
- 27c05d5 (PR #1020): 17.88
- b5d3834 (PR #1021): 17.88
- c2024ec (PR #1003): 55.64 ← 회귀 도입
- 65c8e69 (devel HEAD): 55.64

Fix: src/renderer/layout.rs 4 hunk 복원
- 변경 A: page_number_baseline_y() — (attr & 0x01) → matches!(basis, PaperBased)
- 변경 B: build_page_borders() — 동일 + 주석 + 디버그 로그 (bit1/bit2/footer_inside)
- 변경 C: footer_inside clip 복원 — 페이지 번호 외곽선 바깥 위치 (PR #1011)
- 변경 D: `(bx, mut by, bw, mut bh)` — footer clip 의 by/bh 수정 위함

Parser 측 PR #1011 변경 (basis=PaperBased 주입) 은 PR #1003 cherry-pick 시
보존됨. fix 는 renderer (layout.rs) 한정 단일 파일.

검증 (Stage 2 별도 보고):
- 3 포맷 paper_based=true 단언 (RHWP_DEBUG_PAGE_BORDER)
- HWP3 sample16 border_top y=17.88 복원
- HWP5/HWPX 변환본 무변동 (17.88 유지)
- cargo test --release --lib 1307 passed
- cargo clippy --release --lib -- -D warnings clean
- cargo fmt --check clean

Refs #1029

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@postmelee postmelee deleted the local/task1016 branch June 7, 2026 19:34
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.

2 participants