-
Notifications
You must be signed in to change notification settings - Fork 12
/
SvgRenderer.cs
141 lines (122 loc) · 5.23 KB
/
SvgRenderer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#region License
// Jdenticon-net
// https://github.com/dmester/jdenticon-net
// Copyright © Daniel Mester Pirttijärvi 2016
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
#nullable enable
namespace Jdenticon.Rendering
{
/// <summary>
/// Renders icons as SVG paths.
/// </summary>
public class SvgRenderer : Renderer
{
private readonly Dictionary<Color, SvgPath> pathsByColor = new Dictionary<Color, SvgPath>();
private SvgPath? path;
private int width, height;
private Color backColor;
/// <summary>
/// Creates an instance of <see cref="SvgRenderer"/>.
/// </summary>
/// <param name="width">The width of the icon in pixels.</param>
/// <param name="height">The height of the icon in pixels.</param>
public SvgRenderer(int width, int height)
{
this.width = width;
this.height = height;
}
/// <inheritdoc />
protected override void AddCircleNoTransform(PointF location, float diameter, bool counterClockwise)
{
if (path == null) throw new InvalidOperationException("No active shape. Call BeginShape before calling this method.");
path.AddCircle(location, diameter, counterClockwise);
}
/// <inheritdoc />
protected override void AddPolygonNoTransform(PointF[] points)
{
if (points == null) throw new ArgumentNullException(nameof(points));
if (path == null) throw new InvalidOperationException("No active shape. Call BeginShape before calling this method.");
path.AddPolygon(points);
}
/// <inheritdoc />
public override void SetBackground(Color color, Rectangle iconBounds)
{
backColor = color;
}
/// <inheritdoc />
public override IDisposable BeginShape(Color color)
{
if (!pathsByColor.TryGetValue(color, out path))
{
pathsByColor[color] = path = new SvgPath();
}
return ActionDisposable.Empty;
}
/// <summary>
/// Writes the SVG to the specified <see cref="TextWriter"/>.
/// </summary>
/// <param name="writer">The output writer to which the SVG will be written.</param>
/// <param name="fragment">If <c>true</c> an SVG string without the root svg element will be rendered.</param>
public void Save(TextWriter writer, bool fragment)
{
writer.Write(ToSvg(fragment));
}
/// <summary>
/// Writes the SVG to the specified <see cref="TextWriter"/>.
/// </summary>
/// <param name="fragment">If <c>true</c> an SVG string without the root svg element will be rendered.</param>
public string ToSvg(bool fragment)
{
var svg = new List<string>();
var invariantCulture = CultureInfo.InvariantCulture;
var widthAsString = width.ToString(invariantCulture);
var heightAsString = height.ToString(invariantCulture);
if (!fragment)
{
svg.Add("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" +
widthAsString + "\" height=\"" + heightAsString + "\" viewBox=\"0 0 " +
widthAsString + " " + heightAsString + "\" preserveAspectRatio=\"xMidYMid meet\">");
}
if (backColor.A > 0)
{
var opacity = (float)backColor.A / 255;
svg.Add("<rect fill=\"" + ColorUtils.ToHexString(backColor) + "\" fill-opacity=\"" +
opacity.ToString("0.##", invariantCulture) +
"\" x=\"0\" y=\"0\" width=\"" + widthAsString + "\" height=\"" + heightAsString + "\"/>");
}
foreach (var pair in pathsByColor)
{
svg.Add("<path fill=\"" + ColorUtils.ToHexString(pair.Key) + "\" d=\"" + pair.Value + "\"/>");
}
if (!fragment)
{
svg.Add("</svg>");
}
return string.Concat(svg);
}
}
}