mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[FileExplorer Add-ons] If SVG file is bigger than 2MB save it as html page and preview the page (#18186)
* If SVG file is bigger than 2MB save it as html page and navigate WebView2 to it Reason: WebView2.NavigateToString() has 2MB string limitation * Cleanup and add limitation link to comment * Lower the limit after testing it
This commit is contained in:
@@ -16,7 +16,6 @@ using Microsoft.PowerToys.PreviewHandler.Svg.Utilities;
|
|||||||
using Microsoft.PowerToys.Telemetry;
|
using Microsoft.PowerToys.Telemetry;
|
||||||
using Microsoft.Web.WebView2.Core;
|
using Microsoft.Web.WebView2.Core;
|
||||||
using Microsoft.Web.WebView2.WinForms;
|
using Microsoft.Web.WebView2.WinForms;
|
||||||
using PreviewHandlerCommon;
|
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.PreviewHandler.Svg
|
namespace Microsoft.PowerToys.PreviewHandler.Svg
|
||||||
{
|
{
|
||||||
@@ -38,7 +37,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the virtual host
|
/// Name of the virtual host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string VirtualHostName = "PowerToysLocalSvg";
|
private const string VirtualHostName = "PowerToysLocalSvg";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path of the current assembly.
|
/// Gets the path of the current assembly.
|
||||||
@@ -46,7 +45,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Source: https://stackoverflow.com/a/283917/14774889
|
/// Source: https://stackoverflow.com/a/283917/14774889
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static string AssemblyDirectory
|
private static string AssemblyDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -67,12 +66,20 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool _infoBarAdded;
|
private bool _infoBarAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represent WebView2 user data folder path.
|
||||||
|
/// </summary>
|
||||||
|
private string _webView2UserDataFolder = System.Environment.GetEnvironmentVariable("USERPROFILE") +
|
||||||
|
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgPreview-Temp";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start the preview on the Control.
|
/// Start the preview on the Control.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataSource">Stream reference to access source file.</param>
|
/// <param name="dataSource">Stream reference to access source file.</param>
|
||||||
public override void DoPreview<T>(T dataSource)
|
public override void DoPreview<T>(T dataSource)
|
||||||
{
|
{
|
||||||
|
CleanupWebView2UserDataFolder();
|
||||||
|
|
||||||
string svgData = null;
|
string svgData = null;
|
||||||
bool blocked = false;
|
bool blocked = false;
|
||||||
|
|
||||||
@@ -88,9 +95,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
|
|
||||||
blocked = SvgPreviewHandlerHelper.CheckBlockedElements(svgData);
|
blocked = SvgPreviewHandlerHelper.CheckBlockedElements(svgData);
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031 // Do not catch general exception types
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
#pragma warning restore CA1031 // Do not catch general exception types
|
|
||||||
{
|
{
|
||||||
PreviewError(ex, dataSource);
|
PreviewError(ex, dataSource);
|
||||||
return;
|
return;
|
||||||
@@ -100,9 +105,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
{
|
{
|
||||||
svgData = SvgPreviewHandlerHelper.AddStyleSVG(svgData);
|
svgData = SvgPreviewHandlerHelper.AddStyleSVG(svgData);
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031 // Do not catch general exception types
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
#pragma warning restore CA1031 // Do not catch general exception types
|
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message });
|
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message });
|
||||||
}
|
}
|
||||||
@@ -125,9 +128,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
base.DoPreview(dataSource);
|
base.DoPreview(dataSource);
|
||||||
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
|
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031 // Do not catch general exception types
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
#pragma warning restore CA1031 // Do not catch general exception types
|
|
||||||
{
|
{
|
||||||
PreviewError(ex, dataSource);
|
PreviewError(ex, dataSource);
|
||||||
}
|
}
|
||||||
@@ -169,8 +170,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
|
|
||||||
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
|
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
|
||||||
webView2EnvironmentAwaiter = CoreWebView2Environment
|
webView2EnvironmentAwaiter = CoreWebView2Environment
|
||||||
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
|
.CreateAsync(userDataFolder: _webView2UserDataFolder)
|
||||||
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgPreview-Temp")
|
|
||||||
.ConfigureAwait(true).GetAwaiter();
|
.ConfigureAwait(true).GetAwaiter();
|
||||||
webView2EnvironmentAwaiter.OnCompleted(() =>
|
webView2EnvironmentAwaiter.OnCompleted(() =>
|
||||||
{
|
{
|
||||||
@@ -183,10 +183,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
|
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
|
||||||
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
|
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
|
||||||
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
|
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
|
||||||
|
|
||||||
|
// WebView2.NavigateToString() limitation
|
||||||
|
// See https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks
|
||||||
|
// While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes
|
||||||
|
if (svgData.Length > 1_500_000)
|
||||||
|
{
|
||||||
|
string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html";
|
||||||
|
File.WriteAllText(filename, svgData);
|
||||||
|
_browser.Source = new Uri(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_browser.NavigateToString(svgData);
|
_browser.NavigateToString(svgData);
|
||||||
|
}
|
||||||
|
|
||||||
Controls.Add(_browser);
|
Controls.Add(_browser);
|
||||||
}
|
}
|
||||||
catch (NullReferenceException)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -224,5 +238,25 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
AddTextBoxControl(Properties.Resource.SvgNotPreviewedError);
|
AddTextBoxControl(Properties.Resource.SvgNotPreviewedError);
|
||||||
base.DoPreview(dataSource);
|
base.DoPreview(dataSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleanup the previously created tmp html files from svg files bigger than 2MB.
|
||||||
|
/// </summary>
|
||||||
|
private void CleanupWebView2UserDataFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Cleanup temp dir
|
||||||
|
var dir = new DirectoryInfo(_webView2UserDataFolder);
|
||||||
|
|
||||||
|
foreach (var file in dir.EnumerateFiles("*.html"))
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the virtual host
|
/// Name of the virtual host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string VirtualHostName = "PowerToysLocalSvgThumbnail";
|
private const string VirtualHostName = "PowerToysLocalSvgThumbnail";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path of the current assembly.
|
/// Gets the path of the current assembly.
|
||||||
@@ -57,7 +57,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Source: https://stackoverflow.com/a/283917/14774889
|
/// Source: https://stackoverflow.com/a/283917/14774889
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static string AssemblyDirectory
|
private static string AssemblyDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -68,6 +68,12 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represent WebView2 user data folder path.
|
||||||
|
/// </summary>
|
||||||
|
private string _webView2UserDataFolder = System.Environment.GetEnvironmentVariable("USERPROFILE") +
|
||||||
|
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgThumbnailPreview-Temp";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Render SVG using WebView2 control, capture the WebView2
|
/// Render SVG using WebView2 control, capture the WebView2
|
||||||
/// preview and create Bitmap out of it.
|
/// preview and create Bitmap out of it.
|
||||||
@@ -76,6 +82,8 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
/// <param name="cx">The maximum thumbnail size, in pixels.</param>
|
/// <param name="cx">The maximum thumbnail size, in pixels.</param>
|
||||||
public Bitmap GetThumbnail(string content, uint cx)
|
public Bitmap GetThumbnail(string content, uint cx)
|
||||||
{
|
{
|
||||||
|
CleanupWebView2UserDataFolder();
|
||||||
|
|
||||||
if (cx == 0 || cx > MaxThumbnailSize || string.IsNullOrEmpty(content) || !content.Contains("svg"))
|
if (cx == 0 || cx > MaxThumbnailSize || string.IsNullOrEmpty(content) || !content.Contains("svg"))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -117,8 +125,7 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
|
|
||||||
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
|
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
|
||||||
webView2EnvironmentAwaiter = CoreWebView2Environment
|
webView2EnvironmentAwaiter = CoreWebView2Environment
|
||||||
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
|
.CreateAsync(userDataFolder: _webView2UserDataFolder)
|
||||||
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\SvgThumbnailPreview-Temp")
|
|
||||||
.ConfigureAwait(true).GetAwaiter();
|
.ConfigureAwait(true).GetAwaiter();
|
||||||
webView2EnvironmentAwaiter.OnCompleted(async () =>
|
webView2EnvironmentAwaiter.OnCompleted(async () =>
|
||||||
{
|
{
|
||||||
@@ -129,9 +136,22 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
|
await _browser.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");
|
||||||
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
|
_browser.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
|
||||||
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
|
_browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
|
||||||
|
|
||||||
|
// WebView2.NavigateToString() limitation
|
||||||
|
// See https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks
|
||||||
|
// While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes
|
||||||
|
if (wrappedContent.Length > 1_500_000)
|
||||||
|
{
|
||||||
|
string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html";
|
||||||
|
File.WriteAllText(filename, wrappedContent);
|
||||||
|
_browser.Source = new Uri(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_browser.NavigateToString(wrappedContent);
|
_browser.NavigateToString(wrappedContent);
|
||||||
}
|
}
|
||||||
catch (NullReferenceException)
|
}
|
||||||
|
catch (Exception)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -249,5 +269,25 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg
|
|||||||
{
|
{
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleanup the previously created tmp html files from svg files bigger than 2MB.
|
||||||
|
/// </summary>
|
||||||
|
private void CleanupWebView2UserDataFolder()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Cleanup temp dir
|
||||||
|
var dir = new DirectoryInfo(_webView2UserDataFolder);
|
||||||
|
|
||||||
|
foreach (var file in dir.EnumerateFiles("*.html"))
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user