public static bool TryGetValueInPx(XAttribute attribute, out float value) { value = float.NaN; var text = attribute?.Value; if (string.IsNullOrWhiteSpace(text)) { return(false); } var valueText = ""; string unit = null; foreach (var c in text.Trim()) { if (char.IsLetter(c) && char.ToLower(c) != 'e') { unit += c; } else if (string.IsNullOrWhiteSpace(unit)) { valueText += c; } } if (string.IsNullOrWhiteSpace(unit)) { return(float.TryParse(valueText, out value)); } var orientation = Orientation.Unknown; if (text.Contains("%")) { switch (attribute.Name.ToString()) { case "x": case "cx": case "rx": case "x1": case "x2": case "width": orientation = Orientation.Horizontal; break; case "y": case "cy": case "ry": case "y1": case "y2": case "height": orientation = Orientation.Vertical; break; default: throw new ArgumentException("unexpected % in unit [" + attribute.Name + "] in element <" + attribute.Parent?.Name + " id='" + attribute.Parent?.Attribute("id")?.Value + "'>"); } } return(ElementExtensions.TryGetValueInPx(attribute.Parent, text, orientation, out value)); }
public static void ProcessStyleValue(XElement svgElement, string styleText, BaseElement avElement, List <string> warnings, bool local = false) { if (!string.IsNullOrWhiteSpace(styleText)) { var styleParts = styleText.Split(';'); foreach (var part in styleParts) { if (string.IsNullOrWhiteSpace(part)) { continue; } var tokens = part.Split(':'); if (tokens.Length == 2) { var cmd = tokens[0].Trim(); var value = tokens[1].Trim(); if (AttributeMap.TryGetValue(cmd, out string avAtrName)) { /* * if (value == "inherit" && svgElement.InheritedAttributeValue(cmd) is string inheritedValue) * { * // do nothing because style attributes are already inherited (and thus, should have already been set before reaching here) * } * else */ if (cmd == "fill" || cmd == "stroke") { SetColorStyleValue(cmd, avAtrName, value, svgElement, avElement, warnings); } else if (cmd == "stroke-fill") { SetColorStyleValue("fill", AttributeMap["fill"], value, svgElement, avElement, warnings); SetColorStyleValue("stroke", AttributeMap["stroke"], value, svgElement, avElement, warnings); } /* * { * if (value == "none") * { * avElement.SetAndroidAttributeValue(avAtrName, value); * } * else if (value.StartsWith("url(")) * { * if (!value.StartsWith("url(#")) * throw new Exception("Only anchor URLs are supported at this time."); * * //var iri = value.Substring("url(#".Length).Trim(')').Trim(); * var iri = value.SubstringWithTerminator("url(#".Length, ')').Trim(); * if (svgElement.GetRoot() is XElement root) * { * if (root.Descendants(Namespace.Svg + "linearGradient").FirstOrDefault(e => e.Attribute("id").Value == iri) is XElement svgLinearGradient) * { * if (PaintConverter.ConvertLinearGradient(svgLinearGradient, warnings) is LinearGradient avGradient) * { * if (avElement.Element(AndroidVector.Namespace.Aapt + "attr") is XElement aaptAttr) * aaptAttr.Remove(); * var aapt = new AaptAttr(cmd + "Color", avGradient); * avElement.Add(aapt); * avElement.SetAndroidAttributeValue(avAtrName, null); * return; * } * } * if (root.Descendants(Namespace.Svg + "radialGradient").FirstOrDefault(e => e.Attribute("id").Value == iri) is XElement svgRadialGradient) * { * if (PaintConverter.ConvertRadialGradient(svgRadialGradient, warnings) is RadialGradient avGradient) * { * if (avElement.Element(AndroidVector.Namespace.Aapt + "attr") is XElement aaptAttr) * aaptAttr.Remove(); * var aapt = new AaptAttr(cmd+"Color", avGradient); * avElement.Add(aapt); * avElement.SetAndroidAttributeValue(avAtrName, null); * return; * } * } * warnings.AddWarning("Ignoring gradient because no element found to complete link [" + value + "]."); * return; * } * throw new Exception("Could not find document root"); * * } * else * { * var (hexColor, opacity) = GetHexColorAndFloatOpacity(value, warnings); * avElement.SetAndroidAttributeValue(avAtrName, hexColor); * if (!float.IsNaN(opacity)) * avElement.SetAndroidAttributeValue(cmd + "Alpha", opacity); * } * * } */ else if (cmd == "stroke-width") { ElementExtensions.TryGetValueInPx(svgElement, value, Orientation.Unknown, out float strokeWidth); avElement.SetAndroidAttributeValue(avAtrName, strokeWidth); } else if (cmd == "fill-rule") { if (value == "evenodd") { avElement.SetAndroidAttributeValue(avAtrName, AndroidVector.FillType.EvenOdd.ToString().ToCamelCase()); } else if (value == "nonzero") { avElement.SetAndroidAttributeValue(avAtrName, AndroidVector.FillType.NonZero.ToString().ToCamelCase()); } else { warnings.AddWarning("Ignoring fill-rule because value [" + value + "] in <" + svgElement?.Name + " id='" + svgElement?.Attribute("id")?.Value + "'> is unexpected."); } } else if (cmd == "stroke-linecap" || cmd == "stroke-linejoin") { avElement.SetAndroidAttributeValue(avAtrName, value.ToCamelCase()); var attr = avElement.AndroidAttribute(avAtrName); if (avElement is AndroidVector.Path path) { var cap = path.StrokeLineCap; var join = path.StrokeLineJoin; } } else { avElement.SetAndroidAttributeValue(avAtrName, value); } } else if (cmd == "display") { avElement.SvgDisplayStyle = value; } else if (cmd == "visibility") { avElement.SvgDisplayStyle = (value == "hidden" || value == "collapse") ? "none" : "visible"; } else if (cmd == "opacity" && local) { if (float.TryParse(value, out float opacity)) { avElement.SvgOpacity = opacity; } /* * { * if (avElement.AndroidAttribute("fillAlpha") is XAttribute fillAlphaAttribute && * float.TryParse(fillAlphaAttribute.Value, out float fillAlpha)) * avElement.SetAndroidAttributeValue("fillAlpha", fillAlpha * opacity); * else * avElement.SetAndroidAttributeValue("fillAlpha", opacity); * if (avElement.AndroidAttribute("strokeAlpha") is XAttribute strokeAlphaAttribute && * float.TryParse(strokeAlphaAttribute.Value, out float strokeAlpha)) * avElement.SetAndroidAttributeValue("strokeAlpha", strokeAlpha * opacity); * else * avElement.SetAndroidAttributeValue("strokeAlpha", opacity); * } */ } else if (!IgnoreAttributeMap.Contains(cmd)) { warnings.AddWarning("Ignoring SVG style [" + cmd + "] because could not map to an AndroidVector attribute."); } } else { warnings.AddWarning("Ignoring SVG style [" + part + "] in <" + svgElement?.Name + " id='" + svgElement?.Attribute("id")?.Value + "'> because could not parce into a style and a value."); } } } }
/// <summary> /// Creates AndroidVector.Clip child from Clip attribute of SVG element /// </summary> /// <param name="svgSvgElement"></param> /// <param name="avVector"></param> /// <param name="warnings"></param> /// <returns></returns> public static void ConvertClipAttribute(XElement svgSvgElement, AndroidVector.BaseElement av, List <string> warnings) { const string svgTypeName = "svg"; if (svgSvgElement.Name != Namespace.Svg + svgTypeName) { throw new ArgumentException("Only applicable to SVG <svg> element"); } if (av.Name != "vector" && av.Name != "group") { throw new ArgumentException("Only applicable to Android <vector> and <group> elements."); } if (av.Attribute("clip") is XAttribute clipAttribute) { if (clipAttribute.Value == "auto") { if (svgSvgElement.Attribute("viewBox") is XAttribute svgViewBoxAttribute) { var args = svgViewBoxAttribute.Value.Split(new char[] { ' ', ',' }); if (args.Length < 1 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[0], Orientation.Horizontal, out float x)) { warnings.AddWarning("Ignoring <svg clip='auto'> because 'viewBox' does not contain x coordinate."); return; } if (args.Length < 2 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[1], Orientation.Vertical, out float y)) { warnings.AddWarning("Ignoring <svg clip='auto'> because 'viewBox' does not contain y coordinate."); return; } if (args.Length < 3 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[2], Orientation.Horizontal, out float w)) { warnings.AddWarning("Ignoring <svg clip='auto'> because 'viewBox' does not contain width."); return; } if (args.Length < 4 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[3], Orientation.Vertical, out float h)) { warnings.AddWarning("Ignoring <svg clip='auto'> because 'viewBox' does not contain height."); return; } string pathData = " M " + x + "," + y; pathData += " v " + h; pathData += " h " + w; pathData += " v " + (-h); pathData += " z "; av.Add(new XElement("clip-path", new AndroidVector.AndroidAttribute("pathData", pathData))); } else { warnings.AddWarning("Ignoring <svg clip='auto'> because 'viewBox' attribute is not present."); return; } } else if (clipAttribute.Value.StartsWith("rect(")) { // top, right, bottom, left var args = clipAttribute.Value.Split(new char[] { ' ', ',', '(', ')' }); if (args.Length < 2 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[1], Orientation.Vertical, out float top)) { warnings.AddWarning("Ignoring <svg clip='rect(...)'> because 'rect' does not contain top coordinate."); return; } if (args.Length < 3 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[2], Orientation.Horizontal, out float right)) { warnings.AddWarning("Ignoring <svg clip='rect(...)'> because 'rect' does not contain right coordinate."); return; } if (args.Length < 4 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[3], Orientation.Vertical, out float bottom)) { warnings.AddWarning("Ignoring <svg clip='rect(...)'> because 'rect' does not contain bottom coordinate."); return; } if (args.Length < 5 || !ElementExtensions.TryGetValueInPx(svgSvgElement, args[4], Orientation.Horizontal, out float left)) { warnings.AddWarning("Ignoring <svg clip='rect(...)'> because 'rect' does not contain left coordinate."); return; } string pathData = " M " + top + "," + left; pathData += " V " + bottom; pathData += " H " + right; pathData += " V " + top; pathData += " Z "; av.Add(new XElement("clip-path", new AndroidVector.AndroidAttribute("pathData", pathData))); } else { warnings.AddWarning("Ignoring <svg clip='" + clipAttribute.Value + "'> because '" + clipAttribute.Value + "' is an unexpected attribute value."); return; } } }