This commit is contained in:
Scott Williams 2019-02-07 21:45:39 +00:00
Родитель f41d627f7e
Коммит aafea14480
13 изменённых файлов: 998 добавлений и 259 удалений

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

@ -1,5 +1,9 @@
<?xml version="1.0" standalone="no"?>
<svg width="400" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g fill="red" >
<rect x="10" y="100" width="90" height="90" stroke="black" stroke-width="15"/>
<rect x="160" y="100" rx="20" ry="20" width="90" height="90" fill-opacity="0.5" stroke="black" stroke-width="15"/>
</g>
<rect x="10" y="10" width="90" height="90" stroke="black" fill="none" stroke-width="15"/>
<rect x="160" y="10" rx="20" ry="20" width="90" height="90" fill="none" stroke="black" stroke-width="15"/>
@ -14,5 +18,5 @@
<polygon points="50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180"
stroke="green" fill="transparent" stroke-width="5"/>
<path d="M20,230 Q40,205 50,230 T90,230" fill="none" stroke="blue" stroke-width="5"/>
<path d="M20,230 Q40,205 50,230 T90,230" fill="red" stroke="blue" stroke-width="5"/>
</svg>

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

@ -0,0 +1,86 @@
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
using SixLabors.Shapes;
using AngleSharp.Dom;
using System.Linq;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.Svg.Dom
{
internal static class Extensions
{
public static IPath GenerateStroke<TPixel>(this IPath path, Image<TPixel> img, ISvgShape shape) where TPixel : struct, IPixel<TPixel>
{
var strokeWidth = shape.StrokeWidth.AsPixelXAxis(img);
if (strokeWidth == 0)
{
return null;
}
return Outliner.GenerateOutline(path, strokeWidth, shape.StrokeLineJoin.Style, shape.StrokeLineCap.Style);
}
public static string GetAttributeValueSelfOrGroup(this ISvgElement element, string attribute)
{
var val = element.Attributes[attribute]?.Value;
if (val == null)
{
val = element.Ancestors<ISvgElement>().Where(x => x.TagName == "g").FirstOrDefault()?.GetAttributeValueSelfOrGroup(attribute);
}
return val;
}
public static SvgPaint GetPaint(this ISvgElement element, string attribute, string defaultPaintValue, string defaultOpacityValue)
{
var paint = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultPaintValue;
var opacity = element.GetAttributeValueSelfOrGroup(attribute + "-opacity") ?? defaultOpacityValue;
return SvgPaint.Parse(paint, opacity);
}
public static SvgUnitValue GetUnitValue(this ISvgElement element, string attribute, string defaultValue = null)
{
var val = element.TryGetUnitValue(attribute, defaultValue);
return val ?? SvgUnitValue.Unset;
}
public static SvgUnitValue? TryGetUnitValue(this ISvgElement element, string attribute, string defaultValue = null)
{
var val = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultValue;
return SvgUnitValue.Parse(val);
}
public static SvgLineCap GetLineCap(this ISvgElement element, string attribute, string defaultValue = null)
{
var val = element.TryGetLineCap(attribute, defaultValue);
return val ?? SvgLineCap.Unset;
}
public static SvgLineCap? TryGetLineCap(this ISvgElement element, string attribute, string defaultValue = null)
{
var val = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultValue;
return SvgLineCap.Parse(val);
}
public static SvgLineJoin GetLineJoin(this ISvgElement element, string attribute, string defaultValue = null)
{
var val = element.TryGetLineJoin(attribute, defaultValue);
return val ?? SvgLineJoin.Unset;
}
public static SvgLineJoin? TryGetLineJoin(this ISvgElement element, string attribute, string defaultValue = null)
{
var val = element.GetAttributeValueSelfOrGroup(attribute) ?? defaultValue;
return SvgLineJoin.Parse(val);
}
}
}

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

@ -0,0 +1,11 @@
namespace SixLabors.Svg.Dom
{
internal interface ISvgShape
{
SvgPaint Fill { get; }
SvgPaint Stroke { get; }
SvgUnitValue StrokeWidth { get; }
SvgLineCap StrokeLineCap { get; }
SvgLineJoin StrokeLineJoin { get; }
}
}

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

@ -30,10 +30,10 @@ namespace SixLabors.Svg.Dom
{
var document = new SvgDocument()
{
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
X = element.GetUnitValue("x", "0"),
Y = element.GetUnitValue("y", "0"),
Width = element.GetUnitValue("width"),
Height = element.GetUnitValue("height")
};
var children = element.Children.OfType<ISvgElement>();

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

@ -21,6 +21,11 @@ namespace SixLabors.Svg.Dom
case "circle":
case "ellipse":
return await SvgEllipse.LoadAsync(element);
case "line":
case "polyline":
case "polygon":
case "path":
return await SvgPath.LoadAsync(element);
default:
return null;
}

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

@ -3,11 +3,10 @@ using System.Threading.Tasks;
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
namespace SixLabors.Svg.Dom
{
internal sealed class SvgEllipse : SvgElement
internal sealed class SvgEllipse : SvgElement, ISvgShape
{
public SvgUnitValue X { get; private set; }
@ -17,25 +16,30 @@ namespace SixLabors.Svg.Dom
public SvgPaint Fill { get; private set; }
public SvgPaint Stroke { get; private set; }
public SvgUnitValue StrokeWidth { get; private set; }
public SvgLineCap StrokeLineCap { get; private set; }
public SvgLineJoin StrokeLineJoin { get; private set; }
public static Task<SvgElement> LoadAsync(ISvgElement element)
{
var ellipse = new SvgEllipse()
{
Fill = SvgPaint.Parse(element.Attributes["fill"]?.Value ?? "Black", element.Attributes["fill-opacity"]?.Value ?? "1"),
Stroke = SvgPaint.Parse(element.Attributes["stroke"]?.Value ?? "None", element.Attributes["stroke-opacity"]?.Value ?? "1"),
StrokeWidth = SvgUnitValue.Parse(element.Attributes["stroke-width"]?.Value ?? "1"),
X = SvgUnitValue.Parse(element.Attributes["cx"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["cy"]?.Value)
Fill = element.GetPaint("fill", "Black", "1"),
Stroke = element.GetPaint("stroke", "None", "1"),
StrokeWidth = element.GetUnitValue("stroke-width", "1"),
StrokeLineCap = element.GetLineCap("stroke-linecap", "butt"),
StrokeLineJoin = element.GetLineJoin("stroke-linejoin", "miter"),
X = element.GetUnitValue("cx"),
Y = element.GetUnitValue("cy"),
};
if (element.TagName == "circle")
{
ellipse.RadiusY = ellipse.RadiusX = SvgUnitValue.Parse(element.Attributes["r"]?.Value ?? "0");
ellipse.RadiusY = ellipse.RadiusX = element.GetUnitValue("r", "0");
}
else
{
ellipse.RadiusY = SvgUnitValue.Parse(element.Attributes["ry"]?.Value ?? "0");
ellipse.RadiusX = SvgUnitValue.Parse(element.Attributes["rx"]?.Value ?? "0");
ellipse.RadiusX = element.GetUnitValue("rx", "0");
ellipse.RadiusY = element.GetUnitValue("ry", "0");
}
return Task.FromResult<SvgElement>(ellipse);
@ -47,18 +51,20 @@ namespace SixLabors.Svg.Dom
var fillBrush = Fill.AsBrush<TPixel>();
var strokeBrush = Stroke.AsBrush<TPixel>();
var strokeWidth = this.StrokeWidth.AsPixelXAxis(image);
image.Mutate(x =>
{
if (fillBrush != null)
{
x = x.Fill(fillBrush, rect);
}
if (strokeBrush != null && strokeWidth > 0)
if (strokeBrush != null)
{
var outline = rect.GenerateStroke(image, this);
if (outline != null)
{
var outline = Outliner.GenerateOutline(rect, strokeWidth);
x = x.Fill(strokeBrush, outline);
}
}
});
}

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

@ -0,0 +1,231 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
namespace SixLabors.Svg.Dom
{
internal struct SvgPaint
{
public static readonly SvgPaint Unset = default(SvgPaint);
internal SvgPaint(string value, float opacity)
{
Value = value;
Opacity = opacity;
}
public string Value { get; }
public float Opacity { get; }
public static SvgPaint Parse(string val, string opacity)
{
val = val?.Trim() ?? "";
opacity = opacity?.Trim() ?? "";
float opacityVal = 1;
if (float.TryParse(opacity, out float op))
{
opacityVal = op;
if (opacityVal < 0)
{
opacityVal = 0;
}
if (opacityVal > 1)
{
opacityVal = 1;
}
}
return new SvgPaint(val, opacityVal);
}
private TPixel? GetNamedColor<TPixel>(string name) where TPixel : struct, IPixel<TPixel>
{
var dict = new Dictionary<string, TPixel>(StringComparer.OrdinalIgnoreCase)
{
["AliceBlue"] = NamedColors<TPixel>.AliceBlue,
["MistyRose"] = NamedColors<TPixel>.MistyRose,
["Moccasin"] = NamedColors<TPixel>.Moccasin,
["NavajoWhite"] = NamedColors<TPixel>.NavajoWhite,
["Navy"] = NamedColors<TPixel>.Navy,
["OldLace"] = NamedColors<TPixel>.OldLace,
["Olive"] = NamedColors<TPixel>.Olive,
["MintCream"] = NamedColors<TPixel>.MintCream,
["OliveDrab"] = NamedColors<TPixel>.OliveDrab,
["OrangeRed"] = NamedColors<TPixel>.OrangeRed,
["Orchid"] = NamedColors<TPixel>.Orchid,
["PaleGoldenrod"] = NamedColors<TPixel>.PaleGoldenrod,
["PaleGreen"] = NamedColors<TPixel>.PaleGreen,
["PaleTurquoise"] = NamedColors<TPixel>.PaleTurquoise,
["PaleVioletRed"] = NamedColors<TPixel>.PaleVioletRed,
["Orange"] = NamedColors<TPixel>.Orange,
["PapayaWhip"] = NamedColors<TPixel>.PapayaWhip,
["MidnightBlue"] = NamedColors<TPixel>.MidnightBlue,
["MediumTurquoise"] = NamedColors<TPixel>.MediumTurquoise,
["LightSteelBlue"] = NamedColors<TPixel>.LightSteelBlue,
["LightYellow"] = NamedColors<TPixel>.LightYellow,
["Lime"] = NamedColors<TPixel>.Lime,
["LimeGreen"] = NamedColors<TPixel>.LimeGreen,
["Linen"] = NamedColors<TPixel>.Linen,
["Magenta"] = NamedColors<TPixel>.Magenta,
["MediumVioletRed"] = NamedColors<TPixel>.MediumVioletRed,
["Maroon"] = NamedColors<TPixel>.Maroon,
["MediumBlue"] = NamedColors<TPixel>.MediumBlue,
["MediumOrchid"] = NamedColors<TPixel>.MediumOrchid,
["MediumPurple"] = NamedColors<TPixel>.MediumPurple,
["MediumSeaGreen"] = NamedColors<TPixel>.MediumSeaGreen,
["MediumSlateBlue"] = NamedColors<TPixel>.MediumSlateBlue,
["MediumSpringGreen"] = NamedColors<TPixel>.MediumSpringGreen,
["MediumAquamarine"] = NamedColors<TPixel>.MediumAquamarine,
["LightSlateGray"] = NamedColors<TPixel>.LightSlateGray,
["PeachPuff"] = NamedColors<TPixel>.PeachPuff,
["Pink"] = NamedColors<TPixel>.Pink,
["SpringGreen"] = NamedColors<TPixel>.SpringGreen,
["SteelBlue"] = NamedColors<TPixel>.SteelBlue,
["Tan"] = NamedColors<TPixel>.Tan,
["Teal"] = NamedColors<TPixel>.Teal,
["Thistle"] = NamedColors<TPixel>.Thistle,
["Tomato"] = NamedColors<TPixel>.Tomato,
["Snow"] = NamedColors<TPixel>.Snow,
["Transparent"] = NamedColors<TPixel>.Transparent,
["Violet"] = NamedColors<TPixel>.Violet,
["Wheat"] = NamedColors<TPixel>.Wheat,
["White"] = NamedColors<TPixel>.White,
["WhiteSmoke"] = NamedColors<TPixel>.WhiteSmoke,
["Yellow"] = NamedColors<TPixel>.Yellow,
["YellowGreen"] = NamedColors<TPixel>.YellowGreen,
["Turquoise"] = NamedColors<TPixel>.Turquoise,
["Peru"] = NamedColors<TPixel>.Peru,
["SlateGray"] = NamedColors<TPixel>.SlateGray,
["SkyBlue"] = NamedColors<TPixel>.SkyBlue,
["Plum"] = NamedColors<TPixel>.Plum,
["PowderBlue"] = NamedColors<TPixel>.PowderBlue,
["Purple"] = NamedColors<TPixel>.Purple,
["RebeccaPurple"] = NamedColors<TPixel>.RebeccaPurple,
["Red"] = NamedColors<TPixel>.Red,
["RosyBrown"] = NamedColors<TPixel>.RosyBrown,
["SlateBlue"] = NamedColors<TPixel>.SlateBlue,
["RoyalBlue"] = NamedColors<TPixel>.RoyalBlue,
["Salmon"] = NamedColors<TPixel>.Salmon,
["SandyBrown"] = NamedColors<TPixel>.SandyBrown,
["SeaGreen"] = NamedColors<TPixel>.SeaGreen,
["SeaShell"] = NamedColors<TPixel>.SeaShell,
["Sienna"] = NamedColors<TPixel>.Sienna,
["Silver"] = NamedColors<TPixel>.Silver,
["SaddleBrown"] = NamedColors<TPixel>.SaddleBrown,
["LightSkyBlue"] = NamedColors<TPixel>.LightSkyBlue,
["LightSeaGreen"] = NamedColors<TPixel>.LightSeaGreen,
["LightSalmon"] = NamedColors<TPixel>.LightSalmon,
["Crimson"] = NamedColors<TPixel>.Crimson,
["Cyan"] = NamedColors<TPixel>.Cyan,
["DarkBlue"] = NamedColors<TPixel>.DarkBlue,
["DarkCyan"] = NamedColors<TPixel>.DarkCyan,
["DarkGoldenrod"] = NamedColors<TPixel>.DarkGoldenrod,
["DarkGray"] = NamedColors<TPixel>.DarkGray,
["Cornsilk"] = NamedColors<TPixel>.Cornsilk,
["DarkGreen"] = NamedColors<TPixel>.DarkGreen,
["DarkMagenta"] = NamedColors<TPixel>.DarkMagenta,
["DarkOliveGreen"] = NamedColors<TPixel>.DarkOliveGreen,
["DarkOrange"] = NamedColors<TPixel>.DarkOrange,
["DarkOrchid"] = NamedColors<TPixel>.DarkOrchid,
["DarkRed"] = NamedColors<TPixel>.DarkRed,
["DarkSalmon"] = NamedColors<TPixel>.DarkSalmon,
["DarkKhaki"] = NamedColors<TPixel>.DarkKhaki,
["DarkSeaGreen"] = NamedColors<TPixel>.DarkSeaGreen,
["CornflowerBlue"] = NamedColors<TPixel>.CornflowerBlue,
["Chocolate"] = NamedColors<TPixel>.Chocolate,
["AntiqueWhite"] = NamedColors<TPixel>.AntiqueWhite,
["Aqua"] = NamedColors<TPixel>.Aqua,
["Aquamarine"] = NamedColors<TPixel>.Aquamarine,
["Azure"] = NamedColors<TPixel>.Azure,
["Beige"] = NamedColors<TPixel>.Beige,
["Bisque"] = NamedColors<TPixel>.Bisque,
["Coral"] = NamedColors<TPixel>.Coral,
["Black"] = NamedColors<TPixel>.Black,
["Blue"] = NamedColors<TPixel>.Blue,
["BlueViolet"] = NamedColors<TPixel>.BlueViolet,
["Brown"] = NamedColors<TPixel>.Brown,
["BurlyWood"] = NamedColors<TPixel>.BurlyWood,
["CadetBlue"] = NamedColors<TPixel>.CadetBlue,
["Chartreuse"] = NamedColors<TPixel>.Chartreuse,
["BlanchedAlmond"] = NamedColors<TPixel>.BlanchedAlmond,
["DarkSlateBlue"] = NamedColors<TPixel>.DarkSlateBlue,
["DarkSlateGray"] = NamedColors<TPixel>.DarkSlateGray,
["DarkTurquoise"] = NamedColors<TPixel>.DarkTurquoise,
["Indigo"] = NamedColors<TPixel>.Indigo,
["Ivory"] = NamedColors<TPixel>.Ivory,
["Khaki"] = NamedColors<TPixel>.Khaki,
["Lavender"] = NamedColors<TPixel>.Lavender,
["LavenderBlush"] = NamedColors<TPixel>.LavenderBlush,
["LawnGreen"] = NamedColors<TPixel>.LawnGreen,
["IndianRed"] = NamedColors<TPixel>.IndianRed,
["LemonChiffon"] = NamedColors<TPixel>.LemonChiffon,
["LightCoral"] = NamedColors<TPixel>.LightCoral,
["LightCyan"] = NamedColors<TPixel>.LightCyan,
["LightGoldenrodYellow"] = NamedColors<TPixel>.LightGoldenrodYellow,
["LightGray"] = NamedColors<TPixel>.LightGray,
["LightGreen"] = NamedColors<TPixel>.LightGreen,
["LightPink"] = NamedColors<TPixel>.LightPink,
["LightBlue"] = NamedColors<TPixel>.LightBlue,
["HotPink"] = NamedColors<TPixel>.HotPink,
["Honeydew"] = NamedColors<TPixel>.Honeydew,
["GreenYellow"] = NamedColors<TPixel>.GreenYellow,
["DarkViolet"] = NamedColors<TPixel>.DarkViolet,
["DeepPink"] = NamedColors<TPixel>.DeepPink,
["DeepSkyBlue"] = NamedColors<TPixel>.DeepSkyBlue,
["DimGray"] = NamedColors<TPixel>.DimGray,
["DodgerBlue"] = NamedColors<TPixel>.DodgerBlue,
["Firebrick"] = NamedColors<TPixel>.Firebrick,
["FloralWhite"] = NamedColors<TPixel>.FloralWhite,
["ForestGreen"] = NamedColors<TPixel>.ForestGreen,
["Fuchsia"] = NamedColors<TPixel>.Fuchsia,
["Gainsboro"] = NamedColors<TPixel>.Gainsboro,
["GhostWhite"] = NamedColors<TPixel>.GhostWhite,
["Gold"] = NamedColors<TPixel>.Gold,
["Goldenrod"] = NamedColors<TPixel>.Goldenrod,
["Gray"] = NamedColors<TPixel>.Gray,
["Green"] = NamedColors<TPixel>.Green,
};
if (dict.TryGetValue(name, out var pixel))
{
return pixel;
}
return null;
}
public IBrush<TPixel> AsBrush<TPixel>() where TPixel : struct, IPixel<TPixel>
{
// lets asume the brush is a color for now
// TODO update ColourBuilder to expose named colors
var value = this.Value;
if (string.IsNullOrWhiteSpace(value) || value.Equals("None", StringComparison.OrdinalIgnoreCase))
{
// if a null prush is returned we should skip
return null;
}
var color = GetNamedColor<TPixel>(Value) ?? ColorBuilder<TPixel>.FromHex(value);
if(Opacity == 0)
{
// no-op push here to efficienty sake
color = NamedColors<TPixel>.Transparent;
} else if (Opacity != 1)
{
var vector = color.ToScaledVector4();
vector.W *= Opacity;
color.FromScaledVector4(vector);
}
return new SolidBrush<TPixel>(color);
}
}
}

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

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AngleSharp.Svg.Dom;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
namespace SixLabors.Svg.Dom
{
internal sealed class SvgPath : SvgElement, ISvgShape
{
public IPath Path { get; private set; }
public SvgPaint Fill { get; private set; }
public SvgPaint Stroke { get; private set; }
public SvgUnitValue StrokeWidth { get; private set; }
public SvgLineCap StrokeLineCap { get; private set; }
public SvgLineJoin StrokeLineJoin { get; private set; }
public IEnumerable<SvgPathOperation> PathOperations { get; private set; }
public static Task<SvgElement> LoadAsync(ISvgElement element)
{
var path = new SvgPath()
{
Fill = element.GetPaint("fill", "Black", "1"),
Stroke = element.GetPaint("stroke", "None", "1"),
StrokeWidth = element.GetUnitValue("stroke-width", "1"),
StrokeLineCap = element.GetLineCap("stroke-linecap", "butt"),
StrokeLineJoin = element.GetLineJoin("stroke-linejoin", "miter"),
};
if (element.TagName == "line")
{
path.PathOperations = new[] {
SvgPathOperation.MoveTo(element.GetUnitValue("x1", "0"), element.GetUnitValue("y1", "0"), false),
SvgPathOperation.LineTo(element.GetUnitValue("x2", "0"), element.GetUnitValue("y2", "0"), false),
};
}
else if (element.TagName == "polyline" || element.TagName == "polygon")
{
var pathData = element.GetAttributeValueSelfOrGroup("points");
path.PathOperations = ParsePoints(pathData, element.TagName == "polygon");
}
else if (element.TagName == "path")
{
var pathData = element.GetAttributeValueSelfOrGroup("d");
path.PathOperations = SvgPathOperation.Parse(pathData);
}
if (path.PathOperations == null)
{
return Task.FromResult<SvgElement>(null);
}
return Task.FromResult<SvgElement>(path);
}
private static IEnumerable<SvgPathOperation> ParsePoints(string pathData, bool closePath)
{
if (pathData != null && !pathData.Equals("none", System.StringComparison.OrdinalIgnoreCase))
{
var parts = pathData.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x =>
{
if (int.TryParse(x, out var i))
{
return i;
}
else
{ return (int?)null; }
}).Where(x => x != null).Select(x => x.Value).ToArray();
if (parts.Length > 4)
{
var len = parts.Length / 2;
if (closePath)
{
len++;
}
var ops = new SvgPathOperation[len];
ops[0] = SvgPathOperation.MoveTo(new SvgUnitValue(parts[0], SvgUnitValue.Units.undefined), new SvgUnitValue(parts[1], SvgUnitValue.Units.undefined), false);
for (var i = 2; i < parts.Length; i += 2)
{
ops[i / 2] = SvgPathOperation.LineTo(new SvgUnitValue(parts[i], SvgUnitValue.Units.undefined), new SvgUnitValue(parts[i + 1], SvgUnitValue.Units.undefined), false);
}
if (closePath)
{
ops[len - 1] = SvgPathOperation.ClosePath();
}
return ops;
}
}
return null;
}
internal override void RenderTo<TPixel>(Image<TPixel> image)
{
var rect = this.PathOperations.GeneratePath(image);
var fillBrush = Fill.AsBrush<TPixel>();
var strokeBrush = Stroke.AsBrush<TPixel>();
image.Mutate(x =>
{
if (fillBrush != null)
{
x = x.Fill(fillBrush, rect);
}
if (strokeBrush != null)
{
var outline = rect.GenerateStroke(image, this);
if (outline != null)
{
x = x.Fill(strokeBrush, outline);
}
}
});
}
}
}

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

@ -0,0 +1,401 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SixLabors.Svg.Dom
{
internal struct SvgPathOperation
{
public Operations Op { get; private set; }
public bool IsRelative { get; private set; }
public SvgUnitValue? X { get; private set; }
public SvgUnitValue? Y { get; private set; }
public SvgUnitValue? X1 { get; private set; }
public SvgUnitValue? Y1 { get; private set; }
public SvgUnitValue? X2 { get; private set; }
public SvgUnitValue? Y2 { get; private set; }
public static IEnumerable<SvgPathOperation> Parse(string path)
{
List<SvgPathOperation> operations = new List<SvgPathOperation>();
char cmd = ' ';
for (var i = 0; i < path.Length; i++)
{
switch (path[i])
{
case 'Z':
case 'z':
{
operations.Add(SvgPathOperation.ClosePath());
}
break;
case 'M':
case 'm':
{
var relative = path[i] == 'm';
if (!TryReadFloat(in path, ref i, out var x) || !TryReadFloat(in path, ref i, out var y))
{
return null;
}
operations.Add(SvgPathOperation.MoveTo(x, y, relative));
while (TryReadFloat(in path, ref i, out x) && TryReadFloat(in path, ref i, out y))
{
operations.Add(SvgPathOperation.LineTo(x, y, relative));
}
}
break;
case 'L':
case 'l':
{
var relative = path[i] == 'l';
while (TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
{
operations.Add(SvgPathOperation.LineTo(x, y, relative));
}
}
break;
case 'Q':
case 'q':
{
var relative = path[i] == 'q';
while (TryReadFloat(in path, ref i, out var x1) && TryReadFloat(in path, ref i, out var y1)
&& TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
{
operations.Add(SvgPathOperation.QuadraticTo(x1, y1, x, y, relative));
}
}
break;
case 'T':
case 't':
{
var relative = path[i] == 't';
while (TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
{
SvgUnitValue x1;
SvgUnitValue y1;
var last = operations.Last();
if (last.Op != Operations.QuadraticTo)
{
x1 = last.X.Value;
y1 = last.Y.Value;
}
else
{
x1 = new SvgUnitValue(last.X.Value.Value + (last.X.Value.Value - last.X1.Value.Value), SvgUnitValue.Units.undefined);
y1 = new SvgUnitValue(last.Y.Value.Value + (last.Y.Value.Value - last.Y1.Value.Value), SvgUnitValue.Units.undefined);
}
operations.Add(SvgPathOperation.QuadraticTo(x1, y1, x, y, relative));
}
}
break;
case 'C':
case 'c':
{
var relative = path[i] == 'c';
while (TryReadFloat(in path, ref i, out var x1) && TryReadFloat(in path, ref i, out var y1)
&& TryReadFloat(in path, ref i, out var x2) && TryReadFloat(in path, ref i, out var y2)
&& TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y))
{
operations.Add(SvgPathOperation.CubicTo(x1, y1, x2, y2, x, y, relative));
}
}
break;
case 'S':
case 's':
{
var relative = path[i] == 's';
while (TryReadFloat(in path, ref i, out var x) && TryReadFloat(in path, ref i, out var y)
&& TryReadFloat(in path, ref i, out var x2) && TryReadFloat(in path, ref i, out var y2))
{
SvgUnitValue x1;
SvgUnitValue y1;
var last = operations.Last();
if (last.Op != Operations.CubicTo)
{
x1 = last.X.Value;
y1 = last.Y.Value;
}
else
{
x1 = new SvgUnitValue(last.X.Value.Value + (last.X.Value.Value - last.X2.Value.Value), SvgUnitValue.Units.undefined);
y1 = new SvgUnitValue(last.Y.Value.Value + (last.Y.Value.Value - last.Y2.Value.Value), SvgUnitValue.Units.undefined);
}
operations.Add(SvgPathOperation.CubicTo(x1, y1, x2, y2, x, y, relative));
}
}
break;
default:
if (char.IsWhiteSpace(path[i]) || path[i] == ',' || path[i] == ' ')
{
continue;
}
else
{
return null;
}
}
}
return operations;
}
private static bool TryReadFloat(in string str, ref int position, out SvgUnitValue value)
{
// look for the first character that can be ins float
int floatStart = 0;
for (position++; position < str.Length; position++)
{
if (floatStart == 0)
{
if (char.IsWhiteSpace(str[position]) || str[position] == ',')
{
continue;
}
// we can skip 'whitespace'
}
if (char.IsDigit(str[position]) || str[position] == '-' || str[position] == '.')
{
if (floatStart == 0) { floatStart = position; }
}
else
{
position--;
break;
}
}
if (floatStart == 0)
{
value = SvgUnitValue.Unset;
return false;
}
if (position == str.Length)
{
value = new SvgUnitValue(float.Parse(str.Substring(floatStart)), SvgUnitValue.Units.undefined);
}
else
{
value = new SvgUnitValue(float.Parse(str.Substring(floatStart, position - floatStart + 1)), SvgUnitValue.Units.undefined);
}
return true;
}
public enum Operations
{
MoveTo,
LineTo,
ClosePath,
QuadraticTo,
CubicTo
}
private static SvgPathOperation CubicTo(SvgUnitValue x1, SvgUnitValue y1, SvgUnitValue x2, SvgUnitValue y2, SvgUnitValue x, SvgUnitValue y, bool relative)
{
return new SvgPathOperation()
{
IsRelative = relative,
Op = Operations.CubicTo,
X = x,
Y = y,
X1 = x1,
Y1 = y1,
X2 = x2,
Y2 = y2
};
}
private static SvgPathOperation QuadraticTo(SvgUnitValue x1, SvgUnitValue y1, SvgUnitValue x, SvgUnitValue y, bool relative)
{
return new SvgPathOperation()
{
IsRelative = relative,
Op = Operations.QuadraticTo,
X = x,
Y = y,
X1 = x1,
Y1 = y1
};
}
public static SvgPathOperation MoveTo(SvgUnitValue x, SvgUnitValue y, bool relative)
{
return new SvgPathOperation()
{
IsRelative = relative,
Op = Operations.MoveTo,
X = x,
Y = y
};
}
public static SvgPathOperation LineTo(SvgUnitValue x, SvgUnitValue y, bool relative)
{
return new SvgPathOperation()
{
IsRelative = relative,
Op = Operations.LineTo,
X = x,
Y = y
};
}
internal static SvgPathOperation ClosePath()
{
return new SvgPathOperation()
{
Op = Operations.ClosePath,
};
}
}
internal static class SvgPathOperationExtensions
{
private static PointF Point<TPixel>(Image<TPixel> img, PointF currentPoint, SvgUnitValue x, SvgUnitValue y) where TPixel : struct, IPixel<TPixel>
{
var p = new PointF(x.AsPixelXAxis(img), y.AsPixelYAxis(img));
return currentPoint + p;
}
public static PointF PointMain<TPixel>(this SvgPathOperation operation, Image<TPixel> img, PointF currentPoint) where TPixel : struct, IPixel<TPixel>
{
return Point(img, operation.IsRelative ? currentPoint : default, operation.X.Value, operation.Y.Value);
}
public static PointF Point1<TPixel>(this SvgPathOperation operation, Image<TPixel> img, PointF currentPoint) where TPixel : struct, IPixel<TPixel>
{
return Point(img, operation.IsRelative ? currentPoint : default, operation.X1.Value, operation.Y1.Value);
}
public static PointF Point2<TPixel>(this SvgPathOperation operation, Image<TPixel> img, PointF currentPoint) where TPixel : struct, IPixel<TPixel>
{
return Point(img, operation.IsRelative ? currentPoint : default, operation.X2.Value, operation.Y2.Value);
}
public static IPath GeneratePath<TPixel>(this IEnumerable<SvgPathOperation> operations, Image<TPixel> img) where TPixel : struct, IPixel<TPixel>
{
var pb = new PathRenderer();
foreach (var op in operations)
{
switch (op.Op)
{
case SvgPathOperation.Operations.MoveTo:
pb.MoveTo(op.PointMain(img, pb.CurrentPoint));
break;
case SvgPathOperation.Operations.LineTo:
pb.LineTo(op.PointMain(img, pb.CurrentPoint));
break;
case SvgPathOperation.Operations.QuadraticTo:
pb.QuadraticBezierTo(op.Point1(img, pb.CurrentPoint), op.PointMain(img, pb.CurrentPoint));
break;
case SvgPathOperation.Operations.CubicTo:
pb.CubicBezierTo(op.Point1(img, pb.CurrentPoint), op.Point2(img, pb.CurrentPoint), op.PointMain(img, pb.CurrentPoint));
break;
case SvgPathOperation.Operations.ClosePath:
pb.Close();
break;
default:
break;
}
}
return pb.Path();
}
internal class PathRenderer
{
/// <summary>
/// The builder. TODO: Should this be a property?
/// </summary>
// ReSharper disable once InconsistentNaming
private readonly PathBuilder builder;
private readonly List<IPath> paths = new List<IPath>();
private PointF currentPoint = default(PointF);
private PointF initalPoint = default(PointF);
public PointF CurrentPoint => currentPoint;
public PointF InitalPoint => initalPoint;
/// <summary>
/// Initializes a new instance of the <see cref="BaseGlyphBuilder"/> class.
/// </summary>
public PathRenderer()
{
// glyphs are renderd realative to bottom left so invert the Y axis to allow it to render on top left origin surface
this.builder = new PathBuilder();
}
/// <summary>
/// Gets the paths that have been rendered by this.
/// </summary>
public IPath Path()
{
return this.builder.Build();
}
/// <summary>
/// Draws a cubic bezier from the current point to the <paramref name="point"/>
/// </summary>
/// <param name="secondControlPoint">The second control point.</param>
/// <param name="thirdControlPoint">The third control point.</param>
/// <param name="point">The point.</param>
public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
this.currentPoint = point;
}
/// <summary>
/// Draws a line from the current point to the <paramref name="point"/>.
/// </summary>
/// <param name="point">The point.</param>
public void LineTo(PointF point)
{
this.builder.AddLine(this.currentPoint, point);
this.currentPoint = point;
}
/// <summary>
/// Moves to current point to the supplied vector.
/// </summary>
/// <param name="point">The point.</param>
public void MoveTo(PointF point)
{
this.builder.StartFigure();
this.currentPoint = point;
this.initalPoint = point;
}
/// <summary>
/// Draws a quadratics bezier from the current point to the <paramref name="point"/>
/// </summary>
/// <param name="secondControlPoint">The second control point.</param>
/// <param name="point">The point.</param>
public void QuadraticBezierTo(PointF secondControlPoint, PointF point)
{
this.builder.AddBezier(this.currentPoint, secondControlPoint, point);
this.currentPoint = point;
}
internal void Close()
{
this.LineTo(initalPoint);
//this.builder.CloseFigure();
//this.currentPoint = this.initalPoint;
}
}
}
}

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

@ -2,38 +2,45 @@
using System.Numerics;
using System.Threading.Tasks;
using AngleSharp.Svg.Dom;
using AngleSharp.Css.Dom;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
namespace SixLabors.Svg.Dom
{
internal sealed class SvgRect : SvgElement
{
internal sealed class SvgRect : SvgElement, ISvgShape
{
public SvgUnitValue X { get; private set; }
public SvgUnitValue Y { get; private set; }
public SvgUnitValue Width { get; private set; }
public SvgUnitValue Height { get; private set; }
public SvgPaint Fill { get; private set; }
public SvgPaint Stroke { get; private set; }
public SvgLineCap StrokeLineCap { get; private set; }
public SvgLineJoin StrokeLineJoin { get; private set; }
public SvgUnitValue StrokeWidth { get; private set; }
public SvgUnitValue RadiusX { get; private set; }
public SvgUnitValue RadiusY { get; private set; }
public static Task<SvgElement> LoadAsync(ISvgElement element)
{
var rx = element.TryGetUnitValue("rx");
var ry = element.TryGetUnitValue("ry");
return Task.FromResult<SvgElement>(new SvgRect()
{
Fill = SvgPaint.Parse(element.Attributes["fill"]?.Value ?? "Black", element.Attributes["fill-opacity"]?.Value ?? "1"),
Stroke = SvgPaint.Parse(element.Attributes["stroke"]?.Value ?? "None", element.Attributes["stroke-opacity"]?.Value ?? "1"),
StrokeWidth = SvgUnitValue.Parse(element.Attributes["stroke-width"]?.Value ?? "1"),
X = SvgUnitValue.Parse(element.Attributes["x"]?.Value),
Y = SvgUnitValue.Parse(element.Attributes["y"]?.Value),
RadiusX = SvgUnitValue.Parse(element.Attributes["rx"]?.Value ?? element.Attributes["ry"]?.Value ?? "0"),
RadiusY = SvgUnitValue.Parse(element.Attributes["ry"]?.Value ?? element.Attributes["rx"]?.Value ?? "0"),
Width = SvgUnitValue.Parse(element.Attributes["width"]?.Value),
Height = SvgUnitValue.Parse(element.Attributes["height"]?.Value),
Fill = element.GetPaint("fill", "Black", "1"),
Stroke = element.GetPaint("stroke", "None", "1"),
StrokeWidth = element.GetUnitValue("stroke-width", "1"),
StrokeLineCap = element.GetLineCap("stroke-linecap", "butt"),
StrokeLineJoin = element.GetLineJoin("stroke-linejoin", "miter"),
X = element.GetUnitValue("x"),
Y = element.GetUnitValue("y"),
RadiusX = rx ?? ry ?? SvgUnitValue.Zero,
RadiusY = ry ?? rx ?? SvgUnitValue.Zero,
Width = element.GetUnitValue("width"),
Height = element.GetUnitValue("height")
});
}
@ -51,18 +58,20 @@ namespace SixLabors.Svg.Dom
var fillBrush = Fill.AsBrush<TPixel>();
var strokeBrush = Stroke.AsBrush<TPixel>();
var strokeWidth = this.StrokeWidth.AsPixelXAxis(image);
image.Mutate(x =>
{
if (fillBrush != null)
{
x = x.Fill(fillBrush, rect);
}
if (strokeBrush != null && strokeWidth > 0)
if (strokeBrush != null)
{
var outline = rect.GenerateStroke(image, this);
if (outline != null)
{
var outline = Outliner.GenerateOutline(rect, strokeWidth);
x = x.Fill(strokeBrush, outline);
}
}
});
}
@ -83,19 +92,15 @@ namespace SixLabors.Svg.Dom
//lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image
var center = new Vector2(imageWidth / 2F, imageHeight / 2F);
float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1;
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1;
float rightPos = imageWidth - cornerToptLeft.Bounds.Width ;
float bottomPos = imageHeight - cornerToptLeft.Bounds.Height ;
// move it across the widthof the image - the width of the shape
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos - 0.5f, 0);
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos - 0.5f);
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos - 0.5f, bottomPos - 0.5f);
IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0);
IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos);
IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos);
return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight);
}
}
}

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

@ -1,226 +1,18 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace SixLabors.Svg.Dom
{
internal struct SvgPaint
{
public static readonly SvgPaint Unset = default(SvgPaint);
internal SvgPaint(string value, float opacity)
{
Value = value;
Opacity = opacity;
}
public string Value { get; }
public float Opacity { get; }
public static SvgPaint Parse(string val, string opacity)
{
val = val?.Trim() ?? "";
opacity = opacity?.Trim() ?? "";
float opacityVal = 1;
if (float.TryParse(opacity, out float op))
{
opacityVal = op;
if (opacityVal < 0)
{
opacityVal = 0;
}
if (opacityVal > 1)
{
opacityVal = 1;
}
}
return new SvgPaint(val, opacityVal);
}
private TPixel? GetNamedColor<TPixel>(string name) where TPixel : struct, IPixel<TPixel>
{
var dict = new Dictionary<string, TPixel>(StringComparer.OrdinalIgnoreCase)
{
["AliceBlue"] = NamedColors<TPixel>.AliceBlue,
["MistyRose"] = NamedColors<TPixel>.MistyRose,
["Moccasin"] = NamedColors<TPixel>.Moccasin,
["NavajoWhite"] = NamedColors<TPixel>.NavajoWhite,
["Navy"] = NamedColors<TPixel>.Navy,
["OldLace"] = NamedColors<TPixel>.OldLace,
["Olive"] = NamedColors<TPixel>.Olive,
["MintCream"] = NamedColors<TPixel>.MintCream,
["OliveDrab"] = NamedColors<TPixel>.OliveDrab,
["OrangeRed"] = NamedColors<TPixel>.OrangeRed,
["Orchid"] = NamedColors<TPixel>.Orchid,
["PaleGoldenrod"] = NamedColors<TPixel>.PaleGoldenrod,
["PaleGreen"] = NamedColors<TPixel>.PaleGreen,
["PaleTurquoise"] = NamedColors<TPixel>.PaleTurquoise,
["PaleVioletRed"] = NamedColors<TPixel>.PaleVioletRed,
["Orange"] = NamedColors<TPixel>.Orange,
["PapayaWhip"] = NamedColors<TPixel>.PapayaWhip,
["MidnightBlue"] = NamedColors<TPixel>.MidnightBlue,
["MediumTurquoise"] = NamedColors<TPixel>.MediumTurquoise,
["LightSteelBlue"] = NamedColors<TPixel>.LightSteelBlue,
["LightYellow"] = NamedColors<TPixel>.LightYellow,
["Lime"] = NamedColors<TPixel>.Lime,
["LimeGreen"] = NamedColors<TPixel>.LimeGreen,
["Linen"] = NamedColors<TPixel>.Linen,
["Magenta"] = NamedColors<TPixel>.Magenta,
["MediumVioletRed"] = NamedColors<TPixel>.MediumVioletRed,
["Maroon"] = NamedColors<TPixel>.Maroon,
["MediumBlue"] = NamedColors<TPixel>.MediumBlue,
["MediumOrchid"] = NamedColors<TPixel>.MediumOrchid,
["MediumPurple"] = NamedColors<TPixel>.MediumPurple,
["MediumSeaGreen"] = NamedColors<TPixel>.MediumSeaGreen,
["MediumSlateBlue"] = NamedColors<TPixel>.MediumSlateBlue,
["MediumSpringGreen"] = NamedColors<TPixel>.MediumSpringGreen,
["MediumAquamarine"] = NamedColors<TPixel>.MediumAquamarine,
["LightSlateGray"] = NamedColors<TPixel>.LightSlateGray,
["PeachPuff"] = NamedColors<TPixel>.PeachPuff,
["Pink"] = NamedColors<TPixel>.Pink,
["SpringGreen"] = NamedColors<TPixel>.SpringGreen,
["SteelBlue"] = NamedColors<TPixel>.SteelBlue,
["Tan"] = NamedColors<TPixel>.Tan,
["Teal"] = NamedColors<TPixel>.Teal,
["Thistle"] = NamedColors<TPixel>.Thistle,
["Tomato"] = NamedColors<TPixel>.Tomato,
["Snow"] = NamedColors<TPixel>.Snow,
["Transparent"] = NamedColors<TPixel>.Transparent,
["Violet"] = NamedColors<TPixel>.Violet,
["Wheat"] = NamedColors<TPixel>.Wheat,
["White"] = NamedColors<TPixel>.White,
["WhiteSmoke"] = NamedColors<TPixel>.WhiteSmoke,
["Yellow"] = NamedColors<TPixel>.Yellow,
["YellowGreen"] = NamedColors<TPixel>.YellowGreen,
["Turquoise"] = NamedColors<TPixel>.Turquoise,
["Peru"] = NamedColors<TPixel>.Peru,
["SlateGray"] = NamedColors<TPixel>.SlateGray,
["SkyBlue"] = NamedColors<TPixel>.SkyBlue,
["Plum"] = NamedColors<TPixel>.Plum,
["PowderBlue"] = NamedColors<TPixel>.PowderBlue,
["Purple"] = NamedColors<TPixel>.Purple,
["RebeccaPurple"] = NamedColors<TPixel>.RebeccaPurple,
["Red"] = NamedColors<TPixel>.Red,
["RosyBrown"] = NamedColors<TPixel>.RosyBrown,
["SlateBlue"] = NamedColors<TPixel>.SlateBlue,
["RoyalBlue"] = NamedColors<TPixel>.RoyalBlue,
["Salmon"] = NamedColors<TPixel>.Salmon,
["SandyBrown"] = NamedColors<TPixel>.SandyBrown,
["SeaGreen"] = NamedColors<TPixel>.SeaGreen,
["SeaShell"] = NamedColors<TPixel>.SeaShell,
["Sienna"] = NamedColors<TPixel>.Sienna,
["Silver"] = NamedColors<TPixel>.Silver,
["SaddleBrown"] = NamedColors<TPixel>.SaddleBrown,
["LightSkyBlue"] = NamedColors<TPixel>.LightSkyBlue,
["LightSeaGreen"] = NamedColors<TPixel>.LightSeaGreen,
["LightSalmon"] = NamedColors<TPixel>.LightSalmon,
["Crimson"] = NamedColors<TPixel>.Crimson,
["Cyan"] = NamedColors<TPixel>.Cyan,
["DarkBlue"] = NamedColors<TPixel>.DarkBlue,
["DarkCyan"] = NamedColors<TPixel>.DarkCyan,
["DarkGoldenrod"] = NamedColors<TPixel>.DarkGoldenrod,
["DarkGray"] = NamedColors<TPixel>.DarkGray,
["Cornsilk"] = NamedColors<TPixel>.Cornsilk,
["DarkGreen"] = NamedColors<TPixel>.DarkGreen,
["DarkMagenta"] = NamedColors<TPixel>.DarkMagenta,
["DarkOliveGreen"] = NamedColors<TPixel>.DarkOliveGreen,
["DarkOrange"] = NamedColors<TPixel>.DarkOrange,
["DarkOrchid"] = NamedColors<TPixel>.DarkOrchid,
["DarkRed"] = NamedColors<TPixel>.DarkRed,
["DarkSalmon"] = NamedColors<TPixel>.DarkSalmon,
["DarkKhaki"] = NamedColors<TPixel>.DarkKhaki,
["DarkSeaGreen"] = NamedColors<TPixel>.DarkSeaGreen,
["CornflowerBlue"] = NamedColors<TPixel>.CornflowerBlue,
["Chocolate"] = NamedColors<TPixel>.Chocolate,
["AntiqueWhite"] = NamedColors<TPixel>.AntiqueWhite,
["Aqua"] = NamedColors<TPixel>.Aqua,
["Aquamarine"] = NamedColors<TPixel>.Aquamarine,
["Azure"] = NamedColors<TPixel>.Azure,
["Beige"] = NamedColors<TPixel>.Beige,
["Bisque"] = NamedColors<TPixel>.Bisque,
["Coral"] = NamedColors<TPixel>.Coral,
["Black"] = NamedColors<TPixel>.Black,
["Blue"] = NamedColors<TPixel>.Blue,
["BlueViolet"] = NamedColors<TPixel>.BlueViolet,
["Brown"] = NamedColors<TPixel>.Brown,
["BurlyWood"] = NamedColors<TPixel>.BurlyWood,
["CadetBlue"] = NamedColors<TPixel>.CadetBlue,
["Chartreuse"] = NamedColors<TPixel>.Chartreuse,
["BlanchedAlmond"] = NamedColors<TPixel>.BlanchedAlmond,
["DarkSlateBlue"] = NamedColors<TPixel>.DarkSlateBlue,
["DarkSlateGray"] = NamedColors<TPixel>.DarkSlateGray,
["DarkTurquoise"] = NamedColors<TPixel>.DarkTurquoise,
["Indigo"] = NamedColors<TPixel>.Indigo,
["Ivory"] = NamedColors<TPixel>.Ivory,
["Khaki"] = NamedColors<TPixel>.Khaki,
["Lavender"] = NamedColors<TPixel>.Lavender,
["LavenderBlush"] = NamedColors<TPixel>.LavenderBlush,
["LawnGreen"] = NamedColors<TPixel>.LawnGreen,
["IndianRed"] = NamedColors<TPixel>.IndianRed,
["LemonChiffon"] = NamedColors<TPixel>.LemonChiffon,
["LightCoral"] = NamedColors<TPixel>.LightCoral,
["LightCyan"] = NamedColors<TPixel>.LightCyan,
["LightGoldenrodYellow"] = NamedColors<TPixel>.LightGoldenrodYellow,
["LightGray"] = NamedColors<TPixel>.LightGray,
["LightGreen"] = NamedColors<TPixel>.LightGreen,
["LightPink"] = NamedColors<TPixel>.LightPink,
["LightBlue"] = NamedColors<TPixel>.LightBlue,
["HotPink"] = NamedColors<TPixel>.HotPink,
["Honeydew"] = NamedColors<TPixel>.Honeydew,
["GreenYellow"] = NamedColors<TPixel>.GreenYellow,
["DarkViolet"] = NamedColors<TPixel>.DarkViolet,
["DeepPink"] = NamedColors<TPixel>.DeepPink,
["DeepSkyBlue"] = NamedColors<TPixel>.DeepSkyBlue,
["DimGray"] = NamedColors<TPixel>.DimGray,
["DodgerBlue"] = NamedColors<TPixel>.DodgerBlue,
["Firebrick"] = NamedColors<TPixel>.Firebrick,
["FloralWhite"] = NamedColors<TPixel>.FloralWhite,
["ForestGreen"] = NamedColors<TPixel>.ForestGreen,
["Fuchsia"] = NamedColors<TPixel>.Fuchsia,
["Gainsboro"] = NamedColors<TPixel>.Gainsboro,
["GhostWhite"] = NamedColors<TPixel>.GhostWhite,
["Gold"] = NamedColors<TPixel>.Gold,
["Goldenrod"] = NamedColors<TPixel>.Goldenrod,
["Gray"] = NamedColors<TPixel>.Gray,
["Green"] = NamedColors<TPixel>.Green,
};
if (dict.TryGetValue(name, out var pixel))
{
return pixel;
}
return null;
}
public IBrush<TPixel> AsBrush<TPixel>() where TPixel : struct, IPixel<TPixel>
{
// lets asume the brush is a color for now
// TODO update ColourBuilder to expose named colors
var value = this.Value;
if (string.IsNullOrWhiteSpace(value) || value.Equals("None", StringComparison.OrdinalIgnoreCase))
{
// if a null prush is returned we should skip
return null;
}
var color = GetNamedColor<TPixel>(Value) ?? ColorBuilder<TPixel>.FromHex(value);
return new SolidBrush<TPixel>(color);
}
}
internal struct SvgUnitValue
{
public static readonly SvgUnitValue Unset = default(SvgUnitValue);
public static readonly SvgUnitValue Zero = new SvgUnitValue(0, Units.undefined);
public static readonly SvgUnitValue One = new SvgUnitValue(1, Units.undefined);
public bool IsSet { get; }
public float Value { get; }
@ -272,7 +64,7 @@ namespace SixLabors.Svg.Dom
}
}
public static SvgUnitValue Parse(string val)
public static SvgUnitValue? Parse(string val)
{
val = val?.Trim() ?? "";
if (!string.IsNullOrWhiteSpace(val))
@ -314,7 +106,7 @@ namespace SixLabors.Svg.Dom
}
}
return Unset;
return null;
}
public enum Units
@ -333,4 +125,71 @@ namespace SixLabors.Svg.Dom
}
internal struct SvgLineCap
{
public static readonly SvgLineCap Unset = default(SvgLineCap);
public static readonly SvgLineCap Butt = new SvgLineCap(EndCapStyle.Butt);
public static readonly SvgLineCap Round = new SvgLineCap(EndCapStyle.Round);
public static readonly SvgLineCap Square = new SvgLineCap(EndCapStyle.Square);
public bool IsSet { get; }
public EndCapStyle Style { get; private set; }
public SvgLineCap(EndCapStyle style)
{
this.IsSet = true;
this.Style = style;
}
public static SvgLineCap? Parse(string val)
{
val = (val?.Trim().ToLower() ?? "");
switch (val)
{
case "butt":
return SvgLineCap.Butt;
case "round":
return SvgLineCap.Round;
case "square":
return SvgLineCap.Square;
default:
return null;
}
}
}
internal struct SvgLineJoin
{
public static readonly SvgLineJoin Unset = default(SvgLineJoin);
public static readonly SvgLineJoin Miter = new SvgLineJoin(JointStyle.Miter);
public static readonly SvgLineJoin Round = new SvgLineJoin(JointStyle.Round);
public static readonly SvgLineJoin Square = new SvgLineJoin(JointStyle.Square);
public bool IsSet { get; }
public JointStyle Style { get; private set; }
public SvgLineJoin(JointStyle style)
{
this.IsSet = true;
this.Style = style;
}
public static SvgLineJoin? Parse(string val)
{
val = (val?.Trim().ToLower() ?? "");
switch (val)
{
case "miter":
return SvgLineJoin.Miter;
case "round":
return SvgLineJoin.Round;
case "bevel":
return SvgLineJoin.Square;
default:
return null;
}
}
}
}

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

@ -1,12 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.10.1" />
<PackageReference Include="AngleSharp.Css" Version="0.10.0" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-dev002362" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-dev000112" />
</ItemGroup>
</Project>

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

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>