feat(images): support SVG image imports #2663

Closed
jhermans wants to merge 0 commits from jhermans/Ocideck:feat/svg-image-import into main
First-time contributor

Summary

Add SVG vector images as a supported slide and Image Library format. SVGs can be selected, dropped, stored in the Library, copied into saved or unsaved decks, previewed, presented, and exported without losing vector sharpness.

This also fixes two compatibility regressions found during manual testing:

  • SVGs produced by tools such as Inkscape or LibreOffice that wrap a base64 PNG no longer render blank.
  • An SVG chosen for an unsaved Large Image slide no longer falls back to “Outside the presentation”.

Security trade-off: SVG is active XML rather than an inert raster format, so safety takes precedence over accepting every SVG feature. Import parses the complete, size-bounded document and stores only a renderer-derived allow-list; scripts, event handlers, animation, stylesheets, external URLs, and general data: URLs are removed. The sole data: exception is a base64 PNG whose signature, decoded size (8 MiB maximum), and dimensions (8192 px per edge maximum) are validated. Arbitrary external paths remain blocked; unsaved slides may render only files OciDeck itself placed under its private staging root. I would broaden this subset only if the renderer can safely isolate additional features without increasing file/network access or executable content.

Changes

  • Add .svg to desktop/web image pickers, drag-and-drop, and Library scanning.
  • Parse and sanitise SVG bytes before they enter a deck, staging area, web memory store, or Library import path.
  • Add SafeSvgImage for consistent safe rendering in the Library, slide preview, presenter, audience window, callouts, zoom, and export paths.
  • Preserve supported SVGs as vectors; hide raster-only crop and rotation controls.
  • Support common SVG wrappers containing one validated embedded PNG through the reliable raster rendering path.
  • Permit only OciDeck-owned staged assets to render before an unsaved presentation gets a project directory.
  • Update the file-format, security-design, API, FAQ, and user-guide documentation in English and Dutch.
  • Add import, sanitiser, scanner, Library, slide-rendering, staging, drop, and crop/zoom regression coverage.

Checklist

  • Every commit is signed off (git commit -s) per the
    DCO, with my real name and a reachable email.
  • Maintainer: make check-full passes locally on the exact commit named in
    the PR before merge (format, analysis, tests, coverage, secrets, SAST,
    licences and web hardening). Tested commit: 1d14f05d4.
  • Added/updated tests for the behaviour I changed. The staged-SVG Large Image regression was run against the unfixed resolver and failed because no SafeSvgImage was built; it passes on 1d14f05d4.
  • New UI strings go through context.l10n.d('…') and are translated in
    every supported language. No new UI strings were introduced.
  • If I changed how anything is stored, I updated
    docs/FILE_FORMAT.md.
  • Docs updated where relevant (README / docs/).
  • The SVG storage/security trade-off is described above, including the principle given precedence and what would change that decision.
  • Threat model still holds. docs/SECURITY_DESIGN.md and assurance/risicoafweging.md were revisited. Sanitisation narrows imported SVGs to the existing renderer vocabulary, network references stay blocked, project containment remains enforced, and the unsaved-deck exception is limited to OciDeck's private staging root.

Notes for reviewers

Verification on exact commit 1d14f05d4:

  • Static analysis of the resolved SVG sanitiser/rendering files reports no issues.
  • 80 affected path-security, staging, SVG sanitisation/rendering, custom-bullet, and slide-preview tests passed after merging current main. The pre-merge branch also passed its 180 focused import suites.
  • The new staged-SVG Large Image regression was observed red with the previous resolver behaviour and green with this commit.
  • A Debian 12 release bundle built successfully from this commit and its runtime dependencies resolved without missing libraries.
  • make check-full is intentionally left for the maintainer checklist item.

Suggested manual checks:

  1. Add a pure vector SVG and an Inkscape/LibreOffice SVG containing an embedded PNG to the Image Library.
  2. Add each to a saved and an unsaved Large Image slide.
  3. Confirm both render in the editor, presenter, and export; save the previously unsaved deck and reopen it.
  4. Confirm an SVG with scripts, event handlers, external URLs, or an unsupported data: payload is stripped/rejected rather than executed or fetched.
