From d32ea86314ee1f5bdd995d39aa482cd44783faa5 Mon Sep 17 00:00:00 2001 From: leileizhang Date: Thu, 11 Dec 2025 11:58:01 +0800 Subject: [PATCH] [Peek] Use WebView2 for SVG preview to improve compatibility (#44209) ## Summary of the Pull Request Improves SVG preview compatibility in Peek by using WebView2 instead of `SvgImageSource`. ## Problem `SvgImageSource` has limited SVG feature support and fails to render SVGs with advanced features like `clipPath`, complex gradients, or certain namespace configurations. This results in black previews or icon-only display for many SVG files. ## Solution Render SVG files using WebView2 which provides full SVG specification support through the browser engine. image image ## PR Checklist - [x] Closes: #44193 - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed --- .../MediaPreviewer/ImagePreviewer.cs | 35 +++---------------- .../WebBrowserPreviewer.cs | 14 +++++++- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/ImagePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/ImagePreviewer.cs index 47e488c4d3..fd190bf72a 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/ImagePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/MediaPreviewer/ImagePreviewer.cs @@ -54,8 +54,6 @@ namespace Peek.FilePreviewer.Previewers private bool IsPng() => Item.Extension == ".png"; - private bool IsSvg() => Item.Extension == ".svg"; - private bool IsQoi() => Item.Extension == ".qoi"; private DispatcherQueue Dispatcher { get; } @@ -63,7 +61,7 @@ namespace Peek.FilePreviewer.Previewers private static readonly HashSet _supportedFileTypes = BitmapDecoder.GetDecoderInformationEnumerator() .SelectMany(di => di.FileExtensions) - .Union([".svg", ".qoi"]) + .Union([".qoi"]) .ToHashSet(StringComparer.OrdinalIgnoreCase); public static bool IsItemSupported(IFileSystemItem item) @@ -75,15 +73,7 @@ namespace Peek.FilePreviewer.Previewers { cancellationToken.ThrowIfCancellationRequested(); - if (IsSvg()) - { - var size = await Task.Run(Item.GetSvgSize); - if (size != null) - { - ImageSize = size.Value; - } - } - else if (IsQoi()) + if (IsQoi()) { var size = await Task.Run(Item.GetQoiSize); if (size != null) @@ -176,31 +166,16 @@ namespace Peek.FilePreviewer.Previewers { cancellationToken.ThrowIfCancellationRequested(); - using FileStream stream = ReadHelper.OpenReadOnly(Item.Path); - - if (IsSvg()) - { - var source = new SvgImageSource(); - source.RasterizePixelHeight = ImageSize?.Height ?? 0; - source.RasterizePixelWidth = ImageSize?.Width ?? 0; - - var loadStatus = await source.SetSourceAsync(stream.AsRandomAccessStream()); - if (loadStatus != SvgImageSourceLoadStatus.Success) - { - Logger.LogError("Error loading SVG: " + loadStatus.ToString()); - throw new ImageLoadingException(nameof(source)); - } - - Preview = source; - } - else if (IsQoi()) + if (IsQoi()) { + using FileStream stream = ReadHelper.OpenReadOnly(Item.Path); using var bitmap = QoiImage.FromStream(stream); Preview = await BitmapHelper.BitmapToImageSource(bitmap, true, cancellationToken); } else { + using FileStream stream = ReadHelper.OpenReadOnly(Item.Path); Preview = new BitmapImage(); await ((BitmapImage)Preview).SetSourceAsync(stream.AsRandomAccessStream()); } diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs index 03f4461e50..4d4cf315b4 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs @@ -33,6 +33,10 @@ namespace Peek.FilePreviewer.Previewers // Markdown ".md", + + // SVG - using WebView2 for better compatibility with complex SVGs + // (e.g., from Adobe Illustrator, Inkscape) + ".svg", }; [ObservableProperty] @@ -111,9 +115,10 @@ namespace Peek.FilePreviewer.Previewers { bool isHtml = File.Extension == ".html" || File.Extension == ".htm"; bool isMarkdown = File.Extension == ".md"; + bool isSvg = File.Extension == ".svg"; bool supportedByMonaco = MonacoHelper.SupportedMonacoFileTypes.Contains(File.Extension); - bool useMonaco = supportedByMonaco && !isHtml && !isMarkdown; + bool useMonaco = supportedByMonaco && !isHtml && !isMarkdown && !isSvg; IsDevFilePreview = supportedByMonaco; CustomContextMenu = useMonaco; @@ -128,6 +133,13 @@ namespace Peek.FilePreviewer.Previewers var raw = await ReadHelper.Read(File.Path.ToString()); Preview = new Uri(MarkdownHelper.PreviewTempFile(raw, File.Path, TempFolderPath.Path)); } + else if (isSvg) + { + // SVG files are rendered directly by WebView2 for better compatibility + // with complex SVGs from Adobe Illustrator, Inkscape, etc. + IsDevFilePreview = false; + Preview = new Uri(File.Path); + } else { // Simple html file to preview. Shouldn't do things like enabling scripts or using a virtual mapped directory.