1 | using System;
|
---|
2 | using System.Windows;
|
---|
3 | using System.Windows.Media;
|
---|
4 |
|
---|
5 | using SharpVectors.Dom.Svg;
|
---|
6 | using SharpVectors.Dom.Css;
|
---|
7 | using SharpVectors.Renderers.Utils;
|
---|
8 |
|
---|
9 | namespace SharpVectors.Renderers.Wpf
|
---|
10 | {
|
---|
11 | public sealed class WpfSvgPaint : SvgPaint
|
---|
12 | {
|
---|
13 | #region Private Fields
|
---|
14 |
|
---|
15 | private string _propertyName;
|
---|
16 | private WpfFill _paintFill;
|
---|
17 | private SvgStyleableElement _element;
|
---|
18 | private WpfDrawingContext _context;
|
---|
19 |
|
---|
20 | #endregion
|
---|
21 |
|
---|
22 | #region Constructors and Destructor
|
---|
23 |
|
---|
24 | public WpfSvgPaint(WpfDrawingContext context, SvgStyleableElement elm, string propName)
|
---|
25 | : base(elm.GetComputedStyle("").GetPropertyValue(propName))
|
---|
26 | {
|
---|
27 | _propertyName = propName;
|
---|
28 | _element = elm;
|
---|
29 | _context = context;
|
---|
30 | }
|
---|
31 |
|
---|
32 | #endregion
|
---|
33 |
|
---|
34 | #region Public Properties
|
---|
35 |
|
---|
36 | public WpfFill PaintServer
|
---|
37 | {
|
---|
38 | get
|
---|
39 | {
|
---|
40 | return _paintFill;
|
---|
41 | }
|
---|
42 | }
|
---|
43 |
|
---|
44 | #endregion
|
---|
45 |
|
---|
46 | #region Public Methods
|
---|
47 |
|
---|
48 | public Brush GetBrush()
|
---|
49 | {
|
---|
50 | return GetBrush("fill", true);
|
---|
51 | }
|
---|
52 |
|
---|
53 | public Brush GetBrush(bool setOpacity)
|
---|
54 | {
|
---|
55 | return GetBrush("fill", setOpacity);
|
---|
56 | }
|
---|
57 |
|
---|
58 | public Pen GetPen()
|
---|
59 | {
|
---|
60 | double strokeWidth = GetStrokeWidth();
|
---|
61 | if (strokeWidth == 0)
|
---|
62 | return null;
|
---|
63 |
|
---|
64 | WpfSvgPaint stroke;
|
---|
65 | if (PaintType == SvgPaintType.None)
|
---|
66 | {
|
---|
67 | return null;
|
---|
68 | }
|
---|
69 | else if (PaintType == SvgPaintType.CurrentColor)
|
---|
70 | {
|
---|
71 | stroke = new WpfSvgPaint(_context, _element, "color");
|
---|
72 | }
|
---|
73 | else
|
---|
74 | {
|
---|
75 | stroke = this;
|
---|
76 | }
|
---|
77 |
|
---|
78 | Pen pen = new Pen(stroke.GetBrush("stroke", true), strokeWidth);
|
---|
79 |
|
---|
80 | pen.StartLineCap = pen.EndLineCap = GetLineCap();
|
---|
81 | pen.LineJoin = GetLineJoin();
|
---|
82 | double miterLimit = GetMiterLimit(strokeWidth);
|
---|
83 | if (miterLimit > 0)
|
---|
84 | {
|
---|
85 | pen.MiterLimit = miterLimit;
|
---|
86 | }
|
---|
87 |
|
---|
88 | //pen.MiterLimit = 1.0f;
|
---|
89 |
|
---|
90 | DoubleCollection dashArray = GetDashArray(strokeWidth);
|
---|
91 | if (dashArray != null && dashArray.Count != 0)
|
---|
92 | {
|
---|
93 | bool isValidDashes = true;
|
---|
94 | //Do not draw if dash array had a zero value in it
|
---|
95 | for (int i = 0; i < dashArray.Count; i++)
|
---|
96 | {
|
---|
97 | if (dashArray[i] == 0)
|
---|
98 | {
|
---|
99 | isValidDashes = false;
|
---|
100 | }
|
---|
101 | }
|
---|
102 |
|
---|
103 | if (isValidDashes)
|
---|
104 | {
|
---|
105 | DashStyle dashStyle = new DashStyle(dashArray, GetDashOffset(strokeWidth));
|
---|
106 |
|
---|
107 | pen.DashStyle = dashStyle;
|
---|
108 | // This is the one that works well for the XAML, the default is not Flat as
|
---|
109 | // stated in the documentations...
|
---|
110 | pen.DashCap = PenLineCap.Flat;
|
---|
111 | }
|
---|
112 | }
|
---|
113 |
|
---|
114 | return pen;
|
---|
115 | }
|
---|
116 |
|
---|
117 | #endregion
|
---|
118 |
|
---|
119 | #region Private Methods
|
---|
120 |
|
---|
121 | private double GetOpacity(string fillOrStroke)
|
---|
122 | {
|
---|
123 | double opacityValue = 1;
|
---|
124 |
|
---|
125 | string opacity = _element.GetPropertyValue(fillOrStroke + "-opacity");
|
---|
126 | if (opacity != null && opacity.Length > 0)
|
---|
127 | {
|
---|
128 | opacityValue *= SvgNumber.ParseNumber(opacity);
|
---|
129 | }
|
---|
130 |
|
---|
131 | opacity = _element.GetPropertyValue("opacity");
|
---|
132 | if (opacity != null && opacity.Length > 0)
|
---|
133 | {
|
---|
134 | opacityValue *= SvgNumber.ParseNumber(opacity);
|
---|
135 | }
|
---|
136 |
|
---|
137 | opacityValue = Math.Min(opacityValue, 1);
|
---|
138 | opacityValue = Math.Max(opacityValue, 0);
|
---|
139 |
|
---|
140 | return opacityValue;
|
---|
141 | }
|
---|
142 |
|
---|
143 | private PenLineCap GetLineCap()
|
---|
144 | {
|
---|
145 | switch (_element.GetPropertyValue("stroke-linecap"))
|
---|
146 | {
|
---|
147 | case "round":
|
---|
148 | return PenLineCap.Round;
|
---|
149 | case "square":
|
---|
150 | return PenLineCap.Square;
|
---|
151 | case "butt":
|
---|
152 | return PenLineCap.Flat;
|
---|
153 | case "triangle":
|
---|
154 | return PenLineCap.Triangle;
|
---|
155 | default:
|
---|
156 | return PenLineCap.Flat;
|
---|
157 | }
|
---|
158 | }
|
---|
159 |
|
---|
160 | private PenLineJoin GetLineJoin()
|
---|
161 | {
|
---|
162 | switch (_element.GetPropertyValue("stroke-linejoin"))
|
---|
163 | {
|
---|
164 | case "round":
|
---|
165 | return PenLineJoin.Round;
|
---|
166 | case "bevel":
|
---|
167 | return PenLineJoin.Bevel;
|
---|
168 | default:
|
---|
169 | return PenLineJoin.Miter;
|
---|
170 | }
|
---|
171 | }
|
---|
172 |
|
---|
173 | private double GetStrokeWidth()
|
---|
174 | {
|
---|
175 | string strokeWidth = _element.GetPropertyValue("stroke-width");
|
---|
176 | if (strokeWidth.Length == 0) strokeWidth = "1px";
|
---|
177 |
|
---|
178 | SvgLength strokeWidthLength = new SvgLength(_element, "stroke-width",
|
---|
179 | SvgLengthDirection.Viewport, strokeWidth);
|
---|
180 |
|
---|
181 | return strokeWidthLength.Value;
|
---|
182 | }
|
---|
183 |
|
---|
184 | private double GetMiterLimit(double strokeWidth)
|
---|
185 | {
|
---|
186 | // Use this to prevent the default value of "4" being used...
|
---|
187 | string miterLimitAttr = _element.GetAttribute("stroke-miterlimit");
|
---|
188 | if (String.IsNullOrEmpty(miterLimitAttr))
|
---|
189 | {
|
---|
190 | string strokeLinecap = _element.GetAttribute("stroke-linecap");
|
---|
191 | if (String.Equals(strokeLinecap, "round", StringComparison.OrdinalIgnoreCase))
|
---|
192 | {
|
---|
193 | return 1.0d;
|
---|
194 | }
|
---|
195 | return -1.0d;
|
---|
196 | }
|
---|
197 |
|
---|
198 | string miterLimitStr = _element.GetPropertyValue("stroke-miterlimit");
|
---|
199 | if (String.IsNullOrEmpty(miterLimitStr) || (float)(strokeWidth) <= 0)
|
---|
200 | {
|
---|
201 | return -1.0d;
|
---|
202 | }
|
---|
203 |
|
---|
204 | double miterLimit = SvgNumber.ParseNumber(miterLimitStr);
|
---|
205 | if (miterLimit < 1)
|
---|
206 | throw new SvgException(SvgExceptionType.SvgInvalidValueErr,
|
---|
207 | "stroke-miterlimit can not be less then 1");
|
---|
208 |
|
---|
209 | //if (miterLimit < 1.0d)
|
---|
210 | //{
|
---|
211 | // return -1.0d;
|
---|
212 | //}
|
---|
213 |
|
---|
214 | double ratioLimit = miterLimit / strokeWidth;
|
---|
215 | if (ratioLimit >= 1.8d)
|
---|
216 | {
|
---|
217 | return miterLimit;
|
---|
218 | }
|
---|
219 | else
|
---|
220 | {
|
---|
221 | return 1.0d;
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | private DoubleCollection GetDashArray(double strokeWidth)
|
---|
226 | {
|
---|
227 | string dashArrayText = _element.GetPropertyValue("stroke-dasharray");
|
---|
228 | if (String.IsNullOrEmpty(dashArrayText))
|
---|
229 | {
|
---|
230 | return null;
|
---|
231 | }
|
---|
232 |
|
---|
233 | if (dashArrayText == "none")
|
---|
234 | {
|
---|
235 | return null;
|
---|
236 | }
|
---|
237 | else
|
---|
238 | {
|
---|
239 | SvgNumberList list = new SvgNumberList(dashArrayText);
|
---|
240 |
|
---|
241 | uint len = list.NumberOfItems;
|
---|
242 | //float[] fDashArray = new float[len];
|
---|
243 | DoubleCollection dashArray = new DoubleCollection((int)len);
|
---|
244 |
|
---|
245 | for (uint i = 0; i < len; i++)
|
---|
246 | {
|
---|
247 | //divide by strokeWidth to take care of the difference between Svg and WPF
|
---|
248 | dashArray.Add(list.GetItem(i).Value / strokeWidth);
|
---|
249 | }
|
---|
250 |
|
---|
251 | return dashArray;
|
---|
252 | }
|
---|
253 | }
|
---|
254 |
|
---|
255 | private double GetDashOffset(double strokeWidth)
|
---|
256 | {
|
---|
257 | string dashOffset = _element.GetPropertyValue("stroke-dashoffset");
|
---|
258 | if (dashOffset.Length > 0)
|
---|
259 | {
|
---|
260 | //divide by strokeWidth to take care of the difference between Svg and GDI+
|
---|
261 | SvgLength dashOffsetLength = new SvgLength(_element, "stroke-dashoffset",
|
---|
262 | SvgLengthDirection.Viewport, dashOffset);
|
---|
263 | return dashOffsetLength.Value;
|
---|
264 | }
|
---|
265 | else
|
---|
266 | {
|
---|
267 | return 0;
|
---|
268 | }
|
---|
269 | }
|
---|
270 |
|
---|
271 | private WpfFill GetPaintFill(string uri)
|
---|
272 | {
|
---|
273 | string absoluteUri = _element.ResolveUri(uri);
|
---|
274 |
|
---|
275 | if (_element.Imported && _element.ImportDocument != null &&
|
---|
276 | _element.ImportNode != null)
|
---|
277 | {
|
---|
278 | // We need to determine whether the provided URI refers to element in the
|
---|
279 | // original document or in the current document...
|
---|
280 | SvgStyleableElement styleElm = _element.ImportNode as SvgStyleableElement;
|
---|
281 | if (styleElm != null)
|
---|
282 | {
|
---|
283 | string propertyValue =
|
---|
284 | styleElm.GetComputedStyle("").GetPropertyValue(_propertyName);
|
---|
285 |
|
---|
286 | if (!String.IsNullOrEmpty(propertyValue))
|
---|
287 | {
|
---|
288 | WpfSvgPaint importFill = new WpfSvgPaint(_context, styleElm, _propertyName);
|
---|
289 | if (String.Equals(uri, importFill.Uri, StringComparison.OrdinalIgnoreCase))
|
---|
290 | {
|
---|
291 | WpfFill fill = WpfFill.CreateFill(_element.ImportDocument, absoluteUri);
|
---|
292 | if (fill != null)
|
---|
293 | {
|
---|
294 | return fill;
|
---|
295 | }
|
---|
296 | }
|
---|
297 | }
|
---|
298 | }
|
---|
299 | }
|
---|
300 |
|
---|
301 | return WpfFill.CreateFill(_element.OwnerDocument, absoluteUri);
|
---|
302 | }
|
---|
303 |
|
---|
304 | private Brush GetBrush(string propPrefix, bool setOpacity)
|
---|
305 | {
|
---|
306 | SvgPaint fill;
|
---|
307 | if (PaintType == SvgPaintType.None)
|
---|
308 | {
|
---|
309 | return null;
|
---|
310 | }
|
---|
311 | else if (PaintType == SvgPaintType.CurrentColor)
|
---|
312 | {
|
---|
313 | fill = new WpfSvgPaint(_context, _element, "color");
|
---|
314 | }
|
---|
315 | else
|
---|
316 | {
|
---|
317 | fill = this;
|
---|
318 | }
|
---|
319 |
|
---|
320 | SvgPaintType paintType = fill.PaintType;
|
---|
321 | if (paintType == SvgPaintType.Uri || paintType == SvgPaintType.UriCurrentColor ||
|
---|
322 | paintType == SvgPaintType.UriNone || paintType == SvgPaintType.UriRgbColor ||
|
---|
323 | paintType == SvgPaintType.UriRgbColorIccColor)
|
---|
324 | {
|
---|
325 | _paintFill = GetPaintFill(fill.Uri);
|
---|
326 | if (_paintFill != null)
|
---|
327 | {
|
---|
328 | Brush brush = _paintFill.GetBrush(_context);
|
---|
329 |
|
---|
330 | if (brush != null)
|
---|
331 | {
|
---|
332 | brush.Opacity = GetOpacity(propPrefix);
|
---|
333 | }
|
---|
334 |
|
---|
335 | return brush;
|
---|
336 | }
|
---|
337 | else
|
---|
338 | {
|
---|
339 | if (PaintType == SvgPaintType.UriNone || PaintType == SvgPaintType.Uri)
|
---|
340 | {
|
---|
341 | return null;
|
---|
342 | }
|
---|
343 | else if (PaintType == SvgPaintType.UriCurrentColor)
|
---|
344 | {
|
---|
345 | fill = new WpfSvgPaint(_context, _element, "color");
|
---|
346 | }
|
---|
347 | else
|
---|
348 | {
|
---|
349 | fill = this;
|
---|
350 | }
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 | if (fill == null || fill.RgbColor == null)
|
---|
355 | {
|
---|
356 | return null;
|
---|
357 | }
|
---|
358 |
|
---|
359 | Color? solidColor = WpfConvert.ToColor(fill.RgbColor);
|
---|
360 | if (solidColor == null)
|
---|
361 | {
|
---|
362 | return null;
|
---|
363 | }
|
---|
364 |
|
---|
365 | SolidColorBrush solidBrush = new SolidColorBrush(solidColor.Value);
|
---|
366 | //int opacity = GetOpacity(propPrefix);
|
---|
367 | //solidBrush.Color = Color.FromArgb(opacity, brush.Color);
|
---|
368 | if (setOpacity)
|
---|
369 | {
|
---|
370 | solidBrush.Opacity = GetOpacity(propPrefix);
|
---|
371 | }
|
---|
372 | return solidBrush;
|
---|
373 | }
|
---|
374 |
|
---|
375 | #endregion
|
---|
376 | }
|
---|
377 | }
|
---|