Commit 659ec04b authored by hhchaos's avatar hhchaos

添加项目文件。

parent 78d64b97
Pipeline #157 canceled with stages
using System;
using System.Numerics;
using Windows.Foundation;
using Microsoft.Graphics.Canvas;
namespace SvgConverter
{
public abstract class AnimationBase : IDisposable
{
public Rect ViewBox { get; protected set; }
public abstract float Progress { get; set; }
public CanvasDevice Device { get; protected set; }
public abstract void Dispose();
public abstract Vector2? Draw(
CanvasDrawingSession drawingSession, float drawProgress);
}
}
\ No newline at end of file
Busing System.Reflection;
<?xml version="1.0" encoding="utf-8"?>
<!--
此文件包含运行时指令,应用程序通过反射和其他动态代码模式
所访问的类型的规范。运行时指令用于控制
.NET Native 优化器,并确保它不会删除你的库所访问的代码。如果你的
库不进行任何反射,那么一般而言你无需编辑此文件。但是,
如果你的库反射类型,尤其是传递到它或从它的类型所派生的类型,
那么就应该编写运行时指令。
库中反射最常见的使用方式是发现传递到库的
类型的信息。运行时指令有三种方式来表达传递给库的
类型的要求
1. Parameter、GenericParameter、TypeParameter、TypeEnumerableParameter
使用这些指令可反射作为参数传递的类型。
2. SubTypes
使用 SubTypes 指令反射从其他类型派生的类型。
3. AttributeImplies
使用 AttributeImplies 指令指示你的库需要反射使用
属性修饰的类型或方法。
有关为库编写运行时指令的详细信息,请参阅
https://go.microsoft.com/fwlink/?LinkID=391919
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="SvgConverter">
<!--在此处为库添加指令-->
</Library>
</Directives>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.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>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SvgConverter</RootNamespace>
<AssemblyName>SvgConverter</AssemblyName>
<DefaultLanguage>zh-CN</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.15063.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.10586.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<PlatformTarget>ARM</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<PlatformTarget>ARM</PlatformTarget>
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="AnimationBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SvgParseForWin2D\Utilities\SvgDrawHelper.cs" />
<Compile Include="SvgParseForWin2D\Win2DSvgElement.cs" />
<Compile Include="SvgParseForWin2D\Win2DSvgGeometry.cs" />
<Compile Include="SvgParseForWin2D\Win2DSvgImage.cs" />
<Compile Include="SvgParseForWin2D\Win2DSvgNode.cs" />
<Compile Include="SvgParseForWin2D\Utilities\Win2DSvgParseHelper.cs" />
<Compile Include="SvgParse\Attributes\DisplayMode.cs" />
<Compile Include="SvgParse\Attributes\TextAnchor.cs" />
<Compile Include="SvgParse\Brushes\ISvgBrush.cs" />
<Compile Include="SvgParse\Brushes\SvgGradientStop.cs" />
<Compile Include="SvgParse\Brushes\SvgLinearGradientBrush.cs" />
<Compile Include="SvgParse\Brushes\SvgRadialGradientBrush.cs" />
<Compile Include="SvgParse\Brushes\SvgSolidColorBrush.cs" />
<Compile Include="SvgParse\SvgAttributesHelper\SvgColorHelper.cs" />
<Compile Include="SvgParse\SvgAttributesHelper\SvgLengthHelper.cs" />
<Compile Include="SvgParse\SvgAttributesHelper\SvgTransformHelper.cs" />
<Compile Include="SvgParse\SvgAttributesHelper\SvgBrushHelper.cs" />
<Compile Include="SvgParse\SvgElement.cs" />
<Compile Include="SvgParse\SvgGeometry.cs" />
<Compile Include="SvgParse\SvgImage.cs" />
<Compile Include="SvgParse\SvgNode.cs" />
<Compile Include="SvgParse\SvgNodeGroup.cs" />
<Compile Include="SvgParse\SvgNodeStyle.cs" />
<Compile Include="SvgParse\Utilities\SvgParseHelper.cs" />
<Compile Include="SvgParse\SvgRectangle.cs" />
<Compile Include="SvgParse\SvgText.cs" />
<Compile Include="SvgParse\Utilities\SvgDefsNodeHelper.cs" />
<Compile Include="SvgParse\Utilities\SvgLoadHelper.cs" />
<Compile Include="TextParse\PathMoveDirection.cs" />
<Compile Include="TextParse\PathPoint.cs" />
<Compile Include="TextParse\CharGeometry.cs" />
<Compile Include="TextParse\TextSvgElement.cs" />
<Compile Include="TextParse\TextParseHelper.cs" />
<Compile Include="TextParse\Win2DCharSvgElement.cs" />
<EmbeddedResource Include="Properties\SvgConverter.rd.xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.0.1</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.21.0</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.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>
\ No newline at end of file

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29020.237
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgConverter", "SvgConverter.csproj", "{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|ARM.ActiveCfg = Debug|ARM
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|ARM.Build.0 = Debug|ARM
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|x64.ActiveCfg = Debug|x64
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|x64.Build.0 = Debug|x64
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|x86.ActiveCfg = Debug|x86
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Debug|x86.Build.0 = Debug|x86
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|Any CPU.Build.0 = Release|Any CPU
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|ARM.ActiveCfg = Release|ARM
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|ARM.Build.0 = Release|ARM
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|x64.ActiveCfg = Release|x64
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|x64.Build.0 = Release|x64
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|x86.ActiveCfg = Release|x86
{500B15E8-7089-4EB2-BFF5-4FEAAC44BA56}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {39C58346-1BD7-484B-B331-352CC4626934}
EndGlobalSection
EndGlobal
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SvgConverter.SvgParse.Attributes
{
public enum DisplayMode
{
Inline,
None,
Other
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SvgConverter.SvgParse.Attributes
{
public enum TextAnchor
{
Start,
Middle,
End
}
}
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
namespace SvgConverter.SvgParse.Brushes
{
public interface ISvgBrush
{
float Opacity { get; set; }
Matrix3x2 Transform { get; set; }
ICanvasBrush Parse(ICanvasResourceCreator resourceCreator);
}
}
\ No newline at end of file
using Windows.UI;
namespace SvgConverter.SvgParse.Brushes
{
public struct SvgGradientStop
{
//
// 摘要:
// The position of the gradient stop. Expected to be between 0 and 1, inclusive.
public float Position;
public Color Color;
}
}
\ No newline at end of file
using System.Linq;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
namespace SvgConverter.SvgParse.Brushes
{
public class SvgLinearGradientBrush : ISvgBrush
{
public SvgLinearGradientBrush(SvgGradientStop[] gradientStops)
{
Stops = gradientStops;
}
public SvgGradientStop[] Stops { get; }
//
// 摘要:
// The point on the canvas on which the gradient starts.
public Vector2 StartPoint { get; set; }
//
// 摘要:
// The point on the canvas on which the gradient stops.
public Vector2 EndPoint { get; set; }
public float Opacity { get; set; } = 1;
public Matrix3x2 Transform { get; set; }
public ICanvasBrush Parse(ICanvasResourceCreator resourceCreator)
{
var stops = Stops.Select(o => new CanvasGradientStop
{
Position = o.Position,
Color = o.Color
});
var linearGra = new CanvasLinearGradientBrush(resourceCreator, stops.ToArray())
{
StartPoint = StartPoint,
EndPoint = EndPoint,
Transform = Transform,
Opacity = Opacity
};
return linearGra;
}
}
}
\ No newline at end of file
using System.Linq;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
namespace SvgConverter.SvgParse.Brushes
{
public class SvgRadialGradientBrush : ISvgBrush
{
public SvgRadialGradientBrush(SvgGradientStop[] gradientStops)
{
Stops = gradientStops;
}
public SvgGradientStop[] Stops { get; }
//
// 摘要:
// Specifies the vertical radius of the brush's radial gradient.
public float RadiusY { get; set; }
//
// 摘要:
// Specifies the horizontal radius of the brush's radial gradient.
public float RadiusX { get; set; }
//
// 摘要:
// Specifies a displacement from Center, used to form the brush's radial gradient.
public Vector2 OriginOffset { get; set; }
//
// 摘要:
// Specifies the center of the brush's radial gradient
public Vector2 Center { get; set; }
public float Opacity { get; set; } = 1;
public Matrix3x2 Transform { get; set; }
public ICanvasBrush Parse(ICanvasResourceCreator resourceCreator)
{
var stops = Stops.Select(o => new CanvasGradientStop
{
Position = o.Position,
Color = o.Color
});
var radialGra = new CanvasRadialGradientBrush(resourceCreator, stops.ToArray())
{
Center = Center,
OriginOffset = OriginOffset,
RadiusX = RadiusX,
RadiusY = RadiusY,
Transform = Transform,
Opacity = Opacity
};
return radialGra;
}
}
}
\ No newline at end of file
using System.Numerics;
using Windows.UI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
namespace SvgConverter.SvgParse.Brushes
{
public class SvgSolidColorBrush : ISvgBrush
{
public SvgSolidColorBrush(Color color)
{
Color = color;
}
public Color Color { get; set; }
public float Opacity { get; set; } = 1;
public Matrix3x2 Transform { get; set; }
public ICanvasBrush Parse(ICanvasResourceCreator resourceCreator)
{
return new CanvasSolidColorBrush(resourceCreator, Color)
{
Opacity = Opacity
};
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Numerics;
using System.Xml;
using Windows.Foundation;
using Windows.UI;
using SvgConverter.SvgParse.Brushes;
namespace SvgConverter.SvgParse.SvgAttributesHelper
{
public static class SvgBrushHelper
{
public static ISvgBrush Parse(string brush, Dictionary<string, ISvgBrush> defBrushes)
{
if (string.IsNullOrWhiteSpace(brush) || brush.ToLower().Equals("none"))
return new SvgSolidColorBrush(Colors.Transparent);
if (brush.StartsWith("url(") && defBrushes != null)
{
brush =
brush.Replace("url(", string.Empty)
.Replace("#", string.Empty)
.Replace(")", string.Empty)
.Replace(" ", string.Empty);
if (defBrushes.ContainsKey(brush)) return defBrushes[brush];
return new SvgSolidColorBrush(Colors.Transparent);
}
return new SvgSolidColorBrush(SvgColorHelper.ParseColor(brush));
}
public static SvgLinearGradientBrush ParseLinearGradientBrush(XmlElement brush, Size refSize)
{
if (brush != null && brush.Name == "linearGradient")
{
var stopList = brush.GetElementsByTagName("stop");
var list = new List<SvgGradientStop>();
foreach (XmlNode item in stopList)
{
var offset = SvgLengthHelper.ParseLength(item.Attributes["offset"]?.Value, 1);
var styles = ParseStyle(item.Attributes);
if (styles != null)
{
styles.TryGetValue("stop-color", out var stopColorStr);
var color = string.IsNullOrWhiteSpace(stopColorStr)
? Colors.Black
: SvgColorHelper.ParseColor(stopColorStr);
if (styles.TryGetValue("stop-opacity", out var stopOpacityStr))
color.A = (byte) (SvgLengthHelper.ParseLength(stopOpacityStr, 1) * 255);
list.Add(new SvgGradientStop
{
Position = (float) offset,
Color = color
});
}
}
if (list.Count == 0)
return null;
var linearGra = new SvgLinearGradientBrush(list.ToArray())
{
StartPoint =
new Point(SvgLengthHelper.ParseLength(brush.Attributes["x1"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(brush.Attributes["y1"]?.Value, refSize.Height)).ToVector2(),
EndPoint =
new Point(
SvgLengthHelper.ParseLength(
brush.Attributes["x2"] == null ? "100%" : brush.Attributes["x2"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(brush.Attributes["y2"]?.Value, refSize.Height)).ToVector2(),
Transform = SvgTransformHelper.ParseTransform(brush.Attributes["gradientTransform"]?.Value)
};
//if (brush.Attributes["gradientUnits"]?.Value != "userSpaceOnUse")
//{
// linearGra.StartPoint =
// new Point(linearGra.StartPoint.X * refSize.Width, linearGra.StartPoint.Y * refSize.Height)
// .ToVector2();
// linearGra.EndPoint =
// new Point(linearGra.EndPoint.X * refSize.Width, linearGra.EndPoint.Y * refSize.Height)
// .ToVector2();
//}
return linearGra;
}
return null;
}
public static SvgRadialGradientBrush ParseRadialGradientBrush(XmlElement brush, Size refSize)
{
if (brush != null && brush.Name == "radialGradient")
{
var stopList = brush.GetElementsByTagName("stop");
var list = new List<SvgGradientStop>();
foreach (XmlNode item in stopList)
{
var offset = SvgLengthHelper.ParseLength(item.Attributes["offset"]?.Value, 1);
var styles = ParseStyle(item.Attributes);
if (styles != null)
{
styles.TryGetValue("stop-color", out var stopColorStr);
var color = string.IsNullOrWhiteSpace(stopColorStr)
? Colors.Black
: SvgColorHelper.ParseColor(stopColorStr);
if (styles.TryGetValue("stop-opacity", out var stopOpacityStr))
color.A = (byte) (SvgLengthHelper.ParseLength(stopOpacityStr, 1) * 255);
list.Add(new SvgGradientStop
{
Position = (float) offset,
Color = color
});
}
}
if (list.Count == 0)
return null;
var radialGra = new SvgRadialGradientBrush(list.ToArray())
{
Center =
new Point(
SvgLengthHelper.ParseLength(
brush.Attributes["cx"] == null ? "50%" : brush.Attributes["cx"]?.Value,
refSize.Width),
SvgLengthHelper.ParseLength(
brush.Attributes["cy"] == null ? "50%" : brush.Attributes["cy"]?.Value,
refSize.Height))
.ToVector2(),
OriginOffset = new Point(SvgLengthHelper.ParseLength(brush.Attributes["fx"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(brush.Attributes["fy"]?.Value, refSize.Height)).ToVector2(),
RadiusX =
(float)
SvgLengthHelper.ParseLength(
brush.Attributes["r"] == null ? "50%" : brush.Attributes["r"]?.Value, refSize.Width),
RadiusY =
(float)
SvgLengthHelper.ParseLength(
brush.Attributes["r"] == null ? "50%" : brush.Attributes["r"]?.Value, refSize.Width),
Transform = SvgTransformHelper.ParseTransform(brush.Attributes["gradientTransform"]?.Value)
};
//if (brush.Attributes["gradientUnits"]?.Value != "userSpaceOnUse")
//{
// radialGra.Center =
// new Point(radialGra.Center.X * refSize.Width, radialGra.Center.Y * refSize.Height)
// .ToVector2();
// radialGra.OriginOffset =
// new Point(radialGra.OriginOffset.X * refSize.Width, radialGra.OriginOffset.Y * refSize.Height)
// .ToVector2();
// radialGra.RadiusX = radialGra.RadiusY = (float) (radialGra.RadiusX * refSize.Width);
//}
return radialGra;
}
return null;
}
private static Dictionary<string, string> ParseStyle(XmlAttributeCollection style)
{
if (style != null && style.Count > 0)
{
var dic = new Dictionary<string, string>();
foreach (XmlAttribute item in style)
if (item.Name == "style")
{
var strs = item.Value?.Split(';');
if (strs != null)
foreach (var str in strs)
{
var keyValue = str.Split(':');
if (keyValue?.Length == 2) dic[keyValue[0].Trim()] = keyValue[1].Trim();
}
}
else
{
dic[item.Name] = item.Value;
}
return dic;
}
return null;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Windows.UI;
namespace SvgConverter.SvgParse.SvgAttributesHelper
{
public static class SvgColorHelper
{
private static readonly Dictionary<string, Color> Colors = new Dictionary<string, Color>
{
["aliceblue"] = Color.FromArgb(255, 240, 248, 255),
["antiquewhite"] = Color.FromArgb(255, 250, 235, 215),
["aqua"] = Color.FromArgb(255, 0, 255, 255),
["aquamarine"] = Color.FromArgb(255, 127, 255, 212),
["azure"] = Color.FromArgb(255, 240, 255, 255),
["beige"] = Color.FromArgb(255, 245, 245, 220),
["bisque"] = Color.FromArgb(255, 255, 228, 196),
["black"] = Color.FromArgb(255, 0, 0, 0),
["blanchedalmond"] = Color.FromArgb(255, 255, 235, 205),
["blue"] = Color.FromArgb(255, 0, 0, 255),
["blueviolet"] = Color.FromArgb(255, 138, 43, 226),
["brown"] = Color.FromArgb(255, 165, 42, 42),
["burlywood"] = Color.FromArgb(255, 222, 184, 135),
["cadetblue"] = Color.FromArgb(255, 95, 158, 160),
["chartreuse"] = Color.FromArgb(255, 127, 255, 0),
["chocolate"] = Color.FromArgb(255, 210, 105, 30),
["coral"] = Color.FromArgb(255, 255, 127, 80),
["cornflowerblue"] = Color.FromArgb(255, 100, 149, 237),
["cornsilk"] = Color.FromArgb(255, 255, 248, 220),
["crimson"] = Color.FromArgb(255, 220, 20, 60),
["cyan"] = Color.FromArgb(255, 0, 255, 255),
["darkblue"] = Color.FromArgb(255, 0, 0, 139),
["darkcyan"] = Color.FromArgb(255, 0, 139, 139),
["darkgoldenrod"] = Color.FromArgb(255, 184, 134, 11),
["darkgray"] = Color.FromArgb(255, 169, 169, 169),
["darkgreen"] = Color.FromArgb(255, 0, 100, 0),
["darkgrey"] = Color.FromArgb(255, 169, 169, 169),
["darkkhaki"] = Color.FromArgb(255, 189, 183, 107),
["darkmagenta"] = Color.FromArgb(255, 139, 0, 139),
["darkolivegreen"] = Color.FromArgb(255, 85, 107, 47),
["darkorange"] = Color.FromArgb(255, 255, 140, 0),
["darkorchild"] = Color.FromArgb(255, 255, 140, 0),
["darkorchid"] = Color.FromArgb(255, 153, 50, 204),
["darkred"] = Color.FromArgb(255, 139, 0, 0),
["darksalmon"] = Color.FromArgb(255, 233, 150, 122),
["darkseagreen"] = Color.FromArgb(255, 143, 188, 143),
["darkslateblue"] = Color.FromArgb(255, 72, 61, 139),
["darkslategray"] = Color.FromArgb(255, 47, 79, 79),
["darkslategrey"] = Color.FromArgb(255, 47, 79, 79),
["darkturquoise"] = Color.FromArgb(255, 0, 206, 209),
["darkviolet"] = Color.FromArgb(255, 148, 0, 211),
["deeppink"] = Color.FromArgb(255, 255, 20, 147),
["deepskyblue"] = Color.FromArgb(255, 0, 191, 255),
["dimgray"] = Color.FromArgb(255, 105, 105, 105),
["dimgrey"] = Color.FromArgb(255, 105, 105, 105),
["dodgerblue"] = Color.FromArgb(255, 30, 144, 255),
["firebrick"] = Color.FromArgb(255, 178, 34, 34),
["floralwhite"] = Color.FromArgb(255, 255, 250, 240),
["forestgreen"] = Color.FromArgb(255, 34, 139, 34),
["fuchsia"] = Color.FromArgb(255, 255, 0, 255),
["gainsboro"] = Color.FromArgb(255, 220, 220, 220),
["ghostwhite"] = Color.FromArgb(255, 248, 248, 255),
["gold"] = Color.FromArgb(255, 255, 215, 0),
["goldenrod"] = Color.FromArgb(255, 218, 165, 32),
["gray"] = Color.FromArgb(255, 128, 128, 128),
["grey"] = Color.FromArgb(255, 128, 128, 128),
["green"] = Color.FromArgb(255, 0, 128, 0),
["greenyellow"] = Color.FromArgb(255, 173, 255, 47),
["honeydew"] = Color.FromArgb(255, 240, 255, 240),
["hotpink"] = Color.FromArgb(255, 255, 105, 180),
["indianred"] = Color.FromArgb(255, 205, 92, 92),
["indigo"] = Color.FromArgb(255, 75, 0, 130),
["ivory"] = Color.FromArgb(255, 255, 255, 240),
["khaki"] = Color.FromArgb(255, 240, 230, 140),
["lavender"] = Color.FromArgb(255, 230, 230, 250),
["lavenderblush"] = Color.FromArgb(255, 255, 240, 245),
["lawngreen"] = Color.FromArgb(255, 124, 252, 0),
["lemonchiffon"] = Color.FromArgb(255, 255, 250, 205),
["lightblue"] = Color.FromArgb(255, 173, 216, 230),
["lightcoral"] = Color.FromArgb(255, 240, 128, 128),
["lightcyan"] = Color.FromArgb(255, 224, 255, 255),
["lightgoldenrodyellow"] = Color.FromArgb(255, 250, 250, 210),
["lightgray"] = Color.FromArgb(255, 211, 211, 211),
["lightgreen"] = Color.FromArgb(255, 144, 238, 144),
["lightgrey"] = Color.FromArgb(255, 211, 211, 211),
["lightpink"] = Color.FromArgb(255, 255, 182, 193),
["lightsalmon"] = Color.FromArgb(255, 255, 160, 122),
["lightseagreen"] = Color.FromArgb(255, 32, 178, 170),
["lightskyblue"] = Color.FromArgb(255, 135, 206, 250),
["lightslategray"] = Color.FromArgb(255, 119, 136, 153),
["lightslategrey"] = Color.FromArgb(255, 119, 136, 153),
["lightsteelblue"] = Color.FromArgb(255, 176, 196, 222),
["lightyellow"] = Color.FromArgb(255, 255, 255, 224),
["lime"] = Color.FromArgb(255, 0, 255, 0),
["limegreen"] = Color.FromArgb(255, 50, 205, 50),
["linen"] = Color.FromArgb(255, 250, 240, 230),
["magenta"] = Color.FromArgb(255, 255, 0, 255),
["maroon"] = Color.FromArgb(255, 128, 0, 0),
["mediumaquamarine"] = Color.FromArgb(255, 102, 205, 170),
["mediumblue"] = Color.FromArgb(255, 0, 0, 205),
["mediumorchid"] = Color.FromArgb(255, 186, 85, 211),
["mediumpurple"] = Color.FromArgb(255, 147, 112, 219),
["mediumseagreen"] = Color.FromArgb(255, 60, 179, 113),
["mediumslateblue"] = Color.FromArgb(255, 13, 104, 238),
["mediumspringgreen"] = Color.FromArgb(255, 0, 250, 154),
["mediumturquoise"] = Color.FromArgb(255, 72, 209, 204),
["mediumvioletred"] = Color.FromArgb(255, 199, 21, 133),
["midnightblue"] = Color.FromArgb(255, 25, 25, 112),
["mintcream"] = Color.FromArgb(255, 245, 255, 250),
["mistyrose"] = Color.FromArgb(255, 255, 228, 225),
["moccasin"] = Color.FromArgb(255, 255, 228, 181),
["navajowhite"] = Color.FromArgb(255, 255, 222, 173),
["navy"] = Color.FromArgb(255, 0, 0, 128),
["oldlace"] = Color.FromArgb(255, 253, 245, 230),
["olive"] = Color.FromArgb(255, 128, 128, 0),
["olivedrab"] = Color.FromArgb(255, 107, 142, 35),
["orange"] = Color.FromArgb(255, 255, 165, 0),
["orangered"] = Color.FromArgb(255, 255, 69, 0),
["orchid"] = Color.FromArgb(255, 128, 112, 214),
["palegoldenrod"] = Color.FromArgb(255, 238, 232, 170),
["palegreen"] = Color.FromArgb(255, 152, 251, 152),
["paleturquoise"] = Color.FromArgb(255, 175, 238, 238),
["palevioletred"] = Color.FromArgb(255, 219, 112, 147),
["papayawhip"] = Color.FromArgb(255, 255, 239, 213),
["peachpuff"] = Color.FromArgb(255, 255, 218, 185),
["peru"] = Color.FromArgb(255, 205, 133, 63),
["pink"] = Color.FromArgb(255, 255, 192, 203),
["plum"] = Color.FromArgb(255, 221, 160, 221),
["powderblue"] = Color.FromArgb(255, 176, 224, 230),
["purple"] = Color.FromArgb(255, 128, 0, 128),
["red"] = Color.FromArgb(255, 255, 0, 0),
["rosybrown"] = Color.FromArgb(255, 188, 143, 143),
["royalblue"] = Color.FromArgb(255, 65, 105, 225),
["saddlebrown"] = Color.FromArgb(255, 139, 69, 19),
["salmon"] = Color.FromArgb(255, 250, 128, 114),
["sandybrown"] = Color.FromArgb(255, 244, 164, 96),
["seagreen"] = Color.FromArgb(255, 46, 139, 87),
["seashell"] = Color.FromArgb(255, 255, 245, 238),
["sienna"] = Color.FromArgb(255, 160, 82, 45),
["silver"] = Color.FromArgb(255, 192, 192, 192),
["skyblue"] = Color.FromArgb(255, 135, 206, 235),
["slateblue"] = Color.FromArgb(255, 106, 90, 205),
["slategray"] = Color.FromArgb(255, 112, 128, 144),
["slategrey"] = Color.FromArgb(255, 112, 128, 144),
["snow"] = Color.FromArgb(255, 255, 250, 250),
["springgreen"] = Color.FromArgb(255, 0, 255, 127),
["steelblue"] = Color.FromArgb(255, 70, 130, 180),
["tan"] = Color.FromArgb(255, 210, 180, 140),
["teal"] = Color.FromArgb(255, 0, 128, 128),
["thistle"] = Color.FromArgb(255, 216, 191, 216),
["tomato"] = Color.FromArgb(255, 255, 99, 71),
["turquoise"] = Color.FromArgb(255, 64, 224, 208),
["violet"] = Color.FromArgb(255, 238, 130, 238),
["wheat"] = Color.FromArgb(255, 245, 222, 179),
["white"] = Color.FromArgb(255, 255, 255, 255),
["whitesmoke"] = Color.FromArgb(255, 245, 245, 245),
["yellow"] = Color.FromArgb(255, 255, 255, 0),
["yellowgreen"] = Color.FromArgb(255, 154, 205, 50)
};
public static Color ParseColor(string color)
{
color = color.Replace(" ", string.Empty);
if (string.IsNullOrWhiteSpace(color) || color.ToLower().Equals("none"))
return Windows.UI.Colors.Transparent;
if (color.StartsWith("#"))
return ParseColorFromHexString(color);
if (color.StartsWith("rgb"))
return ParseColorFromRgbString(color);
return ParseColorFromName(color);
}
private static Color ParseColorFromHexString(string color)
{
color = color.Replace("#", string.Empty);
if (color.Length == 3)
{
var str = string.Empty;
foreach (var c in color)
{
str += c;
str += c;
}
color = str;
}
byte alpha = 255;
if (color.Length == 8)
{
alpha = byte.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
color = color.Substring(2);
}
var v = int.Parse(color, NumberStyles.HexNumber);
return new Color
{
A = alpha,
R = Convert.ToByte((v >> 16) & 255),
G = Convert.ToByte((v >> 8) & 255),
B = Convert.ToByte((v >> 0) & 255)
};
}
private static Color ParseColorFromRgbString(string color)
{
var hadAlpha = color.StartsWith("rgba");
color = color.Substring(hadAlpha ? "rgba(".Length : "rgb(".Length)?.Replace(")", "");
byte alpha = 255;
var strs = color?.Split(',').ToList();
if (hadAlpha && strs?.Count == 4)
{
var alphaStr = strs[3];
strs.Remove(alphaStr);
double.TryParse(alphaStr, out var alphaValue);
alpha = (byte) (255 * alphaValue);
}
if (strs?.Count == 3)
{
var vals = new List<int>();
foreach (var str in strs)
{
var item = str.Trim();
int value;
if (item.Contains('%'))
{
item = item.Replace('%', ' ');
item = item.Trim();
value = int.Parse(item);
value = (int) (2.55 * value);
}
else
{
value = int.Parse(item);
}
vals.Add(value);
}
return Color.FromArgb(alpha, (byte) vals[0], (byte) vals[1], (byte) vals[2]);
}
return Windows.UI.Colors.Transparent;
}
private static Color ParseColorFromName(string colorName)
{
if (Colors.ContainsKey(colorName.ToLower()))
return Colors[colorName.ToLower()];
return Windows.UI.Colors.Transparent;
}
}
}
\ No newline at end of file
namespace SvgConverter.SvgParse.SvgAttributesHelper
{
public static class SvgLengthHelper
{
/// <summary>
/// 转换svg中的长度单位(dpi=96,1em=16,1em=1ex)
/// </summary>
/// <param name="length"></param>
/// <param name="refLength"></param>
/// <returns></returns>
public static double ParseLength(string length, double refLength)
{
if (string.IsNullOrWhiteSpace(length))
return 0;
if (double.TryParse(length, out var result))
return result;
length = length.Replace(" ", string.Empty).ToLower();
if (length.Contains("px"))
return ParseLength(length.Replace("px", string.Empty), refLength);
if (length.Contains("%"))
return ParseLength(length.Replace("%", string.Empty), refLength) * refLength / 100;
if (length.Contains("pt"))
return ParseLength(length.Replace("pt", string.Empty), refLength) * 12 / 9;
if (length.Contains("pc"))
return ParseLength(length.Replace("pc", string.Empty), refLength) * 16;
if (length.Contains("em"))
return ParseLength(length.Replace("em", string.Empty), refLength) * 16;
if (length.Contains("ex"))
return ParseLength(length.Replace("ex", string.Empty), refLength) * 16;
if (length.Contains("cm"))
return ParseLength(length.Replace("cm", string.Empty), refLength) * 96 / 0.254;
if (length.Contains("mm"))
return ParseLength(length.Replace("mm", string.Empty), refLength) * 96 / 25.4;
if (length.Contains("in")) return ParseLength(length.Replace("in", string.Empty), refLength) * 96;
return 0;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text.RegularExpressions;
namespace SvgConverter.SvgParse.SvgAttributesHelper
{
public static class SvgTransformHelper
{
public static Matrix3x2 ParseTransform(string transform)
{
if (string.IsNullOrWhiteSpace(transform))
return Matrix3x2.Identity;
transform = transform.Replace(',', ' ');
transform = Regex.Replace(transform, @"\s+", " ");
var transStrList = new List<string>();
do
{
if (char.IsLower(transform[0]))
{
var end = transform.IndexOf(')');
transStrList.Add(transform.Substring(0, end + 1));
transform = transform.Substring(end + 1);
}
else
{
transform = transform.Substring(1);
}
} while (transform.Length > 0);
var result = Matrix3x2.Identity;
foreach (var trans in transStrList)
{
var m = Matrix3x2.Identity;
var str = trans.Trim();
if (str.StartsWith("translate"))
{
str = str.Substring(10, str.Length - 11);
m = GetTranslate(str);
}
else if (str.StartsWith("scale"))
{
str = str.Substring(6, str.Length - 7);
m = GetScale(str);
}
else if (str.StartsWith("rotate"))
{
str = str.Substring(7, str.Length - 8);
m = GetRotate(str);
}
else if (str.StartsWith("skewX"))
{
str = str.Substring(6, str.Length - 7);
if (float.TryParse(str.Trim(), out var angle))
m = Matrix3x2.CreateSkew(angle, 0);
else
continue;
}
else if (str.StartsWith("skewY"))
{
str = str.Substring(6, str.Length - 7);
if (float.TryParse(str.Trim(), out var angle))
m = Matrix3x2.CreateSkew(0, angle);
else
continue;
}
else if (str.StartsWith("matrix"))
{
str = str.Substring(7, str.Length - 8);
m = GetMatrix(str);
}
result = m * result;
}
return result;
}
private static Matrix3x2 GetMatrix(string transform)
{
var strs = transform.Split(' ');
if (strs.Length != 6)
return Matrix3x2.Identity;
float.TryParse(strs[0].Trim(), out var a);
float.TryParse(strs[1].Trim(), out var b);
float.TryParse(strs[2].Trim(), out var c);
float.TryParse(strs[3].Trim(), out var d);
float.TryParse(strs[4].Trim(), out var e);
float.TryParse(strs[5].Trim(), out var f);
return new Matrix3x2(a, b, c, d, e, f);
}
private static Matrix3x2 GetTranslate(string transform)
{
var strs = transform.Split(' ');
if (!(strs.Length > 0))
return Matrix3x2.Identity;
float.TryParse(strs[0].Trim(), out var x);
if (strs.Length <= 1)
return Matrix3x2.CreateTranslation(new Vector2(x, 0));
float.TryParse(strs[1].Trim(), out var y);
return Matrix3x2.CreateTranslation(new Vector2(x, y));
}
private static Matrix3x2 GetScale(string transform)
{
var strs = transform.Split(' ');
if (!(strs.Length > 0))
return Matrix3x2.Identity;
float.TryParse(strs[0].Trim(), out var x);
if (strs.Length <= 1)
return Matrix3x2.CreateScale(x);
float.TryParse(strs[1].Trim(), out var y);
return Matrix3x2.CreateScale(x, y);
}
private static Matrix3x2 GetRotate(string transform)
{
var strs = transform.Split(' ');
if (strs.Length == 0)
return Matrix3x2.Identity;
float.TryParse(strs[0].Trim(), out var angle);
angle *= (float) Math.PI / 180;
if (strs.Length < 3) return Matrix3x2.CreateRotation(angle);
float.TryParse(strs[1].Trim(), out var x);
float.TryParse(strs[2].Trim(), out var y);
return Matrix3x2.CreateRotation(angle, new Vector2(x, y));
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
using Windows.Foundation;
using SvgConverter.SvgParse.Brushes;
using SvgConverter.SvgParse.SvgAttributesHelper;
using SvgConverter.SvgParse.Utilities;
namespace SvgConverter.SvgParse
{
public class SvgElement
{
public Dictionary<string, SvgNodeStyle> CssStyles { get; private set; }
public Dictionary<string, ISvgBrush> DefBrushes { get; private set; }
public Dictionary<string, SvgNodeGroup> DefClipPath { get; private set; }
public Dictionary<string, SvgNode> DefSvgNodes { get; private set; }
public Rect ViewBox => new Rect(Offset, Size);
public Point Offset { get; private set; }
public Size Size { get; private set; }
public List<SvgNode> Children { get; } = new List<SvgNode>();
private static Rect GetRect(string xml)
{
if (string.IsNullOrWhiteSpace(xml))
return new Rect();
var strs = xml.Split(' ');
if (strs.Length == 4)
{
var x = SvgLengthHelper.ParseLength(strs[0], 0);
var y = SvgLengthHelper.ParseLength(strs[1], 0);
var width = SvgLengthHelper.ParseLength(strs[2], 0);
var height = SvgLengthHelper.ParseLength(strs[3], 0);
return new Rect(new Point(x, y), new Size(width, height));
}
return new Rect();
}
private void ParseBaseStyle(Dictionary<string, string> dic)
{
if (dic == null) return;
foreach (var item in dic)
switch (item.Key.ToLower())
{
case "viewbox":
var viewBox = GetRect(item.Value);
Offset = new Point(viewBox.X, viewBox.Y);
Size = new Size(viewBox.Width, viewBox.Height);
break;
case "width":
Size = new Size(SvgLengthHelper.ParseLength(item.Value, Size.Width), Size.Height);
break;
case "height":
Size = new Size(Size.Width, SvgLengthHelper.ParseLength(item.Value, Size.Height));
break;
case "x":
case "top":
Offset = new Point(Offset.X, SvgLengthHelper.ParseLength(item.Value, 0));
break;
case "y":
case "left":
Offset = new Point(SvgLengthHelper.ParseLength(item.Value, 0), Offset.Y);
break;
case "style":
var styleDic = new Dictionary<string, string>();
var strs = item.Value.Split(';');
foreach (var str in strs)
{
var keyValue = str.Split(':');
if (keyValue?.Length == 2) styleDic[keyValue[0].Trim()] = keyValue[1].Trim();
}
ParseBaseStyle(styleDic);
break;
}
}
/// <summary>
/// 从xml字符串中加载svg文档
/// </summary>
/// <param name="xml"></param>
/// <returns></returns>
public static SvgElement LoadFromXml(string xml)
{
var svg = new SvgElement();
var xmlDoc = new XmlDocument();
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Ignore,
IgnoreComments = true,
IgnoreProcessingInstructions = true,
IgnoreWhitespace = true
};
xml = Regex.Replace(xml, @"\s+", " ");
var str = xml.Replace("&ns_extend;", "http://ns.adobe.com/Extensibility/1.0/")
.Replace("&ns_ai;", "http://ns.adobe.com/AdobeIllustrator/10.0/")
.Replace("&ns_graphs;", "http://ns.adobe.com/Graphs/1.0/")
.Replace("&ns_vars;", "http://ns.adobe.com/Variables/1.0/")
.Replace("&ns_imrep;", "http://ns.adobe.com/ImageReplacement/1.0/")
.Replace("&ns_sfw;", "http://ns.adobe.com/SaveForWeb/1.0/")
.Replace("&ns_custom;", "http://ns.adobe.com/GenericCustomNamespace/1.0/")
.Replace("&ns_adobe_xpath;", "http://ns.adobe.com/XPath/1.0/")
.Replace("<switch", "<g")
.Replace("</switch", "</g");
var reader = XmlReader.Create(new StringReader(str), settings);
xmlDoc.Load(reader);
var root = xmlDoc.DocumentElement;
if (root.Name.ToLower().Equals("svg") && root.HasChildNodes)
{
var dic = new Dictionary<string, string>();
foreach (XmlAttribute item in root.Attributes) dic[item.Name] = item.Value;
svg.ParseBaseStyle(dic);
if (svg.Size == new Size())
svg.Size = new Size(500, 500);
svg.DefBrushes = SvgDefsNodeHelper.GetDefBrushes(root, svg.Size);
svg.DefSvgNodes = SvgDefsNodeHelper.GetDefSvgNodes(root, svg.DefBrushes, svg.Size);
svg.DefClipPath = SvgDefsNodeHelper.GetDefClipPath(root, svg.DefSvgNodes, svg.Size);
svg.CssStyles = SvgDefsNodeHelper.GetCssStyles(root, svg.DefBrushes, svg.DefClipPath, svg.Size);
var ele = root.FirstChild;
while (ele != null)
{
if (ele.NodeType == XmlNodeType.Element)
switch (ele.Name.ToLower())
{
case "g":
var group = SvgLoadHelper.GetGeometryGroupFromXml(ele, svg.CssStyles, svg.DefBrushes,
svg.Size, svg.DefSvgNodes, svg.DefClipPath);
if (group != null)
svg.Children.Add(group);
break;
default:
var geo = SvgLoadHelper.GetGeometryFromXml(ele, svg.CssStyles, svg.DefBrushes,
svg.Size, svg.DefSvgNodes, svg.DefClipPath);
if (geo != null)
svg.Children.Add(geo);
break;
}
ele = ele.NextSibling;
}
}
return svg;
}
}
}
\ No newline at end of file
using Windows.UI.Xaml.Media;
namespace SvgConverter.SvgParse
{
public class SvgGeometry : SvgNode
{
public Geometry Geometry { get; set; }
public override SvgNode Clone()
{
var cloneNode = new SvgGeometry()
{
RenderOpacity = RenderOpacity,
RenderTransform = RenderTransform,
Style = Style.Clone(),
Geometry = Geometry
};
return cloneNode;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace SvgConverter.SvgParse
{
public sealed class SvgImage : SvgNode
{
public static readonly List<string> Base64DateHeaders = new List<string>
{
"data:image/png;base64,",
"data:image/jpeg;base64,",
"data:image/jpg;base64,",
"data:image/x-icon;base64,"
};
public Rect ViewRect { get; set; }
public byte[] ImageBytes { get; set; }
public async Task<ImageSource> GetImageSource()
{
if (ImageBytes == null) return null;
var img = new BitmapImage();
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
var buffer = CryptographicBuffer.CreateFromByteArray(ImageBytes);
await randomAccessStream.WriteAsync(buffer);
randomAccessStream.Seek(0);
await img.SetSourceAsync(randomAccessStream);
}
return img;
}
public override SvgNode Clone()
{
var cloneNode = new SvgImage()
{
RenderOpacity = RenderOpacity,
RenderTransform = RenderTransform,
Style = Style.Clone(),
ViewRect = ViewRect,
ImageBytes = ImageBytes
};
return cloneNode;
}
}
}
\ No newline at end of file
using System.Numerics;
namespace SvgConverter.SvgParse
{
public abstract class SvgNode
{
public string Id { get; set; }
public double RenderOpacity { get; set; } = 1;
public Matrix3x2 RenderTransform { get; set; } = Matrix3x2.Identity;
public SvgNodeStyle Style { get; set; } = new SvgNodeStyle();
public abstract SvgNode Clone();
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Linq;
namespace SvgConverter.SvgParse
{
public class SvgNodeGroup : SvgNode
{
public List<SvgNode> Children { get; private set; } = new List<SvgNode>();
public override SvgNode Clone()
{
var cloneNode = new SvgNodeGroup()
{
RenderOpacity = RenderOpacity,
RenderTransform = RenderTransform,
Style = Style.Clone(),
Children = Children.Select(o => o.Clone()).ToList()
};
return cloneNode;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Xml;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml.Media;
using SvgConverter.SvgParse.Attributes;
using SvgConverter.SvgParse.Brushes;
using SvgConverter.SvgParse.SvgAttributesHelper;
namespace SvgConverter.SvgParse
{
public class SvgNodeStyle
{
public DisplayMode Display { get; set; }
public ISvgBrush Stroke { get; set; } = new SvgSolidColorBrush(
Colors.Transparent) {Opacity = 0};
public double StrokeThickness { get; set; } = 1;
public double StrokeMiterLimit { get; set; }
public double Opacity { get; set; } = 1;
public Stretch Stretch { get; set; }
public ISvgBrush Fill { get; set; } = new SvgSolidColorBrush(Colors.Black);
public FillRule FillRule { get; set; } = FillRule.Nonzero;
public Matrix3x2 Transform { get; set; } = Matrix3x2.Identity;
public DoubleCollection StrokeDashArray { get; set; } = new DoubleCollection();
public double StrokeDashOffset { get; set; }
public PenLineCap StrokeLineCap { get; set; }
public PenLineJoin StrokeLineJoin { get; set; } = PenLineJoin.Bevel;
public SvgNodeGroup ParentClipPath { get; set; }
public SvgNodeGroup ClipPath { get; set; }
public SvgNodeStyle Clone()
{
return new SvgNodeStyle
{
Display=Display,
Stroke = Stroke,
StrokeThickness = StrokeThickness,
Fill = Fill,
FillRule = FillRule,
Opacity = Opacity,
Stretch = Stretch,
StrokeDashArray = StrokeDashArray,
StrokeDashOffset = StrokeDashOffset,
StrokeLineCap = StrokeLineCap,
StrokeLineJoin = StrokeLineJoin,
StrokeMiterLimit = StrokeMiterLimit,
Transform = Transform,
ClipPath = ClipPath,
ParentClipPath = ParentClipPath
};
}
public void MergeStyle(string cssStyle, Dictionary<string, ISvgBrush> defBrushes, Size refSize,
Dictionary<string, SvgNodeGroup> defClipPath)
{
if (!string.IsNullOrWhiteSpace(cssStyle))
{
var dic = new Dictionary<string, string>();
var strs = cssStyle.Split(';');
foreach (var str in strs)
{
var keyValue = str.Split(':');
if (keyValue?.Length == 2) dic[keyValue[0].Trim()] = keyValue[1].Trim();
}
MergeStyle(dic, defBrushes, refSize, defClipPath);
}
}
public void MergeStyle(XmlAttributeCollection attr, Dictionary<string, SvgNodeStyle> refCssStyles,
Dictionary<string, ISvgBrush> defBrushes, Size refSize,
Dictionary<string, SvgNodeGroup> defClipPath)
{
if (attr != null)
{
var dic = new Dictionary<string, string>();
foreach (XmlAttribute item in attr) dic[item.Name] = item.Value;
MergeStyle(dic, defBrushes, refSize, defClipPath);
}
}
public void MergeStyle(Dictionary<string, string> dic, Dictionary<string, ISvgBrush> defBrushes, Size refSize,
Dictionary<string, SvgNodeGroup> defClipPath)
{
if (dic == null) return;
foreach (var item in dic)
switch (item.Key.ToLower())
{
case "display":
if (!string.IsNullOrWhiteSpace(item.Value))
switch (item.Value)
{
case "inline":
Display = DisplayMode.Inline;
break;
case "none":
Display = DisplayMode.None;
break;
default:
Display = DisplayMode.Other;
break;
}
break;
case "opacity":
if (!string.IsNullOrWhiteSpace(item.Value))
{
double.TryParse(item.Value, out var opacity);
Opacity = opacity;
}
break;
case "fill":
if (!string.IsNullOrWhiteSpace(item.Value)) Fill = SvgBrushHelper.Parse(item.Value, defBrushes);
break;
case "stroke":
if (!string.IsNullOrWhiteSpace(item.Value))
Stroke = SvgBrushHelper.Parse(item.Value, defBrushes);
break;
case "stroke-width":
if (!string.IsNullOrWhiteSpace(item.Value))
StrokeThickness = SvgLengthHelper.ParseLength(item.Value, refSize.Width);
break;
case "stroke-miterlimit":
if (!string.IsNullOrWhiteSpace(item.Value))
StrokeMiterLimit = SvgLengthHelper.ParseLength(item.Value, refSize.Width);
break;
case "fill-opacity":
if (!string.IsNullOrWhiteSpace(item.Value))
{
double.TryParse(item.Value, out var opacity);
Fill.Opacity = (float) opacity;
}
break;
case "stroke-opacity":
if (!string.IsNullOrWhiteSpace(item.Value))
{
double.TryParse(item.Value, out var opacity);
Stroke.Opacity = (float) opacity;
}
break;
case "fill-rule":
if (!string.IsNullOrWhiteSpace(item.Value))
{
if (item.Value.ToLower().Equals("nonzero"))
FillRule = FillRule.Nonzero;
else if (item.Value.ToLower().Equals("evenodd")) FillRule = FillRule.EvenOdd;
}
break;
case "stroke-dasharray":
if (!string.IsNullOrWhiteSpace(item.Value))
{
var str = item.Value.Replace(',', ' ');
var dashs = str.Split(' ');
if (dashs?.Length != 0)
foreach (var dash in dashs)
StrokeDashArray.Add(SvgLengthHelper.ParseLength(dash, refSize.Width));
var valid = StrokeDashArray.Sum() > 0;
if (!valid)
StrokeDashArray = new DoubleCollection();
}
break;
case "stroke-dashoffset":
if (!string.IsNullOrWhiteSpace(item.Value))
StrokeDashOffset = SvgLengthHelper.ParseLength(item.Value, refSize.Width);
break;
case "stroke-linecap":
if (!string.IsNullOrWhiteSpace(item.Value))
switch (item.Value)
{
case "butt":
StrokeLineCap = PenLineCap.Flat;
break;
case "square":
StrokeLineCap = PenLineCap.Square;
break;
case "round":
StrokeLineCap = PenLineCap.Round;
break;
}
break;
case "stroke-linejoin":
if (!string.IsNullOrWhiteSpace(item.Value))
switch (item.Value)
{
case "miter":
StrokeLineJoin = PenLineJoin.Miter;
break;
case "round":
StrokeLineJoin = PenLineJoin.Round;
break;
case "bevel":
StrokeLineJoin = PenLineJoin.Bevel;
break;
}
break;
case "transform":
if (!string.IsNullOrWhiteSpace(item.Value))
Transform = SvgTransformHelper.ParseTransform(item.Value);
break;
case "clip-path":
if (!string.IsNullOrWhiteSpace(item.Value))
{
var clipPathId = item.Value;
if (clipPathId.StartsWith("url("))
{
clipPathId =
clipPathId.Replace("url(", string.Empty)
.Replace("#", string.Empty)
.Replace(")", string.Empty)
.Replace(" ", string.Empty);
if (defClipPath?.ContainsKey(clipPathId) == true)
ClipPath = defClipPath[clipPathId];
}
}
break;
case "style":
var styleDic = new Dictionary<string, string>();
var strs = item.Value.Split(';');
foreach (var str in strs)
{
var keyValue = str.Split(':');
if (keyValue?.Length == 2) styleDic[keyValue[0].Trim()] = keyValue[1].Trim();
}
MergeStyle(styleDic, defBrushes, refSize, defClipPath);
break;
}
}
public static SvgNodeStyle Parse(string cssStyle, Dictionary<string, ISvgBrush> defBrushes, Size refSize,
Dictionary<string, SvgNodeGroup> defClipPath)
{
var style = new SvgNodeStyle();
if (!string.IsNullOrWhiteSpace(cssStyle))
{
var dic = new Dictionary<string, string>();
var strs = cssStyle.Split(';');
foreach (var str in strs)
{
var keyValue = str.Split(':');
if (keyValue?.Length == 2) dic[keyValue[0].Trim()] = keyValue[1].Trim();
}
style.MergeStyle(dic, defBrushes, refSize, defClipPath);
}
return style;
}
public static SvgNodeStyle Parse(XmlAttributeCollection attr, Dictionary<string, SvgNodeStyle> refCssStyles,
Dictionary<string, ISvgBrush> defBrushes,
Size refSize, Dictionary<string, SvgNodeGroup> defClipPath)
{
var style = new SvgNodeStyle();
if (attr != null)
{
var dic = new Dictionary<string, string>();
foreach (XmlAttribute item in attr) dic[item.Name] = item.Value;
if (dic.ContainsKey("class") && refCssStyles != null)
{
refCssStyles.TryGetValue(dic["class"], out var cssStyle);
if (cssStyle != null)
style = cssStyle.Clone();
}
style.MergeStyle(dic, defBrushes, refSize, defClipPath);
}
return style;
}
}
}
\ No newline at end of file
using Windows.Foundation;
namespace SvgConverter.SvgParse
{
public class SvgRectangle : SvgNode
{
public Rect Rect { get; set; }
public float RadiusX { get; set; }
public float RadiusY { get; set; }
public override SvgNode Clone()
{
var cloneNode = new SvgRectangle()
{
RenderOpacity = RenderOpacity,
RenderTransform = RenderTransform,
Style = Style.Clone(),
Rect = Rect,
RadiusX = RadiusX,
RadiusY = RadiusY
};
return cloneNode;
}
}
}
\ No newline at end of file
using Windows.Foundation;
using SvgConverter.SvgParse.Attributes;
namespace SvgConverter.SvgParse
{
public class SvgText : SvgNode
{
public Point Position { get; set; }
public string Content { get; set; }
public double FontSize { get; set; }
public string FontFamily { get; set; }
public TextAnchor TextAnchor { get; set; }
public override SvgNode Clone()
{
var cloneNode = new SvgText()
{
RenderOpacity = RenderOpacity,
RenderTransform = RenderTransform,
Style = Style.Clone(),
Position = Position,
Content = Content,
FontSize = FontSize,
FontFamily = FontFamily,
TextAnchor = TextAnchor
};
return cloneNode;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Xml;
using Windows.Foundation;
using SvgConverter.SvgParse.Brushes;
using SvgConverter.SvgParse.SvgAttributesHelper;
namespace SvgConverter.SvgParse.Utilities
{
public static class SvgDefsNodeHelper
{
public static Dictionary<string, ISvgBrush> GetDefBrushes(XmlElement xmlElement, Size refSize)
{
var defBrushes = new Dictionary<string, ISvgBrush>();
var linearBrushList = xmlElement.GetElementsByTagName("linearGradient");
foreach (XmlElement item in linearBrushList)
{
var id = item.Attributes["id"]?.Value;
if (!string.IsNullOrWhiteSpace(id))
{
var brush = SvgBrushHelper.ParseLinearGradientBrush(item, refSize);
if (brush != null)
defBrushes[id] = brush;
}
}
var radialBrushList = xmlElement.GetElementsByTagName("radialGradient");
foreach (XmlElement item in radialBrushList)
{
var id = item.Attributes["id"]?.Value;
if (!string.IsNullOrWhiteSpace(id))
{
var brush = SvgBrushHelper.ParseRadialGradientBrush(item, refSize);
if (brush != null)
defBrushes[id] = brush;
}
}
return defBrushes;
}
public static Dictionary<string, SvgNodeStyle> GetCssStyles(XmlElement xmlElement,
Dictionary<string, ISvgBrush> defBrushes, Dictionary<string, SvgNodeGroup> defClipPath, Size refSize)
{
var cssStyles = new Dictionary<string, SvgNodeStyle>();
var styleList = xmlElement.GetElementsByTagName("style");
foreach (XmlNode item in styleList)
{
var styleStr = item.InnerText.Replace(" ", string.Empty)
.Replace("\n", string.Empty)
.Replace("\t", string.Empty);
if (!string.IsNullOrWhiteSpace(styleStr))
{
var styles = styleStr.Split('}');
foreach (var style in styles)
{
if (string.IsNullOrWhiteSpace(style)) continue;
var idEnd = style.IndexOf('{');
if (idEnd == -1) continue;
var keyString = style.Substring(0, idEnd);
var styleString = style.Substring(idEnd + 1);
if (keyString.Length > 1)
{
var keys = keyString.Split(',');
foreach (var keyItem in keys)
{
if (string.IsNullOrWhiteSpace(keyItem))
continue;
var key = keyItem.Trim().Substring(1);
if (cssStyles.ContainsKey(key))
{
cssStyles[key].MergeStyle(styleString, defBrushes, refSize, defClipPath);
}
else
{
var svgStyle = SvgNodeStyle.Parse(styleString, defBrushes, refSize, defClipPath);
cssStyles[key] = svgStyle;
}
}
}
}
}
}
return cssStyles;
}
public static Dictionary<string, SvgNodeGroup> GetDefClipPath(XmlElement xmlElement,
Dictionary<string, SvgNode> defNodes, Size refSize)
{
var defClipPath = new Dictionary<string, SvgNodeGroup>();
var clipPathList = xmlElement.GetElementsByTagName("clipPath");
foreach (XmlNode item in clipPathList)
{
var group = SvgLoadHelper.GetGeometryGroupFromXml(item, null, null, refSize, defNodes);
if (group?.Id != null)
defClipPath[group.Id] = group;
}
return defClipPath;
}
public static Dictionary<string, SvgNode> GetDefSvgNodes(XmlElement xmlElement,
Dictionary<string, ISvgBrush> defBrushes, Size refSize)
{
var defNodes = new Dictionary<string, SvgNode>();
var defNodeList = xmlElement.GetElementsByTagName("defs");
foreach (XmlNode item in defNodeList)
foreach (XmlNode ele in item.ChildNodes)
if (ele.NodeType == XmlNodeType.Element)
switch (ele.Name.ToLower())
{
case "g":
var group = SvgLoadHelper.GetGeometryGroupFromXml(ele, null, defBrushes,
refSize);
if (group?.Id != null)
defNodes[group.Id] = group;
break;
default:
var geo = SvgLoadHelper.GetGeometryFromXml(ele, null, defBrushes,
refSize);
if (geo?.Id != null)
defNodes[geo.Id] = geo;
break;
}
return defNodes;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Windows.Foundation;
using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media;
using SvgConverter.SvgParse.Attributes;
using SvgConverter.SvgParse.Brushes;
using SvgConverter.SvgParse.SvgAttributesHelper;
namespace SvgConverter.SvgParse.Utilities
{
public static class SvgLoadHelper
{
public static SvgNodeGroup GetGeometryGroupFromXml(XmlNode node, Dictionary<string, SvgNodeStyle> refCssStyles,
Dictionary<string, ISvgBrush> defBrushes, Size refSize, Dictionary<string, SvgNode> defNodes = null,
Dictionary<string, SvgNodeGroup> defClipPath = null)
{
var group = new SvgNodeGroup
{
Id = node.Attributes["id"]?.Value,
Style = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath)
};
var ele = node.FirstChild;
while (ele != null)
{
if (ele.NodeType == XmlNodeType.Element)
switch (ele.Name.ToLower())
{
case "g":
var g = GetGeometryGroupFromXml(ele, refCssStyles, defBrushes, refSize, defNodes,
defClipPath);
if (g != null)
group.Children.Add(g);
break;
default:
var geo = GetGeometryFromXml(ele, refCssStyles, defBrushes, refSize, defNodes, defClipPath);
if (geo != null)
group.Children.Add(geo);
break;
}
ele = ele.NextSibling;
}
return group;
}
public static SvgNode GetGeometryFromXml(XmlNode node, Dictionary<string, SvgNodeStyle> refCssStyles,
Dictionary<string, ISvgBrush> defBrushes, Size refSize, Dictionary<string, SvgNode> defNodes = null,
Dictionary<string, SvgNodeGroup> defClipPath = null)
{
switch (node.Name.ToLower())
{
case "path":
var d = node.Attributes["d"]?.Value;
var geometry =
(PathGeometry)
XamlReader.Load(
$"<Geometry xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>{d}</Geometry>");
var pathStyle = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath);
geometry.FillRule = pathStyle.FillRule;
return new SvgGeometry
{
Id = node.Attributes["id"]?.Value,
Geometry = geometry,
Style = pathStyle
};
case "line":
var startPoint = new Point(
SvgLengthHelper.ParseLength(node.Attributes["x1"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["y1"]?.Value, refSize.Height));
var endPoint = new Point(
SvgLengthHelper.ParseLength(node.Attributes["x2"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["y2"]?.Value, refSize.Height));
var linePath =
(PathGeometry)
XamlReader.Load(
$"<Geometry xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>M{startPoint.X},{startPoint.Y}L{endPoint.X},{endPoint.Y}</Geometry>");
return new SvgGeometry
{
Id = node.Attributes["id"]?.Value,
Geometry = linePath,
Style = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath)
};
case "rect":
var rectRect =
new Rect(
new Point(
SvgLengthHelper.ParseLength(node.Attributes["x"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["y"]?.Value, refSize.Height)),
new Size(
SvgLengthHelper.ParseLength(node.Attributes["width"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["height"]?.Value, refSize.Height)));
return new SvgRectangle
{
Id = node.Attributes["id"]?.Value,
Rect = rectRect,
RadiusX = (float) SvgLengthHelper.ParseLength(node.Attributes["rx"]?.Value, refSize.Width),
RadiusY = (float) SvgLengthHelper.ParseLength(node.Attributes["ry"]?.Value, refSize.Width),
Style = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath)
};
case "circle":
case "ellipse":
var ellipse = new EllipseGeometry
{
Center = new Point(SvgLengthHelper.ParseLength(node.Attributes["cx"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["cy"]?.Value, refSize.Height))
};
if (node.Name.ToLower().Equals("circle"))
{
ellipse.RadiusX =
ellipse.RadiusY =
SvgLengthHelper.ParseLength(node.Attributes["r"]?.Value, refSize.Width);
}
else
{
ellipse.RadiusX = SvgLengthHelper.ParseLength(node.Attributes["rx"]?.Value, refSize.Width);
ellipse.RadiusY = SvgLengthHelper.ParseLength(node.Attributes["ry"]?.Value, refSize.Height);
}
return new SvgGeometry
{
Id = node.Attributes["id"]?.Value,
Geometry = ellipse,
Style = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath)
};
case "polyline":
case "polygon":
var points = node.Attributes["points"]?.Value;
if (string.IsNullOrWhiteSpace(points)) break;
points = points.Replace(",", " ");
var strs = points.Split(' ');
var polylineStr = string.Empty;
strs = (from str in strs
where !string.IsNullOrWhiteSpace(str)
select str.Trim()).ToArray();
if (strs.Length == 0) return null;
for (var i = 0; i < strs.Length / 2; i++)
{
if (polylineStr.Equals(string.Empty))
polylineStr += "M";
else
polylineStr += "L";
polylineStr += $"{strs[i * 2]},{strs[i * 2 + 1]}";
}
if (polylineStr.Equals(string.Empty)) break;
if (node.Name.ToLower().Equals("polygon"))
polylineStr += "Z";
var polyPath =
(PathGeometry)
XamlReader.Load(
$"<Geometry xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>{polylineStr}</Geometry>");
var polyStyle = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath);
polyPath.FillRule = polyStyle.FillRule;
return new SvgGeometry
{
Id = node.Attributes["id"]?.Value,
Geometry = polyPath,
Style = polyStyle
};
case "text":
var textContent = node.InnerText;
var fontSize = SvgLengthHelper.ParseLength(node.Attributes["font-size"]?.Value, refSize.Width);
if (string.IsNullOrWhiteSpace(textContent) || fontSize < 0.01) break;
Enum.TryParse<TextAnchor>(node.Attributes["text-anchor"]?.Value, true, out var anchor);
return new SvgText
{
Id = node.Attributes["id"]?.Value,
Content = textContent,
Position = new Point(
SvgLengthHelper.ParseLength(node.Attributes["x"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["y"]?.Value, refSize.Height)),
FontSize = fontSize,
FontFamily = node.Attributes["font-family"]?.Value?.Replace("'", string.Empty),
Style = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize, defClipPath),
TextAnchor = anchor
};
case "image":
var href = node.Attributes["xlink:href"]?.Value;
foreach (var header in SvgImage.Base64DateHeaders)
if (href?.StartsWith(header, StringComparison.CurrentCultureIgnoreCase) ==
true)
{
var content = Convert.FromBase64String(href.Substring(header.Length));
var rect =
new Rect(
new Point(
SvgLengthHelper.ParseLength(node.Attributes["x"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["y"]?.Value, refSize.Height)),
new Size(
SvgLengthHelper.ParseLength(node.Attributes["width"]?.Value, refSize.Width),
SvgLengthHelper.ParseLength(node.Attributes["height"]?.Value, refSize.Height)));
var imgStyle = SvgNodeStyle.Parse(node.Attributes, refCssStyles, defBrushes, refSize,
defClipPath);
return new SvgImage
{
Id = node.Attributes["id"]?.Value,
ViewRect = rect,
ImageBytes = content,
Style = imgStyle
};
}
break;
case "use":
if (defNodes != null)
{
var id = node.Attributes["xlink:href"]?.Value;
if (!string.IsNullOrEmpty(id))
{
id = id.Replace("#", string.Empty).Trim();
if (defNodes.ContainsKey(id))
{
var svgNode = defNodes[id].Clone();
svgNode.Style.MergeStyle(node.Attributes, refCssStyles, defBrushes, refSize,
defClipPath);
return svgNode;
}
}
}
break;
}
return null;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
namespace SvgConverter.SvgParse.Utilities
{
public static class SvgParseHelper
{
public static List<SvgNode> GetSvgGeometrys(SvgElement svg)
{
var list = new List<SvgNode>();
foreach (var item in svg.Children)
{
if (item.Style.Display == Attributes.DisplayMode.None)
continue;
item.RenderOpacity = item.Style.Opacity;
item.RenderTransform = item.Style.Transform;
if (item is SvgNodeGroup group)
list.AddRange(GetSvgGeometrys(group));
else
list.Add(item);
}
return list;
}
public static List<SvgNode> GetSvgGeometrys(SvgNodeGroup grop)
{
var list = new List<SvgNode>();
foreach (var item in grop.Children)
{
if (grop.Style?.ClipPath != null)
item.Style.ParentClipPath = grop.Style.ClipPath;
item.RenderOpacity = item.Style.Opacity * grop.RenderOpacity;
item.RenderTransform = item.Style.Transform * grop.RenderTransform;
if (item is SvgNodeGroup group)
list.AddRange(GetSvgGeometrys(group));
else
list.Add(item);
}
return list;
}
//public static async void Draw(List<SvgNode> items, Panel panel)
//{
// panel.Children.Clear();
// while (items.Count != 0)
// {
// var item = items[0];
// items.RemoveAt(0);
// if (item is SvgGeometry)
// {
// var geo = (SvgGeometry) item;
// var path = new Path()
// {
// Data = geo.Geometry,
// Opacity = geo.RenderOpacity,
// Stretch = geo.Style.Stretch,
// Stroke = geo.Style.Stroke,
// StrokeThickness = geo.Style.StrokeThickness,
// StrokeMiterLimit = geo.Style.StrokeMiterLimit,
// Fill = geo.Style.Fill,
// StrokeDashArray = geo.Style.StrokeDashArray,
// StrokeDashOffset = geo.Style.StrokeDashOffset,
// StrokeDashCap = geo.Style.StrokeLineCap,
// StrokeStartLineCap = geo.Style.StrokeLineCap,
// StrokeEndLineCap = geo.Style.StrokeLineCap,
// StrokeLineJoin = geo.Style.StrokeLineJoin
// };
// //path.RenderTransform = new MatrixTransform()
// //{
// // Matrix =
// // new Matrix(geo.RenderTransform.M11, geo.RenderTransform.M12,
// // geo.RenderTransform.M21, geo.RenderTransform.M22,
// // geo.RenderTransform.M31, geo.RenderTransform.M32)
// //};
// path.Data = geo.Geometry;
// panel.Children.Add(path);
// }
// else if (item is SvgRectangle)
// {
// var rect = (SvgRectangle)item;
// var rectangle = new Rectangle
// {
// Width = rect.Rect.Width,
// Height = rect.Rect.Height,
// Margin = new Thickness(rect.Rect.X, rect.Rect.Y, 0, 0),
// RadiusX = rect.radiusX,
// RadiusY = rect.radiusY,
// Opacity = rect.RenderOpacity,
// Stretch = rect.Style.Stretch,
// Stroke = rect.Style.Stroke,
// StrokeThickness = rect.Style.StrokeThickness,
// StrokeMiterLimit = rect.Style.StrokeMiterLimit,
// Fill = rect.Style.Fill,
// StrokeDashArray = rect.Style.StrokeDashArray,
// StrokeDashOffset = rect.Style.StrokeDashOffset,
// StrokeDashCap = rect.Style.StrokeLineCap,
// StrokeStartLineCap = rect.Style.StrokeLineCap,
// StrokeEndLineCap = rect.Style.StrokeLineCap,
// StrokeLineJoin = rect.Style.StrokeLineJoin
// };
// panel.Children.Add(rectangle);
// }
// else if (item is SvgImage)
// {
// var svgImg = (SvgImage) item;
// var img = new Image
// {
// Opacity = svgImg.RenderOpacity,
// Margin = new Thickness(svgImg.ViewRect.X, svgImg.ViewRect.Y, 0, 0),
// Width = svgImg.ViewRect.Width,
// Height = svgImg.ViewRect.Height,
// Source = await svgImg.GetImageSource()
// };
// //img.RenderTransform = new MatrixTransform()
// //{
// // Matrix =
// // new Matrix(svgImg.RenderTransform.M11, svgImg.RenderTransform.M12,
// // svgImg.RenderTransform.M21, svgImg.RenderTransform.M22,
// // svgImg.RenderTransform.M31, svgImg.RenderTransform.M32)
// //};
// panel.Children.Add(img);
// }
// }
//}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Graphics.Canvas.Geometry;
namespace SvgConverter.SvgParseForWin2D.Utilities
{
public static class SvgDrawHelper
{
public static CanvasStrokeStyle GetPartPathStyle(this Win2DSvgGeometry geo, float drawLength)
{
if (drawLength > geo.PathLength)
return geo.CanvasStrokeStyle;
var noneLength = geo.PathLength - drawLength + geo.StrokeThickness;
var actualDrawLength = drawLength / geo.StrokeThickness;
var actualNoneLength = noneLength / geo.StrokeThickness;
var style = new CanvasStrokeStyle
{
MiterLimit = geo.CanvasStrokeStyle.MiterLimit,
DashOffset = geo.CanvasStrokeStyle.DashOffset,
DashCap = geo.CanvasStrokeStyle.DashCap,
StartCap = geo.CanvasStrokeStyle.StartCap,
EndCap = geo.CanvasStrokeStyle.EndCap,
LineJoin = geo.CanvasStrokeStyle.LineJoin
};
if (geo.CanvasStrokeStyle.CustomDashStyle?.Length > 0)
{
actualDrawLength += style.DashOffset;
actualNoneLength += style.DashOffset;
var dashList = new List<float>();
var i = 0;
float sum = 0;
do
{
sum += geo.CanvasStrokeStyle.CustomDashStyle[i];
if (sum > actualDrawLength)
{
var needLength = geo.CanvasStrokeStyle.CustomDashStyle[i] -
(sum - actualDrawLength);
dashList.Add(needLength);
break;
}
dashList.Add(geo.CanvasStrokeStyle.CustomDashStyle[i]);
i = ++i % geo.CanvasStrokeStyle.CustomDashStyle.Length;
} while (true);
if (dashList.Count % 2 == 0)
dashList.Add(0);
dashList.Add(actualNoneLength);
style.CustomDashStyle = dashList.ToArray();
return style;
}
style.CustomDashStyle = new[] { actualDrawLength, actualNoneLength };
return style;
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml.Media;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using SvgConverter.SvgParse;
using SvgConverter.SvgParse.Attributes;
using SvgConverter.SvgParse.Utilities;
namespace SvgConverter.SvgParseForWin2D.Utilities
{
public static class Win2DSvgParseHelper
{
public static CanvasPathBuilder GetPathBuilder(ICanvasResourceCreator resourceCreator,
PathGeometry geo, FillRule filleRule)
{
var builder = new CanvasPathBuilder(resourceCreator);
if (filleRule.Equals(FillRule.Nonzero))
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
foreach (var item in geo.Figures)
{
builder.BeginFigure(item.StartPoint.ToVector2());
foreach (var seg in item.Segments)
if (seg is BezierSegment bez1)
{
builder.AddCubicBezier(bez1.Point1.ToVector2(), bez1.Point2.ToVector2(),
bez1.Point3.ToVector2());
}
else if (seg is QuadraticBezierSegment bez2)
{
builder.AddQuadraticBezier(bez2.Point1.ToVector2(), bez2.Point2.ToVector2());
}
else if (seg is LineSegment line)
{
builder.AddLine(line.Point.ToVector2());
}
else if (seg is PolyLineSegment poly)
{
foreach (var p in poly.Points) builder.AddLine(p.ToVector2());
}
else if (seg is ArcSegment arc)
{
var dir = arc.SweepDirection == SweepDirection.Clockwise
? CanvasSweepDirection.Clockwise
: CanvasSweepDirection.CounterClockwise;
var size = arc.IsLargeArc ? CanvasArcSize.Large : CanvasArcSize.Small;
builder.AddArc(arc.Point.ToVector2(),
(float) arc.Size.Width, (float) arc.Size.Height, (float) arc.RotationAngle, dir,
size);
}
else if (seg is PolyBezierSegment)
{
//实际转换出的path对象中不包含PolyBezierSegment
}
builder.EndFigure(item.IsClosed ? CanvasFigureLoop.Closed : CanvasFigureLoop.Open);
}
return builder;
}
public static List<Vector2> GetFillPoints(Rect viewRect, int count)
{
var list = new List<Vector2>();
var step = (float) (viewRect.Width + viewRect.Height) / count;
var turn = true;
var width = (float) viewRect.X;
var heigh = (float) viewRect.Y;
for (var i = 0; i < 2 * count; i++)
{
if (turn)
{
width += step;
list.Add(new Vector2(width, (float) viewRect.Y));
}
else
{
heigh += step;
list.Add(new Vector2((float) viewRect.X, heigh));
}
turn = !turn;
}
return list;
}
public static CanvasStrokeStyle ParseStrokeStyle(SvgNodeStyle nodeStyle)
{
var style = new CanvasStrokeStyle
{
MiterLimit = (float) nodeStyle.StrokeMiterLimit,
DashOffset = (float) (nodeStyle.StrokeDashOffset / nodeStyle.StrokeThickness),
DashCap = (CanvasCapStyle) nodeStyle.StrokeLineCap,
StartCap = (CanvasCapStyle) nodeStyle.StrokeLineCap,
EndCap = (CanvasCapStyle) nodeStyle.StrokeLineCap,
LineJoin = (CanvasLineJoin) nodeStyle.StrokeLineJoin
};
if (nodeStyle.StrokeDashArray.Count > 0)
{
var array = nodeStyle.StrokeDashArray.ToArray();
var darray = new float[array.Length];
for (var i = 0; i < array.Length; i++)
//array[i]/nodeStyle.StrokeThickness为Win2D中图形忽略线宽后实际虚线应表现的形式
darray[i] = (float) (array[i] / nodeStyle.StrokeThickness);
style.CustomDashStyle = darray;
}
return style;
}
public static CanvasGeometry ParseGeometry(ICanvasResourceCreator resourceCreator, SvgNode node)
{
CanvasGeometry geometry = null;
if (node is SvgGeometry geo)
{
if (geo.Geometry is PathGeometry pathGeometry)
{
var builder = GetPathBuilder(resourceCreator, pathGeometry,
geo.Style.FillRule);
geometry =
CanvasGeometry.CreatePath(builder);
builder.Dispose();
}
else if (geo.Geometry is EllipseGeometry ellipseGeometry)
{
geometry = CanvasGeometry.CreateEllipse(resourceCreator, ellipseGeometry.Center.ToVector2(),
(float) ellipseGeometry.RadiusX, (float) ellipseGeometry.RadiusY);
}
}
else if (node is SvgRectangle rect)
{
geometry = CanvasGeometry.CreateRoundedRectangle(resourceCreator, rect.Rect, rect.RadiusX,
rect.RadiusY);
}
return geometry;
}
public static CanvasGeometry ParseClipGeometry(ICanvasResourceCreator resourceCreator, SvgNodeGroup clipPath,
SvgNodeGroup parentClipPath)
{
CanvasGeometry clipGeo = null;
if (clipPath?.Children?.Count > 0)
foreach (var clip in clipPath.Children)
{
var geo = ParseGeometry(resourceCreator, clip);
if (geo != null)
clipGeo = clipGeo != null
? clipGeo.CombineWith(geo, clip.RenderTransform, CanvasGeometryCombine.Union)
: geo;
}
CanvasGeometry parentClipGeo = null;
if (parentClipPath?.Children?.Count > 0)
foreach (var clip in parentClipPath.Children)
{
var geo = ParseGeometry(resourceCreator, clip);
if (geo != null)
parentClipGeo = parentClipGeo != null
? parentClipGeo.CombineWith(geo, clip.RenderTransform, CanvasGeometryCombine.Union)
: geo;
}
if (clipGeo == null && parentClipGeo == null)
return null;
if (clipGeo != null && parentClipGeo != null)
return clipGeo.CombineWith(parentClipGeo, Matrix3x2.Identity, CanvasGeometryCombine.Intersect);
if (clipGeo != null)
return clipGeo;
return parentClipGeo;
}
public static async Task<List<Win2DSvgNode>> GetWin2DGeometrys(ICanvasResourceCreator resourceCreator,
SvgElement svg)
{
var svgGeometrys = SvgParseHelper.GetSvgGeometrys(svg);
var pathDrawList = new List<Win2DSvgNode>();
foreach (var item in svgGeometrys)
if (item is SvgGeometry || item is SvgRectangle)
{
var geometry = ParseGeometry(resourceCreator, item);
if (geometry != null)
{
var win2DGeo = new Win2DSvgGeometry
{
Id = item.Id,
ClipGeometry =
ParseClipGeometry(resourceCreator, item.Style.ClipPath, item.Style.ParentClipPath),
SourceCanvasGeometry = geometry,
CanvasStrokeStyle = ParseStrokeStyle(item.Style),
RenderOpacity = item.RenderOpacity,
RenderTransform = item.RenderTransform,
StrokeThickness = (float) item.Style.StrokeThickness,
Stroke = item.Style.Stroke.Parse(resourceCreator),
Fill = item.Style.Fill.Parse(resourceCreator)
};
win2DGeo.PathLength = win2DGeo.CanvasGeometry.ComputePathLength();
win2DGeo.Stroke.Opacity *= (float) item.RenderOpacity;
win2DGeo.Fill.Opacity *= (float) item.RenderOpacity;
if (item.RenderOpacity.Equals(0) && win2DGeo.StrokeThickness > 0)
win2DGeo.RenderMethod = RenderMethod.Mark;
else if (win2DGeo.StrokeThickness < 0.01 || win2DGeo.Stroke.Opacity.Equals(0))
win2DGeo.RenderMethod = RenderMethod.Fill;
pathDrawList.Add(win2DGeo);
}
}
else if (item is SvgText text)
{
var layout = new CanvasTextLayout(resourceCreator, text.Content,
new CanvasTextFormat
{
FontSize = (float) text.FontSize,
FontFamily = text.FontFamily,
WordWrapping = CanvasWordWrapping.NoWrap
}, 0, 0);
var transform = Matrix3x2.Identity;
switch (text.TextAnchor)
{
case TextAnchor.Start:
transform = Matrix3x2.CreateTranslation(
new Point(0, -layout.LayoutBounds.Height).ToVector2()) *
text.RenderTransform;
break;
case TextAnchor.Middle:
transform = Matrix3x2.CreateTranslation(
new Point(-layout.LayoutBounds.Width / 2, -layout.LayoutBounds.Height)
.ToVector2()) * text.RenderTransform;
break;
case TextAnchor.End:
transform = Matrix3x2.CreateTranslation(
new Point(-layout.LayoutBounds.Width, -layout.LayoutBounds.Height)
.ToVector2()) * text.RenderTransform;
break;
}
transform.Translation += text.Position.ToVector2();
var win2DGeo = new Win2DSvgGeometry
{
Id = item.Id,
ClipGeometry =
ParseClipGeometry(resourceCreator, text.Style.ClipPath, text.Style.ParentClipPath),
SourceCanvasGeometry = CanvasGeometry.CreateText(layout),
CanvasStrokeStyle = ParseStrokeStyle(text.Style),
RenderOpacity = text.RenderOpacity,
RenderTransform = transform,
StrokeThickness = (float) text.Style.StrokeThickness,
RenderMethod = RenderMethod.Composite,
Stroke = text.Style.Stroke.Parse(resourceCreator),
Fill = text.Style.Fill.Parse(resourceCreator)
};
win2DGeo.Stroke.Opacity *= (float) text.RenderOpacity;
win2DGeo.Fill.Opacity *= (float) text.RenderOpacity;
pathDrawList.Add(win2DGeo);
}
else if (item is SvgImage img)
{
var win2DImg = await Win2DSvgImage.GetImage(resourceCreator, img.ViewRect, img.ImageBytes);
win2DImg.Id = img.Id;
win2DImg.RenderOpacity = img.RenderOpacity;
win2DImg.RenderTransform = img.RenderTransform;
win2DImg.ClipGeometry =
ParseClipGeometry(resourceCreator, img.Style.ClipPath, img.Style.ParentClipPath);
pathDrawList.Add(win2DImg);
}
return pathDrawList;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Effects;
using SvgConverter.SvgParse;
using SvgConverter.SvgParseForWin2D.Utilities;
namespace SvgConverter.SvgParseForWin2D
{
public class Win2DSvgElement : AnimationBase
{
private readonly object _lockobj = new object();
private int _drawIndex;
private float _progress;
internal Win2DSvgElement()
{
}
public List<Win2DSvgNode> SvgNodeList { get; protected set; }
protected List<Win2DSvgGeometry> DrawNodeList { get; set; }
public double TotalLength { get; protected set; }
public override float Progress
{
get => _progress;
set
{
var oldValue = _progress;
_progress = value > 1 ? 1 : value;
_progress = _progress < 0 ? 0 : _progress;
RefreshDrawProgress(_progress - oldValue);
}
}
public double DrewLength => TotalLength * _progress;
/// <summary>
/// 刷新绘制进度
/// </summary>
/// <param name="dValue">进度差值</param>
private void RefreshDrawProgress(double dValue)
{
lock (_lockobj)
{
var dLength = TotalLength * dValue;
if (dValue == 0)
return;
if (_progress <= 0)
{
foreach (var item in DrawNodeList)
{
item.Progress = 0;
_drawIndex = 0;
}
return;
}
if (_progress >= 1)
{
foreach (var item in DrawNodeList)
{
item.Progress = 1;
_drawIndex = DrawNodeList.Count - 1;
}
return;
}
var step = dValue >= 0 ? 1 : -1;
do
{
if (_drawIndex > DrawNodeList.Count - 1)
{
_drawIndex = DrawNodeList.Count - 1;
break;
}
if (_drawIndex < 0)
{
_drawIndex = 0;
break;
}
var geo = DrawNodeList[_drawIndex];
if (dLength > 0)
{
var leftLength = geo.PathLength * (1 - geo.Progress);
if (dLength > leftLength)
{
dLength -= leftLength;
geo.Progress = 1;
}
else
{
geo.Progress += dLength / geo.PathLength;
break;
}
}
else
{
var drawLength = geo.PathLength * geo.Progress;
if (drawLength > -dLength)
{
geo.Progress += dLength / geo.PathLength;
break;
}
dLength += drawLength;
geo.Progress = 0;
}
_drawIndex += step;
} while (true);
}
}
/// <summary>
/// 按进度绘制svg图像
/// </summary>
/// <param name="drawingSession"></param>
/// <param name="drawProgress">本次绘制的进度</param>
/// <returns></returns>
public override Vector2? Draw(
CanvasDrawingSession drawingSession, float drawProgress)
{
lock (_lockobj)
{
Progress = _progress + drawProgress;
if (Progress <= 0)
return null;
var canFill = Progress >= 1 || TotalLength < 0.01;
var pathCommandList = new CanvasCommandList(drawingSession);
var pathDrawSession = pathCommandList.CreateDrawingSession();
CanvasCommandList compositeCommandList = null;
CanvasDrawingSession compositeDrawSession = null;
CanvasCommandList markCommandList = null;
CanvasDrawingSession markDrawingSession = null;
var sourceTransform = drawingSession.Transform;
if (!canFill)
{
markCommandList = new CanvasCommandList(drawingSession);
markDrawingSession = markCommandList.CreateDrawingSession();
compositeCommandList = new CanvasCommandList(drawingSession);
compositeDrawSession = compositeCommandList.CreateDrawingSession();
drawingSession.Transform = Matrix3x2.Identity;
}
try
{
foreach (var item in SvgNodeList)
if (item is Win2DSvgImage svgimg)
{
if (canFill)
svgimg.Draw(pathDrawSession);
else
using (var imgCommandList = new CanvasCommandList(compositeDrawSession))
{
using (var imgDrawSession = imgCommandList.CreateDrawingSession())
{
svgimg.Draw(imgDrawSession);
}
compositeDrawSession.Transform = sourceTransform;
compositeDrawSession.DrawImage(imgCommandList);
}
}
else if (item is Win2DSvgGeometry svgGeo)
{
switch (item.RenderMethod)
{
case RenderMethod.Draw:
pathDrawSession.Transform = canFill
? svgGeo.RenderTransform
: svgGeo.RenderTransform * sourceTransform;
var p = svgGeo.RenderStroke(pathDrawSession, svgGeo.Stroke);
if (p.HasValue) return Vector2.Transform(p.Value, drawingSession.Transform);
if (canFill)
{
pathDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
}
else
{
compositeDrawSession.Transform = svgGeo.RenderTransform * sourceTransform;
compositeDrawSession.DrawGeometry(svgGeo.CanvasGeometry, svgGeo.Stroke,
svgGeo.StrokeThickness,
svgGeo.CanvasStrokeStyle);
}
break;
case RenderMethod.Mark:
if (!canFill)
{
markDrawingSession.Transform = svgGeo.RenderTransform * sourceTransform;
var pm = svgGeo.RenderStroke(markDrawingSession,
new CanvasSolidColorBrush(drawingSession, Colors.Black));
if (pm.HasValue) return Vector2.Transform(pm.Value, drawingSession.Transform);
}
break;
case RenderMethod.Composite:
if (canFill)
{
pathDrawSession.Transform = svgGeo.RenderTransform;
pathDrawSession.DrawGeometry(svgGeo.CanvasGeometry, svgGeo.Stroke,
svgGeo.StrokeThickness, svgGeo.CanvasStrokeStyle);
pathDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
}
else
{
compositeDrawSession.Transform = svgGeo.RenderTransform * sourceTransform;
compositeDrawSession.DrawGeometry(svgGeo.CanvasGeometry, svgGeo.Stroke,
svgGeo.StrokeThickness, svgGeo.CanvasStrokeStyle);
compositeDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
}
break;
case RenderMethod.Fill:
if (canFill)
{
pathDrawSession.Transform = svgGeo.RenderTransform;
pathDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
}
else
{
compositeDrawSession.Transform = svgGeo.RenderTransform * sourceTransform;
compositeDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
}
break;
case RenderMethod.MarkAndFill:
if (!canFill)
{
markDrawingSession.Transform = svgGeo.RenderTransform * sourceTransform;
var pm = svgGeo.RenderStroke(markDrawingSession,
new CanvasSolidColorBrush(drawingSession, Colors.Black));
compositeDrawSession.Transform = svgGeo.RenderTransform * sourceTransform;
compositeDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
if (pm.HasValue) return Vector2.Transform(pm.Value, drawingSession.Transform);
}
else
{
pathDrawSession.Transform = svgGeo.RenderTransform;
pathDrawSession.FillGeometry(svgGeo.CanvasGeometry, svgGeo.Fill);
}
break;
}
}
}
finally
{
pathDrawSession.Dispose();
drawingSession.DrawImage(pathCommandList);
pathCommandList.Dispose();
if (!canFill)
{
compositeDrawSession.Dispose();
markDrawingSession.Dispose();
var effect = new AlphaMaskEffect
{
Source = compositeCommandList,
AlphaMask = markCommandList
};
drawingSession.DrawImage(effect);
markCommandList.Dispose();
compositeCommandList.Dispose();
effect.Dispose();
}
}
return null;
}
}
/// <summary>
/// 将SvgElement转换为Win2DSvgElement对象
/// </summary>
/// <param name="resourceCreator"></param>
/// <param name="svg"></param>
/// <returns></returns>
public static async Task<Win2DSvgElement> Parse(ICanvasResourceCreator resourceCreator, SvgElement svg)
{
var win2DSvg = new Win2DSvgElement
{
ViewBox = svg.ViewBox
};
var list = await Win2DSvgParseHelper.GetWin2DGeometrys(resourceCreator, svg);
var drawList = list.Where(
item => item.RenderMethod.Equals(RenderMethod.Draw) || item.RenderMethod.Equals(RenderMethod.Mark))
.Cast<Win2DSvgGeometry>().ToList();
win2DSvg.TotalLength =
drawList.Aggregate<Win2DSvgGeometry, double>(0, (current, item) => current + item.PathLength);
if (Math.Abs(win2DSvg.TotalLength) < 0.01)
{
drawList = new List<Win2DSvgGeometry>();
foreach (var item in list)
if (item.RenderMethod.Equals(RenderMethod.Fill) && ((Win2DSvgGeometry) item).StrokeThickness > 0)
{
item.RenderMethod = RenderMethod.MarkAndFill;
drawList.Add((Win2DSvgGeometry) item);
win2DSvg.TotalLength += ((Win2DSvgGeometry) item).PathLength;
}
}
win2DSvg.SvgNodeList = list;
win2DSvg.DrawNodeList = drawList;
win2DSvg.Device = resourceCreator.Device;
return win2DSvg;
}
/// <summary>
/// 保存svg文件图像到指定的流中
/// </summary>
/// <param name="stream"></param>
/// <param name="svg"></param>
/// <returns></returns>
public static async Task RenderImage(IRandomAccessStream stream, SvgElement svg)
{
var device = CanvasDevice.GetSharedDevice();
using (var win2DSvg = await Parse(device, svg))
{
win2DSvg.Progress = 1;
using (
var offScreen = new CanvasRenderTarget(device, (float) svg.ViewBox.Width,
(float) svg.ViewBox.Height,
96))
{
using (var session = offScreen.CreateDrawingSession())
{
session.Clear(Colors.Transparent);
var m = Matrix3x2.CreateTranslation(-(float) svg.ViewBox.X, -(float) svg.ViewBox.Y);
session.Transform = m;
win2DSvg.Draw(session, 0);
}
await offScreen.SaveAsync(stream, CanvasBitmapFileFormat.Png);
}
}
}
public override void Dispose()
{
lock (_lockobj)
{
foreach (var node in SvgNodeList) node.Dispose();
SvgNodeList.Clear();
DrawNodeList.Clear();
}
GC.SuppressFinalize(this);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using SvgConverter.SvgParseForWin2D.Utilities;
namespace SvgConverter.SvgParseForWin2D
{
public class Win2DSvgGeometry : Win2DSvgNode
{
public CanvasGeometry CanvasGeometry
{
get
{
if (ClipGeometry != null)
return SourceCanvasGeometry.CombineWith(ClipGeometry, RenderTransform,
CanvasGeometryCombine.Intersect);
return SourceCanvasGeometry;
}
}
public CanvasGeometry SourceCanvasGeometry { get; set; }
public CanvasStrokeStyle CanvasStrokeStyle { get; set; }
public ICanvasBrush Stroke { get; set; }
public float StrokeThickness { get; set; }
public float PathLength { get; set; }
public double Progress { get; set; }
public bool IsDrewCompleted => Progress >= 1;
public ICanvasBrush Fill { get; set; }
public Vector2? RenderStroke(CanvasDrawingSession drawingSession, ICanvasBrush stroke)
{
if (drawingSession == null) return null;
if (IsDrewCompleted)
{
drawingSession.DrawGeometry(CanvasGeometry, stroke, StrokeThickness, CanvasStrokeStyle);
}
else
{
var drawLenght = (float) (PathLength * Progress);
Progress = drawLenght / PathLength;
var style = this.GetPartPathStyle(drawLenght);
drawingSession.DrawGeometry(CanvasGeometry, stroke,
StrokeThickness, style);
return Vector2.Transform(CanvasGeometry.ComputePointOnPath(drawLenght),
drawingSession.Transform);
}
return null;
}
public override void Dispose()
{
ClipGeometry?.Dispose();
SourceCanvasGeometry?.Dispose();
CanvasStrokeStyle?.Dispose();
GC.SuppressFinalize(this);
}
}
}
\ No newline at end of file
using System;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
using Windows.UI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
namespace SvgConverter.SvgParseForWin2D
{
public class Win2DSvgImage : Win2DSvgNode
{
public Win2DSvgImage(Rect viewRect, CanvasBitmap bitmap)
{
RenderMethod = RenderMethod.Composite;
ViewRect = viewRect;
SourceCanvasBitmap = bitmap;
}
public Rect ViewRect { get; set; }
public CanvasBitmap SourceCanvasBitmap { get; set; }
public void Draw(CanvasDrawingSession targetDrawSession)
{
if (SourceCanvasBitmap == null || ViewRect.Width == 0 || ViewRect.Height == 0)
{
return;
}
if (ClipGeometry != null)
{
using (var offScreen = new CanvasCommandList(SourceCanvasBitmap.Device))
{
using (var drawSession = offScreen.CreateDrawingSession())
{
drawSession.Clear(Colors.Transparent);
var imgCommandList = new CanvasCommandList(SourceCanvasBitmap.Device);
var markCommandList = new CanvasCommandList(SourceCanvasBitmap.Device);
using (var imgDrawSession = imgCommandList.CreateDrawingSession())
{
imgDrawSession.Clear(Colors.Transparent);
imgDrawSession.DrawImage(SourceCanvasBitmap, ViewRect);
}
using (var markDrawSession = markCommandList.CreateDrawingSession())
{
markDrawSession.Clear(Colors.Transparent);
markDrawSession.FillGeometry(ClipGeometry, Colors.Black);
}
var effect = new AlphaMaskEffect
{
Source = imgCommandList,
AlphaMask = markCommandList
};
drawSession.DrawImage(effect);
imgCommandList.Dispose();
markCommandList.Dispose();
}
targetDrawSession.Transform = RenderTransform;
targetDrawSession.DrawImage(offScreen);
}
}
else
{
targetDrawSession.Transform = RenderTransform;
targetDrawSession.DrawImage(SourceCanvasBitmap, ViewRect);
}
}
public static async Task<Win2DSvgImage> GetImage(ICanvasResourceCreator device, Rect viewRect,
byte[] imageBytes)
{
if (imageBytes == null) return null;
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
var buffer = CryptographicBuffer.CreateFromByteArray(imageBytes);
await randomAccessStream.WriteAsync(buffer);
randomAccessStream.Seek(0);
var win2DSvgImg = new Win2DSvgImage(viewRect, await CanvasBitmap.LoadAsync(device, randomAccessStream));
return win2DSvgImg;
}
}
public override void Dispose()
{
ClipGeometry?.Dispose();
SourceCanvasBitmap?.Dispose();
GC.SuppressFinalize(this);
}
}
}
\ No newline at end of file
using System;
using System.Numerics;
using Microsoft.Graphics.Canvas.Geometry;
namespace SvgConverter.SvgParseForWin2D
{
public enum RenderMethod
{
Draw,
Fill,
Mark,
Composite,
MarkAndFill
}
public abstract class Win2DSvgNode : IDisposable
{
public string Id { get; set; }
public RenderMethod RenderMethod { get; set; }
public double RenderOpacity { get; set; } = 1;
public Matrix3x2 RenderTransform { get; set; } = Matrix3x2.Identity;
public CanvasGeometry ClipGeometry { get; set; }
public abstract void Dispose();
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
namespace SvgConverter.TextParse
{
public class CharGeometry
{
public char Char { get; set; }
public string FontFamily { get; set; }
public float FontSize { get; set; }
public List<Vector2[]> PathList { get; set; }
public List<CanvasGeometry> GetGeometrys(ICanvasResourceCreator resourceCreator)
{
if (PathList == null || PathList.Count <= 0) return new List<CanvasGeometry>();
var list = new List<CanvasGeometry>();
foreach (var pathPoints in PathList)
{
if (pathPoints.Length < 2) continue;
var builder = new CanvasPathBuilder(resourceCreator);
builder.BeginFigure(pathPoints[0]);
for (var i = 1; i < pathPoints.Length; i++) builder.AddLine(pathPoints[i]);
builder.EndFigure(CanvasFigureLoop.Open);
list.Add(CanvasGeometry.CreatePath(builder));
builder.Dispose();
}
return list;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
namespace SvgConverter.TextParse
{
public class PathMoveDirection
{
public static readonly PathMoveDirection Left = new PathMoveDirection
{
_id = -1,
OffsetPoint = new PathPoint(0, -1),
_nextMoveDirectionIds = new List<int>
{
4,
3,
5
}
};
public static readonly PathMoveDirection Right = new PathMoveDirection
{
_id = 0,
OffsetPoint = new PathPoint(0, 1),
_nextMoveDirectionIds = new List<int>
{
7,
2,
6
}
};
public static readonly PathMoveDirection Down = new PathMoveDirection
{
_id = 1,
OffsetPoint = new PathPoint(1, 0),
_nextMoveDirectionIds = new List<int>
{
3,
5,
6,
2
}
};
public static readonly PathMoveDirection LowerRight = new PathMoveDirection
{
_id = 2,
OffsetPoint = new PathPoint(1, 1),
_nextMoveDirectionIds = new List<int>
{
1,
6,
7,
0
}
};
public static readonly PathMoveDirection LowerLeft = new PathMoveDirection
{
_id = 3,
OffsetPoint = new PathPoint(1, -1),
_nextMoveDirectionIds = new List<int>
{
-1,
4,
5,
1
}
};
public static readonly PathMoveDirection LowerLeftLeft = new PathMoveDirection
{
_id = 4,
OffsetPoint = new PathPoint(1, -2),
_nextMoveDirectionIds = new List<int>
{
-1,
3,
5
}
};
public static readonly PathMoveDirection LowerLeftRight = new PathMoveDirection
{
_id = 5,
OffsetPoint = new PathPoint(2, -1),
_nextMoveDirectionIds = new List<int>
{
4,
3,
1,
6
}
};
public static readonly PathMoveDirection LowerRightLeft = new PathMoveDirection
{
_id = 6,
OffsetPoint = new PathPoint(2, 1),
_nextMoveDirectionIds = new List<int>
{
5,
1,
2,
7
}
};
public static readonly PathMoveDirection LowerRightRight = new PathMoveDirection
{
_id = 7,
OffsetPoint = new PathPoint(1, 2),
_nextMoveDirectionIds = new List<int>
{
6,
2,
0
}
};
private int _id;
private List<int> _nextMoveDirectionIds;
public PathPoint OffsetPoint { get; private set; }
public List<PathMoveDirection> GetNextMoveDirections()
{
var list = new List<PathMoveDirection>();
foreach (var id in _nextMoveDirectionIds)
if (id == Down._id)
list.Add(Down);
else if (id == Right._id)
list.Add(Right);
else if (id == LowerRight._id)
list.Add(LowerRight);
else if (id == LowerLeft._id)
list.Add(LowerLeft);
else if (id == Left._id)
list.Add(Left);
else if (id == LowerRightLeft._id)
list.Add(LowerRightLeft);
else if (id == LowerRightRight._id)
list.Add(LowerRightRight);
else if (id == LowerLeftLeft._id)
list.Add(LowerLeftLeft);
else if (id == LowerLeftRight._id) list.Add(LowerLeftRight);
return list;
}
}
}
\ No newline at end of file
using System.Numerics;
namespace SvgConverter.TextParse
{
public struct PathPoint
{
public PathPoint(int row, int col)
{
Row = row;
Col = col;
}
public int Row { get; }
public int Col { get; }
public Vector2 ToVector2(Vector2 refSplitVector2)
{
return new Vector2
{
X = refSplitVector2.X * (Col + 0.5f),
Y = refSplitVector2.Y * (Row + 0.5f)
};
}
public override bool Equals(object obj)
{
if (!(obj is PathPoint)) return false;
var point = (PathPoint) obj;
return Row == point.Row &&
Col == point.Col;
}
public override int GetHashCode()
{
var hashCode = 1084646500;
hashCode = hashCode * -1521134295 + base.GetHashCode();
hashCode = hashCode * -1521134295 + Row.GetHashCode();
hashCode = hashCode * -1521134295 + Col.GetHashCode();
return hashCode;
}
public static PathPoint operator +(PathPoint point1, PathPoint point2)
{
return new PathPoint(point1.Row + point2.Row, point1.Col + point2.Col);
}
public static bool operator ==(PathPoint point1, PathPoint point2)
{
return point1.Row == point2.Row && point1.Col == point2.Col;
}
public static bool operator !=(PathPoint point1, PathPoint point2)
{
return point1.Row != point2.Row || point1.Col != point2.Col;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Windows.Foundation;
using Windows.UI.Text;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
namespace SvgConverter.TextParse
{
public static class TextParseHelper
{
public static CharGeometry GetGeometrys(int rowCount, int colCount, int pathWidthLimit, char text,
string fontFamily, float fontSize)
{
var textLayout = new CanvasTextLayout(CanvasDevice.GetSharedDevice(), new string(new[] {text}),
new CanvasTextFormat
{
FontWeight = new FontWeight
{
Weight = 1
},
FontFamily = fontFamily,
FontSize = fontSize,
WordWrapping = CanvasWordWrapping.NoWrap
}, 0, 0);
var textDotArray = GetDotArray(textLayout, rowCount, colCount);
var drawBounds = textLayout.DrawBounds;
textLayout.Dispose();
var directs = new List<PathMoveDirection>
{
PathMoveDirection.Right,
PathMoveDirection.LowerRightRight,
PathMoveDirection.LowerRight,
PathMoveDirection.LowerRightLeft,
PathMoveDirection.Down,
PathMoveDirection.LowerLeftLeft,
PathMoveDirection.LowerLeft,
PathMoveDirection.LowerLeftRight
};
var pathList = new List<List<PathPoint>>();
for (var i = 0; i < rowCount; i++)
for (var j = 0; j < colCount; j++)
{
if (!textDotArray[i, j]) continue;
var list = GetMaxPath(textDotArray, new PathPoint(i, j), directs);
textDotArray[i, j] = false;
if (list == null) continue;
list.Insert(0, new PathPoint(i, j));
pathList.Add(list);
foreach (var p in list)
for (var k = -pathWidthLimit / 2; k < pathWidthLimit / 2 + 1; k++)
for (var l = -pathWidthLimit / 2; l < pathWidthLimit / 2 + 1; l++)
InvalidPoint(textDotArray, p, new PathPoint(k, l));
}
return new CharGeometry
{
Char = text,
FontFamily = fontFamily,
FontSize = fontSize,
PathList = GetGeometrys(pathList, new Size(drawBounds.Width, drawBounds.Height),
rowCount, colCount)
};
}
private static List<Vector2[]> GetGeometrys(List<List<PathPoint>> pathList, Size sise, int rowCount,
int colCount)
{
if (pathList == null || pathList.Count <= 0) return new List<Vector2[]>();
var splitPoint = new Point(sise.Width / colCount, sise.Height / rowCount).ToVector2();
return (from pathPoints in pathList
where pathPoints.Count >= 2
select (from point in pathPoints select point.ToVector2(splitPoint))
into points
select points.ToArray()).ToList();
}
private static void InvalidPoint(bool[,] dotArrays, PathPoint sourcePoint, PathPoint offsetPoint)
{
var point = sourcePoint + offsetPoint;
var i = dotArrays.GetLength(0);
var j = dotArrays.GetLength(1);
if (0 <= point.Row && point.Row < i && 0 <= point.Col && point.Col < j)
dotArrays[point.Row, point.Col] = false;
}
private static List<PathPoint> GetMaxPath(bool[,] dotArrays, PathPoint startPoint,
IEnumerable<PathMoveDirection> canMoveDirections)
{
var maxLengthList = new List<PathPoint>();
foreach (var direct in canMoveDirections)
{
if (!CanMove(dotArrays, startPoint, direct))
continue;
var dList = GetPath(dotArrays, startPoint, direct);
if (dList != null && dList.Count > maxLengthList.Count) maxLengthList = dList;
}
return maxLengthList;
}
private static List<PathPoint> GetPath(bool[,] dotArrays, PathPoint startPoint,
PathMoveDirection moveDirection)
{
var list = new List<PathPoint>();
if (!CanMove(dotArrays, startPoint, moveDirection))
return GetMaxPath(dotArrays, startPoint, moveDirection.GetNextMoveDirections());
var point = startPoint + moveDirection.OffsetPoint;
list.Add(point);
var next = GetPath(dotArrays, point, moveDirection);
if (next != null)
list.AddRange(next);
return list;
}
private static bool CanMove(bool[,] dotArrays, PathPoint startPoint, PathMoveDirection moveDirection)
{
var nextPoint = startPoint + moveDirection.OffsetPoint;
var next2Point = nextPoint + moveDirection.OffsetPoint;
if (0 <= nextPoint.Row
&& nextPoint.Row < dotArrays.GetLength(0)
&& 0 <= nextPoint.Col
&& nextPoint.Col < dotArrays.GetLength(1)
&& 0 <= next2Point.Row
&& next2Point.Row < dotArrays.GetLength(0)
&& 0 <= next2Point.Col
&& next2Point.Col < dotArrays.GetLength(1))
return dotArrays[nextPoint.Row, nextPoint.Col] &&
dotArrays[next2Point.Row, next2Point.Col];
return false;
}
private static bool[,] GetDotArray(CanvasTextLayout text, int rowCount, int colCount)
{
var path = new bool[rowCount, colCount];
var startVector2 = new Vector2((float) text.DrawBounds.X, (float) text.DrawBounds.Y);
var rectSize = new Size(text.DrawBounds.Width / colCount, text.DrawBounds.Height / rowCount);
var textGeometry = CanvasGeometry.CreateText(text);
for (var i = 0; i < rowCount; i++)
{
startVector2.X = (float) text.DrawBounds.X;
for (var j = 0; j < colCount; j++)
{
path[i, j] = textGeometry.FillContainsPoint(
new Point(startVector2.X + rectSize.Height / 2, startVector2.Y + rectSize.Width / 2)
.ToVector2());
startVector2.X += (float) rectSize.Width;
}
startVector2.Y += (float) rectSize.Height;
}
textGeometry.Dispose();
return path;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Text;
namespace SvgConverter.TextParse
{
public class TextSvgElement : AnimationBase
{
private readonly Dictionary<int, Tuple<float, CharGeometry>> _charGeometrys =
new Dictionary<int, Tuple<float, CharGeometry>>();
private readonly List<Tuple<Rect, Win2DCharSvgElement>> _charSvgElements =
new List<Tuple<Rect, Win2DCharSvgElement>>();
private readonly object _lockobj = new object();
private float _progress;
private TextSvgElement()
{
}
public double TotalLength { get; private set; }
public double DrewLength => TotalLength * _progress;
public override float Progress
{
get => _progress;
set
{
_progress = value >= 1 ? 1 : value;
RefreshDrawProgress();
}
}
public string Text { get; private set; }
public string FontFamily { get; private set; }
public Color Foreground { get; private set; }
public double FontSize { get; private set; }
/// <summary>
/// 刷新绘制进度
/// </summary>
private void RefreshDrawProgress()
{
lock (_lockobj)
{
var length = DrewLength;
foreach (var item in _charSvgElements)
if (length >= item.Item2.TotalLength)
{
length -= item.Item2.TotalLength;
item.Item2.Progress = 1;
}
else
{
item.Item2.Progress = (float) (length / item.Item2.TotalLength);
length = 0;
}
}
}
public override Vector2? Draw(
CanvasDrawingSession drawingSession, float drawProgress)
{
lock (_lockobj)
{
Progress += drawProgress;
var sourceTransform = drawingSession.Transform;
drawingSession.Transform = Matrix3x2.Identity;
var needDrawLenght = TotalLength * drawProgress;
var charCommandList = new CanvasCommandList(drawingSession);
var charDrawingSession = charCommandList.CreateDrawingSession();
try
{
foreach (var item in _charSvgElements)
{
charDrawingSession.Transform =
Matrix3x2.CreateTranslation(new Point(item.Item1.X, item.Item1.Y)
.ToVector2());
charDrawingSession.Transform = charDrawingSession.Transform * sourceTransform;
if (item.Item2.Progress >= 1)
{
item.Item2.Draw(charDrawingSession, 0);
}
else
{
var leftLength = item.Item2.TotalLength - item.Item2.DrewLength;
if (needDrawLenght > leftLength)
{
item.Item2.Draw(charDrawingSession, 1 - item.Item2.Progress);
needDrawLenght -= leftLength;
}
else
{
var p = item.Item2.Draw(charDrawingSession,
(float) (needDrawLenght / item.Item2.TotalLength));
if (p.HasValue) return Vector2.Transform(p.Value, drawingSession.Transform);
}
}
}
return null;
}
finally
{
charDrawingSession.Dispose();
drawingSession.DrawImage(charCommandList);
charCommandList.Dispose();
}
}
}
public override void Dispose()
{
lock (_lockobj)
{
foreach (var node in _charSvgElements) node.Item2.Dispose();
}
GC.SuppressFinalize(this);
}
public void ChangeFontColor(Color newFontColor)
{
if (Foreground != newFontColor)
lock (_lockobj)
{
Foreground = newFontColor;
foreach (var item in _charSvgElements) item.Item2.ChangeFontColor(newFontColor);
}
}
public static async Task<TextSvgElement> Parse(ICanvasResourceCreator resourceCreator, string text,
string fontFamily, Color foreground,
float fontSize = 72f)
{
if (string.IsNullOrWhiteSpace(text))
return null;
var chars = text.ToCharArray();
var strLayout = new CanvasTextLayout(CanvasDevice.GetSharedDevice(), text,
new CanvasTextFormat
{
FontFamily = fontFamily,
FontSize = fontSize,
WordWrapping = CanvasWordWrapping.NoWrap
}, 0, 0);
var textGeo = new TextSvgElement
{
Text = text,
FontFamily = fontFamily,
Foreground = foreground,
FontSize = fontSize,
ViewBox = strLayout.LayoutBounds
};
var taskDic = new Dictionary<char, Task>();
var lockobj = new object();
for (var i = 0; i < chars.Length; i++)
{
if (taskDic.Keys.Contains(chars[i]) || chars[i].Equals(' ')) continue;
var tuple = new Tuple<Dictionary<int, Tuple<float, CharGeometry>>, int, char, string>(
textGeo._charGeometrys, i,
chars[i], fontFamily);
var task =
new Task(o =>
{
if (!(o is Tuple<Dictionary<int, Tuple<float, CharGeometry>>, int, char, string> t))
return;
var charGeometrys = t.Item1;
var index = t.Item2;
var letter = t.Item3;
var font = t.Item4;
int rowCount = 72, colCount = 72, pathWidthLimit = 6;
var thickness = fontSize / 10f;
if (char.IsNumber(letter))
{
rowCount = 48;
colCount = 16;
pathWidthLimit = 8;
thickness *= 2f;
}
else if (char.IsUpper(letter))
{
rowCount = 48;
colCount = 16;
pathWidthLimit = 8;
thickness *= 2f;
}
else if (char.IsLower(letter))
{
rowCount = 48;
colCount = 16;
pathWidthLimit = 8;
thickness *= 1.5f;
}
else if (char.IsPunctuation(letter) || char.IsSymbol(letter))
{
rowCount = 16;
colCount = 16;
pathWidthLimit = 4;
}
var charGeo = TextParseHelper.GetGeometrys(rowCount, colCount, pathWidthLimit, letter, font,
fontSize);
lock (lockobj)
{
charGeometrys[index] = new Tuple<float, CharGeometry>(thickness, charGeo);
}
}, tuple);
taskDic[chars[i]] = task;
task.Start();
}
await Task.WhenAll(taskDic.Values.ToArray());
for (var i = 0; i < chars.Length; i++)
{
if (chars[i].Equals(' ')) continue;
var rect = strLayout.GetCharacterRegions(i, 1)[0].LayoutBounds;
if (textGeo._charGeometrys.TryGetValue(text.IndexOf(chars[i]), out var charGeo))
{
var charSvg =
Win2DCharSvgElement.Parse(resourceCreator, charGeo.Item2, foreground, charGeo.Item1);
textGeo._charSvgElements.Add(new Tuple<Rect, Win2DCharSvgElement>(rect, charSvg));
textGeo.TotalLength += charSvg.TotalLength;
}
}
strLayout.Dispose();
return textGeo;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Windows.Foundation;
using Windows.UI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using SvgConverter.SvgParseForWin2D;
namespace SvgConverter.TextParse
{
public class Win2DCharSvgElement : Win2DSvgElement
{
internal Win2DCharSvgElement()
{
}
public char Char { get; private set; }
public string FontFamily { get; private set; }
public Color Foreground { get; private set; }
public double FontSize { get; private set; }
public void ChangeFontColor(Color newFontColor)
{
if (Foreground != newFontColor && Device != null)
{
Foreground = newFontColor;
if (SvgNodeList?.Count > 0 &&
SvgNodeList[0] is Win2DSvgGeometry geo)
{
geo.Fill = new CanvasSolidColorBrush(Device, newFontColor);
geo.Stroke = new CanvasSolidColorBrush(Device, newFontColor);
}
}
}
public static Win2DCharSvgElement Parse(ICanvasResourceCreator resourceCreator, CharGeometry charGeo,
Color foreground, float thickness)
{
var textLayout = new CanvasTextLayout(resourceCreator, new string(new[] {charGeo.Char}),
new CanvasTextFormat
{
FontFamily = charGeo.FontFamily,
FontSize = charGeo.FontSize,
WordWrapping = CanvasWordWrapping.NoWrap
}, 0, 0);
var win2DSvg = new Win2DCharSvgElement
{
Device = resourceCreator.Device,
Char = charGeo.Char,
Foreground = foreground,
FontFamily = charGeo.FontFamily,
FontSize = charGeo.FontSize,
ViewBox =
new Rect(new Point(), new Size(textLayout.LayoutBounds.Width, textLayout.LayoutBounds.Height)),
SvgNodeList = new List<Win2DSvgNode>()
};
var drawList = charGeo.GetGeometrys(resourceCreator).Select(path => new Win2DSvgGeometry
{
SourceCanvasGeometry = path,
PathLength = path.ComputePathLength(),
RenderOpacity = 1,
Stroke = new CanvasSolidColorBrush(resourceCreator, Colors.Black),
Fill = new CanvasSolidColorBrush(resourceCreator, Colors.Transparent),
StrokeThickness = thickness,
RenderMethod = RenderMethod.Mark,
CanvasStrokeStyle = new CanvasStrokeStyle
{
LineJoin = CanvasLineJoin.Round,
StartCap = CanvasCapStyle.Round,
EndCap = CanvasCapStyle.Round
},
RenderTransform =
Matrix3x2.CreateTranslation(new Point(textLayout.DrawBounds.X, textLayout.DrawBounds.Y)
.ToVector2())
}).ToList();
var win2DGeo = new Win2DSvgGeometry
{
SourceCanvasGeometry = CanvasGeometry.CreateText(textLayout),
CanvasStrokeStyle = new CanvasStrokeStyle(),
RenderOpacity = 1,
Stroke = new CanvasSolidColorBrush(resourceCreator, foreground),
Fill = new CanvasSolidColorBrush(resourceCreator, foreground),
StrokeThickness = 1,
RenderMethod = RenderMethod.Composite
};
win2DSvg.TotalLength = drawList
.Aggregate<Win2DSvgGeometry, double>(0,
(current, item) => current + item.PathLength);
win2DSvg.SvgNodeList.Add(win2DGeo);
win2DSvg.SvgNodeList.AddRange(drawList);
win2DSvg.DrawNodeList = drawList;
return win2DSvg;
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment