mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-13 19:19:10 +00:00
Add SVGA animation preview support
This commit is contained in:
@@ -48,7 +48,7 @@ public class Plugin : IViewer
|
|||||||
".pbm", ".pcx", ".pef", ".pgm", ".png", ".pnm", ".ppm", ".psb", ".psd", ".ptx", ".pxn",
|
".pbm", ".pcx", ".pef", ".pgm", ".png", ".pnm", ".ppm", ".psb", ".psd", ".ptx", ".pxn",
|
||||||
".qoi",
|
".qoi",
|
||||||
".r3d", ".raf", ".raw", ".rw2", ".rwl", ".rwz",
|
".r3d", ".raf", ".raw", ".rw2", ".rwl", ".rwz",
|
||||||
".sr2", ".srf", ".srw", ".svg", ".svgz",
|
".sr2", ".srf", ".srw", ".svg", ".svga", ".svgz",
|
||||||
".tga", ".tif", ".tiff",
|
".tga", ".tif", ".tiff",
|
||||||
".wdp", ".webp", ".wmf",
|
".wdp", ".webp", ".wmf",
|
||||||
".x3f", ".xcf", ".xbm", ".xpm",
|
".x3f", ".xcf", ".xbm", ".xpm",
|
||||||
@@ -114,7 +114,8 @@ public class Plugin : IViewer
|
|||||||
|
|
||||||
public void Prepare(string path, ContextObject context)
|
public void Prepare(string path, ContextObject context)
|
||||||
{
|
{
|
||||||
if (path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase))
|
if (path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| path.EndsWith(".svga", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (SettingHelper.Get("RenderSvgWeb", true, "QuickLook.Plugin.ImageViewer"))
|
if (SettingHelper.Get("RenderSvgWeb", true, "QuickLook.Plugin.ImageViewer"))
|
||||||
{
|
{
|
||||||
@@ -145,7 +146,8 @@ public class Plugin : IViewer
|
|||||||
|
|
||||||
public void View(string path, ContextObject context)
|
public void View(string path, ContextObject context)
|
||||||
{
|
{
|
||||||
if (path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase))
|
if (path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| path.EndsWith(".svga", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (SettingHelper.Get("RenderSvgWeb", true, "QuickLook.Plugin.ImageViewer"))
|
if (SettingHelper.Get("RenderSvgWeb", true, "QuickLook.Plugin.ImageViewer"))
|
||||||
{
|
{
|
||||||
|
@@ -91,7 +91,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Resources\svg2html.*">
|
<EmbeddedResource Include="Resources\svg*">
|
||||||
<LogicalName>QuickLook.Plugin.ImageViewer.Resources.%(RecursiveDir)%(Filename)%(Extension)</LogicalName>
|
<LogicalName>QuickLook.Plugin.ImageViewer.Resources.%(RecursiveDir)%(Filename)%(Extension)</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>SVGA Preview</title>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<script src="https://unpkg.com/svga/dist/index.min.js"></script>
|
||||||
|
<script src="svga2html.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* SvgaViewer: Provides SVGA animation preview with the following features.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Requires the following HTML structure:
|
||||||
|
* <canvas id="canvas">
|
||||||
|
* </canvas>
|
||||||
|
* - SVGA file path is obtained via chrome.webview.hostObjects.external.GetPath()
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Loads and plays SVGA animation files
|
||||||
|
* - Uses SVGA.js library for parsing and playback
|
||||||
|
* - Automatically starts playback after loading
|
||||||
|
* - Handles asynchronous loading and mounting of SVGA files
|
||||||
|
*/
|
||||||
|
class SvgaViewer {
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play SVGA file.
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
async play() {
|
||||||
|
const path = await chrome.webview.hostObjects.external.GetPath();
|
||||||
|
const parser = new SVGA.Parser();
|
||||||
|
|
||||||
|
// Because the path is a local file, we need to convert it to a URL format
|
||||||
|
parser.load('https://' + path).then(svga => {
|
||||||
|
const player = new SVGA.Player(document.getElementById('canvas'));
|
||||||
|
player.mount(svga).then(() => {
|
||||||
|
player.start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the SVGA viewer and play
|
||||||
|
new SvgaViewer().play();
|
@@ -99,7 +99,7 @@ public class SvgImagePanel : WebpagePanel
|
|||||||
|
|
||||||
ObjectForScripting ??= new ScriptHandler(path);
|
ObjectForScripting ??= new ScriptHandler(path);
|
||||||
|
|
||||||
_homePage = _resources["/svg2html.html"];
|
_homePage = _resources[path.EndsWith(".svga", StringComparison.OrdinalIgnoreCase) ? "/svga2html.html" : "/svg2html.html"];
|
||||||
NavigateToUri(new Uri("file://quicklook/"));
|
NavigateToUri(new Uri("file://quicklook/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +145,21 @@ public class SvgImagePanel : WebpagePanel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (requestedUri.Scheme == "https")
|
||||||
|
{
|
||||||
|
var localPath = $"{requestedUri.Authority}:{requestedUri.AbsolutePath}".Replace('/', '\\');
|
||||||
|
|
||||||
|
if (localPath.StartsWith(_fallbackPath, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (File.Exists(localPath))
|
||||||
|
{
|
||||||
|
var fileStream = File.OpenRead(localPath);
|
||||||
|
var response = _webView.CoreWebView2.Environment.CreateWebResourceResponse(
|
||||||
|
fileStream, 200, "OK", MimeTypes.GetContentType() + "\r\nAccess-Control-Allow-Origin: *");
|
||||||
|
args.Response = response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -197,6 +212,16 @@ public sealed class ScriptHandler(string path)
|
|||||||
{
|
{
|
||||||
public string Path { get; } = path;
|
public string Path { get; } = path;
|
||||||
|
|
||||||
|
public async Task<string> GetPath()
|
||||||
|
{
|
||||||
|
return await Task.FromResult(new Uri(Path).AbsolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetUri()
|
||||||
|
{
|
||||||
|
return await Task.FromResult(new Uri(Path).AbsoluteUri);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<string> GetSvgContent()
|
public async Task<string> GetSvgContent()
|
||||||
{
|
{
|
||||||
if (File.Exists(Path))
|
if (File.Exists(Path))
|
||||||
|
@@ -23,38 +23,42 @@ public class SvgMetaProvider(string path)
|
|||||||
{
|
{
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
try
|
|
||||||
|
if (_path.EndsWith(".svg", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var svgContent = File.ReadAllText(_path);
|
try
|
||||||
var svg = XElement.Parse(svgContent);
|
|
||||||
XNamespace ns = svg.Name.Namespace;
|
|
||||||
|
|
||||||
string widthAttr = svg.Attribute("width")?.Value;
|
|
||||||
string heightAttr = svg.Attribute("height")?.Value;
|
|
||||||
|
|
||||||
float? width = TryParseSvgLength(widthAttr);
|
|
||||||
float? height = TryParseSvgLength(heightAttr);
|
|
||||||
|
|
||||||
if (width.HasValue && height.HasValue)
|
|
||||||
{
|
{
|
||||||
_size = new Size { Width = width.Value, Height = height.Value };
|
var svgContent = File.ReadAllText(_path);
|
||||||
}
|
var svg = XElement.Parse(svgContent);
|
||||||
|
XNamespace ns = svg.Name.Namespace;
|
||||||
|
|
||||||
string viewBoxAttr = svg.Attribute("viewBox")?.Value;
|
string widthAttr = svg.Attribute("width")?.Value;
|
||||||
if (!string.IsNullOrEmpty(viewBoxAttr))
|
string heightAttr = svg.Attribute("height")?.Value;
|
||||||
{
|
|
||||||
var parts = viewBoxAttr.Split([' ', ','], StringSplitOptions.RemoveEmptyEntries);
|
float? width = TryParseSvgLength(widthAttr);
|
||||||
if (parts.Length == 4 &&
|
float? height = TryParseSvgLength(heightAttr);
|
||||||
float.TryParse(parts[2], out float vbWidth) &&
|
|
||||||
float.TryParse(parts[3], out float vbHeight))
|
if (width.HasValue && height.HasValue)
|
||||||
{
|
{
|
||||||
_size = new Size { Width = vbWidth, Height = vbHeight };
|
_size = new Size { Width = width.Value, Height = height.Value };
|
||||||
|
}
|
||||||
|
|
||||||
|
string viewBoxAttr = svg.Attribute("viewBox")?.Value;
|
||||||
|
if (!string.IsNullOrEmpty(viewBoxAttr))
|
||||||
|
{
|
||||||
|
var parts = viewBoxAttr.Split([' ', ','], StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length == 4 &&
|
||||||
|
float.TryParse(parts[2], out float vbWidth) &&
|
||||||
|
float.TryParse(parts[3], out float vbHeight))
|
||||||
|
{
|
||||||
|
_size = new Size { Width = vbWidth, Height = vbHeight };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception e)
|
||||||
catch (Exception e)
|
{
|
||||||
{
|
Debug.WriteLine(e);
|
||||||
Debug.WriteLine(e);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _size;
|
return _size;
|
||||||
|
Reference in New Issue
Block a user