feat: embed images as base64 data URIs in SVG/HTML export#335
Merged
edwardkim merged 2 commits intoedwardkim:develfrom Apr 26, 2026
Merged
feat: embed images as base64 data URIs in SVG/HTML export#335edwardkim merged 2 commits intoedwardkim:develfrom
edwardkim merged 2 commits intoedwardkim:develfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
SVG/HTML 내보내기에서 이미지 데이터를 base64 data URI로 임베딩하여, 기존의 placeholder(회색 박스) 대신 실제 이미지가 포함된 결과물을 생성하려는 PR입니다.
Changes:
SvgRenderer::draw_image에서 MIME 감지 + base64 인코딩을 통해<image href="data:...">로 임베딩HtmlRenderer::draw_image에서<img src="data:...">로 임베딩 및 WMF→SVG 변환 경로 추가detect_image_mime_type를pub(crate)로 변경하여 HTML에서 재사용
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/renderer/svg.rs |
SVG 이미지 렌더링용 data URI 생성 로직 추가, MIME 감지 함수 가시성 확장 |
src/renderer/html.rs |
HTML 이미지 렌더링용 data URI 생성 로직 추가, SVG 모듈의 MIME/WMF 변환 유틸 재사용 |
Comments suppressed due to low confidence (1)
src/renderer/svg.rs:2105
- detect_image_mime_type is now reused by the HTML renderer, but it recognizes fewer formats than the WASM web_canvas implementation (e.g., WEBP via RIFF/WEBP signature and ICO via 00 00 01 00). As a result, those images would be exported with data:application/octet-stream;base64,... and won’t render in HTML/SVG. Consider extending this function to detect at least WEBP and ICO to match web_canvas behavior.
pub(crate) fn detect_image_mime_type(data: &[u8]) -> &'static str {
if data.len() >= 8 {
// PNG: 89 50 4E 47 0D 0A 1A 0A
if data.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) {
return "image/png";
}
// JPEG: FF D8 FF
if data.starts_with(&[0xFF, 0xD8, 0xFF]) {
return "image/jpeg";
}
// GIF: GIF87a or GIF89a
if data.starts_with(b"GIF87a") || data.starts_with(b"GIF89a") {
return "image/gif";
}
// BMP: BM
if data.starts_with(&[0x42, 0x4D]) {
return "image/bmp";
}
// WMF: Placeable (D7 CD C6 9A) 또는 Standard (01 00 09 00)
if data.starts_with(&[0xD7, 0xCD, 0xC6, 0x9A]) || data.starts_with(&[0x01, 0x00, 0x09, 0x00]) {
return "image/x-wmf";
}
// TIFF: II or MM
if data.starts_with(&[0x49, 0x49, 0x2A, 0x00]) || data.starts_with(&[0x4D, 0x4D, 0x00, 0x2A]) {
return "image/tiff";
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+394
to
410
| fn draw_image(&mut self, data: &[u8], x: f64, y: f64, w: f64, h: f64) { | ||
| let mime_type = detect_image_mime_type(data); | ||
| let (render_data, render_mime): (std::borrow::Cow<[u8]>, &str) = if mime_type == "image/x-wmf" { | ||
| match convert_wmf_to_svg(data) { | ||
| Some(svg_bytes) => (std::borrow::Cow::Owned(svg_bytes), "image/svg+xml"), | ||
| None => (std::borrow::Cow::Borrowed(data), mime_type), | ||
| } | ||
| } else { | ||
| (std::borrow::Cow::Borrowed(data), mime_type) | ||
| }; | ||
| let base64_data = base64::engine::general_purpose::STANDARD.encode(&*render_data); | ||
| let data_uri = format!("data:{};base64,{}", render_mime, base64_data); | ||
| self.output.push_str(&format!( | ||
| "<div class=\"hwp-image\" style=\"position:absolute;left:{}px;top:{}px;width:{}px;height:{}px;background:#eee;\"></div>\n", | ||
| x, y, w, h, | ||
| "<img class=\"hwp-image\" src=\"{}\" style=\"position:absolute;left:{}px;top:{}px;width:{}px;height:{}px;\" />\n", | ||
| data_uri, x, y, w, h, | ||
| )); | ||
| } |
Comment on lines
+394
to
410
| fn draw_image(&mut self, data: &[u8], x: f64, y: f64, w: f64, h: f64) { | ||
| let mime_type = detect_image_mime_type(data); | ||
| let (render_data, render_mime): (std::borrow::Cow<[u8]>, &str) = if mime_type == "image/x-wmf" { | ||
| match convert_wmf_to_svg(data) { | ||
| Some(svg_bytes) => (std::borrow::Cow::Owned(svg_bytes), "image/svg+xml"), | ||
| None => (std::borrow::Cow::Borrowed(data), mime_type), | ||
| } | ||
| } else { | ||
| (std::borrow::Cow::Borrowed(data), mime_type) | ||
| }; | ||
| let base64_data = base64::engine::general_purpose::STANDARD.encode(&*render_data); | ||
| let data_uri = format!("data:{};base64,{}", render_mime, base64_data); | ||
| self.output.push_str(&format!( | ||
| "<div class=\"hwp-image\" style=\"position:absolute;left:{}px;top:{}px;width:{}px;height:{}px;background:#eee;\"></div>\n", | ||
| x, y, w, h, | ||
| "<img class=\"hwp-image\" src=\"{}\" style=\"position:absolute;left:{}px;top:{}px;width:{}px;height:{}px;\" />\n", | ||
| data_uri, x, y, w, h, | ||
| )); | ||
| } |
Previously draw_image rendered a grey placeholder rect (SVG) or empty div (HTML). Now it detects the image MIME type, converts WMF to SVG when needed, and encodes the image data as a base64 data URI. This matches the existing behavior in render_picture and web_canvas, bringing the SVG/HTML export backends to parity. - SVG: <image href="data:image/...;base64,..."/> - HTML: <img src="data:image/...;base64,..."/> - Made detect_image_mime_type pub(crate) for reuse in html.rs - WMF conversion reused from existing convert_wmf_to_svg Verified with samples/hwp-img-001.hwp: 4 images (JPEG, PNG, BMP) correctly embedded in SVG output.
Address Copilot review feedback: 1. render_node's RenderNodeType::Image branch now calls draw_image when image data is present, instead of always emitting a grey placeholder div. Falls back to placeholder when data is None. 2. Add 3 unit tests for draw_image: PNG, JPEG, and unknown format headers, asserting correct data URI MIME types in output.
37a0850 to
b882104
Compare
Owner
|
@oksure 님, 두 번째 PR 감사합니다 🙏 검증
처리PR #334 와 동일 패턴으로 cherry-pick + author 보존 + force-push + base devel 로 변경 완료. CI 통과 직후 admin merge 진행하겠습니다.
|
edwardkim
approved these changes
Apr 26, 2026
Owner
edwardkim
left a comment
There was a problem hiding this comment.
검증 완료 (1000 lib + 6/6 svg_snapshot + clippy + wasm32 + CI SUCCESS + 시각 검증). admin merge.
edwardkim
added a commit
that referenced
this pull request
Apr 26, 2026
@oksure 의 두 번째 PR. cherry-pick + author 보존 + force-push + admin merge (commit 1bac451). draw_image (svg/html) 의 placeholder → base64 data URI 임베딩. render_picture / web_canvas 와의 backend 정합. 검증: 1000 lib (997 → +3 신규) + svg_snapshot 6/6 + clippy + wasm32 + hwp-img-001.hwp 시각 검증 (JPEG/PNG data URI 임베딩 확인). Co-Authored-By: Hyunwoo Park <oksure@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edwardkim
added a commit
that referenced
this pull request
Apr 26, 2026
edwardkim
added a commit
that referenced
this pull request
Apr 26, 2026
라이브러리 버전 동기화 (Cargo.toml / rhwp-vscode / npm/editor / rhwp-studio): 0.7.3 → 0.7.6 브라우저 확장 (rhwp-firefox): 0.2.1 → 0.2.2 (AMO 재제출용) - manifest strict_min_version 142 + viewer 번들 보안 sanitize 반영 본 사이클 외부 기여 PR: - #268/#334 (@oksure): replaceOne API - #279/#282 (@seanshin): 목차 리더 + 페이지번호 정렬 - #324/#327 (@planet6897): form-002 인너 표 페이지 분할 - #335 (@oksure): SVG/HTML draw_image base64 임베딩 - #338/#339 (@postmelee): Firefox AMO 워닝 해결 - #340/#341 (@planet6897): typeset 경로 정합 - #342/#343 (@planet6897): Task #321~#332 통합 + 회귀 해소 rhwp-firefox/README.md 에 v0.2.2 변경 이력 + 기여자 감사 섹션 추가 (@postmelee, @seanshin 인정). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
seanshin
pushed a commit
to seanshin/rhwp
that referenced
this pull request
Apr 26, 2026
- 최근 변경: v0.7.3 → v0.7.6 (2026-04-26) 교체 - PR edwardkim#266 (Task edwardkim#157), edwardkim#273 (Task edwardkim#267), edwardkim#282 (Task edwardkim#279) by @seanshin - PR edwardkim#256, edwardkim#327, edwardkim#341, edwardkim#343 by @planet6897 - PR edwardkim#334, edwardkim#335 by @oksure, PR edwardkim#339 by @postmelee - devel 섹션: 머지된 항목 제거, 현재 분석 중(edwardkim#362/edwardkim#345) + 계획 중(edwardkim#150/edwardkim#253) 반영 - 테스트 수: 891+ → 1000+ - README_EN.md 동일 내용 영문 반영
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
변경 요약
SVG/HTML 내보내기에서 이미지를 base64 data URI로 임베딩합니다.
기존에는
draw_image트레이트 메서드가 회색 placeholder (<rect>/<div>)만 출력했습니다. 이제render_picture및web_canvas와 동일하게 이미지를 MIME 타입 감지 + base64 인코딩하여 data URI로 삽입합니다.<image href="data:image/...;base64,..."/><img src="data:image/...;base64,..."/>convert_wmf_to_svg로 SVG 변환 후 임베딩detect_image_mime_type를pub(crate)로 변경하여 html.rs에서 재사용검증
samples/hwp-img-001.hwp로 SVG 내보내기 확인:data:image/jpeg;base64,...,data:image/png;base64,...,data:image/bmp;base64,...테스트
cargo test통과 (937 tests, 0 failures)cargo clippy -- -D warnings통과