Index: xword/ContentFiltering/ContentFiltering.csproj =================================================================== --- xword/ContentFiltering/ContentFiltering.csproj (revision 22315) +++ xword/ContentFiltering/ContentFiltering.csproj (working copy) @@ -61,6 +61,7 @@ + @@ -98,6 +99,7 @@ + Index: xword/ContentFiltering/Html/CSSUtil.cs =================================================================== --- xword/ContentFiltering/Html/CSSUtil.cs (revision 0) +++ xword/ContentFiltering/Html/CSSUtil.cs (revision 0) @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Collections; + +namespace ContentFiltering.Html +{ + /// + /// Provides static methods for working with CSS. + /// + public class CSSUtil + { + /// + /// Inlines CSS for nodes with an id and/or class attribute. + /// + /// A reference to an XmlDocument. + public static void InlineCSS(ref XmlDocument xmlDoc) + { + XmlNodeList styleNodes = xmlDoc.GetElementsByTagName("style"); + XmlNodeList allElements = xmlDoc.GetElementsByTagName("*"); + Hashtable identifiedCSSClassesAndIDs = ExtractCSSClassesAndIDs(styleNodes); + + foreach (XmlNode element in allElements) + { + XmlAttribute classAttribute = element.Attributes["class"]; + XmlAttribute idAttribute = element.Attributes["id"]; + + if (classAttribute != null) + { + string cssClassName = classAttribute.Value; + + if (identifiedCSSClassesAndIDs.ContainsKey(cssClassName)) + { + XmlAttribute styleAttribute = null; + styleAttribute = element.Attributes["style"]; + if (styleAttribute == null) + { + styleAttribute = element.Attributes.Append(xmlDoc.CreateAttribute("style")); + styleAttribute.Value = ""; + } + styleAttribute.Value += identifiedCSSClassesAndIDs[cssClassName]; + } + } + + if (idAttribute != null) + { + string cssIdName = idAttribute.Value; + if (identifiedCSSClassesAndIDs.ContainsKey(cssIdName)) + { + XmlAttribute styleAttribute = null; + styleAttribute = element.Attributes["style"]; + if (styleAttribute == null) + { + styleAttribute = element.Attributes.Append(xmlDoc.CreateAttribute("style")); + styleAttribute.Value = ""; + } + styleAttribute.Value += identifiedCSSClassesAndIDs[cssIdName]; + } + } + + } + } + + + /// + /// Extracts the CSS classes and ids from the 'style' nodes. + /// + /// A list of style nodes from the document. + /// A hashtable with CSS classes names (and CSS ids names) and their properties. + private static Hashtable ExtractCSSClassesAndIDs(XmlNodeList styleNodes) + { + Hashtable identifiedCSSClassesAndIDs = new Hashtable(); + foreach (XmlNode styleNode in styleNodes) + { + char[] separator = { '}' }; + string[] css = styleNode.InnerText.Split(separator, StringSplitOptions.RemoveEmptyEntries); + foreach (string cssClass in css) + { + //several CSS classes can have same properties + string properties = ""; + List classesNames = new List(); + + int firstBrace = cssClass.IndexOf('{'); + if (firstBrace < 0) + { + continue; + } + properties = cssClass.Substring(firstBrace + 1).Replace('"', '\''); + char[] comma = { ',' }; + string[] cssNames = cssClass.Substring(0, firstBrace).Split(comma); + foreach (string className in cssNames) + { + //do not include the dot in the CSS class name or the pound in CSS id + classesNames.Add(className.Trim().Substring(1)); + } + + foreach (string identifiedClassName in classesNames) + { + identifiedCSSClassesAndIDs.Add(identifiedClassName, properties); + } + } + } + return identifiedCSSClassesAndIDs; + } + + + + } +} Index: xword/ContentFiltering/Test/Html/CSSUtilTest.cs =================================================================== --- xword/ContentFiltering/Test/Html/CSSUtilTest.cs (revision 0) +++ xword/ContentFiltering/Test/Html/CSSUtilTest.cs (revision 0) @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Xml; +using ContentFiltering.Html; +using ContentFiltering.Test.Util; + +namespace ContentFiltering.Test.Html +{ + /// + /// Test class for CSSUtil. + /// + [TestFixture] + public class CSSUtilTest + { + private string initialHTML; + private string expectedHTML; + private XmlDocument initialXmlDoc; + private XmlDocument expectedXmlDoc; + + /// + /// Default constructor. + /// + public CSSUtilTest() + { + initialHTML = ""; + expectedHTML = ""; + initialXmlDoc = new XmlDocument(); + expectedXmlDoc = new XmlDocument(); + } + + /// + /// Global test setup. + /// + [TestFixtureSetUp] + public void GlobalSetup() + { + initialHTML = "TITLE" + + "

HEADER 1

" + + "

Text 1

" + + "

Text 2

" + + "

Text 3 Text 4 Text 5

" + + ""; + + expectedHTML = "TITLE" + + "

HEADER 1

" + + "

Text 1

" + + "

Text 2

" + + "

Text 3 Text 4 Text 5

" + + ""; + + initialXmlDoc.LoadXml(initialHTML); + expectedXmlDoc.LoadXml(expectedHTML); + } + + /// + /// Tests the InlineCSS method. + /// + [Test] + public void TestInlineCSS() + { + CSSUtil.InlineCSS(ref initialXmlDoc); + Assert.IsTrue(XmlDocComparator.AreIdentical(initialXmlDoc, expectedXmlDoc)); + } + } +}