public static AndroidVector.Vector ConvertSvg(XDocument svgDocument, List <string> warnings) { var svgElement = svgDocument.Root; if (svgDocument.Root.Name != Namespace.Svg + "svg") { throw new ArgumentException("document must have <svg> root"); } var vector = new AndroidVector.Vector(); AndroidVector.Group avGroup = vector; float width = 0, height = 0; if (svgElement.Attribute("width") is XAttribute widthAttribute && AttributeExtensions.TryGetValueInPx(widthAttribute, out width)) { vector.Width = new AndroidVector.UnitizedFloat(width, AndroidVector.Unit.Px); }
public static AndroidVector.Group AddClipPathElement(this XElement svgElement, AndroidVector.Group avGroup, List <string> warnings) { string typeName = svgElement.Name.LocalName; avGroup = ClipConverter.ConvertClipPathAttribute(svgElement, avGroup, warnings); CommonAttributes.ProcessAttributes(svgElement, avGroup, null, warnings); var avClip = new AndroidVector.ClipPath(); foreach (var child in svgElement.Elements()) { if (child.Name == Namespace.Svg + "use") { float tx = 0, ty = 0; if (child.Attribute("x") is XAttribute xAttribute) { AttributeExtensions.TryGetValueInPx(xAttribute, out tx); } if (child.Attribute("y") is XAttribute yAttribute) { AttributeExtensions.TryGetValueInPx(yAttribute, out ty); } if (tx != 0 || ty != 0) { avGroup.SvgTransforms.Add(Matrix.CreateTranslate(tx, ty)); } if (child.Attribute(Namespace.xlinkNs + "href") is XAttribute hrefAttribute) { if (hrefAttribute.Value.StartsWith("#")) { var href = hrefAttribute.Value.Trim(new char[] { '#', ' ' }); var root = child.GetRoot(); if (root.Descendants().Where(e => e.Attribute("id")?.Value == href).FirstOrDefault() is XElement useElement) { if (useElement.Name == Namespace.Svg + "clipPath") { avGroup = ClipConverter.ConvertClipPathAttribute(useElement, avGroup, warnings); CommonAttributes.ProcessAttributes(useElement, avGroup, new List <string> { "x", "y" }, warnings); AddClipPathElement(useElement, avGroup, warnings); } else if (GeometryConverter.ConvertElementToPathData(useElement, warnings) is string pathData) { avClip.PathData += pathData; warnings.AddWarning("A union of two (or more) clippaths has been found. If these clippaths overlap, you're likely not going to like the result. Suggestion: Use a vector image editor (like InkScape) to alter your union of clippaths to be one path, without crossing segments."); } else { warnings.AddWarning("Ignoring <" + useElement.Name.LocalName + " id='" + useElement.Attribute("id")?.Value + "'> inside of <" + child.Name.LocalName + " id='" + child.Attribute("id")?.Value + "' xlink:href='" + hrefAttribute.Value + "' because could not find element referenced by xlink:href attribute."); } } else { warnings.AddWarning("Ignoring <use id='" + svgElement.Attribute("id")?.Value + "' xlink:href='" + hrefAttribute.Value + "' because could not find element referenced by xlink:href attribute."); } } else { warnings.AddWarning("Ignoring <use id='" + svgElement.Attribute("id")?.Value + "' xlink:href='" + hrefAttribute.Value + "' because xlink:href attribute is not a local anchor."); } } else { warnings.AddWarning("Ignoring <use id='" + svgElement.Attribute("id")?.Value + "'> because cannot find xlink:href attribute."); } // var tmpGroup = new AndroidVector.Group(); } else if (GeometryConverter.ConvertElementToPathData(child, warnings) is string pathData) { avClip.PathData += pathData; } } if (!string.IsNullOrWhiteSpace(avClip.PathData)) { CommonAttributes.SetTransforms(svgElement, avClip, warnings); avGroup.Add(avClip); } return(avGroup); }
//TODO: Process clip paths after all of the SVG has been converted (just like gradients) public static AndroidVector.Group ConvertClipPathAttribute(this XElement svgElement, AndroidVector.Group avGroup, List <string> warnings) { if (svgElement.Attribute("clip-path") is XAttribute svgAttribute) { var svgAttributeValue = svgAttribute.Value.Trim(); if (svgAttributeValue == "inherit") { var parent = svgAttribute.Parent.Parent; while (parent != null) { if (parent.Attribute(svgAttribute.Name) is XAttribute parentAttribute && parentAttribute.Value.Trim() != "inherit") { svgAttributeValue = parentAttribute.Value.Trim(); return(avGroup); } } } if (svgElement.Name != Namespace.Svg + "g" || svgElement.Name != Namespace.Svg + "clipPath") { avGroup = Converter.NestGroup(avGroup); } if (svgAttributeValue.StartsWith("url(")) { if (!svgAttributeValue.StartsWith("url(#")) { throw new Exception("Only anchor URLs are supported at this time."); } var iri = svgAttributeValue.Substring("url(#".Length).Trim(')').Trim(); if (svgElement.GetRoot() is XElement root) { if (root.Descendants(Namespace.Svg + "clipPath").FirstOrDefault(e => e.Attribute("id").Value == iri) is XElement svgClipPathElement) { //AndroidVector.Group result = avGroup is AndroidVector.Group ? avGroup : new AndroidVector.Group(); avGroup = AddClipPathElement(svgClipPathElement, avGroup, warnings); return(avGroup); } warnings.AddWarning("Ignoring clip-path because no element found to complete clipPath link to " + svgAttributeValue + "."); return(avGroup); } throw new Exception("could not find document root"); } else if (GeometryConverter.ConvertCssShapeToPathData(svgElement, svgAttributeValue, warnings) is string pathData) { avGroup.Add(new ClipPath(pathData)); } else { warnings.AddWarning("Ignoring clip-path attribute because could not interpret value : " + svgAttributeValue + "."); } } return(avGroup); }