HtmlForeignExtensions
A collection of useful helpers when working with foreign content.
using AngleSharp.Dom;
using AngleSharp.Dom.Mathml;
using AngleSharp.Dom.Svg;
using AngleSharp.Extensions;
using AngleSharp.Html;
using System;
using System.Collections.Generic;
namespace AngleSharp.Parser.Html
{
internal static class HtmlForeignExtensions
{
private static readonly Dictionary<string, string> svgAttributeNames = new Dictionary<string, string>(StringComparer.Ordinal) {
{
"attributename",
"attributeName"
},
{
"attributetype",
"attributeType"
},
{
"basefrequency",
"baseFrequency"
},
{
"baseprofile",
"baseProfile"
},
{
"calcmode",
"calcMode"
},
{
"clippathunits",
"clipPathUnits"
},
{
"contentscripttype",
"contentScriptType"
},
{
"contentstyletype",
"contentStyleType"
},
{
"diffuseconstant",
"diffuseConstant"
},
{
"edgemode",
"edgeMode"
},
{
"externalresourcesrequired",
"externalResourcesRequired"
},
{
"filterres",
"filterRes"
},
{
"filterunits",
"filterUnits"
},
{
"glyphref",
"glyphRef"
},
{
"gradienttransform",
"gradientTransform"
},
{
"gradientunits",
"gradientUnits"
},
{
"kernelmatrix",
"kernelMatrix"
},
{
"kernelunitlength",
"kernelUnitLength"
},
{
"keypoints",
"keyPoints"
},
{
"keysplines",
"keySplines"
},
{
"keytimes",
"keyTimes"
},
{
"lengthadjust",
"lengthAdjust"
},
{
"limitingconeangle",
"limitingConeAngle"
},
{
"markerheight",
"markerHeight"
},
{
"markerunits",
"markerUnits"
},
{
"markerwidth",
"markerWidth"
},
{
"maskcontentunits",
"maskContentUnits"
},
{
"maskunits",
"maskUnits"
},
{
"numoctaves",
"numOctaves"
},
{
"pathlength",
"pathLength"
},
{
"patterncontentunits",
"patternContentUnits"
},
{
"patterntransform",
"patternTransform"
},
{
"patternunits",
"patternUnits"
},
{
"pointsatx",
"pointsAtX"
},
{
"pointsaty",
"pointsAtY"
},
{
"pointsatz",
"pointsAtZ"
},
{
"preservealpha",
"preserveAlpha"
},
{
"preserveaspectratio",
"preserveAspectRatio"
},
{
"primitiveunits",
"primitiveUnits"
},
{
"refx",
"refX"
},
{
"refy",
"refY"
},
{
"repeatcount",
"repeatCount"
},
{
"repeatdur",
"repeatDur"
},
{
"requiredextensions",
"requiredExtensions"
},
{
"requiredfeatures",
"requiredFeatures"
},
{
"specularconstant",
"specularConstant"
},
{
"specularexponent",
"specularExponent"
},
{
"spreadmethod",
"spreadMethod"
},
{
"startoffset",
"startOffset"
},
{
"stddeviation",
"stdDeviation"
},
{
"stitchtiles",
"stitchTiles"
},
{
"surfacescale",
"surfaceScale"
},
{
"systemlanguage",
"systemLanguage"
},
{
"tablevalues",
"tableValues"
},
{
"targetx",
"targetX"
},
{
"targety",
"targetY"
},
{
"textlength",
"textLength"
},
{
"viewbox",
"viewBox"
},
{
"viewtarget",
"viewTarget"
},
{
"xchannelselector",
"xChannelSelector"
},
{
"ychannelselector",
"yChannelSelector"
},
{
"zoomandpan",
"zoomAndPan"
}
};
private static readonly Dictionary<string, string> svgAdjustedTagNames = new Dictionary<string, string>(StringComparer.Ordinal) {
{
"altglyph",
"altGlyph"
},
{
"altglyphdef",
"altGlyphDef"
},
{
"altglyphitem",
"altGlyphItem"
},
{
"animatecolor",
"animateColor"
},
{
"animatemotion",
"animateMotion"
},
{
"animatetransform",
"animateTransform"
},
{
"clippath",
"clipPath"
},
{
"feblend",
"feBlend"
},
{
"fecolormatrix",
"feColorMatrix"
},
{
"fecomponenttransfer",
"feComponentTransfer"
},
{
"fecomposite",
"feComposite"
},
{
"feconvolvematrix",
"feConvolveMatrix"
},
{
"fediffuselighting",
"feDiffuseLighting"
},
{
"fedisplacementmap",
"feDisplacementMap"
},
{
"fedistantlight",
"feDistantLight"
},
{
"feflood",
"feFlood"
},
{
"fefunca",
"feFuncA"
},
{
"fefuncb",
"feFuncB"
},
{
"fefuncg",
"feFuncG"
},
{
"fefuncr",
"feFuncR"
},
{
"fegaussianblur",
"feGaussianBlur"
},
{
"feimage",
"feImage"
},
{
"femerge",
"feMerge"
},
{
"femergenode",
"feMergeNode"
},
{
"femorphology",
"feMorphology"
},
{
"feoffset",
"feOffset"
},
{
"fepointlight",
"fePointLight"
},
{
"fespecularlighting",
"feSpecularLighting"
},
{
"fespotlight",
"feSpotLight"
},
{
"fetile",
"feTile"
},
{
"feturbulence",
"feTurbulence"
},
{
"foreignobject",
"foreignObject"
},
{
"glyphref",
"glyphRef"
},
{
"lineargradient",
"linearGradient"
},
{
"radialgradient",
"radialGradient"
},
{
"textpath",
"textPath"
}
};
public static string SanatizeSvgTagName(this string localName)
{
string value = null;
if (svgAdjustedTagNames.TryGetValue(localName, out value))
return value;
return localName;
}
public static MathElement Setup(this MathElement element, HtmlTagToken tag)
{
int count = tag.Attributes.Count;
for (int i = 0; i < count; i++) {
KeyValuePair<string, string> keyValuePair = tag.Attributes[i];
string key = keyValuePair.Key;
keyValuePair = tag.Attributes[i];
string value = keyValuePair.Value;
element.AdjustAttribute(key.AdjustToMathAttribute(), value);
}
return element;
}
public static SvgElement Setup(this SvgElement element, HtmlTagToken tag)
{
int count = tag.Attributes.Count;
for (int i = 0; i < count; i++) {
KeyValuePair<string, string> keyValuePair = tag.Attributes[i];
string key = keyValuePair.Key;
keyValuePair = tag.Attributes[i];
string value = keyValuePair.Value;
element.AdjustAttribute(key.AdjustToSvgAttribute(), value);
}
return element;
}
public static void AdjustAttribute(this Element element, string name, string value)
{
if (IsXLinkAttribute(name))
element.SetAttribute(NamespaceNames.XLinkUri, name.Substring(name.IndexOf(':') + 1), value);
else if (IsXmlAttribute(name)) {
element.SetAttribute(NamespaceNames.XmlUri, name, value);
} else if (IsXmlNamespaceAttribute(name)) {
element.SetAttribute(NamespaceNames.XmlNsUri, name, value);
} else {
element.SetOwnAttribute(name, value, false);
}
}
public static string AdjustToMathAttribute(this string attributeName)
{
if (attributeName.Is("definitionurl"))
return "definitionURL";
return attributeName;
}
public static string AdjustToSvgAttribute(this string attributeName)
{
string value = null;
if (svgAttributeNames.TryGetValue(attributeName, out value))
return value;
return attributeName;
}
private static bool IsXmlNamespaceAttribute(string name)
{
if (name.Length > 4) {
if (!name.Is(NamespaceNames.XmlNsPrefix))
return name.Is("xmlns:xlink");
return true;
}
return false;
}
private static bool IsXmlAttribute(string name)
{
if (name.Length > 7 && "xml:".EqualsSubset(name, 0, 4)) {
if (!TagNames.Base.EqualsSubset(name, 4, 4) && !AttributeNames.Lang.EqualsSubset(name, 4, 4))
return AttributeNames.Space.EqualsSubset(name, 4, 5);
return true;
}
return false;
}
private static bool IsXLinkAttribute(string name)
{
if (name.Length > 9 && "xlink:".EqualsSubset(name, 0, 6)) {
if (!AttributeNames.Actuate.EqualsSubset(name, 6, 7) && !AttributeNames.Arcrole.EqualsSubset(name, 6, 7) && !AttributeNames.Href.EqualsSubset(name, 6, 4) && !AttributeNames.Role.EqualsSubset(name, 6, 4) && !AttributeNames.Show.EqualsSubset(name, 6, 4) && !AttributeNames.Type.EqualsSubset(name, 6, 4))
return AttributeNames.Title.EqualsSubset(name, 6, 5);
return true;
}
return false;
}
private static bool EqualsSubset(this string a, string b, int index, int length)
{
return string.Compare(a, 0, b, index, length, StringComparison.Ordinal) == 0;
}
}
}