зеркало из https://github.com/wieslawsoltes/SVG.git
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:
Родитель
2d3d9f361d
Коммит
e570088730
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче