Added initial work for reading SVG files
This commit is contained in:
Родитель
a59241b290
Коммит
7d3bb0db78
12
build.cake
12
build.cake
|
@ -59,6 +59,7 @@ Task ("libs")
|
|||
ReplaceTextInFiles ("./binding/Binding/Properties/SkiaSharpAssemblyInfo.cs", "{GIT_SHA}", sha);
|
||||
ReplaceTextInFiles ("./source/SkiaSharp.Views/SkiaSharp.Views.Shared/Properties/SkiaSharpViewsAssemblyInfo.cs", "{GIT_SHA}", sha);
|
||||
ReplaceTextInFiles ("./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms.Shared/Properties/SkiaSharpViewsFormsAssemblyInfo.cs", "{GIT_SHA}", sha);
|
||||
ReplaceTextInFiles ("./source/SkiaSharp.Svg/SkiaSharp.Svg/Properties/SkiaSharpSvgAssemblyInfo.cs", "{GIT_SHA}", sha);
|
||||
}
|
||||
|
||||
// create all the directories
|
||||
|
@ -103,6 +104,8 @@ Task ("libs")
|
|||
CopyFileToDirectory ("./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms/bin/Release/SkiaSharp.Views.Forms.dll", "./output/portable/");
|
||||
CopyFileToDirectory ("./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms.UWP/bin/Release/SkiaSharp.Views.Forms.dll", "./output/uwp/");
|
||||
|
||||
// copy SVG
|
||||
CopyFileToDirectory ("./source/SkiaSharp.Svg/SkiaSharp.Svg/bin/Release/SkiaSharp.Svg.dll", "./output/portable/");
|
||||
}
|
||||
|
||||
if (IsRunningOnUnix ()) {
|
||||
|
@ -137,6 +140,9 @@ Task ("libs")
|
|||
CopyFileToDirectory ("./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms/bin/Release/SkiaSharp.Views.Forms.dll", "./output/portable/");
|
||||
CopyFileToDirectory ("./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms.Android/bin/Release/SkiaSharp.Views.Forms.dll", "./output/android/");
|
||||
CopyFileToDirectory ("./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms.iOS/bin/Release/SkiaSharp.Views.Forms.dll", "./output/ios/");
|
||||
|
||||
// copy SVG
|
||||
CopyFileToDirectory ("./source/SkiaSharp.Svg/SkiaSharp.Svg/bin/Release/SkiaSharp.Svg.dll", "./output/portable/");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -266,6 +272,8 @@ Task ("nuget")
|
|||
PackageNuGet ("./nuget/SkiaSharp.Views.Forms.Mac.nuspec", "./output/");
|
||||
}
|
||||
}
|
||||
// SVG is a PCL
|
||||
PackageNuGet ("./nuget/SkiaSharp.Svg.nuspec", "./output/");
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -313,6 +321,7 @@ Task ("set-versions")
|
|||
{ "SkiaSharp", "1.54.1" },
|
||||
{ "SkiaSharp.Views", "1.54.1-beta1" },
|
||||
{ "SkiaSharp.Views.Forms", "1.54.1-beta1" },
|
||||
{ "SkiaSharp.Svg", "1.54.1-beta1" },
|
||||
};
|
||||
|
||||
var files = new List<string> ();
|
||||
|
@ -348,6 +357,9 @@ Task ("set-versions")
|
|||
UpdateAssemblyInfo (
|
||||
"./source/SkiaSharp.Views.Forms/SkiaSharp.Views.Forms.Shared/Properties/SkiaSharpViewsFormsAssemblyInfo.cs",
|
||||
version, fileVersion, sha);
|
||||
UpdateAssemblyInfo (
|
||||
"./source/SkiaSharp.Svg/SkiaSharp.Svg/Properties/SkiaSharpSvgAssemblyInfo.cs",
|
||||
version, fileVersion, sha);
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>SkiaSharp.Svg</id>
|
||||
<title>SVG Support for SkiaSharp</title>
|
||||
<version>1.54.1-beta1</version>
|
||||
<authors>Xamarin Inc.</authors>
|
||||
<owners>Xamarin Inc.</owners>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This package adds support for reading SVG files into SkiaSharp.</description>
|
||||
<copyright>Copyright (c) Xamarin Inc. 2016</copyright>
|
||||
<licenseUrl>https://github.com/mono/SkiaSharp/blob/master/LICENSE.md</licenseUrl>
|
||||
<projectUrl>https://github.com/mono/SkiaSharp</projectUrl>
|
||||
<iconUrl>https://cdn.rawgit.com/mono/SkiaSharp/v1.53.0/images/skia_256x256.png</iconUrl>
|
||||
<dependencies>
|
||||
<dependency id="SkiaSharp" version="1.54.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="output/portable/SkiaSharp.Svg.dll" target="lib/portable-net45+win8+wpa81+wp8" />
|
||||
</files>
|
||||
</package>
|
|
@ -0,0 +1,17 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Cake.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyTitle("SkiaSharp.Svg")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SkiaSharp.Svg")]
|
||||
[assembly: AssemblyVersion("1.54.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.54.1.0")]
|
||||
[assembly: AssemblyInformationalVersion("1.54.1.0-{GIT_SHA}")]
|
||||
[assembly: AssemblyCopyright("Xamarin Inc.")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
|
|
@ -0,0 +1,997 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace SkiaSharp
|
||||
{
|
||||
public class SKSvg
|
||||
{
|
||||
private const float DefaultPPI = 160f;
|
||||
private const bool DefaultThrowOnUnsupportedElement = false;
|
||||
|
||||
private static readonly IFormatProvider icult = CultureInfo.InvariantCulture;
|
||||
private static readonly XNamespace xlink = "http://www.w3.org/1999/xlink";
|
||||
private static readonly char[] WS = new char[] { ' ', '\t', '\n', '\r' };
|
||||
private static readonly Regex unitRe = new Regex("px|pt|em|ex|pc|cm|mm|in");
|
||||
private static readonly Regex percRe = new Regex("%");
|
||||
private static readonly Regex fillUrlRe = new Regex(@"url\s*\(\s*#([^\)]+)\)");
|
||||
private static readonly Regex keyValueRe = new Regex(@"\s*([\w-]+)\s*:\s*(.*)");
|
||||
private static readonly Regex WSRe = new Regex(@"\s{2,}");
|
||||
|
||||
private readonly Dictionary<string, XElement> defs = new Dictionary<string, XElement>();
|
||||
|
||||
public SKSvg()
|
||||
: this(DefaultPPI)
|
||||
{
|
||||
}
|
||||
|
||||
public SKSvg(float pixelsPerInch)
|
||||
{
|
||||
PixelsPerInch = pixelsPerInch;
|
||||
ThrowOnUnsupportedElement = DefaultThrowOnUnsupportedElement;
|
||||
}
|
||||
|
||||
public float PixelsPerInch { get; set; }
|
||||
public bool ThrowOnUnsupportedElement { get; set; }
|
||||
public SKPicture Picture { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string Version { get; private set; }
|
||||
|
||||
public SKPicture Load(Stream stream)
|
||||
{
|
||||
return Load(XDocument.Load(stream));
|
||||
}
|
||||
|
||||
private SKPicture Load(XDocument xdoc)
|
||||
{
|
||||
var svg = xdoc.Root;
|
||||
var ns = svg.Name.Namespace;
|
||||
|
||||
// find the defs (gradients) - and follow all hrefs
|
||||
foreach (var d in svg.Descendants())
|
||||
{
|
||||
var id = d.Attribute("id")?.Value?.Trim();
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
defs[id] = ReadDefinition(d);
|
||||
}
|
||||
|
||||
Version = svg.Attribute("version")?.Value;
|
||||
Title = svg.Element(ns + "title")?.Value;
|
||||
Description = svg.Element(ns + "desc")?.Value ?? svg.Element(ns + "description")?.Value;
|
||||
|
||||
// get the dimensions
|
||||
var widthA = svg.Attribute("width");
|
||||
var heightA = svg.Attribute("height");
|
||||
var width = ReadNumber(widthA);
|
||||
var height = ReadNumber(heightA);
|
||||
var size = new SKSize(width, height);
|
||||
|
||||
var viewBox = SKRect.Create(size);
|
||||
var viewBoxA = svg.Attribute("viewBox") ?? svg.Attribute("viewPort");
|
||||
if (viewBoxA != null)
|
||||
{
|
||||
viewBox = ReadRectangle(viewBoxA.Value);
|
||||
}
|
||||
|
||||
if (widthA != null && widthA.Value.Contains("%"))
|
||||
{
|
||||
size.Width *= viewBox.Width;
|
||||
}
|
||||
if (heightA != null && heightA.Value.Contains("%"))
|
||||
{
|
||||
size.Height *= viewBox.Height;
|
||||
}
|
||||
|
||||
// craete the picture from the elements
|
||||
using (var recorder = new SKPictureRecorder())
|
||||
using (var canvas = recorder.BeginRecording(SKRect.Create(size)))
|
||||
{
|
||||
LoadElements(svg.Elements(), canvas);
|
||||
|
||||
Picture = recorder.EndRecording();
|
||||
}
|
||||
|
||||
return Picture;
|
||||
}
|
||||
|
||||
private void LoadElements(IEnumerable<XElement> elements, SKCanvas canvas)
|
||||
{
|
||||
foreach (var e in elements)
|
||||
{
|
||||
ReadElement(e, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadElement(XElement e, SKCanvas canvas)
|
||||
{
|
||||
ReadElement(e, canvas, null, CreatePaint());
|
||||
}
|
||||
|
||||
private void ReadElement(XElement e, SKCanvas canvas, SKPaint stroke, SKPaint fill)
|
||||
{
|
||||
ReadPaints(e, ref stroke, ref fill);
|
||||
|
||||
// transform matrix
|
||||
var transform = ReadTransform(e.Attribute("transform")?.Value ?? string.Empty);
|
||||
canvas.Save();
|
||||
canvas.Concat(ref transform);
|
||||
|
||||
// SVG elements
|
||||
var elementName = e.Name.LocalName;
|
||||
switch (elementName)
|
||||
{
|
||||
case "text":
|
||||
if (stroke != null || fill != null)
|
||||
{
|
||||
ReadText(e, canvas, stroke?.Clone(), fill?.Clone());
|
||||
}
|
||||
break;
|
||||
case "rect":
|
||||
if (stroke != null || fill != null)
|
||||
{
|
||||
var x = ReadNumber(e.Attribute("x"));
|
||||
var y = ReadNumber(e.Attribute("y"));
|
||||
var width = ReadNumber(e.Attribute("width"));
|
||||
var height = ReadNumber(e.Attribute("height"));
|
||||
var rx = ReadNumber(e.Attribute("rx"));
|
||||
var ry = ReadNumber(e.Attribute("ry"));
|
||||
var rect = SKRect.Create(x, y, width, height);
|
||||
if (rx > 0 || ry > 0)
|
||||
{
|
||||
if (fill != null)
|
||||
canvas.DrawRoundRect(rect, rx, ry, fill);
|
||||
if (stroke != null)
|
||||
canvas.DrawRoundRect(rect, rx, ry, stroke);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fill != null)
|
||||
canvas.DrawRect(rect, fill);
|
||||
if (stroke != null)
|
||||
canvas.DrawRect(rect, stroke);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "ellipse":
|
||||
if (stroke != null || fill != null)
|
||||
{
|
||||
var cx = ReadNumber(e.Attribute("cx"));
|
||||
var cy = ReadNumber(e.Attribute("cy"));
|
||||
var rx = ReadNumber(e.Attribute("rx"));
|
||||
var ry = ReadNumber(e.Attribute("ry"));
|
||||
if (fill != null)
|
||||
canvas.DrawOval(cx, cy, rx, ry, fill);
|
||||
if (stroke != null)
|
||||
canvas.DrawOval(cx, cy, rx, ry, stroke);
|
||||
}
|
||||
break;
|
||||
case "circle":
|
||||
if (stroke != null || fill != null)
|
||||
{
|
||||
var cx = ReadNumber(e.Attribute("cx"));
|
||||
var cy = ReadNumber(e.Attribute("cy"));
|
||||
var rr = ReadNumber(e.Attribute("r"));
|
||||
if (fill != null)
|
||||
canvas.DrawCircle(cx, cy, rr, fill);
|
||||
if (stroke != null)
|
||||
canvas.DrawCircle(cx, cy, rr, stroke);
|
||||
}
|
||||
break;
|
||||
case "path":
|
||||
if (stroke != null || fill != null)
|
||||
{
|
||||
var d = e.Attribute("d")?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(d))
|
||||
{
|
||||
var path = SKPath.ParseSvgPathData(d);
|
||||
if (fill != null)
|
||||
canvas.DrawPath(path, fill);
|
||||
if (stroke != null)
|
||||
canvas.DrawPath(path, stroke);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "polygon":
|
||||
case "polyline":
|
||||
if (stroke != null || fill != null)
|
||||
{
|
||||
var close = elementName == "polygon";
|
||||
var p = e.Attribute("points")?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(p))
|
||||
{
|
||||
var path = ReadPolyPath(p, close);
|
||||
if (fill != null)
|
||||
canvas.DrawPath(path, fill);
|
||||
if (stroke != null)
|
||||
canvas.DrawPath(path, stroke);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "g":
|
||||
if (e.HasElements)
|
||||
{
|
||||
foreach (var gElement in e.Elements())
|
||||
{
|
||||
ReadElement(gElement, canvas, stroke?.Clone(), fill?.Clone());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "use":
|
||||
if (e.HasAttributes)
|
||||
{
|
||||
var href = ReadHref(e);
|
||||
if (href != null)
|
||||
{
|
||||
// TODO: copy/process other attributes
|
||||
|
||||
var x = ReadNumber(e.Attribute("x"));
|
||||
var y = ReadNumber(e.Attribute("y"));
|
||||
var useTransform = SKMatrix.MakeTranslation(x, y);
|
||||
|
||||
canvas.Save();
|
||||
canvas.Concat(ref useTransform);
|
||||
|
||||
ReadElement(href, canvas, stroke?.Clone(), fill?.Clone());
|
||||
|
||||
canvas.Restore();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "line":
|
||||
if (stroke != null)
|
||||
{
|
||||
var x1 = ReadNumber(e.Attribute("x1"));
|
||||
var x2 = ReadNumber(e.Attribute("x2"));
|
||||
var y1 = ReadNumber(e.Attribute("y1"));
|
||||
var y2 = ReadNumber(e.Attribute("y2"));
|
||||
canvas.DrawLine(x1, y1, x2, y2, stroke);
|
||||
}
|
||||
break;
|
||||
case "switch":
|
||||
if (e.HasElements)
|
||||
{
|
||||
foreach (var ee in e.Elements())
|
||||
{
|
||||
var requiredFeatures = ee.Attribute("requiredFeatures");
|
||||
var requiredExtensions = ee.Attribute("requiredExtensions");
|
||||
var systemLanguage = ee.Attribute("systemLanguage");
|
||||
|
||||
// TODO: evaluate requiredFeatures, requiredExtensions and systemLanguage
|
||||
var isVisible =
|
||||
requiredFeatures == null &&
|
||||
requiredExtensions == null &&
|
||||
systemLanguage == null;
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
ReadElement(ee, canvas, stroke?.Clone(), fill?.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "defs":
|
||||
case "title":
|
||||
case "desc":
|
||||
case "description":
|
||||
// already read earlier
|
||||
break;
|
||||
default:
|
||||
LogOrThrow($"SVG element '{elementName}' is not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
// restore matrix
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
private void ReadText(XElement e, SKCanvas canvas, SKPaint stroke, SKPaint fill)
|
||||
{
|
||||
// TODO: stroke
|
||||
|
||||
var x = ReadNumber(e.Attribute("x"));
|
||||
var y = ReadNumber(e.Attribute("y"));
|
||||
var xy = new SKPoint(x, y);
|
||||
|
||||
ReadFontAttributes(e, fill);
|
||||
fill.TextAlign = ReadTextAlignment(e);
|
||||
|
||||
ReadTextSpans(e, canvas, xy, stroke, fill);
|
||||
}
|
||||
|
||||
private void ReadTextSpans(XElement e, SKCanvas canvas, SKPoint location, SKPaint stroke, SKPaint fill)
|
||||
{
|
||||
var nodes = e.Nodes().ToArray();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
var c = nodes[i];
|
||||
bool isFirst = i == 0;
|
||||
bool isLast = i == nodes.Length - 1;
|
||||
|
||||
if (c.NodeType == XmlNodeType.Text)
|
||||
{
|
||||
// TODO: check for preserve whitespace
|
||||
|
||||
var textSegments = ((XText)c).Value.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var count = textSegments.Length;
|
||||
if (count > 0)
|
||||
{
|
||||
if (isFirst)
|
||||
textSegments[0] = textSegments[0].TrimStart();
|
||||
if (isLast)
|
||||
textSegments[count - 1] = textSegments[count - 1].TrimEnd();
|
||||
var text = WSRe.Replace(string.Concat(textSegments), " ");
|
||||
|
||||
canvas.DrawText(text, location.X, location.Y, fill);
|
||||
|
||||
location.X += fill.MeasureText(text);
|
||||
}
|
||||
}
|
||||
else if (c.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
var ce = (XElement)c;
|
||||
if (ce.Name.LocalName == "tspan")
|
||||
{
|
||||
var spanFill = fill.Clone();
|
||||
|
||||
// the current span may want to change the cursor position
|
||||
location.X = ReadOptionalNumber(ce.Attribute("x")) ?? location.X;
|
||||
location.Y = ReadOptionalNumber(ce.Attribute("y")) ?? location.Y;
|
||||
|
||||
ReadFontAttributes(ce, spanFill);
|
||||
|
||||
var text = ce.Value.Trim();
|
||||
|
||||
canvas.DrawText(text, location.X, location.Y, spanFill);
|
||||
|
||||
location.X += spanFill.MeasureText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadFontAttributes(XElement e, SKPaint paint)
|
||||
{
|
||||
var fontStyle = ReadStyle(e);
|
||||
|
||||
string ffamily;
|
||||
if (!fontStyle.TryGetValue("font-family", out ffamily) || string.IsNullOrWhiteSpace(ffamily))
|
||||
ffamily = paint.Typeface?.FamilyName;
|
||||
var fweight = ReadFontWeight(fontStyle, paint.Typeface?.FontWeight ?? (int)SKFontStyleWeight.Normal);
|
||||
var fwidth = ReadFontWidth(fontStyle, paint.Typeface?.FontWidth ?? (int)SKFontStyleWidth.Normal);
|
||||
var fstyle = ReadFontStyle(fontStyle, paint.Typeface?.FontSlant ?? SKFontStyleSlant.Upright);
|
||||
paint.Typeface = SKTypeface.FromFamilyName(ffamily, fweight, fwidth, fstyle);
|
||||
|
||||
string fsize;
|
||||
if (fontStyle.TryGetValue("font-size", out fsize) && !string.IsNullOrWhiteSpace(fsize))
|
||||
paint.TextSize = ReadNumber(fsize);
|
||||
}
|
||||
|
||||
private static SKFontStyleSlant ReadFontStyle(Dictionary<string, string> fontStyle, SKFontStyleSlant defaultStyle = SKFontStyleSlant.Upright)
|
||||
{
|
||||
SKFontStyleSlant style = defaultStyle;
|
||||
|
||||
string fstyle;
|
||||
if (fontStyle.TryGetValue("font-style", out fstyle) && !string.IsNullOrWhiteSpace(fstyle))
|
||||
{
|
||||
switch (fstyle)
|
||||
{
|
||||
case "italic":
|
||||
style = SKFontStyleSlant.Italic;
|
||||
break;
|
||||
case "oblique":
|
||||
style = SKFontStyleSlant.Oblique;
|
||||
break;
|
||||
case "normal":
|
||||
style = SKFontStyleSlant.Upright;
|
||||
break;
|
||||
default:
|
||||
style = defaultStyle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
private int ReadFontWidth(Dictionary<string, string> fontStyle, int defaultWidth = (int)SKFontStyleWidth.Normal)
|
||||
{
|
||||
int width = defaultWidth;
|
||||
|
||||
string fwidth;
|
||||
if (fontStyle.TryGetValue("font-stretch", out fwidth) && !string.IsNullOrWhiteSpace(fwidth) && !int.TryParse(fwidth, out width))
|
||||
{
|
||||
switch (fwidth)
|
||||
{
|
||||
case "ultra-condensed":
|
||||
width = (int)SKFontStyleWidth.UltraCondensed;
|
||||
break;
|
||||
case "extra-condensed":
|
||||
width = (int)SKFontStyleWidth.ExtraCondensed;
|
||||
break;
|
||||
case "condensed":
|
||||
width = (int)SKFontStyleWidth.Condensed;
|
||||
break;
|
||||
case "semi-condensed":
|
||||
width = (int)SKFontStyleWidth.SemiCondensed;
|
||||
break;
|
||||
case "normal":
|
||||
width = (int)SKFontStyleWidth.Normal;
|
||||
break;
|
||||
case "semi-expanded":
|
||||
width = (int)SKFontStyleWidth.SemiExpanded;
|
||||
break;
|
||||
case "expanded":
|
||||
width = (int)SKFontStyleWidth.Expanded;
|
||||
break;
|
||||
case "extra-expanded":
|
||||
width = (int)SKFontStyleWidth.ExtraExpanded;
|
||||
break;
|
||||
case "ultra-expanded":
|
||||
width = (int)SKFontStyleWidth.UltraExpanded;
|
||||
break;
|
||||
case "wider":
|
||||
width = width + 1;
|
||||
break;
|
||||
case "narrower":
|
||||
width = width - 1;
|
||||
break;
|
||||
default:
|
||||
width = defaultWidth;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.Min(Math.Max((int)SKFontStyleWidth.UltraCondensed, width), (int)SKFontStyleWidth.UltraExpanded);
|
||||
}
|
||||
|
||||
private int ReadFontWeight(Dictionary<string, string> fontStyle, int defaultWeight = (int)SKFontStyleWeight.Normal)
|
||||
{
|
||||
int weight = defaultWeight;
|
||||
|
||||
string fweight;
|
||||
if (fontStyle.TryGetValue("font-weight", out fweight) && !string.IsNullOrWhiteSpace(fweight) && !int.TryParse(fweight, out weight))
|
||||
{
|
||||
switch (fweight)
|
||||
{
|
||||
case "normal":
|
||||
weight = (int)SKFontStyleWeight.Normal;
|
||||
break;
|
||||
case "bold":
|
||||
weight = (int)SKFontStyleWeight.Bold;
|
||||
break;
|
||||
case "bolder":
|
||||
weight = weight + 100;
|
||||
break;
|
||||
case "lighter":
|
||||
weight = weight - 100;
|
||||
break;
|
||||
default:
|
||||
weight = defaultWeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.Min(Math.Max((int)SKFontStyleWeight.Thin, weight), (int)SKFontStyleWeight.ExtraBlack);
|
||||
}
|
||||
|
||||
private void LogOrThrow(string message)
|
||||
{
|
||||
if (ThrowOnUnsupportedElement)
|
||||
throw new NotSupportedException(message);
|
||||
else
|
||||
Debug.WriteLine(message);
|
||||
}
|
||||
|
||||
private string GetString(Dictionary<string, string> style, string name, string defaultValue = "")
|
||||
{
|
||||
string v;
|
||||
if (style.TryGetValue(name, out v))
|
||||
return v;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> ReadStyle(string style)
|
||||
{
|
||||
var d = new Dictionary<string, string>();
|
||||
var kvs = style.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var kv in kvs)
|
||||
{
|
||||
var m = keyValueRe.Match(kv);
|
||||
if (m.Success)
|
||||
{
|
||||
var k = m.Groups[1].Value;
|
||||
var v = m.Groups[2].Value;
|
||||
d[k] = v;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> ReadStyle(XElement e)
|
||||
{
|
||||
// get from local attributes
|
||||
var dic = e.Attributes().ToDictionary(k => k.Name.LocalName, v => v.Value);
|
||||
|
||||
var style = e.Attribute("style")?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(style))
|
||||
{
|
||||
// get from stlye attribute
|
||||
var styleDic = ReadStyle(style);
|
||||
|
||||
// overwrite
|
||||
foreach (var pair in styleDic)
|
||||
dic[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
return dic;
|
||||
}
|
||||
|
||||
private void ReadPaints(XElement e, ref SKPaint stroke, ref SKPaint fill)
|
||||
{
|
||||
ReadPaints(ReadStyle(e), ref stroke, ref fill);
|
||||
}
|
||||
|
||||
private void ReadPaints(Dictionary<string, string> style, ref SKPaint strokePaint, ref SKPaint fillPaint)
|
||||
{
|
||||
// stroke
|
||||
var stroke = GetString(style, "stroke").Trim();
|
||||
if (string.IsNullOrEmpty(stroke))
|
||||
{
|
||||
// no change
|
||||
}
|
||||
else if (stroke.Equals("none", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
strokePaint = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strokePaint == null)
|
||||
strokePaint = CreatePaint(true);
|
||||
|
||||
SKColor color;
|
||||
if (SKColor.TryParse(stroke, out color))
|
||||
{
|
||||
// preserve alpha
|
||||
if (color.Alpha == 255)
|
||||
strokePaint.Color = color.WithAlpha(strokePaint.Color.Alpha);
|
||||
else
|
||||
strokePaint.Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
// stroke attributes
|
||||
var strokeWidth = GetString(style, "stroke-width");
|
||||
if (!string.IsNullOrWhiteSpace(strokeWidth))
|
||||
{
|
||||
if (strokePaint == null)
|
||||
strokePaint = CreatePaint(true);
|
||||
strokePaint.StrokeWidth = ReadNumber(strokeWidth);
|
||||
}
|
||||
|
||||
var strokeOpacity = GetString(style, "stroke-opacity");
|
||||
if (!string.IsNullOrWhiteSpace(strokeOpacity))
|
||||
{
|
||||
if (strokePaint == null)
|
||||
strokePaint = CreatePaint(true);
|
||||
strokePaint.Color = strokePaint.Color.WithAlpha((byte)(ReadNumber(strokeOpacity) * 255));
|
||||
}
|
||||
|
||||
// fill
|
||||
var fill = GetString(style, "fill").Trim();
|
||||
if (string.IsNullOrEmpty(fill))
|
||||
{
|
||||
// no change
|
||||
}
|
||||
else if (fill.Equals("none", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fillPaint = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fillPaint == null)
|
||||
fillPaint = CreatePaint();
|
||||
|
||||
SKColor color;
|
||||
if (SKColor.TryParse(fill, out color))
|
||||
{
|
||||
// preserve alpha
|
||||
if (color.Alpha == 255)
|
||||
fillPaint.Color = color.WithAlpha(fillPaint.Color.Alpha);
|
||||
else
|
||||
fillPaint.Color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
var read = false;
|
||||
var urlM = fillUrlRe.Match(fill);
|
||||
if (urlM.Success)
|
||||
{
|
||||
var id = urlM.Groups[1].Value.Trim();
|
||||
|
||||
XElement defE;
|
||||
if (defs.TryGetValue(id, out defE))
|
||||
{
|
||||
var gradientShader = ReadGradient(defE);
|
||||
if (gradientShader != null)
|
||||
{
|
||||
// TODO: multiple shaders
|
||||
|
||||
fillPaint.Shader = gradientShader;
|
||||
read = true;
|
||||
}
|
||||
// else try another type (eg: image)
|
||||
}
|
||||
else
|
||||
{
|
||||
LogOrThrow($"Invalid fill url reference: {id}");
|
||||
}
|
||||
}
|
||||
|
||||
if (!read)
|
||||
{
|
||||
LogOrThrow($"Unsupported fill: {fill}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fill attributes
|
||||
var fillOpacity = GetString(style, "fill-opacity");
|
||||
if (!string.IsNullOrWhiteSpace(fillOpacity))
|
||||
{
|
||||
if (fillPaint == null)
|
||||
fillPaint = CreatePaint();
|
||||
|
||||
fillPaint.Color = fillPaint.Color.WithAlpha((byte)(ReadNumber(fillOpacity) * 255));
|
||||
}
|
||||
}
|
||||
|
||||
private SKPaint CreatePaint(bool stroke = false)
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
IsStroke = stroke,
|
||||
Color = SKColors.Black
|
||||
};
|
||||
}
|
||||
|
||||
private SKMatrix ReadTransform(string raw)
|
||||
{
|
||||
var t = SKMatrix.MakeIdentity();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
var calls = raw.Trim().Split(new[] { ')' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var c in calls)
|
||||
{
|
||||
var args = c.Split(new[] { '(', ',', ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var nt = SKMatrix.MakeIdentity();
|
||||
switch (args[0])
|
||||
{
|
||||
case "matrix":
|
||||
if (args.Length == 7)
|
||||
{
|
||||
nt.Values = new float[]
|
||||
{
|
||||
ReadNumber(args[1]), ReadNumber(args[3]), ReadNumber(args[5]),
|
||||
ReadNumber(args[2]), ReadNumber(args[4]), ReadNumber(args[6]),
|
||||
0, 0, 1
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
LogOrThrow($"Matrices are expected to have 6 elements, this one has {args.Length - 1}");
|
||||
}
|
||||
break;
|
||||
case "translate":
|
||||
if (args.Length >= 3)
|
||||
{
|
||||
nt = SKMatrix.MakeTranslation(ReadNumber(args[1]), ReadNumber(args[2]));
|
||||
}
|
||||
else if (args.Length >= 2)
|
||||
{
|
||||
nt = SKMatrix.MakeTranslation(ReadNumber(args[1]), 0);
|
||||
}
|
||||
break;
|
||||
case "scale":
|
||||
if (args.Length >= 3)
|
||||
{
|
||||
nt = SKMatrix.MakeScale(ReadNumber(args[1]), ReadNumber(args[2]));
|
||||
}
|
||||
else if (args.Length >= 2)
|
||||
{
|
||||
var sx = ReadNumber(args[1]);
|
||||
nt = SKMatrix.MakeScale(sx, sx);
|
||||
}
|
||||
break;
|
||||
case "rotate":
|
||||
var a = ReadNumber(args[1]);
|
||||
if (args.Length >= 4)
|
||||
{
|
||||
var x = ReadNumber(args[2]);
|
||||
var y = ReadNumber(args[3]);
|
||||
var t1 = SKMatrix.MakeTranslation(x, y);
|
||||
var t2 = SKMatrix.MakeRotationDegrees(a);
|
||||
var t3 = SKMatrix.MakeTranslation(-x, -y);
|
||||
SKMatrix.Concat(ref nt, ref t1, ref t2);
|
||||
SKMatrix.Concat(ref nt, ref nt, ref t3);
|
||||
}
|
||||
else
|
||||
{
|
||||
nt = SKMatrix.MakeRotationDegrees(a);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogOrThrow($"Can't transform {args[0]}");
|
||||
break;
|
||||
}
|
||||
SKMatrix.Concat(ref t, ref t, ref nt);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
private SKPath ReadPolyPath(string pointsData, bool closePath)
|
||||
{
|
||||
var path = new SKPath();
|
||||
var points = pointsData.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
var point = points[i];
|
||||
var xy = point.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var x = ReadNumber(xy[0]);
|
||||
var y = ReadNumber(xy[1]);
|
||||
if (i == 0)
|
||||
{
|
||||
path.MoveTo(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
path.LineTo(x, y);
|
||||
}
|
||||
}
|
||||
if (closePath)
|
||||
{
|
||||
path.Close();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private SKTextAlign ReadTextAlignment(XElement element)
|
||||
{
|
||||
string value = null;
|
||||
if (element != null)
|
||||
{
|
||||
var attrib = element.Attribute("text-anchor");
|
||||
if (attrib != null && !string.IsNullOrWhiteSpace(attrib.Value))
|
||||
value = attrib.Value;
|
||||
else
|
||||
{
|
||||
var style = element.Attribute("style");
|
||||
if (style != null && !string.IsNullOrWhiteSpace(style.Value))
|
||||
{
|
||||
value = GetString(ReadStyle(style.Value), "text-anchor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case "end":
|
||||
return SKTextAlign.Right;
|
||||
case "middle":
|
||||
return SKTextAlign.Center;
|
||||
default:
|
||||
return SKTextAlign.Left;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, SKPaint> linearGradients = new Dictionary<string, SKPaint>();
|
||||
|
||||
private SKShader ReadGradient(XElement defE)
|
||||
{
|
||||
switch (defE.Name.LocalName)
|
||||
{
|
||||
case "linearGradient":
|
||||
return ReadLinearGradient(defE);
|
||||
case "radialGradient":
|
||||
return ReadRadialGradient(defE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SKShader ReadRadialGradient(XElement e)
|
||||
{
|
||||
var centerX = ReadNumber(e.Attribute("cx"));
|
||||
var centerY = ReadNumber(e.Attribute("cy"));
|
||||
//var focusX = ReadOptionalNumber(e.Attribute("fx")) ?? centerX;
|
||||
//var focusY = ReadOptionalNumber(e.Attribute("fy")) ?? centerY;
|
||||
var radius = ReadNumber(e.Attribute("r"));
|
||||
var absolute = e.Attribute("gradientUnits")?.Value == "userSpaceOnUse";
|
||||
var tileMode = ReadSpreadMethod(e);
|
||||
var stops = ReadStops(e);
|
||||
|
||||
// TODO: check gradientTransform attribute
|
||||
// TODO: use absolute
|
||||
|
||||
return SKShader.CreateRadialGradient(
|
||||
new SKPoint(centerX, centerY),
|
||||
radius,
|
||||
stops.Values.ToArray(),
|
||||
stops.Keys.ToArray(),
|
||||
tileMode);
|
||||
}
|
||||
|
||||
private SKShader ReadLinearGradient(XElement e)
|
||||
{
|
||||
var startX = ReadNumber(e.Attribute("x1"));
|
||||
var startY = ReadNumber(e.Attribute("y1"));
|
||||
var endX = ReadNumber(e.Attribute("x2"));
|
||||
var endY = ReadNumber(e.Attribute("y2"));
|
||||
var absolute = e.Attribute("gradientUnits")?.Value == "userSpaceOnUse";
|
||||
var tileMode = ReadSpreadMethod(e);
|
||||
var stops = ReadStops(e);
|
||||
|
||||
// TODO: check gradientTransform attribute
|
||||
// TODO: use absolute
|
||||
|
||||
return SKShader.CreateLinearGradient(
|
||||
new SKPoint(startX, startY),
|
||||
new SKPoint(endX, endY),
|
||||
stops.Values.ToArray(),
|
||||
stops.Keys.ToArray(),
|
||||
tileMode);
|
||||
}
|
||||
|
||||
private static SKShaderTileMode ReadSpreadMethod(XElement e)
|
||||
{
|
||||
var repeat = e.Attribute("spreadMethod")?.Value;
|
||||
switch (repeat)
|
||||
{
|
||||
case "reflect":
|
||||
return SKShaderTileMode.Mirror;
|
||||
case "repeat":
|
||||
return SKShaderTileMode.Repeat;
|
||||
case "pad":
|
||||
default:
|
||||
return SKShaderTileMode.Clamp;
|
||||
}
|
||||
}
|
||||
|
||||
private XElement ReadDefinition(XElement e)
|
||||
{
|
||||
var union = new XElement(e.Name);
|
||||
union.Add(e.Elements());
|
||||
union.Add(e.Attributes());
|
||||
|
||||
var child = ReadHref(e);
|
||||
if (child != null)
|
||||
{
|
||||
union.Add(child.Elements());
|
||||
union.Add(child.Attributes().Where(a => union.Attribute(a.Name) == null));
|
||||
}
|
||||
|
||||
return union;
|
||||
}
|
||||
|
||||
private XElement ReadHref(XElement e)
|
||||
{
|
||||
var href = e.Attribute(xlink + "href")?.Value?.Substring(1);
|
||||
XElement child;
|
||||
if (string.IsNullOrEmpty(href) || !defs.TryGetValue(href, out child))
|
||||
{
|
||||
child = null;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
private SortedDictionary<float, SKColor> ReadStops(XElement e)
|
||||
{
|
||||
var stops = new SortedDictionary<float, SKColor>();
|
||||
|
||||
var ns = e.Name.Namespace;
|
||||
foreach (var se in e.Elements(ns + "stop"))
|
||||
{
|
||||
var style = ReadStyle(se);
|
||||
|
||||
var offset = ReadNumber(style["offset"]);
|
||||
var color = SKColors.Black;
|
||||
byte alpha = 255;
|
||||
|
||||
string stopColor;
|
||||
if (style.TryGetValue("stop-color", out stopColor))
|
||||
{
|
||||
// preserve alpha
|
||||
if (SKColor.TryParse(stopColor, out color) && color.Alpha == 255)
|
||||
alpha = color.Alpha;
|
||||
}
|
||||
|
||||
string stopOpacity;
|
||||
if (style.TryGetValue("stop-opacity", out stopOpacity))
|
||||
{
|
||||
alpha = (byte)(ReadNumber(stopOpacity) * 255);
|
||||
}
|
||||
|
||||
color = color.WithAlpha(alpha);
|
||||
stops[offset] = color;
|
||||
}
|
||||
|
||||
return stops;
|
||||
}
|
||||
|
||||
private float ReadNumber(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
return 0;
|
||||
|
||||
var s = raw.Trim();
|
||||
var m = 1.0f;
|
||||
|
||||
if (unitRe.IsMatch(s))
|
||||
{
|
||||
if (s.EndsWith("in", StringComparison.Ordinal))
|
||||
{
|
||||
m = PixelsPerInch;
|
||||
}
|
||||
else if (s.EndsWith("cm", StringComparison.Ordinal))
|
||||
{
|
||||
m = PixelsPerInch / 2.54f;
|
||||
}
|
||||
else if (s.EndsWith("mm", StringComparison.Ordinal))
|
||||
{
|
||||
m = PixelsPerInch / 25.4f;
|
||||
}
|
||||
else if (s.EndsWith("pt", StringComparison.Ordinal))
|
||||
{
|
||||
m = PixelsPerInch / 72.0f;
|
||||
}
|
||||
else if (s.EndsWith("pc", StringComparison.Ordinal))
|
||||
{
|
||||
m = PixelsPerInch / 6.0f;
|
||||
}
|
||||
s = s.Substring(0, s.Length - 2);
|
||||
}
|
||||
else if (percRe.IsMatch(s))
|
||||
{
|
||||
s = s.Substring(0, s.Length - 1);
|
||||
m = 0.01f;
|
||||
}
|
||||
|
||||
float v;
|
||||
if (!float.TryParse(s, NumberStyles.Float, icult, out v))
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
return m * v;
|
||||
}
|
||||
|
||||
private float ReadNumber(XAttribute a) => ReadNumber(a?.Value);
|
||||
|
||||
private float? ReadOptionalNumber(XAttribute a) => a == null ? (float?)null : ReadNumber(a.Value);
|
||||
|
||||
private SKRect ReadRectangle(string s)
|
||||
{
|
||||
var r = new SKRect();
|
||||
var p = s.Split(WS, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (p.Length > 0)
|
||||
r.Left = ReadNumber(p[0]);
|
||||
if (p.Length > 1)
|
||||
r.Top = ReadNumber(p[1]);
|
||||
if (p.Length > 2)
|
||||
r.Right = r.Left + ReadNumber(p[2]);
|
||||
if (p.Length > 3)
|
||||
r.Bottom = r.Top + ReadNumber(p[3]);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{04C4399A-6740-4733-B6B7-F968232A76C8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SkiaSharp</RootNamespace>
|
||||
<AssemblyName>SkiaSharp.Svg</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TargetFrameworkProfile>Profile78</TargetFrameworkProfile>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .NET Framework is automatically included -->
|
||||
<ProjectReference Include="..\..\..\Binding\SkiaSharp.Portable\SkiaSharp.Portable.csproj">
|
||||
<Project>{7aa90628-2fdd-4585-af2f-cc51cfa8b52a}</Project>
|
||||
<Name>SkiaSharp.Portable</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\SkiaSharpSvgAssemblyInfo.cs" />
|
||||
<Compile Include="SKSvg.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -43,6 +43,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp", "SkiaSharp", "{
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp.Views", "SkiaSharp.Views", "{F19E1537-81B2-4D4F-A69E-78DC73ACC141}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp.Svg", "SkiaSharp.Svg", "{F9C7D51F-468C-4E58-BB75-2317DB99C8A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Svg", "SkiaSharp.Svg\SkiaSharp.Svg\SkiaSharp.Svg.csproj", "{04C4399A-6740-4733-B6B7-F968232A76C8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Shared\SkiaSharp.Views.Forms.Shared.projitems*{1555d119-8598-4e4d-91ac-d313f94a1673}*SharedItemsImports = 4
|
||||
|
@ -124,6 +128,10 @@ Global
|
|||
{7AA90628-2FDD-4585-AF2F-CC51CFA8B52A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7AA90628-2FDD-4585-AF2F-CC51CFA8B52A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7AA90628-2FDD-4585-AF2F-CC51CFA8B52A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -143,5 +151,6 @@ Global
|
|||
{5180E370-A455-42BB-99F9-97BD269B8A52} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{4588A759-3853-49B8-8A68-6C7917BE9220} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{7AA90628-2FDD-4585-AF2F-CC51CFA8B52A} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8} = {F9C7D51F-468C-4E58-BB75-2317DB99C8A7}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -31,6 +31,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp.Views", "SkiaShar
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Views.WPF", "SkiaSharp.Views\SkiaSharp.Views.WPF\SkiaSharp.Views.WPF.csproj", "{743CF830-D458-41A9-865A-F85126562015}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp.Svg", "SkiaSharp.Svg", "{F9C7D51F-468C-4E58-BB75-2317DB99C8A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Svg", "SkiaSharp.Svg\SkiaSharp.Svg\SkiaSharp.Svg.csproj", "{04C4399A-6740-4733-B6B7-F968232A76C8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Shared\SkiaSharp.Views.Forms.Shared.projitems*{1555d119-8598-4e4d-91ac-d313f94a1673}*SharedItemsImports = 4
|
||||
|
@ -82,6 +86,10 @@ Global
|
|||
{743CF830-D458-41A9-865A-F85126562015}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{743CF830-D458-41A9-865A-F85126562015}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{743CF830-D458-41A9-865A-F85126562015}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -96,5 +104,6 @@ Global
|
|||
{EB1BBDCC-FB07-40D5-8B9E-0079E2C2F2DF} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{BAB615AA-956E-4079-B260-DD7B1F52EC7D} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{743CF830-D458-41A9-865A-F85126562015} = {F19E1537-81B2-4D4F-A69E-78DC73ACC141}
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8} = {F9C7D51F-468C-4E58-BB75-2317DB99C8A7}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -57,6 +57,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Views.WPF", "Skia
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp.Views.Forms", "SkiaSharp.Views.Forms", "{DCADA8CC-D50A-4BD9-B2E6-86696A43D819}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SkiaSharp.Svg", "SkiaSharp.Svg", "{F9C7D51F-468C-4E58-BB75-2317DB99C8A7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Svg", "SkiaSharp.Svg\SkiaSharp.Svg\SkiaSharp.Svg.csproj", "{04C4399A-6740-4733-B6B7-F968232A76C8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Shared\SkiaSharp.Views.Forms.Shared.projitems*{1555d119-8598-4e4d-91ac-d313f94a1673}*SharedItemsImports = 4
|
||||
|
@ -168,6 +172,10 @@ Global
|
|||
{743CF830-D458-41A9-865A-F85126562015}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{743CF830-D458-41A9-865A-F85126562015}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{743CF830-D458-41A9-865A-F85126562015}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -197,5 +205,6 @@ Global
|
|||
{EB1BBDCC-FB07-40D5-8B9E-0079E2C2F2DF} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{BAB615AA-956E-4079-B260-DD7B1F52EC7D} = {C335869B-7CC8-4239-B4A5-8031AA9758D3}
|
||||
{743CF830-D458-41A9-865A-F85126562015} = {F19E1537-81B2-4D4F-A69E-78DC73ACC141}
|
||||
{04C4399A-6740-4733-B6B7-F968232A76C8} = {F9C7D51F-468C-4E58-BB75-2317DB99C8A7}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
Загрузка…
Ссылка в новой задаче