Accept SVG as an image, and keep it vector - #72
Merged
Conversation
WPF has no SVG decoder, so this takes SharpVectors.Wpf. It produces a DrawingGroup rather than pixels, which is the point on a canvas whose zoom is unbounded: an SVG container is redrawn at whatever size it is shown at instead of being rasterized once on arrival. Decision 21 records why that library and not Direct2D, which is already referenced through Vortice but implements an SVG subset with no <text> element - most of what a DAX SVG measure emits. Adding .svg to the import catalog is what carries it into drag-and-drop and .wimport, since both already route through it. Clipboard paste needed the work: SVG arrives either under one of five format names or, more often here, as markup that was simply copied as text, so the shape of the content decides. The root element has to be svg, otherwise a page of HTML with a picture inside it would paste as an image. Copying an SVG container puts the markup and a bitmap out together, so an editor gets the source and a slide gets the picture. Two details worth stating. The renderer is given ExternalResourcesAccessModes.Ignore - its default fetches whatever the markup names, which would let a dropped file turn opening a board into an outbound request. And a vector is grown to a legible edge on arrival, because icons are authored at 24 units and would otherwise land as a speck on a 2400-wide row; enlarging costs a bitmap its sharpness and a vector nothing. Assets were already stored as opaque bytes, so the board format did not move. A board holding an SVG opened in 1.0.3 draws the missing-image placeholder rather than failing to open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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 now works everywhere an image works: clipboard paste, Explorer drag-and-drop,
File → Import, and.wimportrecipes. It is stored as its markup and redrawn at whatever size it is shown at, rather than rasterized once on arrival.Why a dependency
WPF has no SVG decoder, so supporting SVG at all meant taking a rendering library — including the option of rasterizing on arrival, which needs one just the same. That made the choice about which library, not whether to have one.
SharpVectors.Wpfproduces aDrawingGroup, which is the point on a canvas whose zoom is unbounded. It is BSD-3, managed-only, and ships no native binary, so it stays out of the signing step and out of the per-architecture question the installer would otherwise have to answer. Direct2D was the tempting alternative —Vorticeis already referenced and it would have cost nothing — but it implements a restricted SVG subset with no<text>element, which is most of what a DAX SVG measure emits. Decision 21 records this.What carried and what needed work
Adding
.svgtoImportCatalogis what carries it into drag-and-drop and.wimport, since both already route through it. Paste needed the work. SVG arrives either under one of five clipboard format names or, more often here, as markup that was simply copied as text, so the shape of the content decides. The root element has to besvg— matching on "contains<svg" would make a page of HTML with a picture inside it paste as an image. Copying an SVG container publishes the markup and a bitmap together, so an editor receives the source and a slide receives the picture.BoardImageCodecbecomes the one place an asset's bytes turn into something drawable, andBoardSurfacecachesImageSourcerather thanBitmapSource.Two details worth stating
The renderer is given
ExternalResourcesAccessModes.Ignore. Its default fetches whatever the markup names, which would let a pasted or dropped file turn opening a board into an outbound request.A vector is grown to a legible edge on arrival, because icons are authored at 16 or 24 units and would otherwise land as a speck on a 2400-wide row. Enlarging costs a bitmap its sharpness and a vector nothing.
Compatibility
Assets were already stored as opaque bytes carrying a content type, so the board format did not move — it is still version 5. The decoder is chosen from the bytes rather than the stored content type, so boards written before this change are unaffected. A board holding an SVG opened in 1.0.3 draws the missing-image placeholder for it rather than failing to open.
.svgis deliberately not registered as a file type by the installer. Whiteboard opens SVG; it does not take it from the browser.Verification
Beyond the smoke tests, the real code paths were exercised against a throwaway harness:
<text>, gradients, namespace prefixes, viewBox-only, and a doctype prologue all render.EnsureViewboxSizewas needed for this: without it a label centred in a 300×60 canvas arrived cropped to its glyphs at 147×26 and stretched.<image href="http://…">is ignored, with no fetch..wboardbyte-for-byte and renders identically at 1024×512 — vector, not a 200×100 bitmap enlarged.docs/samples/contoso-workshop.wimport, which now includes an SVG container, resolves with zero missing files.🤖 Generated with Claude Code