## Summary Add SVG vector images as a supported slide and Image Library format. SVGs can be selected, dropped, stored in the Library, copied into saved or unsaved decks, previewed, presented, and exported without losing vector sharpness. This also fixes two compatibility regressions found during manual testing: - SVGs produced by tools such as Inkscape or LibreOffice that wrap a base64 PNG no longer render blank. - An SVG chosen for an unsaved Large Image slide no longer falls back to “Outside the presentation”. Security trade-off: SVG is active XML rather than an inert raster format, so safety takes precedence over accepting every SVG feature. Import parses the complete, size-bounded document and stores only a renderer-derived allow-list; scripts, event handlers, animation, stylesheets, external URLs, and general `data:` URLs are removed. The sole `data:` exception is a base64 PNG whose signature, decoded size (8 MiB maximum), and dimensions (8192 px per edge maximum) are validated. Arbitrary external paths remain blocked; unsaved slides may render only files OciDeck itself placed under its private staging root. I would broaden this subset only if the renderer can safely isolate additional features without increasing file/network access or executable content. ## Changes - Add `.svg` to desktop/web image pickers, drag-and-drop, and Library scanning. - Parse and sanitise SVG bytes before they enter a deck, staging area, web memory store, or Library import path. - Add `SafeSvgImage` for consistent safe rendering in the Library, slide preview, presenter, audience window, callouts, zoom, and export paths. - Preserve supported SVGs as vectors; hide raster-only crop and rotation controls. - Support common SVG wrappers containing one validated embedded PNG through the reliable raster rendering path. - Permit only OciDeck-owned staged assets to render before an unsaved presentation gets a project directory. - Update the file-format, security-design, API, FAQ, and user-guide documentation in English and Dutch. - Add import, sanitiser, scanner, Library, slide-rendering, staging, drop, and crop/zoom regression coverage. ## Checklist - [x] Every commit is **signed off** (`git commit -s`) per the [DCO](../dco.txt), with my real name and a reachable email. - [ ] Maintainer: `make check-full` passes locally on the exact commit named in the PR before merge (format, analysis, tests, coverage, secrets, SAST, licences and web hardening). Tested commit: `1d14f05d4`. - [x] Added/updated tests for the behaviour I changed. The staged-SVG Large Image regression was run against the unfixed resolver and failed because no `SafeSvgImage` was built; it passes on `1d14f05d4`. - [x] New UI strings go through `context.l10n.d('…')` **and** are translated in every supported language. No new UI strings were introduced. - [x] If I changed how anything is stored, I updated [`docs/FILE_FORMAT.md`](../docs/FILE_FORMAT.md). - [x] Docs updated where relevant (README / docs/). - [x] The SVG storage/security trade-off is described above, including the principle given precedence and what would change that decision. - [x] **Threat model still holds.** `docs/SECURITY_DESIGN.md` and `assurance/risicoafweging.md` were revisited. Sanitisation narrows imported SVGs to the existing renderer vocabulary, network references stay blocked, project containment remains enforced, and the unsaved-deck exception is limited to OciDeck's private staging root. ## Notes for reviewers Verification on exact commit `1d14f05d4`: - Static analysis of the resolved SVG sanitiser/rendering files reports no issues. - 80 affected path-security, staging, SVG sanitisation/rendering, custom-bullet, and slide-preview tests passed after merging current `main`. The pre-merge branch also passed its 180 focused import suites. - The new staged-SVG Large Image regression was observed red with the previous resolver behaviour and green with this commit. - A Debian 12 release bundle built successfully from this commit and its runtime dependencies resolved without missing libraries. - `make check-full` is intentionally left for the maintainer checklist item. Suggested manual checks: 1. Add a pure vector SVG and an Inkscape/LibreOffice SVG containing an embedded PNG to the Image Library. 2. Add each to a saved and an unsaved Large Image slide. 3. Confirm both render in the editor, presenter, and export; save the previously unsaved deck and reopen it. 4. Confirm an SVG with scripts, event handlers, external URLs, or an unsupported `data:` payload is stripped/rejected rather than executed or fetched.
Signed-off-by: Jeroen Hermans <j.hermans@cloudaware.eu>
Signed-off-by: Jeroen Hermans <j.hermans@cloudaware.eu>
Signed-off-by: Jeroen Hermans <j.hermans@cloudaware.eu>
Signed-off-by: Jeroen Hermans <j.hermans@cloudaware.eu>

# Conflicts:
#	lib/utils/sanitize_svg.dart
#	lib/widgets/slides/previews/media_previews_image.dart
#	test/sanitize_svg_test.dart
jhermans force-pushed feat/svg-image-import from 40f416b6f5 to 1d14f05d4e 2026-10-29 12:30:15 +00:00 Compare
Owner

Gemerged via vervangende PR #2666 (merge-commit 55f5cabdaa3f). De inzender-commit 1d14f05d4 is behouden; daarbovenop staat één maintainer-commit met poortreparaties (format, bestandsplafond, SOURCE_MAP, CHANGELOG) en de begrensde decode van ingebedde PNG (cappedMemoryImage). Bedankt voor de bijdrage!

Gemerged via vervangende PR #2666 (merge-commit `55f5cabdaa3f`). De inzender-commit `1d14f05d4` is behouden; daarbovenop staat één maintainer-commit met poortreparaties (format, bestandsplafond, SOURCE_MAP, CHANGELOG) en de begrensde decode van ingebedde PNG (`cappedMemoryImage`). Bedankt voor de bijdrage!
brenno closed this pull request 2026-10-29 16:30:08 +00:00

Pull request closed

Sign in to join this conversation.
No description provided.