public static string MoveCssInline(string htmlInput, bool removeStyleElements, Uri basePath) { using (var reader = new System.IO.StringReader(htmlInput)) { var web = new System.Net.WebClient(); var sgmlReader = new Pipes.Sgml.SgmlReader(); sgmlReader.DocType = "HTML"; sgmlReader.WhitespaceHandling = WhitespaceHandling.All; sgmlReader.CaseFolding = CaseFolding.ToLower; sgmlReader.InputStream = reader; sgmlReader.SimulatedNode = "html"; sgmlReader.StripDocType = false; var doc = new XmlDocument(); doc.Load(sgmlReader); var root = doc.DocumentElement; if (root.ChildNodes.OfType<XmlElement>().Count() == 1) root = root.ChildNodes.OfType<XmlElement>().Single(); var parser = new Parser(); var engine = new HtmlQueryEngine(); var visitor = new MatchVisitor(engine) { Comparison = StringComparison.OrdinalIgnoreCase }; var css = new StringBuilder(); var nodes = doc.SelectNodes("//style").OfType<System.Xml.XmlNode>().ToList(); foreach (var node in nodes) { css.AppendLine(node.InnerText); if (removeStyleElements) node.ParentNode.RemoveChild(node); } nodes = doc.SelectNodes("//link[@rel='stylesheet' and @href]").OfType<System.Xml.XmlNode>().ToList(); foreach (var node in nodes) { css.Append("@import url('").Append(node.Attributes["href"].Value).Append("')"); if (node.Attributes["media"] != null && !string.IsNullOrEmpty(node.Attributes["media"].Value)) { css.Append(" ").Append(node.Attributes["media"].Value); } css.AppendLine(";"); if (removeStyleElements) node.ParentNode.RemoveChild(node); } var stylesheet = parser.Parse(css.ToString()); var elements = root.SelectNodes("//*").OfType<XmlElement>().Select(e => new Pipes.Xml.XmlElementWrapper(e)).ToList(); var settings = new GlobalStyleContext(); settings.ResourceLoader = p => { var href = (basePath == null ? new Uri(p) : new Uri(basePath, p)); return DownloadStream(href); }; var rules = stylesheet.Rules.GetStyleRules(settings).ToList(); IEnumerable<StyleRule> elemRules; string inlineStyle; IEnumerable<Property> props; foreach (var elem in elements) { inlineStyle = elem.Attribute<string>("style", null); elemRules = rules; if (!string.IsNullOrEmpty(inlineStyle)) { elemRules = elemRules.Concat(Enumerable.Repeat(Utils.ParseInline(inlineStyle), 1)); } props = elemRules.ApplicableProperties(elem, engine, StringComparison.OrdinalIgnoreCase, settings); if (props.Any()) { inlineStyle = props.Select(p => p.ToString()).Aggregate((p, c) => p + ";" + c); elem.Attribute("style", inlineStyle); } } return doc.OuterXml; } }
public void Visit(PseudoFunction selector) { switch (selector.Type) { case PseudoTypes.FunctionNot: var visitor = new MatchVisitor(new HtmlQueryEngine()); var sel = selector.Body as ISelector; _matches = _matches.Where(n => { visitor.Initialize(n); sel.Visit(visitor); return !visitor.IsMatch(); }); break; case PseudoTypes.FunctionContains: case PseudoTypes.FunctionDir: default: NotSupported(); break; } }
public static IEnumerable<Property> ApplicableProperties(this IEnumerable<StyleRule> rules, IQueryableNode node, IQueryEngine engine, StringComparison comparison, GlobalStyleContext settings) { var visitor = new MatchVisitor(engine); visitor.Comparison = comparison; var terms = new Dictionary<string, TermSpecificity>(); TermSpecificity existing; int specificity = 0; int propSpecificity; foreach (var style in rules) { if (style.Selector != null) { visitor.Initialize(node); style.Selector.Visit(visitor); } if (style.Selector == null || visitor.IsMatch()) { specificity = style.Selector == null ? 0 : (visitor.MatchSpecificity > 0 ? visitor.MatchSpecificity : style.Selector.GetSpecificity()); foreach (var prop in style.Declarations.SelectMany(p => p.Expand(settings.LeftToRight))) { propSpecificity = specificity + (prop.Important ? (1 << 20) : 0); if (terms.TryGetValue(prop.Name, out existing)) { if (propSpecificity >= existing.Specificity) { existing.Property = prop; } } else { terms[prop.Name] = new TermSpecificity() { Property = prop, Specificity = propSpecificity }; } } } } return terms.Values.Select(v => v.Property); }