private Tuple <string, ICssValue> ParseValue(string name, string value, string priority, bool presentation) { var important = priority == "important"; if (!presentation) { name = name.ToLower(); } ICssValue parsedValue = null; switch (name) { case "fill": case "stroke": parsedValue = new SvgPaint(value); break; case "stroke-width": parsedValue = SvgLength.Parse(value, presentation); break; case "stop-color": parsedValue = new SvgColor(value); break; case "fill-opacity": case "stroke-opacity": case "stop-opacity": case "opacity": parsedValue = SvgNumber.Parse(value, 0.0F, 1.0F); break; case "clip-path": parsedValue = new SvgIri(value); break; case "fill-rule": case "clip-rule": parsedValue = new SvgFillRule(presentation ? value : value.ToLower()); break; } if (!this._cache.ContainsKey(name)) { var result = Tuple.Create(value, parsedValue); this._cache.Add(name, result); return(result); } else if (important) { var result = Tuple.Create(value, parsedValue); this._cache[name] = result; return(result); } return(null); }
/// <summary> /// Common code for rendering a marker once the orientation angle has been calculated /// </summary> /// <param name="fAngle"></param> /// <param name="pRenderer"></param> /// <param name="pOwner"></param> /// <param name="pMarkerPoint"></param> private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pMarkerPoint) { using (var pRenderPen = CreatePen(pOwner, pRenderer)) { using (var markerPath = GetClone(pOwner)) { using (var transMatrix = new Matrix()) { transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y); if (Orient.IsAuto) { transMatrix.Rotate(fAngle); } else { transMatrix.Rotate(Orient.Angle); } switch (MarkerUnits) { case SvgMarkerUnits.strokeWidth: transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) * pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)), AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) * pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this))); break; case SvgMarkerUnits.userSpaceOnUse: transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this), -RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this)); break; } markerPath.Transform(transMatrix); if (pRenderPen != null) { pRenderer.DrawPath(pRenderPen, markerPath); } SvgPaintServer pFill = this.Children.First().Fill; SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for? float fOpacity = FillOpacity; if (pFill != null) { using (var pBrush = pFill.GetBrush(this, pRenderer, fOpacity)) { pRenderer.FillPath(pBrush, markerPath); } } } } } }
/// <summary> /// Common code for rendering a marker once the orientation angle has been calculated /// </summary> /// <param name="fAngle"></param> /// <param name="pRenderer"></param> /// <param name="pOwner"></param> /// <param name="pMarkerPoint"></param> private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint) { Pen pRenderPen = CreatePen(pOwner); GraphicsPath markerPath = GetClone(pOwner); Matrix transMatrix = new Matrix(); transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y); if (Orient.IsAuto) { transMatrix.Rotate(fAngle); } else { transMatrix.Rotate(Orient.Angle); } switch (MarkerUnits) { case SvgMarkerUnits.strokeWidth: transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth)); break; case SvgMarkerUnits.userSpaceOnUse: transMatrix.Translate(-RefX, -RefY); break; } markerPath.Transform(transMatrix); pRenderer.DrawPath(pRenderPen, markerPath); SvgPaintServer pFill = Fill; SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for? float fOpacity = FillOpacity; if (pFill != null) { Brush pBrush = pFill.GetBrush(this, fOpacity); pRenderer.FillPath(pBrush, markerPath); pBrush.Dispose(); } pRenderPen.Dispose(); markerPath.Dispose(); transMatrix.Dispose(); }
/// <summary> /// Determine the filled simple segments of a path, splitting lines and curves appropriately. /// </summary> /// <param name="path">The path that is supposed to be compiled.</param> /// <param name="fillRule">The fill rule used to determine the filled components</param> /// <returns>The set of simple path components.</returns> public static CompiledDrawing CompileFill(SvgPathSegmentList path, SvgFillRule fillRule = SvgFillRule.EvenOdd) { var curveData = path.SplitCurves(); var curves = new List <Curve>(); foreach (var data in curveData) { // Add all open curves curves.AddRange(data.Curves); // Force close the open curves var p0 = data.Curves[0].At(0); var p1 = data.Curves[data.Curves.Length - 1].At(1); if (!DoubleUtils.RoughlyEquals(p0, p1)) { curves.Add(Curve.Line(p1, p0)); } } return(CompileCurves(curves, fillRule)); }
internal static CompiledDrawing CompileCurves(List <Curve> curves, SvgFillRule fillRule) { // Reunite all intersections to subdivide the curves var curveRootSets = new SortedDictionary <double, Double2> [curves.Count]; for (int i = 0; i < curveRootSets.Length; i++) { curveRootSets[i] = new SortedDictionary <double, Double2>() { [0] = curves[i].At(0), [1] = curves[i].At(1) } } ; // Get all intersections for (int i = 0; i < curves.Count; i++) { for (int j = i + 1; j < curves.Count; j++) { foreach (var pair in Curve.Intersections(curves[i], curves[j])) { if (!GeometricUtils.Inside01(pair.A) || !GeometricUtils.Inside01(pair.B)) { continue; } curveRootSets[i][pair.A] = curves[i].At(pair.A); curveRootSets[j][pair.B] = curves[j].At(pair.B); } } } // Cluster the intersections var curveRootClusters = DerivePointClustersFromRootSets(curveRootSets); // Finally, we can start building the DCEL var dcel = new DCEL.DCEL(); for (int i = 0; i < curves.Count; i++) { var prevPair = new KeyValuePair <double, int>(double.NaN, 0); foreach (var curPair in curveRootClusters[i]) { if (!double.IsNaN(prevPair.Key)) { Curve curve; if (prevPair.Key == 0 && curPair.Key == 1) { curve = curves[i]; } else { curve = curves[i].Subcurve(prevPair.Key, curPair.Key); } foreach (var c in curve.Simplify()) { // Skip degenerate curves if (c.IsDegenerate) { continue; } dcel.AddCurve(c, prevPair.Value, curPair.Value); //Console.WriteLine(dcel); //Console.ReadLine(); } } prevPair = curPair; } } //Console.WriteLine(dcel); //Console.ReadLine(); // Now, we remove wedges and assign the fill numbers dcel.RemoveWedges(); //Console.WriteLine(dcel); //Console.ReadLine(); dcel.AssignFillNumbers(); //Console.WriteLine(dcel); //Console.ReadLine(); // Pick the appropriate predicate for the fill rule Func <DCEL.Face, bool> facePredicate; if (fillRule == SvgFillRule.EvenOdd) { facePredicate = f => f.FillNumber % 2 != 0; } else { facePredicate = f => f.FillNumber != 0; } // Simplify the faces dcel.SimplifyFaces(facePredicate); //Console.WriteLine(dcel); //Console.ReadLine(); // Generate the filled faces var fills = dcel.Faces.Where(facePredicate).Select(face => new FillFace(face.Contours.Select(contour => contour.CyclicalSequence.Select(e => e.Curve).ToArray()).ToArray())); // Generace the filled faces return(CompiledDrawing.ConcatMany(fills.Select(CompiledDrawing.FromFace))); }
//========================================================================== public SvgDrawableBaseElement(SvgDocument document, SvgBaseElement parent, XElement drawableBaseElement) : base(document, parent, drawableBaseElement) { XAttribute opacity_attribute = drawableBaseElement.Attribute("opacity"); if (opacity_attribute != null) { Opacity = SvgLength.Parse(opacity_attribute.Value); } XAttribute fill_opacity_attribute = drawableBaseElement.Attribute("fill-opacity"); if (fill_opacity_attribute != null) { FillOpacity = SvgLength.Parse(fill_opacity_attribute.Value); } XAttribute stroke_opacity_attribute = drawableBaseElement.Attribute("stroke-opacity"); if (stroke_opacity_attribute != null) { StrokeOpacity = SvgLength.Parse(stroke_opacity_attribute.Value); } XAttribute transform_attribute = drawableBaseElement.Attribute("transform"); if (transform_attribute != null) { Transform = SvgTransform.Parse(transform_attribute.Value); } XAttribute fill_attribute = drawableBaseElement.Attribute("fill"); if (fill_attribute != null) { Fill = SvgPaint.Parse(fill_attribute.Value); } XAttribute stroke_attribute = drawableBaseElement.Attribute("stroke"); if (stroke_attribute != null) { Stroke = SvgPaint.Parse(stroke_attribute.Value); } XAttribute stroke_width_attribute = drawableBaseElement.Attribute("stroke-width"); if (stroke_width_attribute != null) { StrokeWidth = SvgLength.Parse(stroke_width_attribute.Value); } XAttribute stroke_linecap_attribute = drawableBaseElement.Attribute("stroke-linecap"); if (stroke_linecap_attribute != null) { switch (stroke_linecap_attribute.Value) { case "butt": StrokeLinecap = SvgStrokeLinecap.Butt; break; case "round": StrokeLinecap = SvgStrokeLinecap.Round; break; case "square": StrokeLinecap = SvgStrokeLinecap.Square; break; case "inherit": StrokeLinecap = SvgStrokeLinecap.Inherit; break; default: throw new NotImplementedException(); } } XAttribute stroke_linejoin_attribute = drawableBaseElement.Attribute("stroke-linejoin"); if (stroke_linejoin_attribute != null) { switch (stroke_linejoin_attribute.Value) { case "miter": StrokeLinejoin = SvgStrokeLinejoin.Miter; break; case "round": StrokeLinejoin = SvgStrokeLinejoin.Round; break; case "bevel": StrokeLinejoin = SvgStrokeLinejoin.Bevel; break; case "inherit": StrokeLinejoin = SvgStrokeLinejoin.Inherit; break; default: throw new NotSupportedException(); } } XAttribute stroke_miterlimit_attribute = drawableBaseElement.Attribute("stroke-miterlimit"); if (stroke_miterlimit_attribute != null) { if (stroke_miterlimit_attribute.Value == "inherit") { StrokeMiterlimit = Double.NaN; } else { double miterlimit = Double.Parse(stroke_miterlimit_attribute.Value, CultureInfo.InvariantCulture.NumberFormat); //if(miterlimit < 1) //throw new NotSupportedException("A miterlimit less than 1 is not supported."); StrokeMiterlimit = miterlimit; } } XAttribute stroke_dasharray_attribute = drawableBaseElement.Attribute("stroke-dasharray"); if (stroke_dasharray_attribute != null) { if (stroke_dasharray_attribute.Value == "none") { StrokeDasharray = null; } else if (stroke_dasharray_attribute.Value == "inherit") { StrokeDasharray = new SvgLength[0]; } else { List <SvgLength> lengths = new List <SvgLength>(); foreach (string length in stroke_dasharray_attribute.Value.Split(',')) { lengths.Add(SvgLength.Parse(length)); } if (lengths.Count % 2 == 1) { StrokeDasharray = new SvgLength[lengths.Count * 2]; for (int i = 0; i < lengths.Count - 1; ++i) { StrokeDasharray[i] = lengths[i]; StrokeDasharray[i + lengths.Count] = lengths[i]; } } else { StrokeDasharray = lengths.ToArray(); } } } XAttribute stroke_dashoffset_attribute = drawableBaseElement.Attribute("stroke-dashoffset"); if (stroke_dashoffset_attribute != null) { StrokeDashoffset = SvgLength.Parse(stroke_dashoffset_attribute.Value); } XAttribute clip_attribute = drawableBaseElement.Attribute("clip-path"); if (clip_attribute != null) { string clip_path = clip_attribute.Value.Trim(); if (clip_path.StartsWith("url")) { clip_path = clip_path.Substring(3).Trim(); if (clip_path.StartsWith("(") && clip_path.EndsWith(")")) { clip_path = clip_path.Substring(1, clip_path.Length - 2).Trim(); if (clip_path.StartsWith("#")) { ClipPath = clip_path.Substring(1); } } } } XAttribute filter_attribute = drawableBaseElement.Attribute("filter"); if (filter_attribute != null) { string filter = filter_attribute.Value.Trim(); if (filter.StartsWith("url")) { filter = filter.Substring(3).Trim(); if (filter.StartsWith("(") && filter.EndsWith(")")) { filter = filter.Substring(1, filter.Length - 2).Trim(); if (filter.StartsWith("#")) { Filter = filter.Substring(1); } } } } XAttribute mask_attribute = drawableBaseElement.Attribute("mask"); if (mask_attribute != null) { string mask = mask_attribute.Value.Trim(); if (mask.StartsWith("url")) { mask = mask.Substring(3).Trim(); if (mask.StartsWith("(") && mask.EndsWith(")")) { mask = mask.Substring(1, mask.Length - 2).Trim(); if (mask.StartsWith("#")) { Mask = mask.Substring(1); } } } } XAttribute display_attribute = drawableBaseElement.Attribute("display"); if (display_attribute != null) { switch (display_attribute.Value) { case "inline": Display = SvgDisplay.Inline; break; case "block": Display = SvgDisplay.Block; break; case "list-item": Display = SvgDisplay.ListItem; break; case "run-in": Display = SvgDisplay.RunIn; break; case "compact": Display = SvgDisplay.Compact; break; case "marker": Display = SvgDisplay.Marker; break; case "table": Display = SvgDisplay.Table; break; case "inline-table": Display = SvgDisplay.InlineTable; break; case "table-row-group": Display = SvgDisplay.TableRowGroup; break; case "table-header-group": Display = SvgDisplay.TableHeaderGroup; break; case "table-footer-group": Display = SvgDisplay.TableFooterGroup; break; case "table-row": Display = SvgDisplay.TableRow; break; case "table-column-group": Display = SvgDisplay.TableColumnGroup; break; case "table-column": Display = SvgDisplay.TableColumn; break; case "table-cell": Display = SvgDisplay.TableCell; break; case "table-caption": Display = SvgDisplay.TableCaption; break; case "none": Display = SvgDisplay.None; break; default: throw new NotImplementedException(); } } XAttribute fill_rule_attribute = drawableBaseElement.Attribute("fill-rule"); if (fill_rule_attribute != null) { switch (fill_rule_attribute.Value) { case "nonzero": FillRule = SvgFillRule.Nonzero; break; case "evenodd": FillRule = SvgFillRule.Evenodd; break; case "inherit": FillRule = SvgFillRule.Inherit; break; default: throw new NotImplementedException(); } } // color, color-interpolation, color-rendering // viewBox attribute // preserveAspectRatio attribute // overflow foreach (XElement element in from element in drawableBaseElement.Elements() where element.Name.NamespaceName == "http://www.w3.org/2000/svg" select element) { switch (element.Name.LocalName) { default: throw new NotImplementedException(String.Format("Unhandled element: {0}", element)); } } }
/// <summary> /// Common code for rendering a marker once the orientation angle has been calculated /// </summary> /// <param name="fAngle"></param> /// <param name="pRenderer"></param> /// <param name="pOwner"></param> /// <param name="pMarkerPoint"></param> private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pMarkerPoint) { using (var pRenderPen = CreatePen(pOwner, pRenderer)) { using (var markerPath = GetClone(pOwner, pRenderer)) { using (var transMatrix = new Matrix()) { transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y); if (Orient.IsAuto) { transMatrix.Rotate(fAngle); } else { transMatrix.Rotate(Orient.Angle); } switch (MarkerUnits) { case SvgMarkerUnits.StrokeWidth: if (ViewBox.Width > 0 && ViewBox.Height > 0) { transMatrix.Scale(MarkerWidth, MarkerHeight); var strokeWidth = pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this); transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) * strokeWidth), AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) * strokeWidth)); } else { // SvgMarkerUnits.UserSpaceOnUse // TODO: We know this isn't correct. // But use this until the TODOs from AdjustForViewBoxWidth and AdjustForViewBoxHeight are done. // MORE see Unit Test "MakerEndTest.TestArrowCodeCreation()" transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this), -RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this)); } break; case SvgMarkerUnits.UserSpaceOnUse: transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this), -RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this)); break; } if (MarkerElement != null && MarkerElement.Transforms != null) { using (var matrix = MarkerElement.Transforms.GetMatrix()) transMatrix.Multiply(matrix); } markerPath.Transform(transMatrix); if (pRenderPen != null) { pRenderer.DrawPath(pRenderPen, markerPath); } SvgPaintServer pFill = this.Children.First().Fill; SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for? if (pFill != null) { using (var pBrush = pFill.GetBrush(this, pRenderer, FixOpacityValue(FillOpacity))) { pRenderer.FillPath(pBrush, markerPath); } } } } } }
//========================================================================== public SvgDrawableContainerBaseElement(SvgDocument document, SvgBaseElement parent, XElement drawableContainerElement) : base(document, parent, drawableContainerElement) { XAttribute viewBox_attribute = drawableContainerElement.Attribute("viewBox"); if (viewBox_attribute != null) { this.ViewBox = SvgViewbox.Parse(viewBox_attribute.Value); } XAttribute opacity_attribute = drawableContainerElement.Attribute("opacity"); SvgLength.TryUpdate(ref Opacity, opacity_attribute?.Value); XAttribute transform_attribute = drawableContainerElement.Attribute("transform"); if (transform_attribute != null) { Transform = SvgTransform.Parse(transform_attribute.Value); } XAttribute clip_attribute = drawableContainerElement.Attribute("clip-path"); if (clip_attribute != null) { ClipPath = SvgURL.Parse(clip_attribute.Value); } XAttribute filter_attribute = drawableContainerElement.Attribute("filter"); if (filter_attribute != null) { Filter = SvgURL.Parse(filter_attribute.Value); } XAttribute mask_attribute = drawableContainerElement.Attribute("mask"); if (mask_attribute != null) { Mask = SvgURL.Parse(mask_attribute.Value); } XAttribute display_attribute = drawableContainerElement.Attribute("display"); if (display_attribute != null) { switch (display_attribute.Value) { case "inline": Display = SvgDisplay.Inline; break; case "block": Display = SvgDisplay.Block; break; case "list-item": Display = SvgDisplay.ListItem; break; case "run-in": Display = SvgDisplay.RunIn; break; case "compact": Display = SvgDisplay.Compact; break; case "marker": Display = SvgDisplay.Marker; break; case "table": Display = SvgDisplay.Table; break; case "inline-table": Display = SvgDisplay.InlineTable; break; case "table-row-group": Display = SvgDisplay.TableRowGroup; break; case "table-header-group": Display = SvgDisplay.TableHeaderGroup; break; case "table-footer-group": Display = SvgDisplay.TableFooterGroup; break; case "table-row": Display = SvgDisplay.TableRow; break; case "table-column-group": Display = SvgDisplay.TableColumnGroup; break; case "table-column": Display = SvgDisplay.TableColumn; break; case "table-cell": Display = SvgDisplay.TableCell; break; case "table-caption": Display = SvgDisplay.TableCaption; break; case "none": Display = SvgDisplay.None; break; default: throw new NotImplementedException(); } } XAttribute fill_opacity_attribute = drawableContainerElement.Attribute("fill-opacity"); SvgLength.TryUpdate(ref FillOpacity, fill_opacity_attribute?.Value); XAttribute stroke_opacity_attribute = drawableContainerElement.Attribute("stroke-opacity"); SvgLength.TryUpdate(ref StrokeOpacity, stroke_opacity_attribute?.Value); XAttribute fill_attribute = drawableContainerElement.Attribute("fill"); if (fill_attribute != null) { Fill = SvgPaint.Parse(fill_attribute.Value); } XAttribute stroke_attribute = drawableContainerElement.Attribute("stroke"); if (stroke_attribute != null) { Stroke = SvgPaint.Parse(stroke_attribute.Value); } XAttribute stroke_width_attribute = drawableContainerElement.Attribute("stroke-width"); SvgLength.TryUpdate(ref StrokeWidth, stroke_width_attribute?.Value); XAttribute stroke_linecap_attribute = drawableContainerElement.Attribute("stroke-linecap"); if (stroke_linecap_attribute != null) { switch (stroke_linecap_attribute.Value) { case "butt": StrokeLinecap = SvgStrokeLinecap.Butt; break; case "round": StrokeLinecap = SvgStrokeLinecap.Round; break; case "square": StrokeLinecap = SvgStrokeLinecap.Square; break; case "inherit": StrokeLinecap = SvgStrokeLinecap.Inherit; break; default: throw new NotImplementedException(); } } XAttribute stroke_linejoin_attribute = drawableContainerElement.Attribute("stroke-linejoin"); if (stroke_linejoin_attribute != null) { switch (stroke_linejoin_attribute.Value) { case "miter": StrokeLinejoin = SvgStrokeLinejoin.Miter; break; case "round": StrokeLinejoin = SvgStrokeLinejoin.Round; break; case "bevel": StrokeLinejoin = SvgStrokeLinejoin.Bevel; break; case "inherit": StrokeLinejoin = SvgStrokeLinejoin.Inherit; break; default: throw new NotSupportedException(); } } XAttribute stroke_miterlimit_attribute = drawableContainerElement.Attribute("stroke-miterlimit"); if (stroke_miterlimit_attribute != null) { if (stroke_miterlimit_attribute.Value == "inherit") { StrokeMiterlimit = Double.NaN; } else { double miterlimit = Double.Parse(stroke_miterlimit_attribute.Value, CultureInfo.InvariantCulture.NumberFormat); //if(miterlimit < 1) //throw new NotSupportedException("A miterlimit less than 1 is not supported."); StrokeMiterlimit = miterlimit; } } XAttribute stroke_dasharray_attribute = drawableContainerElement.Attribute("stroke-dasharray"); if (stroke_dasharray_attribute != null) { if (stroke_dasharray_attribute.Value == "none") { StrokeDasharray = null; } else if (stroke_dasharray_attribute.Value == "inherit") { StrokeDasharray = new SvgLength[0]; } else { List <SvgLength> lengths = new List <SvgLength>(); var lengthTokens = stroke_dasharray_attribute.Value.Replace(";", "") .Trim() .Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string length in lengthTokens) { lengths.Add(SvgLength.Parse(length)); } if (lengths.Count % 2 == 1) { StrokeDasharray = new SvgLength[lengths.Count * 2]; for (int i = 0; i < lengths.Count - 1; ++i) { StrokeDasharray[i] = lengths[i]; StrokeDasharray[i + lengths.Count] = lengths[i]; } } else { StrokeDasharray = lengths.ToArray(); } } } XAttribute stroke_dashoffset_attribute = drawableContainerElement.Attribute("stroke-dashoffset"); SvgLength.TryUpdate(ref StrokeDashoffset, stroke_dashoffset_attribute?.Value); XAttribute fill_rule_attribute = drawableContainerElement.Attribute("fill-rule"); if (fill_rule_attribute != null) { switch (fill_rule_attribute.Value) { case "nonzero": FillRule = SvgFillRule.Nonzero; break; case "evenodd": FillRule = SvgFillRule.Evenodd; break; case "inherit": FillRule = SvgFillRule.Inherit; break; default: throw new NotImplementedException(); } } // color, color-interpolation, color-rendering XAttribute width_attribute = drawableContainerElement.Attribute("width"); SvgLength.TryUpdate(ref Width, width_attribute?.Value); XAttribute height_attribute = drawableContainerElement.Attribute("height"); SvgLength.TryUpdate(ref Height, height_attribute?.Value); XAttribute preserveAspectRatio_attribute = drawableContainerElement.Attribute("preserveAspectRatio"); if (preserveAspectRatio_attribute != null) { switch (preserveAspectRatio_attribute.Value) { case "none": if (Width != null && Height != null) { var scaleTransform = new SvgScaleTransform( Width.ToDouble() / ViewBox.Value.Width, Height.ToDouble() / ViewBox.Value.Height); Width.ToDouble(); if (Transform == null) { Transform = scaleTransform; } else { Transform = new SvgTransformGroup(new[] { Transform, scaleTransform }); } } break; } } // overflow }
//========================================================================== public SvgDrawableBaseElement(SvgDocument document, SvgBaseElement parent, XElement drawableBaseElement) : base(document, parent, drawableBaseElement) { XAttribute opacity_attribute = drawableBaseElement.Attribute("opacity"); if(opacity_attribute != null) Opacity = SvgLength.Parse(opacity_attribute.Value); XAttribute fill_opacity_attribute = drawableBaseElement.Attribute("fill-opacity"); if(fill_opacity_attribute != null) FillOpacity = SvgLength.Parse(fill_opacity_attribute.Value); XAttribute stroke_opacity_attribute = drawableBaseElement.Attribute("stroke-opacity"); if(stroke_opacity_attribute != null) StrokeOpacity = SvgLength.Parse(stroke_opacity_attribute.Value); XAttribute transform_attribute = drawableBaseElement.Attribute("transform"); if(transform_attribute != null) Transform = SvgTransform.Parse(transform_attribute.Value); XAttribute fill_attribute = drawableBaseElement.Attribute("fill"); if(fill_attribute != null) Fill = SvgPaint.Parse(fill_attribute.Value); XAttribute stroke_attribute = drawableBaseElement.Attribute("stroke"); if(stroke_attribute != null) Stroke = SvgPaint.Parse(stroke_attribute.Value); XAttribute stroke_width_attribute = drawableBaseElement.Attribute("stroke-width"); if(stroke_width_attribute != null) StrokeWidth = SvgLength.Parse(stroke_width_attribute.Value); XAttribute stroke_linecap_attribute = drawableBaseElement.Attribute("stroke-linecap"); if(stroke_linecap_attribute != null) switch(stroke_linecap_attribute.Value) { case "butt": StrokeLinecap = SvgStrokeLinecap.Butt; break; case "round": StrokeLinecap = SvgStrokeLinecap.Round; break; case "square": StrokeLinecap = SvgStrokeLinecap.Square; break; case "inherit": StrokeLinecap = SvgStrokeLinecap.Inherit; break; default: throw new NotImplementedException(); } XAttribute stroke_linejoin_attribute = drawableBaseElement.Attribute("stroke-linejoin"); if(stroke_linejoin_attribute != null) switch(stroke_linejoin_attribute.Value) { case "miter": StrokeLinejoin = SvgStrokeLinejoin.Miter; break; case "round": StrokeLinejoin = SvgStrokeLinejoin.Round; break; case "bevel": StrokeLinejoin = SvgStrokeLinejoin.Bevel; break; case "inherit": StrokeLinejoin = SvgStrokeLinejoin.Inherit; break; default: throw new NotSupportedException(); } XAttribute stroke_miterlimit_attribute = drawableBaseElement.Attribute("stroke-miterlimit"); if(stroke_miterlimit_attribute != null) { if(stroke_miterlimit_attribute.Value == "inherit") StrokeMiterlimit = Double.NaN; else { double miterlimit = Double.Parse(stroke_miterlimit_attribute.Value, CultureInfo.InvariantCulture.NumberFormat); //if(miterlimit < 1) //throw new NotSupportedException("A miterlimit less than 1 is not supported."); StrokeMiterlimit = miterlimit; } } XAttribute stroke_dasharray_attribute = drawableBaseElement.Attribute("stroke-dasharray"); if(stroke_dasharray_attribute != null) { if(stroke_dasharray_attribute.Value == "none") StrokeDasharray = null; else if(stroke_dasharray_attribute.Value == "inherit") StrokeDasharray = new SvgLength[0]; else { List<SvgLength> lengths = new List<SvgLength>(); foreach(string length in stroke_dasharray_attribute.Value.Split(',')) lengths.Add(SvgLength.Parse(length)); if(lengths.Count % 2 == 1) { StrokeDasharray = new SvgLength[lengths.Count * 2]; for(int i = 0; i < lengths.Count - 1; ++i) { StrokeDasharray[i] = lengths[i]; StrokeDasharray[i + lengths.Count] = lengths[i]; } } else StrokeDasharray = lengths.ToArray(); } } XAttribute stroke_dashoffset_attribute = drawableBaseElement.Attribute("stroke-dashoffset"); if(stroke_dashoffset_attribute != null) StrokeDashoffset = SvgLength.Parse(stroke_dashoffset_attribute.Value); XAttribute clip_attribute = drawableBaseElement.Attribute("clip-path"); if(clip_attribute != null) { string clip_path = clip_attribute.Value.Trim(); if(clip_path.StartsWith("url")) { clip_path = clip_path.Substring(3).Trim(); if(clip_path.StartsWith("(") && clip_path.EndsWith(")")) { clip_path = clip_path.Substring(1, clip_path.Length - 2).Trim(); if(clip_path.StartsWith("#")) ClipPath = clip_path.Substring(1); } } } XAttribute filter_attribute = drawableBaseElement.Attribute("filter"); if(filter_attribute != null) { string filter = filter_attribute.Value.Trim(); if(filter.StartsWith("url")) { filter = filter.Substring(3).Trim(); if(filter.StartsWith("(") && filter.EndsWith(")")) { filter = filter.Substring(1, filter.Length - 2).Trim(); if(filter.StartsWith("#")) Filter = filter.Substring(1); } } } XAttribute mask_attribute = drawableBaseElement.Attribute("mask"); if(mask_attribute != null) { string mask = mask_attribute.Value.Trim(); if(mask.StartsWith("url")) { mask = mask.Substring(3).Trim(); if(mask.StartsWith("(") && mask.EndsWith(")")) { mask = mask.Substring(1, mask.Length - 2).Trim(); if(mask.StartsWith("#")) Mask = mask.Substring(1); } } } XAttribute display_attribute = drawableBaseElement.Attribute("display"); if(display_attribute != null) switch(display_attribute.Value) { case "inline": Display = SvgDisplay.Inline; break; case "block": Display = SvgDisplay.Block; break; case "list-item": Display = SvgDisplay.ListItem; break; case "run-in": Display = SvgDisplay.RunIn; break; case "compact": Display = SvgDisplay.Compact; break; case "marker": Display = SvgDisplay.Marker; break; case "table": Display = SvgDisplay.Table; break; case "inline-table": Display = SvgDisplay.InlineTable; break; case "table-row-group": Display = SvgDisplay.TableRowGroup; break; case "table-header-group": Display = SvgDisplay.TableHeaderGroup; break; case "table-footer-group": Display = SvgDisplay.TableFooterGroup; break; case "table-row": Display = SvgDisplay.TableRow; break; case "table-column-group": Display = SvgDisplay.TableColumnGroup; break; case "table-column": Display = SvgDisplay.TableColumn; break; case "table-cell": Display = SvgDisplay.TableCell; break; case "table-caption": Display = SvgDisplay.TableCaption; break; case "none": Display = SvgDisplay.None; break; default: throw new NotImplementedException(); } XAttribute fill_rule_attribute = drawableBaseElement.Attribute("fill-rule"); if(fill_rule_attribute != null) switch(fill_rule_attribute.Value) { case "nonzero": FillRule = SvgFillRule.Nonzero; break; case "evenodd": FillRule = SvgFillRule.Evenodd; break; case "inherit": FillRule = SvgFillRule.Inherit; break; default: throw new NotImplementedException(); } // color, color-interpolation, color-rendering // viewBox attribute // preserveAspectRatio attribute // overflow foreach(XElement element in from element in drawableBaseElement.Elements() where element.Name.NamespaceName == "http://www.w3.org/2000/svg" select element) switch(element.Name.LocalName) { default: throw new NotImplementedException(String.Format("Unhandled element: {0}", element)); } }