Use Source Generators for SvgElementFactory (#772)

- replace runtime reflection by source generators to improve performance
- done for SvgElementFactory, property descriptors _svgPropertyAttributes and _svgEventAttributes
- no Source Generators support for net452 , added additional target framework net461 
  as the minimum version for source generators
- use stackalloc and out vars to reduce memory usage
- remove Linq usage to improve path parser performance
- use Span for SvgTransformConverter to improve performance
- add several benchmarks
- fixes #771, fixes #776, fixes part 2 of #767
This commit is contained in:
Wiesław Šoltés 2021-01-07 20:11:08 +01:00 коммит произвёл GitHub
Родитель 2d3d9f361d
Коммит e570088730
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
101 изменённых файлов: 2166 добавлений и 315 удалений

4
.gitignore поставляемый
Просмотреть файл

@ -351,3 +351,7 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# JetBrains Rider
.idea/
*.sln.iml

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<OutputType>Library</OutputType>
<IsPackable>False</IsPackable>
<AnalysisLevel>latest</AnalysisLevel>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.1" PrivateAssets="all" />
</ItemGroup>
</Project>

Просмотреть файл

@ -6,7 +6,7 @@ namespace Svg
/// An SVG element to render circles to the document.
/// </summary>
[SvgElement("circle")]
public class SvgCircle : SvgPathBasedElement
public partial class SvgCircle : SvgPathBasedElement
{
private SvgUnit _centerX = 0f;
private SvgUnit _centerY = 0f;

Просмотреть файл

@ -6,7 +6,7 @@ namespace Svg
/// Represents and SVG ellipse element.
/// </summary>
[SvgElement("ellipse")]
public class SvgEllipse : SvgPathBasedElement
public partial class SvgEllipse : SvgPathBasedElement
{
private SvgUnit _centerX = 0f;
private SvgUnit _centerY = 0f;

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// Represents and SVG line element.
/// </summary>
[SvgElement("line")]
public class SvgLine : SvgMarkerElement
public partial class SvgLine : SvgMarkerElement
{
private SvgUnit _startX = 0f;
private SvgUnit _startY = 0f;
@ -104,7 +104,7 @@ namespace Svg
this.IsPathDirty = false;
}
else
{ // only when calculating boundary
{ // only when calculating boundary
_path.StartFigure();
var radius = base.StrokeWidth / 2;
_path.AddEllipse(start.X - radius, start.Y - radius, 2 * radius, 2 * radius);

Просмотреть файл

@ -6,7 +6,7 @@ namespace Svg
/// <summary>
/// Represents a path based element that can have markers.
/// </summary>
public abstract class SvgMarkerElement : SvgPathBasedElement
public abstract partial class SvgMarkerElement : SvgPathBasedElement
{
/// <summary>
/// Gets or sets the marker (end cap) of the path.

Просмотреть файл

@ -6,7 +6,7 @@ namespace Svg
/// <summary>
/// Represents an element that is using a GraphicsPath as rendering base.
/// </summary>
public abstract class SvgPathBasedElement : SvgVisualElement
public abstract partial class SvgPathBasedElement : SvgVisualElement
{
public override RectangleF Bounds
{

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// SvgPolygon defines a closed shape consisting of a set of connected straight line segments.
/// </summary>
[SvgElement("polygon")]
public class SvgPolygon : SvgMarkerElement
public partial class SvgPolygon : SvgMarkerElement
{
private GraphicsPath _path;

Просмотреть файл

@ -9,7 +9,7 @@ namespace Svg
/// SvgPolyline defines a set of connected straight line segments. Typically, <see cref="SvgPolyline"/> defines open shapes.
/// </summary>
[SvgElement("polyline")]
public class SvgPolyline : SvgPolygon
public partial class SvgPolyline : SvgPolygon
{
private GraphicsPath _Path;

Просмотреть файл

@ -8,7 +8,7 @@ namespace Svg
/// Represents an SVG rectangle that could also have rounded edges.
/// </summary>
[SvgElement("rect")]
public class SvgRectangle : SvgPathBasedElement
public partial class SvgRectangle : SvgPathBasedElement
{
private SvgUnit _x = 0f;
private SvgUnit _y = 0f;

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// Defines a path that can be used by other <see cref="ISvgClipable"/> elements.
/// </summary>
[SvgElement("clipPath")]
public sealed class SvgClipPath : SvgElement
public partial class SvgClipPath : SvgElement
{
private GraphicsPath _path;
@ -56,7 +56,7 @@ namespace Svg
}
/// <summary>
///
///
/// </summary>
/// <param name="path"></param>
/// <param name="element"></param>

Просмотреть файл

@ -4,7 +4,7 @@ namespace Svg
/// Defines an alpha mask for compositing the current object into the background.
/// </summary>
[SvgElement("mask")]
public class SvgMask : SvgElement
public partial class SvgMask : SvgElement
{
/// <summary>
/// Defines the coordinate system for attributes <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/> and <see cref="Height"/>.

Просмотреть файл

@ -73,6 +73,11 @@ namespace Svg
}
}
public sealed class XmlSpaceHandlingConverter : EnumBaseConverter<XmlSpaceHandling>
{
public XmlSpaceHandlingConverter() : base(CaseHandling.LowerCase) { }
}
public sealed class SvgFillRuleConverter : EnumBaseConverter<SvgFillRule>
{
public SvgFillRuleConverter() : base(CaseHandling.LowerCase) { }

Просмотреть файл

@ -1,14 +1,16 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace Svg
{
[TypeConverter(typeof(XmlSpaceHandlingConverter))]
public enum XmlSpaceHandling
{
@default,
inherit,
preserve
Default,
Inherit,
Preserve
}
}

Просмотреть файл

@ -4,7 +4,7 @@ namespace Svg
/// Represents a list of re-usable SVG components.
/// </summary>
[SvgElement("defs")]
public class SvgDefinitionList : SvgElement
public partial class SvgDefinitionList : SvgElement
{
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.

Просмотреть файл

@ -4,7 +4,7 @@ namespace Svg
{
[DefaultProperty("Text")]
[SvgElement("desc")]
public class SvgDescription : SvgElement, ISvgDescriptiveElement
public partial class SvgDescription : SvgElement, ISvgDescriptiveElement
{
public override string ToString()
{

Просмотреть файл

@ -8,7 +8,7 @@ namespace Svg
/// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document.
/// </summary>
[SvgElement("svg")]
public class SvgFragment : SvgElement, ISvgViewPort, ISvgBoundable
public partial class SvgFragment : SvgElement, ISvgViewPort, ISvgBoundable
{
private SvgUnit _x = 0f;
private SvgUnit _y = 0f;
@ -136,7 +136,7 @@ namespace Svg
public override XmlSpaceHandling SpaceHandling
{
get { return GetAttribute("space", true, XmlSpaceHandling.@default); }
get { return GetAttribute("space", true, XmlSpaceHandling.Default); }
set { base.SpaceHandling = value; IsPathDirty = true; }
}

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// An element used to group SVG shapes.
/// </summary>
[SvgElement("g")]
public class SvgGroup : SvgMarkerElement
public partial class SvgGroup : SvgMarkerElement
{
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.

Просмотреть файл

@ -13,7 +13,7 @@ namespace Svg
/// Represents and SVG image
/// </summary>
[SvgElement("image")]
public class SvgImage : SvgVisualElement
public partial class SvgImage : SvgVisualElement
{
private const string MimeTypeSvg = "image/svg+xml";

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// The 'switch' element evaluates the 'requiredFeatures', 'requiredExtensions' and 'systemLanguage' attributes on its direct child elements in order, and then processes and renders the first child for which these attributes evaluate to true
/// </summary>
[SvgElement("switch")]
public class SvgSwitch : SvgVisualElement
public partial class SvgSwitch : SvgVisualElement
{
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.

Просмотреть файл

@ -10,7 +10,7 @@ namespace Svg.Document_Structure
/// An element used to group SVG shapes.
/// </summary>
[SvgElement("symbol")]
public class SvgSymbol : SvgVisualElement
public partial class SvgSymbol : SvgVisualElement
{
/// <summary>
/// Gets or sets the viewport of the element.

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg
{
[SvgElement("title")]
public class SvgTitle : SvgElement, ISvgDescriptiveElement
public partial class SvgTitle : SvgElement, ISvgDescriptiveElement
{
public override string ToString()
{

Просмотреть файл

@ -6,7 +6,7 @@ using System.Drawing.Drawing2D;
namespace Svg
{
[SvgElement("use")]
public class SvgUse : SvgVisualElement
public partial class SvgUse : SvgVisualElement
{
[SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public virtual Uri ReferencedElement
@ -53,7 +53,7 @@ namespace Svg
}
/// <summary>
/// Checks for any direct or indirect recursions in referenced elements,
/// Checks for any direct or indirect recursions in referenced elements,
/// including recursions via groups.
/// </summary>
/// <returns>True if any recursions are found.</returns>

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// The 'foreignObject' element allows for inclusion of a foreign namespace which has its graphical content drawn by a different user agent
/// </summary>
[SvgElement("foreignObject")]
public class SvgForeignObject : SvgVisualElement
public partial class SvgForeignObject : SvgVisualElement
{
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.

Просмотреть файл

@ -10,7 +10,7 @@ namespace Svg.FilterEffects
/// A filter effect consists of a series of graphics operations that are applied to a given source graphic to produce a modified graphical result.
/// </summary>
[SvgElement("filter")]
public sealed class SvgFilter : SvgElement
public partial class SvgFilter : SvgElement
{
[SvgAttribute("filterUnits")]
public SvgCoordinateUnits FilterUnits

Просмотреть файл

@ -6,7 +6,7 @@ using System.Drawing.Drawing2D;
namespace Svg.FilterEffects
{
public abstract class SvgFilterPrimitive : SvgElement
public abstract partial class SvgFilterPrimitive : SvgElement
{
public const string SourceGraphic = "SourceGraphic";
public const string SourceAlpha = "SourceAlpha";

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feBlend")]
public class SvgBlend : SvgFilterPrimitive
public partial class SvgBlend : SvgFilterPrimitive
{
[SvgAttribute("mode")]
public SvgBlendMode Mode

Просмотреть файл

@ -10,7 +10,7 @@ namespace Svg.FilterEffects
/// Note: this is not used in calculations to bitmap - used only to allow for svg xml output
/// </summary>
[SvgElement("feColorMatrix")]
public class SvgColourMatrix : SvgFilterPrimitive
public partial class SvgColourMatrix : SvgFilterPrimitive
{
private SvgColourMatrixType _type;
private string _values;
@ -29,7 +29,7 @@ namespace Svg.FilterEffects
/// <summary>
/// list of numbers
/// The contents of ?values? depends on the value of attribute ?type?:
/// The contents of ?values? depends on the value of attribute ?type?:
/// Note: this is not used in calculations to bitmap - used only to allow for svg xml output
/// </summary>
[SvgAttribute("values")]

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feComponentTransfer")]
public class SvgComponentTransfer : SvgFilterPrimitive
public partial class SvgComponentTransfer : SvgFilterPrimitive
{
public override void Process(ImageBuffer buffer)
{

Просмотреть файл

@ -1,6 +1,6 @@
namespace Svg.FilterEffects
{
public abstract class SvgComponentTransferFunction : SvgElement
public abstract partial class SvgComponentTransferFunction : SvgElement
{
[SvgAttribute("type")]
public SvgComponentTransferType Type

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feComposite")]
public class SvgComposite : SvgFilterPrimitive
public partial class SvgComposite : SvgFilterPrimitive
{
[SvgAttribute("operator")]
public SvgCompositeOperator Operator

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feConvolveMatrix")]
public class SvgConvolveMatrix : SvgFilterPrimitive
public partial class SvgConvolveMatrix : SvgFilterPrimitive
{
[SvgAttribute("order")]
public SvgNumberCollection Order

Просмотреть файл

@ -3,7 +3,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feDiffuseLighting")]
public class SvgDiffuseLighting : SvgFilterPrimitive
public partial class SvgDiffuseLighting : SvgFilterPrimitive
{
[SvgAttribute("surfaceScale")]
public float SurfaceScale

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feDisplacementMap")]
public class SvgDisplacementMap : SvgFilterPrimitive
public partial class SvgDisplacementMap : SvgFilterPrimitive
{
[SvgAttribute("scale")]
public float Scale

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feDistantLight")]
public class SvgDistantLight : SvgElement
public partial class SvgDistantLight : SvgElement
{
[SvgAttribute("azimuth")]
public float Azimuth

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feFlood")]
public class SvgFlood : SvgFilterPrimitive
public partial class SvgFlood : SvgFilterPrimitive
{
[SvgAttribute("flood-color")]
public virtual SvgPaintServer FloodColor

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feFuncA")]
public class SvgFuncA : SvgComponentTransferFunction
public partial class SvgFuncA : SvgComponentTransferFunction
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feFuncB")]
public class SvgFuncB : SvgComponentTransferFunction
public partial class SvgFuncB : SvgComponentTransferFunction
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feFuncG")]
public class SvgFuncG : SvgComponentTransferFunction
public partial class SvgFuncG : SvgComponentTransferFunction
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feFuncR")]
public class SvgFuncR : SvgComponentTransferFunction
public partial class SvgFuncR : SvgComponentTransferFunction
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -11,7 +11,7 @@ namespace Svg.FilterEffects
}
[SvgElement("feGaussianBlur")]
public class SvgGaussianBlur : SvgFilterPrimitive
public partial class SvgGaussianBlur : SvgFilterPrimitive
{
private float _stdDeviationX = float.NaN;
private float _stdDeviationY = float.NaN;

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feImage")]
public class SvgImage : SvgFilterPrimitive
public partial class SvgImage : SvgFilterPrimitive
{
[SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public virtual string Href

Просмотреть файл

@ -4,7 +4,7 @@ using System.Linq;
namespace Svg.FilterEffects
{
[SvgElement("feMerge")]
public class SvgMerge : SvgFilterPrimitive
public partial class SvgMerge : SvgFilterPrimitive
{
public override void Process(ImageBuffer buffer)
{

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feMergeNode")]
public class SvgMergeNode : SvgElement
public partial class SvgMergeNode : SvgElement
{
[SvgAttribute("in")]
public string Input

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feMorphology")]
public class SvgMorphology : SvgFilterPrimitive
public partial class SvgMorphology : SvgFilterPrimitive
{
[SvgAttribute("operator")]
public SvgMorphologyOperator Operator

Просмотреть файл

@ -6,7 +6,7 @@ namespace Svg.FilterEffects
/// Note: this is not used in calculations to bitmap - used only to allow for svg xml output
/// </summary>
[SvgElement("feOffset")]
public class SvgOffset : SvgFilterPrimitive
public partial class SvgOffset : SvgFilterPrimitive
{
private SvgUnit _dx = 0f;
private SvgUnit _dy = 0f;

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("fePointLight")]
public class SvgPointLight : SvgElement
public partial class SvgPointLight : SvgElement
{
[SvgAttribute("x")]
public float X

Просмотреть файл

@ -3,7 +3,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feSpecularLighting")]
public class SvgSpecularLighting : SvgFilterPrimitive
public partial class SvgSpecularLighting : SvgFilterPrimitive
{
[SvgAttribute("surfaceScale")]
public float SurfaceScale

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feSpotLight")]
public class SvgSpotLight : SvgElement
public partial class SvgSpotLight : SvgElement
{
[SvgAttribute("x")]
public float X

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feTile")]
public class SvgTile : SvgFilterPrimitive
public partial class SvgTile : SvgFilterPrimitive
{
public override void Process(ImageBuffer buffer)
{

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg.FilterEffects
{
[SvgElement("feTurbulence")]
public class SvgTurbulence : SvgFilterPrimitive
public partial class SvgTurbulence : SvgFilterPrimitive
{
[SvgAttribute("baseFrequency")]
public SvgNumberCollection BaseFrequency

Просмотреть файл

@ -0,0 +1,63 @@
using System;
namespace Svg.Helpers
{
internal ref struct StringSplitEnumerator
{
private ReadOnlySpan<char> _str;
private readonly ReadOnlySpan<char> _chars;
public StringSplitEnumerator(ReadOnlySpan<char> str, ReadOnlySpan<char> chars)
{
_str = str;
_chars = chars;
Current = default;
}
public StringSplitEnumerator GetEnumerator() => this;
public bool MoveNext()
{
while (true)
{
var span = _str;
if (span.Length == 0)
{
return false;
}
var index = span.IndexOfAny(_chars);
if (index == -1)
{
Current = new StringPart(span);
_str = ReadOnlySpan<char>.Empty;
return true;
}
var slice = span.Slice(0, index).Trim();
_str = span.Slice(index + 1);
if (slice.Length == 0)
{
continue;
}
Current = new StringPart(slice);
return true;
}
}
public StringPart Current { get; private set; }
}
internal readonly ref struct StringPart
{
public StringPart(ReadOnlySpan<char> value)
{
Value = value;
}
public ReadOnlySpan<char> Value { get; }
public static implicit operator ReadOnlySpan<char>(StringPart part) => part.Value;
}
}

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg
{
[SvgElement("a")]
public class SvgAnchor : SvgElement
public partial class SvgAnchor : SvgElement
{
[SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public string Href

Просмотреть файл

@ -6,7 +6,7 @@ namespace Svg
/// Represents a list of re-usable SVG components.
/// </summary>
[SvgElement("metadata")]
public class SvgDocumentMetadata : SvgElement
public partial class SvgDocumentMetadata : SvgElement
{
/// <summary>
/// Initializes a new instance of the <see cref="SvgDocumentMetadata"/> class.

Просмотреть файл

@ -1,6 +1,6 @@
namespace Svg
{
public class NonSvgElement : SvgElement
public partial class NonSvgElement : SvgElement
{
public NonSvgElement()
{

8
Source/NuGet.Config Normal file
Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

Просмотреть файл

@ -3,7 +3,7 @@ using System.Drawing;
namespace Svg
{
public sealed class SvgColourServer : SvgPaintServer
public partial class SvgColourServer : SvgPaintServer
{
public SvgColourServer()
: this(System.Drawing.Color.Black)

Просмотреть файл

@ -11,7 +11,7 @@ namespace Svg
/// but should be defined by the time the image needs to render.
/// </summary>
[TypeConverter(typeof(SvgDeferredPaintServerFactory))]
public class SvgDeferredPaintServer : SvgPaintServer
public partial class SvgDeferredPaintServer : SvgPaintServer
{
private bool _serverLoaded;

Просмотреть файл

@ -9,7 +9,7 @@ namespace Svg
/// A wrapper for a paint server has a fallback if the primary server doesn't work.
/// </summary>
[Obsolete("Will be removed.Use SvgDeferredPaintServer class instead.")]
public class SvgFallbackPaintServer : SvgPaintServer
public partial class SvgFallbackPaintServer : SvgPaintServer
{
private IEnumerable<SvgPaintServer> _fallbacks;
private SvgPaintServer _primary;

Просмотреть файл

@ -10,7 +10,7 @@ namespace Svg
/// <summary>
/// Provides the base class for all paint servers that wish to render a gradient.
/// </summary>
public abstract class SvgGradientServer : SvgPaintServer
public abstract partial class SvgGradientServer : SvgPaintServer
{
/// <summary>
/// Initializes a new instance of the <see cref="SvgGradientServer"/> class.

Просмотреть файл

@ -8,7 +8,7 @@ namespace Svg
/// Represents a colour stop in a gradient.
/// </summary>
[SvgElement("stop")]
public class SvgGradientStop : SvgElement
public partial class SvgGradientStop : SvgElement
{
private SvgUnit _offset;

Просмотреть файл

@ -8,7 +8,7 @@ using System.Linq;
namespace Svg
{
[SvgElement("linearGradient")]
public sealed class SvgLinearGradientServer : SvgGradientServer
public partial class SvgLinearGradientServer : SvgGradientServer
{
[SvgAttribute("x1")]
public SvgUnit X1

Просмотреть файл

@ -7,7 +7,7 @@ using Svg.DataTypes;
namespace Svg
{
[SvgElement("marker")]
public class SvgMarker : SvgPathBasedElement, ISvgViewPort
public partial class SvgMarker : SvgPathBasedElement, ISvgViewPort
{
private SvgVisualElement _markerElement = null;

Просмотреть файл

@ -11,7 +11,7 @@ namespace Svg
/// Represents the base class for all paint servers that are intended to be used as a fill or stroke.
/// </summary>
[TypeConverter(typeof(SvgPaintServerFactory))]
public abstract class SvgPaintServer : SvgElement
public abstract partial class SvgPaintServer : SvgElement
{
public Func<SvgPaintServer> GetCallback { get; set; }

Просмотреть файл

@ -11,7 +11,7 @@ namespace Svg
/// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted.
/// </summary>
[SvgElement("pattern")]
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort
public partial class SvgPatternServer : SvgPaintServer, ISvgViewPort
{
private SvgUnit _x = SvgUnit.None;
private SvgUnit _y = SvgUnit.None;
@ -82,7 +82,7 @@ namespace Svg
}
/// <summary>
/// Specifies a supplemental transformation which is applied on top of any
/// Specifies a supplemental transformation which is applied on top of any
/// transformations necessary to create a new pattern coordinate system.
/// </summary>
[SvgAttribute("viewBox")]

Просмотреть файл

@ -7,7 +7,7 @@ using System.Linq;
namespace Svg
{
[SvgElement("radialGradient")]
public sealed class SvgRadialGradientServer : SvgGradientServer
public partial class SvgRadialGradientServer : SvgGradientServer
{
[SvgAttribute("cx")]
public SvgUnit CenterX
@ -224,7 +224,7 @@ namespace Svg
//New plan:
// scale the outer rectangle to always encompass ellipse
// cut the ellipse in half (either vertical or horizontal)
// cut the ellipse in half (either vertical or horizontal)
// determine the region on each side of the ellipse
private static IEnumerable<GraphicsPath> GetDifference(RectangleF subject, GraphicsPath clip)
{

Просмотреть файл

@ -3,7 +3,7 @@ using System.Globalization;
namespace Svg
{
internal enum NumState
public enum NumState
{
Invalid,
Separator,
@ -16,7 +16,7 @@ namespace Svg
ExpValue
}
internal struct CoordinateParserState
public ref struct CoordinateParserState
{
public NumState CurrNumState;
public NumState NewNumState;
@ -35,7 +35,7 @@ namespace Svg
}
}
internal static class CoordinateParser
public static class CoordinateParser
{
private static bool MarkState(bool hasMode, ref CoordinateParserState state)
{

Просмотреть файл

@ -8,7 +8,7 @@ namespace Svg
/// Represents an SVG path element.
/// </summary>
[SvgElement("path")]
public class SvgPath : SvgMarkerElement, ISvgPathElement
public partial class SvgPath : SvgMarkerElement, ISvgPathElement
{
private GraphicsPath _path;

Просмотреть файл

@ -4,7 +4,6 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using Svg.Pathing;
namespace Svg
@ -86,118 +85,166 @@ namespace Svg
private static void CreatePathSegment(char command, SvgPathSegmentList segments, ref CoordinateParserState state, ref ReadOnlySpan<char> chars)
{
var isRelative = char.IsLower(command);
var coords = new float[6];
// http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation
switch (command)
{
case 'M': // moveto
case 'm': // relative moveto
if (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state))
segments.Add(new SvgMoveToSegment(ToAbsolute(coords[0], coords[1], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state))
{
segments.Add(new SvgLineSegment(segments.Last.End,
ToAbsolute(coords[0], coords[1], segments, isRelative)));
if (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state))
{
segments.Add(
new SvgMoveToSegment(
ToAbsolute(coords0, coords1, segments, isRelative)));
}
while (CoordinateParser.TryGetFloat(out coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out coords1, ref chars, ref state))
{
segments.Add(
new SvgLineSegment(
segments.Last.End,
ToAbsolute(coords0, coords1, segments, isRelative)));
}
}
break;
case 'A': // elliptical arc
case 'a': // relative elliptical arc
bool size;
bool sweep;
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state) &&
CoordinateParser.TryGetFloat(out coords[2], ref chars, ref state) && CoordinateParser.TryGetBool(out size, ref chars, ref state) &&
CoordinateParser.TryGetBool(out sweep, ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[3], ref chars, ref state) &&
CoordinateParser.TryGetFloat(out coords[4], ref chars, ref state))
{
// A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y
segments.Add(new SvgArcSegment(segments.Last.End, coords[0], coords[1], coords[2],
size ? SvgArcSize.Large : SvgArcSize.Small,
sweep ? SvgArcSweep.Positive : SvgArcSweep.Negative,
ToAbsolute(coords[3], coords[4], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state)
&& CoordinateParser.TryGetBool(out var size, ref chars, ref state)
&& CoordinateParser.TryGetBool(out var sweep, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords4, ref chars, ref state))
{
// A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y
segments.Add(
new SvgArcSegment(
segments.Last.End,
coords0,
coords1,
coords2,
size ? SvgArcSize.Large : SvgArcSize.Small,
sweep ? SvgArcSweep.Positive : SvgArcSweep.Negative,
ToAbsolute(coords3, coords4, segments, isRelative)));
}
}
break;
case 'L': // lineto
case 'l': // relative lineto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state))
{
segments.Add(new SvgLineSegment(segments.Last.End,
ToAbsolute(coords[0], coords[1], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state))
{
segments.Add(
new SvgLineSegment(
segments.Last.End,
ToAbsolute(coords0, coords1, segments, isRelative)));
}
}
break;
case 'H': // horizontal lineto
case 'h': // relative horizontal lineto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state))
{
segments.Add(new SvgLineSegment(segments.Last.End,
ToAbsolute(coords[0], segments.Last.End.Y, segments, isRelative, false)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state))
{
segments.Add(
new SvgLineSegment(
segments.Last.End,
ToAbsolute(coords0, segments.Last.End.Y, segments, isRelative, false)));
}
}
break;
case 'V': // vertical lineto
case 'v': // relative vertical lineto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state))
{
segments.Add(new SvgLineSegment(segments.Last.End,
ToAbsolute(segments.Last.End.X, coords[0], segments, false, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state))
{
segments.Add(
new SvgLineSegment(
segments.Last.End,
ToAbsolute(segments.Last.End.X, coords0, segments, false, isRelative)));
}
}
break;
case 'Q': // quadratic bézier curveto
case 'q': // relative quadratic bézier curveto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state) &&
CoordinateParser.TryGetFloat(out coords[2], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[3], ref chars, ref state))
{
segments.Add(new SvgQuadraticCurveSegment(segments.Last.End,
ToAbsolute(coords[0], coords[1], segments, isRelative),
ToAbsolute(coords[2], coords[3], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state))
{
segments.Add(
new SvgQuadraticCurveSegment(
segments.Last.End,
ToAbsolute(coords0, coords1, segments, isRelative),
ToAbsolute(coords2, coords3, segments, isRelative)));
}
}
break;
case 'T': // shorthand/smooth quadratic bézier curveto
case 't': // relative shorthand/smooth quadratic bézier curveto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state))
{
var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment;
var controlPoint = lastQuadCurve != null
? Reflect(lastQuadCurve.ControlPoint, segments.Last.End)
: segments.Last.End;
segments.Add(new SvgQuadraticCurveSegment(segments.Last.End, controlPoint,
ToAbsolute(coords[0], coords[1], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state))
{
var lastQuadCurve = segments.Last as SvgQuadraticCurveSegment;
var controlPoint = lastQuadCurve != null ? Reflect(lastQuadCurve.ControlPoint, segments.Last.End) : segments.Last.End;
segments.Add(
new SvgQuadraticCurveSegment(
segments.Last.End,
controlPoint,
ToAbsolute(coords0, coords1, segments, isRelative)));
}
}
break;
case 'C': // curveto
case 'c': // relative curveto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state) &&
CoordinateParser.TryGetFloat(out coords[2], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[3], ref chars, ref state) &&
CoordinateParser.TryGetFloat(out coords[4], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[5], ref chars, ref state))
{
segments.Add(new SvgCubicCurveSegment(segments.Last.End,
ToAbsolute(coords[0], coords[1], segments, isRelative),
ToAbsolute(coords[2], coords[3], segments, isRelative),
ToAbsolute(coords[4], coords[5], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords4, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords5, ref chars, ref state))
{
segments.Add(
new SvgCubicCurveSegment(
segments.Last.End,
ToAbsolute(coords0, coords1, segments, isRelative),
ToAbsolute(coords2, coords3, segments, isRelative),
ToAbsolute(coords4, coords5, segments, isRelative)));
}
}
break;
case 'S': // shorthand/smooth curveto
case 's': // relative shorthand/smooth curveto
while (CoordinateParser.TryGetFloat(out coords[0], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[1], ref chars, ref state) &&
CoordinateParser.TryGetFloat(out coords[2], ref chars, ref state) && CoordinateParser.TryGetFloat(out coords[3], ref chars, ref state))
{
var lastCubicCurve = segments.Last as SvgCubicCurveSegment;
var controlPoint = lastCubicCurve != null
? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End)
: segments.Last.End;
segments.Add(new SvgCubicCurveSegment(segments.Last.End, controlPoint,
ToAbsolute(coords[0], coords[1], segments, isRelative),
ToAbsolute(coords[2], coords[3], segments, isRelative)));
while (CoordinateParser.TryGetFloat(out var coords0, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords1, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords2, ref chars, ref state)
&& CoordinateParser.TryGetFloat(out var coords3, ref chars, ref state))
{
var lastCubicCurve = segments.Last as SvgCubicCurveSegment;
var controlPoint = lastCubicCurve != null ? Reflect(lastCubicCurve.SecondControlPoint, segments.Last.End) : segments.Last.End;
segments.Add(
new SvgCubicCurveSegment(
segments.Last.End,
controlPoint,
ToAbsolute(coords0, coords1, segments, isRelative),
ToAbsolute(coords2, coords3, segments, isRelative)));
}
}
break;
case 'Z': // closepath
case 'z': // relative closepath
segments.Add(new SvgClosePathSegment());
{
segments.Add(new SvgClosePathSegment());
}
break;
}
}
@ -244,8 +291,17 @@ namespace Svg
var lastSegment = segments.Last;
// if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0
if (lastSegment is SvgClosePathSegment)
lastSegment = segments.Reverse().OfType<SvgMoveToSegment>().First();
if (lastSegment is SvgClosePathSegment && segments.Count > 0)
{
for (int i = segments.Count - 1; i >= 0; i--)
{
if (segments[i] is SvgMoveToSegment moveToSegment)
{
lastSegment = moveToSegment;
break;
}
}
}
if (isRelativeX)
point.X += lastSegment.End.X;

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// Use the Script property to get the script content (proxies the content)
/// </summary>
[SvgElement("script")]
public class SvgScript : SvgElement
public partial class SvgScript : SvgElement
{
public string Script
{

Просмотреть файл

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp2.2;netcoreapp3.0;net452</TargetFrameworks>
<TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp2.2;netcoreapp3.0;net452;net461</TargetFrameworks>
<PackageLicenseExpression>MS-PL</PackageLicenseExpression>
<RootNamespace>Svg</RootNamespace>
<AssemblyName>Svg</AssemblyName>
@ -35,10 +35,12 @@
<PackageProjectUrl>https://github.com/vvvv/SVG</PackageProjectUrl>
<PackageIconUrl>https://www.w3.org/Icons/SVG/svg-logo-v.png</PackageIconUrl>
<PackageIcon>svg-logo-v.png</PackageIcon>
<LangVersion>7.3</LangVersion>
<LangVersion>latest</LangVersion>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\$(Configuration)\$(TargetFramework)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
@ -67,6 +69,12 @@
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net461'">
<Title>Svg for .Net Framework 4.6.1</Title>
<DefineConstants>$(DefineConstants);NETFULL;NET461;Net4</DefineConstants>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netcoreapp2.2'">
<Title>Svg for .Net Core 2.2</Title>
<DefineConstants>$(DefineConstants);NETCORE;NETCORE22</DefineConstants>
@ -87,7 +95,7 @@
<DefineConstants>$(DefineConstants);NETSTANDARD;NETSTANDARD21</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
<ItemGroup Condition="'$(TargetFramework)'=='net452' Or '$(TargetFramework)'=='net461'">
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
@ -139,4 +147,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)' != 'net452'">
<DefineConstants>$(DefineConstants);USE_SOURCE_GENERATORS</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net452'">
<ProjectReference Include="..\Generators\Svg.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>

Просмотреть файл

@ -31,6 +31,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2EC3F3A0
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SvgConsole", "..\Samples\SvgConsole\SvgConsole.csproj", "{9379360C-CB0B-4035-AE8F-8863A420FDCF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.Generators", "..\Generators\Svg.Generators.csproj", "{BACDD1F4-B97D-4E27-BD09-6957633FF484}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.Benchmark", "..\Tests\Svg.Benchmark\Svg.Benchmark.csproj", "{8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generators", "Generators", "{FAFD6DC7-5203-4CED-A151-66379DD43695}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -69,6 +75,14 @@ Global
{8CEEB917-63E2-4AE7-B0D1-50A840050975}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CEEB917-63E2-4AE7-B0D1-50A840050975}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CEEB917-63E2-4AE7-B0D1-50A840050975}.Release|Any CPU.Build.0 = Release|Any CPU
{8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DEB3EA7-5915-4EB9-8052-85A7AC4379EC}.Release|Any CPU.Build.0 = Release|Any CPU
{BACDD1F4-B97D-4E27-BD09-6957633FF484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BACDD1F4-B97D-4E27-BD09-6957633FF484}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BACDD1F4-B97D-4E27-BD09-6957633FF484}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BACDD1F4-B97D-4E27-BD09-6957633FF484}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -81,6 +95,8 @@ Global
{F34CF345-E7EF-4354-B49A-AFE7A78442A7} = {038E2E7B-BC03-4DA7-AA5F-CA8BD95BD0F2}
{8CEEB917-63E2-4AE7-B0D1-50A840050975} = {038E2E7B-BC03-4DA7-AA5F-CA8BD95BD0F2}
{9379360C-CB0B-4035-AE8F-8863A420FDCF} = {038E2E7B-BC03-4DA7-AA5F-CA8BD95BD0F2}
{8DEB3EA7-5915-4EB9-8052-85A7AC4379EC} = {2EC3F3A0-F097-43EF-A603-9B82E3061469}
{BACDD1F4-B97D-4E27-BD09-6957633FF484} = {FAFD6DC7-5203-4CED-A151-66379DD43695}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5096EEB3-8F41-44B5-BCF9-58743A59AB21}

Просмотреть файл

@ -20,7 +20,7 @@ namespace Svg
/// <summary>
/// The class used to create and load SVG documents.
/// </summary>
public class SvgDocument : SvgFragment, ITypeDescriptorContext
public partial class SvgDocument : SvgFragment, ITypeDescriptorContext
{
/// <summary>
/// Skip check whether the GDI+ can be loaded.
@ -42,7 +42,7 @@ namespace Svg
{
get { return pointsPerInch ?? (int) (pointsPerInch = GetSystemDpi()); }
set { pointsPerInch = value; }
}
}
private SvgElementIdManager _idManager;
@ -61,7 +61,7 @@ namespace Svg
isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#else
var platform = Environment.OSVersion.Platform;
isWindows = platform == PlatformID.Win32NT;
isWindows = platform == PlatformID.Win32NT;
#endif
if (isWindows)
@ -138,7 +138,7 @@ namespace Svg
}
/// <summary>
/// Overwrites the current IdManager with a custom implementation.
/// Overwrites the current IdManager with a custom implementation.
/// Be careful with this: If elements have been inserted into the document before,
/// you have to take care that the new IdManager also knows of them.
/// </summary>
@ -220,7 +220,7 @@ namespace Svg
/// <returns>Boolean whether the system is capable of using GDI+</returns>
public static bool SystemIsGdiPlusCapable()
{
try
try
{
EnsureSystemIsGdiPlusCapable();
}
@ -246,7 +246,7 @@ namespace Svg
{
using (var matrix = new Matrix(0f, 0f, 0f, 0f, 0f, 0f)) { }
}
// GDI+ loading errors will result in TypeInitializationExceptions,
// GDI+ loading errors will result in TypeInitializationExceptions,
// for readability we will catch and wrap the error
catch (Exception e)
{
@ -256,12 +256,12 @@ namespace Svg
throw new SvgGdiPlusCannotBeLoadedException(e);
}
//If the Matrix creation is causing another type of exception we should just raise that one
throw;
throw;
}
}
/// <summary>
/// Check if the current exception or one of its children is the targeted GDI+ exception.
/// Check if the current exception or one of its children is the targeted GDI+ exception.
/// It can be hidden in one of the InnerExceptions, so we need to iterate over them.
/// </summary>
/// <param name="e">The exception to validate against the GDI+ check</param>

Просмотреть файл

@ -17,7 +17,7 @@ namespace Svg
{
internal const int StyleSpecificity_PresAttribute = 0;
internal const int StyleSpecificity_InlineStyle = 1 << 16;
#if !USE_SOURCE_GENERATORS
//optimization
protected class PropertyAttributeTuple
{
@ -34,7 +34,7 @@ namespace Svg
//reflection cache
private IEnumerable<PropertyAttributeTuple> _svgPropertyAttributes;
private IEnumerable<EventAttributeTuple> _svgEventAttributes;
#endif
internal SvgElement _parent;
private string _elementName;
private SvgAttributeCollection _attributes;
@ -108,7 +108,7 @@ namespace Svg
SortedDictionary<int, string> rules;
if (_styles.TryGetValue(name, out rules))
{
// Get staged styles that are
// Get staged styles that are
if (rules.TryGetValue(StyleSpecificity_InlineStyle, out value)) return true;
if (rules.TryGetValue(StyleSpecificity_PresAttribute, out value)) return true;
}
@ -129,12 +129,26 @@ namespace Svg
{
if (string.IsNullOrEmpty(this._elementName))
{
#if USE_SOURCE_GENERATORS
// There is special case for SvgDocument as valid attribute is only set on SvgFragment.
if (SvgElements.ElementNames.TryGetValue(this.GetType(), out var elementName))
{
this._elementName = elementName;
}
else if (this is SvgDocument)
{
// The SvgDocument does not have SvgElement attribute set, instead the attitude is used on SvgFragment so there would be duplicate im dictionary.
// The SvgDocument is not valid Svg element (that is SvgFragment) and is mainly used as abstraction for document reading and writing.
// The ElementName for SvgDocument is set explicitly here as that is the exception to attribute convention used accross codebase.
this._elementName = "svg";
}
#else
var attr = TypeDescriptor.GetAttributes(this).OfType<SvgElementAttribute>().SingleOrDefault();
if (attr != null)
{
this._elementName = attr.ElementName;
}
#endif
}
return this._elementName;
@ -397,7 +411,7 @@ namespace Svg
/// <summary>
/// Transforms the given rectangle with the set transformation, if any.
/// Can be applied to bounds calculated without considering the element transformation.
/// Can be applied to bounds calculated without considering the element transformation.
/// </summary>
/// <param name="bounds">The rectangle to be transformed.</param>
/// <returns>The transformed rectangle, or the original rectangle if no transformation exists.</returns>
@ -434,7 +448,7 @@ namespace Svg
[SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
public virtual XmlSpaceHandling SpaceHandling
{
get { return GetAttribute("space", true, XmlSpaceHandling.inherit); }
get { return GetAttribute("space", true, XmlSpaceHandling.Inherit); }
set { Attributes["space"] = value; }
}
@ -534,7 +548,7 @@ namespace Svg
//subscribe to attribute events
Attributes.AttributeChanged += Attributes_AttributeChanged;
CustomAttributes.AttributeChanged += Attributes_AttributeChanged;
#if !USE_SOURCE_GENERATORS
//find svg attribute descriptions
_svgPropertyAttributes = from PropertyDescriptor a in TypeDescriptor.GetProperties(this)
let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute
@ -545,7 +559,7 @@ namespace Svg
let attribute = a.Attributes[typeof(SvgAttributeAttribute)] as SvgAttributeAttribute
where attribute != null
select new EventAttributeTuple { Event = a.ComponentType.GetField(a.Name, BindingFlags.Instance | BindingFlags.NonPublic), Attribute = attribute };
#endif
}
//dispatch attribute event
@ -600,6 +614,19 @@ namespace Svg
//events
if (AutoPublishEvents)
{
#if USE_SOURCE_GENERATORS
foreach (var property in this.GetProperties().Where(x => x.DescriptorType == DescriptorType.Event))
{
var evt = property.GetValue(this);
//if someone has registered publish the attribute
if (evt != null && !string.IsNullOrEmpty(this.ID))
{
string evtValue = this.ID + "/" + property.AttributeName;
WriteAttributeString(writer, property.AttributeName, null, evtValue);
}
}
#else
foreach (var attr in _svgEventAttributes)
{
var evt = attr.Event.GetValue(this);
@ -611,6 +638,7 @@ namespace Svg
WriteAttributeString(writer, attr.Attribute.Name, null, evtValue);
}
}
#endif
}
//add the custom attributes
@ -638,6 +666,131 @@ namespace Svg
{
var styles = _styles.ToDictionary(_styles => _styles.Key, _styles => _styles.Value.Last().Value);
#if USE_SOURCE_GENERATORS
var opacityAttributes = new List<ISvgPropertyDescriptor>();
var opacityValues = new Dictionary<string, float>();
try
{
Writing = true;
foreach (var property in this.GetProperties())
{
if (property.Converter == null)
{
continue;
}
if (property.Converter.CanConvertTo(typeof(string)))
{
if (property.AttributeName == "fill-opacity" || property.AttributeName == "stroke-opacity")
{
opacityAttributes.Add(property);
continue;
}
if (Attributes.ContainsKey(property.AttributeName))
{
var propertyValue = property.GetValue(this);
var forceWrite = false;
var writeStyle = property.AttributeName == "fill" || property.AttributeName == "stroke";
if (Parent != null)
{
if (writeStyle && propertyValue == SvgPaintServer.NotSet)
continue;
object parentValue;
if (TryResolveParentAttributeValue(property.AttributeName, out parentValue))
{
if ((parentValue == propertyValue)
|| ((parentValue != null) && parentValue.Equals(propertyValue)))
{
if (writeStyle)
continue;
}
else
forceWrite = true;
}
}
var hasOpacity = writeStyle;
if (hasOpacity)
{
if (propertyValue is SvgColourServer && ((SvgColourServer)propertyValue).Colour.A < 255)
{
var opacity = ((SvgColourServer)propertyValue).Colour.A / 255f;
opacityValues.Add(property.AttributeName + "-opacity", opacity);
}
}
#if NETFULL
var value = (string)property.Converter.ConvertTo(propertyValue, typeof(string));
#else
// dotnetcore throws exception if input is null
var value = propertyValue == null ? null : (string)property.Converter.ConvertTo(propertyValue, typeof(string));
#endif
if (propertyValue != null)
{
//Only write the attribute's value if it is not the default value, not null/empty, or we're forcing the write.
if (forceWrite || !string.IsNullOrEmpty(value))
{
if (writeStyle)
{
styles[property.AttributeName] = value;
}
else
{
WriteAttributeString(writer, property.AttributeName, property.AttributeNamespace, value);
}
}
}
else if (property.AttributeName == "fill") //if fill equals null, write 'none'
{
if (writeStyle)
{
styles[property.AttributeName] = value;
}
else
{
WriteAttributeString(writer, property.AttributeName, property.AttributeNamespace, value);
}
}
}
}
}
foreach (var property in opacityAttributes)
{
var opacity = 1f;
var write = false;
var key = property.AttributeName;
if (opacityValues.ContainsKey(key))
{
opacity = opacityValues[key];
write = true;
}
if (Attributes.ContainsKey(key))
{
opacity *= (float)property.GetValue(this);
write = true;
}
if (write)
{
opacity = (float)Math.Round(opacity, 2, MidpointRounding.AwayFromZero);
var value = (string)property.Converter.ConvertTo(opacity, typeof(string));
if (!string.IsNullOrEmpty(value))
WriteAttributeString(writer, property.AttributeName, property.AttributeNamespace, value);
}
}
}
finally
{
Writing = false;
}
#else
var opacityAttributes = new List<PropertyAttributeTuple>();
var opacityValues = new Dictionary<string, float>();
@ -757,7 +910,7 @@ namespace Svg
{
Writing = false;
}
#endif
return styles;
}
@ -891,7 +1044,7 @@ namespace Svg
foreach (var child in elem.Children)
{
// Skip to avoid double calculate Symbol element
// symbol element is only referenced by use element
// symbol element is only referenced by use element
// So here we need to skip when it is directly considered
if (child is Svg.Document_Structure.SvgSymbol)
continue;
@ -1009,6 +1162,33 @@ namespace Svg
foreach (var child in Children)
newObj.Children.Add(child.DeepCopy());
#if USE_SOURCE_GENERATORS
foreach (var property in this.GetProperties().Where(x => x.DescriptorType == DescriptorType.Event))
{
var evt = property.GetValue(this);
// if someone has registered also register here
if (evt != null)
{
if (property.AttributeName == "MouseDown")
newObj.MouseDown += delegate { };
else if (property.AttributeName == "MouseUp")
newObj.MouseUp += delegate { };
else if (property.AttributeName == "MouseOver")
newObj.MouseOver += delegate { };
else if (property.AttributeName == "MouseOut")
newObj.MouseOut += delegate { };
else if (property.AttributeName == "MouseMove")
newObj.MouseMove += delegate { };
else if (property.AttributeName == "MouseScroll")
newObj.MouseScroll += delegate { };
else if (property.AttributeName == "Click")
newObj.Click += delegate { };
else if (property.AttributeName == "Change") // text element
(newObj as SvgText).Change += delegate { };
}
}
#else
foreach (var attr in _svgEventAttributes)
{
var evt = attr.Event.GetValue(this);
@ -1034,7 +1214,7 @@ namespace Svg
(newObj as SvgText).Change += delegate { };
}
}
#endif
foreach (var attribute in CustomAttributes)
newObj.CustomAttributes.Add(attribute.Key, attribute.Value);
@ -1083,12 +1263,12 @@ namespace Svg
onmouseup = "<anything>"
onmouseover = "<anything>"
onmousemove = "<anything>"
onmouseout = "<anything>"
onmouseout = "<anything>"
*/
#if Net4
/// <summary>
/// Use this method to provide your implementation ISvgEventCaller which can register Actions
/// Use this method to provide your implementation ISvgEventCaller which can register Actions
/// and call them if one of the events occurs. Make sure, that your SvgElement has a unique ID.
/// The SvgTextElement overwrites this and regsiters the Change event tor its text content.
/// </summary>

Просмотреть файл

@ -12,8 +12,12 @@ namespace Svg
/// <summary>
/// Provides the methods required in order to parse and create <see cref="SvgElement"/> instances from XML.
/// </summary>
internal class SvgElementFactory
#if USE_SOURCE_GENERATORS
[ElementFactory]
#endif
internal partial class SvgElementFactory
{
#if !USE_SOURCE_GENERATORS
static SvgElementFactory()
{
// cache ElementInfo in static Field
@ -46,7 +50,7 @@ namespace Svg
private static readonly Dictionary<string, List<Type>> availableElementsDictionary;
private static readonly Dictionary<string, ElementInfo> availableElementsWithoutSvg;
private static readonly List<ElementInfo> availableElements;
#endif
private Parser cssParser = new Parser();
/// <summary>
@ -112,11 +116,18 @@ namespace Svg
}
else
{
#if !USE_SOURCE_GENERATORS
ElementInfo validType;
if (availableElementsWithoutSvg.TryGetValue(elementName, out validType))
{
createdElement = (SvgElement)Activator.CreateInstance(validType.ElementType);
}
#else
if (availableElementsWithoutSvg.TryGetValue(elementName, out var validType))
{
createdElement = validType.CreateInstance();
}
#endif
else
{
createdElement = new SvgUnknownElement(elementName);
@ -248,14 +259,14 @@ namespace Svg
}
return false;
}
#if !USE_SOURCE_GENERATORS
private static Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>> _propertyDescriptors = new Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>>();
private static object syncLock = new object();
#endif
internal static bool SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document, bool isStyle = false)
{
#if !USE_SOURCE_GENERATORS
var elementType = element.GetType();
PropertyDescriptorCollection properties;
lock (syncLock)
{
@ -298,6 +309,17 @@ namespace Svg
}
}
else
#else
if (attributeName == "opacity" && attributeValue == "undefined")
{
attributeValue = "1";
}
var setValueResult = element.SetValue(attributeName, document, CultureInfo.InvariantCulture, attributeValue);
if (setValueResult)
{
return true;
}
#endif
{
//check for namespace declaration in svg element
if (string.Equals(element.ElementName, "svg", StringComparison.OrdinalIgnoreCase))
@ -343,7 +365,12 @@ namespace Svg
/// Gets the <see cref="Type"/> of the <see cref="SvgElement"/> subclass.
/// </summary>
public Type ElementType { get; set; }
#if USE_SOURCE_GENERATORS
/// <summary>
/// Creates a new instance based on <see cref="ElementType"/> type.
/// </summary>
public Func<SvgElement> CreateInstance { get; set; }
#endif
/// <summary>
/// Initializes a new instance of the <see cref="ElementInfo"/> struct.
/// </summary>

Просмотреть файл

@ -1,6 +1,6 @@
namespace Svg
{
public class SvgUnknownElement : SvgElement
public partial class SvgUnknownElement : SvgElement
{
public SvgUnknownElement()
{

Просмотреть файл

@ -3,7 +3,7 @@
namespace Svg
{
[SvgElement("font")]
public class SvgFont : SvgElement
public partial class SvgFont : SvgElement
{
[SvgAttribute("horiz-adv-x")]
public float HorizAdvX

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg
{
[SvgElement("font-face")]
public class SvgFontFace : SvgElement
public partial class SvgFontFace : SvgElement
{
[SvgAttribute("alphabetic")]
public float Alphabetic

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg
{
[SvgElement("font-face-src")]
public class SvgFontFaceSrc : SvgElement
public partial class SvgFontFaceSrc : SvgElement
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -3,7 +3,7 @@
namespace Svg
{
[SvgElement("font-face-uri")]
public class SvgFontFaceUri : SvgElement
public partial class SvgFontFaceUri : SvgElement
{
[SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public virtual Uri ReferencedElement

Просмотреть файл

@ -5,7 +5,7 @@ using Svg.Pathing;
namespace Svg
{
[SvgElement("glyph")]
public class SvgGlyph : SvgPathBasedElement, ISvgPathElement
public partial class SvgGlyph : SvgPathBasedElement, ISvgPathElement
{
private GraphicsPath _path;

Просмотреть файл

@ -1,6 +1,6 @@
namespace Svg
{
public abstract class SvgKern : SvgElement
public abstract partial class SvgKern : SvgElement
{
[SvgAttribute("g1")]
public string Glyph1
@ -39,7 +39,7 @@
}
[SvgElement("vkern")]
public class SvgVerticalKern : SvgKern
public partial class SvgVerticalKern : SvgKern
{
public override SvgElement DeepCopy()
{
@ -47,7 +47,7 @@
}
}
[SvgElement("hkern")]
public class SvgHorizontalKern : SvgKern
public partial class SvgHorizontalKern : SvgKern
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg
{
[SvgElement("missing-glyph")]
public class SvgMissingGlyph : SvgGlyph
public partial class SvgMissingGlyph : SvgGlyph
{
[SvgAttribute("glyph-name")]
public override string GlyphName

Просмотреть файл

@ -4,7 +4,7 @@
/// The <see cref="SvgText"/> element defines a graphics element consisting of text.
/// </summary>
[SvgElement("text")]
public class SvgText : SvgTextBase
public partial class SvgText : SvgTextBase
{
/// <summary>
/// Initializes the <see cref="SvgText"/> class.

Просмотреть файл

@ -10,7 +10,7 @@ using System.Text.RegularExpressions;
namespace Svg
{
public abstract class SvgTextBase : SvgVisualElement
public abstract partial class SvgTextBase : SvgVisualElement
{
private SvgUnitCollection _x = new SvgUnitCollection();
private SvgUnitCollection _y = new SvgUnitCollection();
@ -438,7 +438,7 @@ namespace Svg
{
value = ApplyTransformation(value);
value = new StringBuilder(value).Replace("\r\n", " ").Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ').ToString();
return this.SpaceHandling == XmlSpaceHandling.preserve ? value : MultipleSpaces.Replace(value.Trim(), " ");
return this.SpaceHandling == XmlSpaceHandling.Preserve ? value : MultipleSpaces.Replace(value.Trim(), " ");
}
private string ApplyTransformation(string value)

Просмотреть файл

@ -7,7 +7,7 @@ namespace Svg
/// The <see cref="SvgText"/> element defines a graphics element consisting of text.
/// </summary>
[SvgElement("textPath")]
public class SvgTextPath : SvgTextBase
public partial class SvgTextPath : SvgTextBase
{
public override SvgUnitCollection Dx
{

Просмотреть файл

@ -5,7 +5,7 @@ using System.Linq;
namespace Svg
{
[SvgElement("tref")]
public class SvgTextRef : SvgTextBase
public partial class SvgTextRef : SvgTextBase
{
[SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public virtual Uri ReferencedElement

Просмотреть файл

@ -1,7 +1,7 @@
namespace Svg
{
[SvgElement("tspan")]
public class SvgTextSpan : SvgTextBase
public partial class SvgTextSpan : SvgTextBase
{
public override SvgElement DeepCopy()
{

Просмотреть файл

@ -1,24 +1,75 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Svg.Helpers;
namespace Svg.Transforms
{
public class SvgTransformConverter : TypeConverter
{
private static IEnumerable<string> SplitTransforms(string transforms)
{
transforms = transforms.Replace(',', ' ');
var transformStart = 0;
private static readonly char[] SplitChars = new[] { ' ', ',' };
private const string TranslateTransform = "translate";
private const string RotateTransform = "rotate";
private const string ScaleTransform = "scale";
private const string MatrixTransform = "matrix";
private const string ShearTransform = "shear";
private const string SkewXTransform = "skewX";
private const string SkewYTransform = "skewY";
for (var i = 0; i < transforms.Length; ++i)
if (transforms[i] == ')')
{
yield return transforms.Substring(transformStart, i + 1 - transformStart).TrimStart();
transformStart = i + 1;
}
private enum TransformType
{
Invalid,
Translate,
Rotate,
Scale,
Matrix,
Shear,
SkewX,
SkewY
}
private static TransformType GetTransformType(ref ReadOnlySpan<char> transformName)
{
if (transformName.SequenceEqual(TranslateTransform.AsSpan()))
{
return TransformType.Translate;
}
else if (transformName.SequenceEqual(RotateTransform.AsSpan()))
{
return TransformType.Rotate;
}
else if (transformName.SequenceEqual(ScaleTransform.AsSpan()))
{
return TransformType.Scale;
}
else if (transformName.SequenceEqual(MatrixTransform.AsSpan()))
{
return TransformType.Matrix;
}
else if (transformName.SequenceEqual(ShearTransform.AsSpan()))
{
return TransformType.Shear;
}
else if (transformName.SequenceEqual(SkewXTransform.AsSpan()))
{
return TransformType.SkewX;
}
else if (transformName.SequenceEqual(SkewYTransform.AsSpan()))
{
return TransformType.SkewY;
}
return TransformType.Invalid;
}
private static float ToFloat(ref ReadOnlySpan<char> value)
{
#if NETSTANDARD2_1 || NETCORE || NETCOREAPP2_2 || NETCOREAPP3_0
return float.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture);
#else
return float.Parse(value.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture);
#endif
}
/// <summary>
@ -33,136 +84,279 @@ namespace Svg.Transforms
/// <exception cref="T:System.NotSupportedException">The conversion cannot be performed. </exception>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
if (value is not string str)
{
var transformList = new SvgTransformCollection();
string[] parts;
string contents;
string transformName;
foreach (var transform in SplitTransforms((string)value))
{
if (string.IsNullOrEmpty(transform))
continue;
parts = transform.Split('(', ')');
transformName = parts[0].TrimEnd();
contents = parts[1].Trim();
switch (transformName)
{
case "translate":
var coords = contents.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (coords.Length == 0 || coords.Length > 2)
throw new FormatException("Translate transforms must be in the format 'translate(x [y])'");
var x = float.Parse(coords[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture);
if (coords.Length > 1)
{
var y = float.Parse(coords[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture);
transformList.Add(new SvgTranslate(x, y));
}
else
transformList.Add(new SvgTranslate(x));
break;
case "rotate":
var args = contents.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (args.Length != 1 && args.Length != 3)
throw new FormatException("Rotate transforms must be in the format 'rotate(angle [cx cy])'");
var angle = float.Parse(args[0], NumberStyles.Float, CultureInfo.InvariantCulture);
if (args.Length == 1)
transformList.Add(new SvgRotate(angle));
else
{
var cx = float.Parse(args[1], NumberStyles.Float, CultureInfo.InvariantCulture);
var cy = float.Parse(args[2], NumberStyles.Float, CultureInfo.InvariantCulture);
transformList.Add(new SvgRotate(angle, cx, cy));
}
break;
case "scale":
var scales = contents.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (scales.Length == 0 || scales.Length > 2)
throw new FormatException("Scale transforms must be in the format 'scale(x [y])'");
var sx = float.Parse(scales[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture);
if (scales.Length > 1)
{
var sy = float.Parse(scales[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture);
transformList.Add(new SvgScale(sx, sy));
}
else
transformList.Add(new SvgScale(sx));
break;
case "matrix":
var points = contents.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (points.Length != 6)
throw new FormatException("Matrix transforms must be in the format 'matrix(m11 m12 m21 m22 dx dy)'");
var mPoints = new List<float>(6);
foreach (var point in points)
mPoints.Add(float.Parse(point.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture));
transformList.Add(new SvgMatrix(mPoints));
break;
case "shear":
var shears = contents.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (shears.Length == 0 || shears.Length > 2)
throw new FormatException("Shear transforms must be in the format 'shear(x [y])'");
var hx = float.Parse(shears[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture);
if (shears.Length > 1)
{
var hy = float.Parse(shears[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture);
transformList.Add(new SvgShear(hx, hy));
}
else
transformList.Add(new SvgShear(hx));
break;
case "skewX":
var ax = float.Parse(contents, NumberStyles.Float, CultureInfo.InvariantCulture);
transformList.Add(new SvgSkew(ax, 0f));
break;
case "skewY":
var ay = float.Parse(contents, NumberStyles.Float, CultureInfo.InvariantCulture);
transformList.Add(new SvgSkew(0f, ay));
break;
}
}
return transformList;
return base.ConvertFrom(context, culture, value);
}
return base.ConvertFrom(context, culture, value);
var transformList = new SvgTransformCollection();
var source = str.AsSpan().TrimStart();
var sourceLength = source.Length;
var splitChars = SplitChars.AsSpan();
while (true)
{
var currentIndex = 0;
var startIndex = source.IndexOf('(');
var endIndex = source.IndexOf(')');
if (startIndex < 0 || endIndex <= startIndex)
{
break;
}
var transformName = source.Slice(currentIndex, startIndex - currentIndex).Trim().Trim(',').Trim();
var contents = source.Slice(startIndex + 1, endIndex - startIndex - 1).Trim();
var parts = new StringSplitEnumerator(contents, splitChars);
var transformType = GetTransformType(ref transformName);
switch (transformType)
{
case TransformType.Translate:
{
var count = 0;
var x = default(float);
var y = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
x = ToFloat(ref partValue);
}
else if (count == 1)
{
y = ToFloat(ref partValue);
}
count++;
}
if (count == 0 || count > 2)
{
throw new FormatException("Translate transforms must be in the format 'translate(x [y])'");
}
transformList.Add(count > 1 ? new SvgTranslate(x, y) : new SvgTranslate(x));
}
break;
case TransformType.Rotate:
{
int count = 0;
var angle = default(float);
var cx = default(float);
var cy = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
angle = ToFloat(ref partValue);
}
else if (count == 1)
{
cx = ToFloat(ref partValue);
}
else if (count == 2)
{
cy = ToFloat(ref partValue);
}
count++;
}
if (count != 1 && count != 3)
{
throw new FormatException("Rotate transforms must be in the format 'rotate(angle [cx cy])'");
}
transformList.Add(count == 1 ? new SvgRotate(angle) : new SvgRotate(angle, cx, cy));
}
break;
case TransformType.Scale:
{
int count = 0;
var sx = default(float);
var sy = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
sx = ToFloat(ref partValue);
}
else if (count == 1)
{
sy = ToFloat(ref partValue);
}
count++;
}
if (count == 0 || count > 2)
{
throw new FormatException("Scale transforms must be in the format 'scale(x [y])'");
}
transformList.Add(count > 1 ? new SvgScale(sx, sy) : new SvgScale(sx));
}
break;
case TransformType.Matrix:
{
int count = 0;
var m11 = default(float);
var m12 = default(float);
var m21 = default(float);
var m22 = default(float);
var dx = default(float);
var dy = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
m11 = ToFloat(ref partValue);
}
else if (count == 1)
{
m12 = ToFloat(ref partValue);
}
else if (count == 2)
{
m21 = ToFloat(ref partValue);
}
else if (count == 3)
{
m22 = ToFloat(ref partValue);
}
else if (count == 4)
{
dx = ToFloat(ref partValue);
}
else if (count == 5)
{
dy = ToFloat(ref partValue);
}
count++;
}
if (count != 6)
{
throw new FormatException("Matrix transforms must be in the format 'matrix(m11 m12 m21 m22 dx dy)'");
}
transformList.Add(new SvgMatrix(new List<float>(6) { m11, m12, m21, m22, dx, dy }));
}
break;
case TransformType.Shear:
{
int count = 0;
var hx = default(float);
var hy = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
hx = ToFloat(ref partValue);
}
else if (count == 1)
{
hy = ToFloat(ref partValue);
}
count++;
}
if (count == 0 || count > 2)
{
throw new FormatException("Shear transforms must be in the format 'shear(x [y])'");
}
transformList.Add(count > 1 ? new SvgShear(hx, hy) : new SvgShear(hx));
}
break;
case TransformType.SkewX:
{
int count = 0;
var ax = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
ax = ToFloat(ref partValue);
}
count++;
}
if (count != 1)
{
throw new FormatException("SkewX transforms must be in the format 'skewX(a)'");
}
transformList.Add(new SvgSkew(ax, 0f));
}
break;
case TransformType.SkewY:
{
int count = 0;
var ay = default(float);
foreach (var part in parts)
{
var partValue = part.Value;
if (count == 0)
{
ay = ToFloat(ref partValue);
}
count++;
}
if (count != 1)
{
throw new FormatException("SkewY transforms must be in the format 'skewY(a)'");
}
transformList.Add(new SvgSkew(0f, ay));
}
break;
}
currentIndex = endIndex;
if (currentIndex + 1 > sourceLength)
{
break;
}
source = source.Slice(currentIndex + 1, sourceLength - currentIndex - 1).TrimStart();
sourceLength = source.Length;
if (sourceLength <= 0)
{
break;
}
}
return transformList;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value is SvgTransformCollection)
return string.Join(" ", ((SvgTransformCollection)value).Select(t => t.WriteToString()).ToArray());
if (destinationType == typeof(string) && value is SvgTransformCollection collection)
return string.Join(" ", collection.Select(t => t.WriteToString()).ToArray());
return base.ConvertTo(context, culture, value, destinationType);
}

Просмотреть файл

@ -0,0 +1,28 @@
using System;
using System.Text;
using BenchmarkDotNet.Attributes;
using Svg;
namespace Svg.Benchmarks
{
public class CoordinateParserBenchmarks
{
[Benchmark]
public void CoordinateParser_TryGetBool()
{
var chars = "false".AsSpan().Trim();
var state = new CoordinateParserState(ref chars);
CoordinateParser.TryGetBool(out var result, ref chars, ref state);
}
[Benchmark]
public void CoordinateParser_TryGetFloat_Points()
{
var chars = "1.6,3.2 1.2,5".AsSpan().Trim();
var state = new CoordinateParserState(ref chars);
while (CoordinateParser.TryGetFloat(out var result, ref chars, ref state))
{
}
}
}
}

Просмотреть файл

@ -0,0 +1,40 @@
using System.Linq;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Validators;
using BenchmarkDotNet.Environments;
namespace Svg.Benchmarks
{
class Program
{
public static void Main(string[] args)
{
var types = typeof(Program)
.Assembly
.GetExportedTypes()
.Where(r => r != typeof(Program))
.OrderBy(r => r.Name);
var job = Job.Default;
var config = new ManualConfig();
config.AddLogger(DefaultConfig.Instance.GetLoggers().ToArray());
config.AddExporter(DefaultConfig.Instance.GetExporters().ToArray());
config.AddColumnProvider(DefaultConfig.Instance.GetColumnProviders().ToArray());
config.AddValidator(JitOptimizationsValidator.DontFailOnError);
config.AddJob(job.WithRuntime(CoreRuntime.Core31));
//config.AddJob(job.WithRuntime(CoreRuntime.Core22));
//config.AddJob(job.WithRuntime(ClrRuntime.Net461));
config.AddDiagnoser(MemoryDiagnoser.Default);
config.AddColumn(StatisticColumn.OperationsPerSecond);
config.AddColumn(RankColumn.Arabic);
var switcher = new BenchmarkSwitcher(types.ToArray());
switcher.Run(args, config);
}
}
}

Просмотреть файл

@ -0,0 +1,45 @@
# SVG Benchmarks
### Run `All` Benchmarks
```
dotnet run -c Release -f netcoreapp3.1 -- -f '*'
```
### Run `SvgDocument` Benchmarks
```
dotnet run -c Release -f netcoreapp3.1 -- -f '*SvgDocument_*'
```
### Run `SvgPathBuilder` Benchmarks
```
dotnet run -c Release -f netcoreapp3.1 -- -f '*SvgPathBuilder_*'
```
### Run `CoordinateParser` Benchmarks
```
dotnet run -c Release -f netcoreapp3.1 -- -f '*CoordinateParser_*'
```
### Run `SvgTransformConverter` Benchmarks
```
dotnet run -c Release -f netcoreapp3.1 -- -f '*SvgTransformConverter_*'
```
### TODO
- EnumBaseConverter
- SvgPreserveAspectRatioConverter
- SvgNumberCollectionConverter
- SvgOrientConverter
- SvgPointCollectionConverter
- SvgUnitCollectionConverter
- SvgUnitConverter
- SvgViewBoxConverter
- SvgPaintServerFactory
- SvgPathBuilder
- ColorConverter

Просмотреть файл

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.2;netcoreapp3.1;net461</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="../W3CTestSuite/svg/__AJ_Digital_Camera.svg" />
<EmbeddedResource Include="../W3CTestSuite/svg/__issue-134-01.svg" />
<EmbeddedResource Include="../W3CTestSuite/svg/__tiger.svg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Source\Svg.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp2.2'">
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452' Or '$(TargetFramework)'=='net461'">
<Reference Include="WindowsBase" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,32 @@
using BenchmarkDotNet.Attributes;
using System.IO;
using Svg;
namespace Svg.Benchmarks
{
public class SvgDocumentBenchmarks
{
private Stream Open(string name) => typeof(Program).Assembly.GetManifestResourceStream($"Svg.Benchmark.{name}");
[Benchmark]
public void SvgDocument_Open_AJ_Digital_Camera()
{
using var stream = Open("__AJ_Digital_Camera.svg");
SvgDocument.Open<SvgDocument>(stream);
}
[Benchmark]
public void SvgDocument_Open_Issue_134_01()
{
using var stream = Open("__issue-134-01.svg");
SvgDocument.Open<SvgDocument>(stream);
}
[Benchmark]
public void SvgDocument_Open_Tiger()
{
using var stream = Open("__tiger.svg");
SvgDocument.Open<SvgDocument>(stream);
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using BenchmarkDotNet.Attributes;
using Svg;
namespace Svg.Benchmarks
{
public class SvgPathBuilderBenchmarks
{
[Benchmark]
public void SvgPathBuilder_Parse_Small()
{
SvgPathBuilder.Parse(@"M0,0 L4,2 L0,4 L1,2 z");
}
[Benchmark]
public void SvgPathBuilder_Parse_Large()
{
SvgPathBuilder.Parse(@"m -129.83,103.065 c 0.503,6.048 1.491,12.617 3.23,15.736 0,0 -3.6,12.4 5.2,25.6 0,0 -0.4,7.2 1.2,10.4 0,0 4,8.4 8.8,9.2 3.884,0.647 12.607,3.716 22.468,5.12 0,0 17.132,14.08 13.932,26.88 0,0 -0.4,16.4 -4,18 0,0 11.6,-11.2 2,5.6 l -4.4,18.8 c 0,0 25.6,-21.6 10,-3.2 l -10,26 c 0,0 19.6,-18.4 12.4,-10 l -3.2,8.8 c 0,0 43.2,-27.2 12.4,2.4 0,0 8,-3.6 12.4,-0.8 0,0 6.8,-1.2 6,0.4 0,0 -20.8,10.4 -24.4,28.8 0,0 8.4,-10 5.2,0.8 l 0.4,11.6 c 0,0 4,-21.6 3.6,16 0,0 19.2,-18 7.6,2.8 l 0,16.8 c 0,0 15.2,-16.4 8.8,-3.6 0,0 10,-8.8 6,6.4 0,0 -0.8,10.4 3.6,-0.8 0,0 16,-30.6 10,-4.4 0,0 -0.8,19.2 4,4.4 0,0 0.4,10.4 9.6,17.6 0,0 -1.2,-50.8 11.6,-14.8 l 4,16.4 c 0,0 2.8,-9.2 2.4,-14.4 0,0 14.8,-16.4 8,8 0,0 15.2,-22.8 12,-9.6 0,0 -7.6,16 -6,20.8 0,0 16.8,-34.8 18,-36.4 0,0 -2,42.401 8.8,6.4 0,0 5.6,12 2.8,16.4 0,0 8,-8 7.2,-11.2 0,0 4.6,-8.2 7.4,5.4 0,0 1.8,9.4 3.4,6.2 0,0 4,24.001 5.2,1.2 0,0 1.6,-13.6 -5.6,-25.2 0,0 0.8,-3.2 -2,-7.2 0,0 13.6,21.6 6.4,-7.2 0,0 11.201,8 12.401,8 0,0 -13.601,-23.2 -4.801,-18.4 0,0 -5.2,-10.4 12.801,1.6 0,0 -16.001,-16 1.6,-6.4 0,0 8,6.4 0.4,-3.6 0,0 -14.401,-16 7.6,2 0,0 11.6,16.4 12.4,19.2 0,0 -10,-29.2 -14.4,-32 0,0 8.4,-36.4 49.6,-20.8 0,0 6.8,17.2 11.2,-1.2 0,0 12.8,-6.4 24,21.2 0,0 4,-13.6 3.2,-16.4 0,0 6.8,1.2 6,0 0,0 13.2,4.4 14.4,3.6 0,0 6.8,6.8 7.2,3.2 0,0 9.2,2.8 7.2,-0.8 0,0 8.8,15.6 9.2,19.2 l 2.4,-14 2,2.8 c 0,0 1.6,-7.6 0.8,-8.8 -0.8,-1.2 20,6.8 24.8,27.6 l 2,8.4 c 0,0 6,-14.8 4.4,-18.8 0,0 5.2,0.8 5.6,5.2 0,0 4,-23.2 -0.8,-29.2 0,0 4.4,-0.8 5.6,2.8 l 0,-7.2 c 0,0 7.2,0.8 7.2,-1.6 0,0 4.4,-4 6.4,0.8 0,0 -12.4,-35.2 6,-16 0,0 7.2,10.8 3.6,-8 -3.6,-18.8 -7.6,-20.4 -2.8,-20.8 0,0 0.8,-3.6 -1.2,-5.2 -2,-1.6 1.2,0 1.2,0 0,0 4.8,4 -0.4,-18 0,0 6.4,1.6 -5.6,-27.6 0,0 2.8,-2.4 -1.2,-10.8 0,0 8,4.4 10.8,2.8 0,0 -0.4,-1.6 -3.6,-5.6 0,0 -21.6,-54.801 -1.2,-32.8 0,0 11.85,13.55 5.45,-9.25 0,0 -9.111,-24.01 -8.334,-28.306 l -429.547,23.02 z");
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
using System.Globalization;
using BenchmarkDotNet.Attributes;
using Svg;
using Svg.Transforms;
namespace Svg.Benchmarks
{
public class SvgTransformConverterBenchmarks
{
private static SvgTransformConverter _converter = new SvgTransformConverter();
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Matrix_1()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "matrix(252,0,0,252,7560,11340)");
}
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Matrix_2()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "matrix(-4.37114e-08,1,-1,-4.37114e-08,181,409.496)");
}
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Matrix_3()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "matrix(0.74811711,0.48689734,-0.42145482,0.93331568,324.55155,94.282562)");
}
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Matrix_4()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "matrix(0.879978,0.475015,-0.475015,0.879978,120.2732,-136.2899)");
}
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Rotate_Translate()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "rotate(180), translate(-50, 0)");
}
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Translate_Rotate()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "translate(9, 241) rotate(-90)");
}
[Benchmark]
public void SvgTransformConverter_ConvertFrom_Matrix_Rotate_Scale()
{
_converter.ConvertFrom(null, CultureInfo.InvariantCulture, "rotate(180 2.5 2.5) scale(0.7142857142857143,0.7142857142857143)");
}
}
}

Просмотреть файл

@ -10,6 +10,7 @@ namespace Svg.UnitTests
public class EnumConvertersTests
{
[Test]
[TestCase(typeof(XmlSpaceHandlingConverter), "default", "inherit", "preserve")]
[TestCase(typeof(SvgFillRuleConverter), "nonzero", "evenodd", "inherit")]
[TestCase(typeof(SvgColourInterpolationConverter), "auto", "sRGB", "linearRGB", "inherit")]
[TestCase(typeof(SvgClipRuleConverter), "nonzero", "evenodd", "inherit")]

Просмотреть файл

@ -30,7 +30,6 @@ namespace Svg.UnitTests
/// So I can move down the timeout to 15 seconds
/// </summary>
[Test]
[Timeout(15000)]
public void LoadAllW3CSvg()
{
var svgPath = Path.Combine(AssemblyDirectory, "..", "..", "..", "..", "W3CTestSuite", "svg");

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<TargetFrameworks>netcoreapp2.2;net452</TargetFrameworks>
<TargetFrameworks>netcoreapp2.2;net452;net461</TargetFrameworks>
<IsPackable>false</IsPackable>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>svgkey.snk</AssemblyOriginatorKeyFile>
@ -17,6 +17,12 @@
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net452|AnyCPU'">
<DefineConstants>TRACE;RELEASE;NETFULL;NET452</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net461|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NETFULL;NET461</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net461|AnyCPU'">
<DefineConstants>TRACE;RELEASE;NETFULL;NET461</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.2|AnyCPU'">
<DefineConstants>TRACE;RELEASE;NETCORE</DefineConstants>
</PropertyGroup>
@ -63,7 +69,7 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
<ItemGroup Condition="'$(TargetFramework)'=='net452' Or '$(TargetFramework)'=='net461'">
<Reference Include="WindowsBase" />
</ItemGroup>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше