feat(images): support SVG image imports (replaces #2663) #2666

Merged
brenno merged 4 commits from feat/svg-image-import into main 2026-10-29 16:29:44 +00:00
Owner

Vervangende PR voor #2663 van @jhermans — de fork staat geen maintainer-edits toe, dus de poortreparaties staan als aparte commit bovenop de originele (behouden) inzender-commit.

Summary (uit #2663)

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.

Maintainer-wijzigingen bovenop #2663

De originele head 1d14f05d4 faalde op vier poorten en één decode-guard:

  • dart format op image_service.dart en image_service_test.dart.
  • Bestandsplafond: image_service.dart groeide naar 1042 regels. De SVG-helpers staan nu in lib/services/image_service_svg.dart (part of), wat het bestand op 995 en de klasse op 947 houdt.
  • Klasseplafond: _ImageCarouselPickerState kwam 3 regels over zijn plafond; de uitgeschreven _exts-lijst deelt nu supportedImageExtensions met de service.
  • safe_svg_image_test.dart wachtte met een vaste 100 ms; nu pumpUntil op de werkelijke uitkomst.
  • image_decode_guard_test: de embedded-PNG-route decodeerde met een bare Image.memory; nu cappedMemoryImage (beide assen begrensd).
  • docs/SOURCE_MAP.md en CHANGELOG.md aangevuld.

Verification

make check (format, analyze, alle statische poorten, 14.963 tests, coverage-vloeren, 1.238 pakkettests, 83 goldens), make check-secrets, make sast, make check-web, make check-l10n-orphans, licenses, sbom-verify, deps-check, shellcheck en check-marp — allemaal groen op head 43634f212.

Review-notities (niet-blokkerend)

  • isStagedPath is een lexicale containment-check; de lees-route doet voor projectPath == null geen realpath/symlink-controle. Enige praktische aanvalsvector vergt schrijftoegang tot de stagingmap van de gebruiker (per-user temp op macOS); documenteerbaar als bekende grens.
  • SafeSvgImage gebruikt errorBuilder als placeholderBuilder tijdens laden — korte fout-frame bij trage SVG, cosmetisch.
  • Remote data:/http: SVG's renderen niet (rasterdecode faalt → placeholder); consistent met de ingestelde grens.
Vervangende PR voor #2663 van @jhermans — de fork staat geen maintainer-edits toe, dus de poortreparaties staan als aparte commit bovenop de originele (behouden) inzender-commit. ## Summary (uit #2663) 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. ## Maintainer-wijzigingen bovenop #2663 De originele head `1d14f05d4` faalde op vier poorten en één decode-guard: - `dart format` op `image_service.dart` en `image_service_test.dart`. - Bestandsplafond: `image_service.dart` groeide naar 1042 regels. De SVG-helpers staan nu in `lib/services/image_service_svg.dart` (`part of`), wat het bestand op 995 en de klasse op 947 houdt. - Klasseplafond: `_ImageCarouselPickerState` kwam 3 regels over zijn plafond; de uitgeschreven `_exts`-lijst deelt nu `supportedImageExtensions` met de service. - `safe_svg_image_test.dart` wachtte met een vaste 100 ms; nu `pumpUntil` op de werkelijke uitkomst. - `image_decode_guard_test`: de embedded-PNG-route decodeerde met een bare `Image.memory`; nu `cappedMemoryImage` (beide assen begrensd). - `docs/SOURCE_MAP.md` en `CHANGELOG.md` aangevuld. ## Verification `make check` (format, analyze, alle statische poorten, 14.963 tests, coverage-vloeren, 1.238 pakkettests, 83 goldens), `make check-secrets`, `make sast`, `make check-web`, `make check-l10n-orphans`, `licenses`, `sbom-verify`, `deps-check`, `shellcheck` en `check-marp` — allemaal groen op head `43634f212`. ### Review-notities (niet-blokkerend) - `isStagedPath` is een lexicale containment-check; de lees-route doet voor `projectPath == null` geen realpath/symlink-controle. Enige praktische aanvalsvector vergt schrijftoegang tot de stagingmap van de gebruiker (per-user temp op macOS); documenteerbaar als bekende grens. - `SafeSvgImage` gebruikt `errorBuilder` als `placeholderBuilder` tijdens laden — korte fout-frame bij trage SVG, cosmetisch. - Remote `data:`/`http:` SVG's renderen niet (rasterdecode faalt → placeholder); consistent met de ingestelde grens.
Signed-off-by: Jeroen Hermans <j.hermans@cloudaware.eu>
De feature-commit faalde lokaal op vier poorten die de inzender niet
draaide: dart format (2 bestanden), het 1000-regels-bestandsplafond van
image_service.dart (1042 regels; SVG-helpers verhuisd naar part-
bestand image_service_svg.dart), het klasseplafond van
_ImageCarouselPickerState (+3; de uitgeschreven extensielijst maakt
plaats voor de gedeelde supportedImageExtensions) en een vaste
wachttijd in safe_svg_image_test (pumpUntil). SOURCE_MAP en CHANGELOG
ontbraken eveneens.

Signed-off-by: Brenno de Winter <brennodewinter@users.noreply.github.com>
brenno merged commit 55f5cabdaa into main 2026-10-29 16:29:44 +00:00
Sign in to join this conversation.
No description provided.