// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; using OpenXmlPowerTools; using OpenXmlPowerTools.HtmlToWml; using OpenXmlPowerTools.HtmlToWml.CSS; using System.Globalization; #if false Sort: 1. User agent declarations 1. Default value 2. Specified in default style sheet (which I am going to supply) 2. User normal declarations 1. Specified in user-supplied style sheet 3. Author normal declarations 1. Specified in style sheet (can defer this until later) 2. Specified in style element 3. Specified in style attribute 4. Author important declarations 1. Specified in style sheet 5. User important declarations 1. Specified in style sheet Sort Key (most significant first) {0} 9 if user supplied style sheet, high {0} 8 if author supplied style sheet, high {0} 7 if style attribute, high {0} 6 if style attribute, normal {0} 5 if author suplied style sheet, normal {0} 4 if user supplied style sheet, normal {0} 3 if user-agent default style sheet, high {0} 2 if user-agent default style sheet, normal {0} 1 if inherited {1} count of number of ID attributes in selector {2} count of number of attributes and pseudo classes in selector {3} count of number of element names and pseudo elements in selector {4} sequence number in order added to list 1. Process user-agent default style sheet 2. Process user-supplied style sheet 3. process author-supplied style sheet 4. process STYLE element 5. process style attribute on all elements 6. expand all shorthand properties - the new properties have the same sort key as shorthand prop 7. Add initial values for all properties that don't have values The various types of properties: - Set by the style sheet explicitely - Set at root level of hierarchy so that other elements can inherit (if no other ancestor) - Set as initial value for certain elements - all properties have an initial value. Some properties apply to only certain elements. - Some properties inherit automatically, such as font-family. - Some properties are set to inherit in the stylesheet. - border-top-color etc. are set to the color property, which then inherits. Properties either inherit, or are set to an initial value. Those properties that inherit are set to initial value only at the top of the tree. Those props that are set to an initial value throughout the tree do not inherit. It is either one or the other. Following is my new theory of the correct algorithm: - set all properties from the cascade. - Set initial values for properties at the top of the tree. These specifically need to be set for other properties that will inherit from them. - create a matrix of all elements, and which properties need to be set for each element. - iterate through the tree. SetAllValues for each element for each property call GetComputedPropertyValue // implemented in a recursive fashion to set & get its computed value. GetComputedPropertyValue if (property is already computed) return the computed value if (property is set) compute value set the computed value return the computed value if (property is inherited (either because it was set to inherit, or because it is an inherited property)) if (parent exists) call GetComputedValue on parent set the computed value return the computed value else call GetInitialValue for property compute value set the computed value return the computed value else call GetInitialValue for property compute value set the computed value return the computed value ComputeValue this needs to be specifically coded for each property if value is relative (em, ex, percentage, if property is not on font-size GetComputedValue for font-size compute value accordingly return value if property is on font-size call GetComputedValue for font-size of parent compute value accordingly return value if value is percentage if margin-top, margin-bottom, margin-right, margin-left, padding-top, padding-bottom, padding-right, padding-left call GetComputedValue for width of containing block if value is absolute (in, cm, mm, pt, pc, px) return value #endif namespace OpenXmlPowerTools.HtmlToWml { class CssApplier { private static List PropertyInfoList = new List() { // color // Value: | inherit // Initial: depends on UA // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "color" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "black", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, }, // direction // Value: ltr | rtl | inherit // Initial: ltr // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "direction" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "ltr", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // line-height // Value: normal | | | | // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: refer to the font size of the element itself // Computed value: for and the absolute value, otherwise as specified. new PropertyInfo { Names = new[] { "line-height" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element, "font-size", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // visibility // Value: visible | hidden | collapse | inherit // Initial: visible // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "visibility" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "visible", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // list-style-type // Value: disc | circle | square | decimal | decimal-leading-zero | // lower-roman | upper-roman | lower-greek | lower-latin | // upper-latin | armenian | georgian | lower-alpha | upper-alpha | // none | inherit // Initial: disc // Applies to: elements with display: list-item // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "list-style-type" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "list-item") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "disc", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // list-style-image // Value: | none | inherit // Initial: none // Applies to: elements with ’display: list-item’ // Inherited: yes // Percentages: N/A // Computed value: absolute URI or ’none’ new PropertyInfo { Names = new[] { "list-style-image" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "list-item") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // list-style-position // Value: inside | outside | inherit // Initial: outside // Applies to: elements with ’display: list-item’ // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "list-style-position" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "list-item") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // font-family // Value: [[ | ] [, | // ]* ] | inherit // Initial: depends on user agent // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "font-family" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = settings.MinorLatinFont, Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = (element, assignedValue, settings) => assignedValue, }, // font-style // Value: normal | italic | oblique | inherit // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "font-style" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // font-variant // Value: normal | small-caps | inherit // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "font-variant" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // font-weight // Value: normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | // 600 | 700 | 800 | 900 | inherit // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: see text new PropertyInfo { Names = new[] { "font-weight" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // font-size // Value: | | | | // inherit // Initial: medium // Applies to: all elements // Inherited: yes // Percentages: refer to inherited font size // Computed value: absolute length new PropertyInfo { Names = new[] { "font-size" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = ((double)settings.DefaultFontSize).ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, Unit = CssUnit.PT } } }, ComputedValue = (element, assignedValue, settings) => ComputeAbsoluteFontSize(element, assignedValue, settings), }, // text-indent // Value: | | inherit // Initial: 0 // Applies to: block containers // Inherited: yes // Percentages: refer to width of containing block // Computed value: the percentage as specified or the absolute length new PropertyInfo { Names = new[] { "text-indent" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "block") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // text-align // Value: left | right | center | justify | inherit // Initial: a nameless value that acts as ’left’ if ’direction’ is ’ltr’, ’right’ if // ’direction’ is ’rtl’ // Applies to: block containers // Inherited: yes // Percentages: N/A // Computed value: the initial value or as spec new PropertyInfo { Names = new[] { "text-align" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "block") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "left", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, // todo should be based on the direction property ComputedValue = null, }, // text-decoration // Value: none | [ underline || overline || line-through || blink ] | inherit // Initial: none // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "text-decoration" }, Inherits = true, // todo need to read css 16.3.1 in full detail to understand how this is implemented. Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = null, }, // letter-spacing // Value: normal | | inherit // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: ’normal’ or absolute length // word-spacing // Value: normal | | inherit // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: for ’normal’ the value 0; otherwise the absolute length new PropertyInfo { Names = new[] { "letter-spacing", "word-spacing" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => ComputeAbsoluteLength(element, assignedValue, settings, null), }, // white-space // Value: normal | pre | nowrap | pre-wrap | pre-line | inherit // Initial: normal // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "white-space" }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = null, }, // caption-side // Value: top | bottom | inherit // Initial: top // Applies to: 'table-caption' elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "caption-side" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table-caption") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "top", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = null, }, // border-collapse // Value: collapse | separate | inherit // Initial: separate // Applies to: ’table’ and ’inline-table’ elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "border-collapse" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table" || display.ToString() == "inline-table") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "separate", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = null, }, // border-spacing // Value: ? | inherit // Initial: 0 // Applies to: ’table’ and ’inline-table’ elements // Inherited: yes // Percentages: N/A // Computed value: two absolute lengths new PropertyInfo { Names = new[] { "border-spacing" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table" || display.ToString() == "inline-table") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => ComputeAbsoluteLength(element, assignedValue, settings, null), // todo need to handle two lengths here }, // empty-cells // Value: show | hide | inherit // Initial: show // Applies to: 'table-cell' elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "empty-cells" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table" || display.ToString() == "table-cell") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "show", } } }, ComputedValue = null, }, // margin-top, margin-bottom // Value: | inherit // Initial: 0 // Applies to: all elements except elements with table display types other than table-caption, table, and inline-table // all elements except th, td, tr // Inherited: no // Percentages: refer to width of containing block // Computed value: the percentage as specified or the absolute length new PropertyInfo { Names = new[] { "margin-top", "margin-bottom", }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table-caption" || display.ToString() == "table" || display.ToString() == "inline-table") return false; return true; }, InitialValue = (element, settings) => { if (settings.DefaultBlockContentMargin != null) { if (settings.DefaultBlockContentMargin == "auto") return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }; else if (settings.DefaultBlockContentMargin.ToLower().EndsWith("pt")) { string s1 = settings.DefaultBlockContentMargin.Substring(0, settings.DefaultBlockContentMargin.Length - 2); double d1; if (double.TryParse(s1, NumberStyles.Float, CultureInfo.InvariantCulture, out d1)) { return new CssExpression { Terms = new List { new CssTerm { Value = d1.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT } } }; } } throw new OpenXmlPowerToolsException("invalid setting"); } return new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT } } }; }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // margin-right, margin-left // Value: | inherit // Initial: 0 // Applies to: all elements except elements with table display types other than table-caption, table, and inline-table // all elements except th, td, tr // Inherited: no // Percentages: refer to width of containing block // Computed value: the percentage as specified or the absolute length new PropertyInfo { Names = new[] { "margin-right", "margin-left", }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table-caption" || display.ToString() == "table" || display.ToString() == "inline-table") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // padding-top, padding-right, padding-bottom, padding-left // Value: | inherit // Initial: 0 // Applies to: all elements except table-row-group, table-header-group, // table-footer-group, table-row, table-column-group and table-column // all elements except tr // Inherited: no // Percentages: refer to width of containing block // Computed value: the percentage as specified or the absolute length new PropertyInfo { Names = new[] { "padding-top", "padding-right", "padding-bottom", "padding-left" }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); string dv = display.ToString(); if (dv == "table-row-group" || dv == "table-header-group" || dv == "table-footer-group" || dv == "table-row" || dv == "table-column-group" || dv == "table-column") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // border-top-width, border-right-width, border-bottom-width, border-left-width // Value: | inherit // Initial: medium // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: absolute length; '0' if the border style is 'none' or 'hidden' new PropertyInfo { Names = new[] { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { string assignedValueStr = assignedValue.ToString(); if (assignedValueStr == "thin") return new CssExpression { Terms = new List { new CssTerm { Value = "0.75", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; if (assignedValueStr == "medium") return new CssExpression { Terms = new List { new CssTerm { Value = "3.0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; if (assignedValueStr == "thick") return new CssExpression { Terms = new List { new CssTerm { Value = "4.5", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; return ComputeAbsoluteLength(element, assignedValue, settings, null); }, }, // border-top-style, border-right-style, border-bottom-style, border-left-style // Value: | inherit // Initial: none // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: as specified new PropertyInfo { Names = new[] { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // display // Value: inline | block | list-item | inline-block | table | inline-table | // table-row-group | table-header-group | table-footer-group | // table-row | table-column-group | table-column | table-cell | // table-caption | none | inherit // Initial: inline // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: see text new PropertyInfo { Names = new[] { "display", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "inline", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // position // Value: static | relative | absolute | fixed | inherit // Initial: static // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: as specified new PropertyInfo { Names = new[] { "position", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "static", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // float // Value: left | right | none | inherit // Initial: none // Applies to: all, but see 9.7 p. 153 // Inherited: no // Percentages: N/A // Computed value: as specified new PropertyInfo { Names = new[] { "float", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // unicode-bidi // Value: normal | embed | bidi-override | inherit // Initial: normal // Applies to: all elements, but see prose // Inherited: no // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "unicode-bidi", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // background-color // Value: | transparent | inherit // Initial: transparent // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "background-color", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "transparent", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, }, // text-transform // Value: capitalize | uppercase | lowercase | none | inherit // Initial: none // Applies to: all elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "text-transform", }, Inherits = true, Includes = (e, settings) => true, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, ComputedValue = null, }, // table-layout // Value: auto | fixed | inherit // Initial: auto // Applies to: ’table’ and ’inline-table’ elements // Inherited: no // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "table-layout" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table" || display.ToString() == "inline-table") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = null, }, // empty-cells // Value: show | hide | inherit // Initial: show // Applies to: 'table-cell' elements // Inherited: yes // Percentages: N/A // Computed value: as spec new PropertyInfo { Names = new[] { "border-spacing" }, Inherits = true, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); if (display.ToString() == "table-cell") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "show", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = null, }, // border-top-color, border-right-color, border-bottom-color, border-left-color // Value: | transparent | inherit // Initial: the value of the color property // Applies to: all elements // Inherited: no // Percentages: N/A // Computed value: when taken from the ’color’ property, the computed value of // ’color’; otherwise, as specified new PropertyInfo { Names = new[] { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", }, Inherits = false, Includes = (e, settings) => true, InitialValue = (e, settings) => { var display = GetComputedPropertyValue(null, e, "color", settings); return display; }, ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, }, // width // Value: | | auto | inherit // Initial: auto // Applies to: all elements but non-replaced in-line elements, table rows, and row groups // Inherited: no // Percentages: refer to width of containing block // Computed value: the percentage or 'auto' as specified or the absolute length new PropertyInfo { Names = new[] { "width" }, Inherits = false, Includes = (e, settings) => { if (e.Name == XhtmlNoNamespace.img) return true; var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-row" || dv == "table-row-group") return false; return true; }, InitialValue = (element, settings) => { if (element.Parent == null) { double? pageWidth = (double?)settings.SectPr.Elements(W.pgSz).Attributes(W._w).FirstOrDefault(); if (pageWidth == null) pageWidth = 12240; double? leftMargin = (double?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); if (leftMargin == null) leftMargin = 1440; double? rightMargin = (double?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); if (rightMargin == null) rightMargin = 1440; double width = (double)(pageWidth - leftMargin - rightMargin) / 20; return new CssExpression { Terms = new List { new CssTerm { Value = width.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, Unit = CssUnit.PT, } } }; } return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }; }, ComputedValue = (element, assignedValue, settings) => { if (element.Name != XhtmlNoNamespace.caption && element.Name != XhtmlNoNamespace.td && element.Name != XhtmlNoNamespace.th && element.Name != XhtmlNoNamespace.tr && element.Name != XhtmlNoNamespace.table && assignedValue.IsAuto) { PropertyInfo pi = PropertyInfoList.FirstOrDefault(p => p.Names.Contains("width")); string display = GetComputedPropertyValue(pi, element, "display", settings).ToString(); if (display != "inline") { CssExpression parentPropertyValue = GetComputedPropertyValue(pi, element.Parent, "width", settings); return parentPropertyValue; } } CssExpression valueForPercentage = null; XElement elementToQuery = element.Parent; while (elementToQuery != null) { valueForPercentage = GetComputedPropertyValue(null, elementToQuery, "width", settings); if (valueForPercentage.IsAuto) { elementToQuery = elementToQuery.Parent; continue; } break; } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // min-width // Value: | | inherit // Initial: 0 // Applies to: all elements but non-replaced in-line elements, table rows, and row groups // Inherited: no // Percentages: refer to width of containing block // Computed value: the percentage as spec or the absolute length new PropertyInfo { Names = new[] { "min-width" }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-row" || dv == "table-row-group") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // max-width // Value: | | none | inherit // Initial: none // Applies to: all elements but non-replaced in-line elements, table rows, and row groups // Inherited: no // Percentages: refer to width of containing block // Computed value: the percentage as spec or the absolute length new PropertyInfo { Names = new[] { "max-width" }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-row" || dv == "table-row-group") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // height // Value: | | auto | inherit // Initial: auto // Applies to: all elements but non-replaced in-line elements, table columns, and column groups // Inherited: no // Percentages: see prose // Computed value: the percentage as spec or the absolute length new PropertyInfo { Names = new[] { "height" }, Inherits = false, Includes = (e, settings) => { if (e.Name == XhtmlNoNamespace.img) return true; var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-row" || dv == "table-row-group") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // min-height // Value: | | inherit // Initial: 0 // Applies to: all elements but non-replaced in-line elements, table columns, and column groups // Inherited: no // Percentages: see prose // Computed value: the percentage as spec or the absolute length new PropertyInfo { Names = new[] { "min-height" }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-column" || dv == "table-column-group") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // max-height // Value: | | none | inherit // Initial: none // Applies to: all elements but non-replaced in-line elements, table columns, and column groups // Inherited: no // Percentages: refer to height of containing block // Computed value: the percentage as spec or the absolute length new PropertyInfo { Names = new[] { "max-height" }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-column" || dv == "table-column-group") return false; return true; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => { CssExpression valueForPercentage = null; if (element.Parent != null) valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, // vertical-align // Value: baseline | sub | super | top | text-top | middle | bottom | text-bottom | // | | inherit // Initial: baseline // Applies to: inline-level and 'table-cell' elements // Inherited: no // Percentages: refer to the line height of the element itself // Computed value: for and the absolute length, otherwise as specified. new PropertyInfo { Names = new[] { "vertical-align" }, Inherits = false, Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); if (dv == "inline" || dv == "table-cell") return true; return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "baseline", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => assignedValue, // todo fix }, // positioned elements are not supported // // top // Value: | | auto | inherit // Initial: auto // Applies to: positioned elements // Inherited: no // Percentages: refer to height of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. // // right // Value: | | auto | inherit // Initial: auto // Applies to: positioned elements // Inherited: no // Percentages: refer to width of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. // // bottom // Value: | | auto | inherit // Initial: auto // Applies to: positioned elements // Inherited: no // Percentages: refer to height of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. // // left // Value: | | auto | inherit // Initial: auto // Applies to: positioned elements // Inherited: no // Percentages: refer to width of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. // floated elements are not supported // // clear // Value: none | left | right | both | inherit // Initial: none // Applies to: block-level elements // Inherited: no // Percentages: N/A // Computed value: as specified // // z-index // Value: auto | integer | inherit // Initial: auto // Applies to: positioned elements // Inherited: no // Percentages: N/A // Computed value: as spec }; /* * 1. Process user-agent default style sheet * 2. Process user-supplied style sheet * 3. process author-supplied style sheet * 4. process STYLE element * 5. process style attribute on all elements * 6. expand all shorthand properties - the new properties have the same sort key as shorthand prop * 7. Add initial values for all properties that don't have values */ public static void ApplyAllCss( string defaultCss, string authorCss, string userCss, XElement newXHtml, HtmlToWmlConverterSettings settings, out CssDocument defaultCssDoc, out CssDocument authorCssDoc, out CssDocument userCssDoc, string annotatedHtmlDumpFileName) { int propertySequence = 1; CssParser defaultCssParser = new CssParser(); defaultCssDoc = defaultCssParser.ParseText(defaultCss); ApplyCssDocument( defaultCssDoc, newXHtml, Property.HighOrderPriority.UserAgentNormal, Property.HighOrderPriority.UserAgentHigh, ref propertySequence); //// todo dump here, see if margin is set on body. //if (annotatedHtmlDumpFileName != null) //{ // StringBuilder sb = new StringBuilder(); // WriteXHtmlWithAnnotations(newXHtml, sb); // File.WriteAllText(annotatedHtmlDumpFileName, sb.ToString()); // Environment.Exit(0); //} //DumpCss(userAgentCssDoc); //Environment.Exit(0); CssParser userCssParser = new CssParser(); userCssDoc = userCssParser.ParseText(userCss); ApplyCssDocument( userCssDoc, newXHtml, Property.HighOrderPriority.UserHigh, Property.HighOrderPriority.UserNormal, ref propertySequence); //DumpCss(userCssDoc); //Environment.Exit(0); CssParser authorCssParser = new CssParser(); authorCssDoc = authorCssParser.ParseText(authorCss); ApplyCssDocument( authorCssDoc, newXHtml, Property.HighOrderPriority.AuthorNormal, Property.HighOrderPriority.AuthorHigh, ref propertySequence); //string s = DumpCss(authorCssDoc); //File.WriteAllText("CssTreeDump.txt", s); //Environment.Exit(0); // If processing style element, do it here. ApplyStyleAttributes(newXHtml, ref propertySequence); ExpandShorthandProperties(newXHtml, settings); SetAllValues(newXHtml, settings); if (annotatedHtmlDumpFileName != null) { StringBuilder sb = new StringBuilder(); WriteXHtmlWithAnnotations(newXHtml, sb); File.WriteAllText(annotatedHtmlDumpFileName, sb.ToString()); } } private static void SetAllValues(XElement xHtml, HtmlToWmlConverterSettings settings) { foreach (var element in xHtml.DescendantsAndSelf()) { foreach (var propertyInfo in PropertyInfoList) { if (propertyInfo.Includes(element, settings)) { foreach (var name in propertyInfo.Names) { GetComputedPropertyValue(propertyInfo, element, name, settings); } } } } } #if false GetComputedPropertyValue if (property is already computed) return the computed value if (property is set) compute value set the computed value return the computed value if (property is inherited (either because it was set to inherit, or because it is an inherited property)) if (parent exists) call GetComputedValue on parent return the computed value else call GetInitialValue for property compute value set the computed value return the computed value #endif public static CssExpression GetComputedPropertyValue(PropertyInfo propertyInfo, XElement element, string propertyName, HtmlToWmlConverterSettings settings) { // if (property is already computed) // return the computed value Dictionary computedValues = element.Annotation>(); if (computedValues == null) { computedValues = new Dictionary(); element.AddAnnotation(computedValues); } if (computedValues.ContainsKey(propertyName)) { CssExpression r = computedValues[propertyName]; return r; } // if property is not set or property is set to inherited value, then get inherited or initialized value. string pName = propertyName.ToLower(); if (propertyInfo == null) { propertyInfo = PropertyInfoList.FirstOrDefault(pi => pi.Names.Contains(pName)); if (propertyInfo == null) throw new OpenXmlPowerToolsException("all possible properties should be in the list"); } Dictionary propList = element.Annotation>(); if (propList == null) { CssExpression computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, false, settings); return computedValue; } if (!propList.ContainsKey(pName)) { CssExpression computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, false, settings); return computedValue; } Property prop = propList[pName]; string propStr = prop.Expression.ToString(); if (propStr == "inherited" || propStr == "auto") { CssExpression computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, true, settings); return computedValue; } // if property is set, then compute the value, return the computed value CssExpression computedValue2; if (propertyInfo.ComputedValue == null) computedValue2 = prop.Expression; else { computedValue2 = propertyInfo.ComputedValue(element, prop.Expression, settings); } computedValues.Add(propertyName, computedValue2); return computedValue2; } //if (property is inherited (either because it was set to inherit, or because it is an inherited property)) // if (parent exists) // call GetComputedValue on parent // return the computed value //else // call GetInitialValue for property // compute value // set the computed value // return the computed value public static CssExpression GetInheritedOrInitializedValue(Dictionary computedValues, PropertyInfo propertyInfo, XElement element, string propertyName, bool valueIsInherit, HtmlToWmlConverterSettings settings) { if ((propertyInfo.Inherits || valueIsInherit) && element.Parent != null && propertyInfo.Includes(element.Parent, settings)) { CssExpression parentPropertyValue = GetComputedPropertyValue(propertyInfo, element.Parent, propertyName, settings); computedValues.Add(propertyName, parentPropertyValue); return parentPropertyValue; } CssExpression initialPropertyValue = propertyInfo.InitialValue(element, settings); CssExpression computedValue; if (propertyInfo.ComputedValue == null) computedValue = initialPropertyValue; else computedValue = propertyInfo.ComputedValue(element, initialPropertyValue, settings); computedValues.Add(propertyName, computedValue); return computedValue; } private static void ApplyCssDocument( CssDocument cssDoc, XElement xHtml, Property.HighOrderPriority notImportantHighOrderSort, Property.HighOrderPriority importantHighOrderSort, ref int propertySequence) { foreach (var ruleSet in cssDoc.RuleSets) { foreach (var selector in ruleSet.Selectors) { ApplySelector(selector, ruleSet, xHtml, notImportantHighOrderSort, importantHighOrderSort, ref propertySequence); } } } private static CssExpression ComputeAbsoluteLength(XElement element, CssExpression assignedValue, HtmlToWmlConverterSettings settings, CssExpression lengthForPercentage) { if (assignedValue.Terms.Count != 1) throw new OpenXmlPowerToolsException("Should not have a unit with more than one term"); string value = assignedValue.Terms.First().Value; bool negative = assignedValue.Terms.First().Sign == '-'; if (value == "thin") { CssExpression newExpr1 = new CssExpression { Terms = new List { new CssTerm { Value = ".3", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr1; } if (value == "medium") { CssExpression newExpr2 = new CssExpression { Terms = new List { new CssTerm { Value = "1.20", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr2; } if (value == "thick") { CssExpression newExpr3 = new CssExpression { Terms = new List { new CssTerm { Value = "1.80", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr3; } if (value == "auto" || value == "normal" || value == "none") return assignedValue; CssUnit? unit = assignedValue.Terms.First().Unit; if (unit == CssUnit.PT || unit == null) return assignedValue; if (unit == CssUnit.Percent && lengthForPercentage == null) return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }; double decValue; if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue)) throw new OpenXmlPowerToolsException("value did not parse"); if (negative) decValue = -decValue; double? newPtSize = null; if (unit == CssUnit.Percent) { double ptSize; if (!double.TryParse(lengthForPercentage.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out ptSize)) throw new OpenXmlPowerToolsException("did not return a double?"); newPtSize = ptSize * decValue / 100d; } else if (unit == CssUnit.EM || unit == CssUnit.EX) { CssExpression fontSize = GetComputedPropertyValue(null, element, "font-size", settings); double decFontSize; if (!double.TryParse(fontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out decFontSize)) throw new OpenXmlPowerToolsException("Internal error"); newPtSize = (unit == CssUnit.EM) ? decFontSize * decValue : decFontSize * decValue / 2; } else { if (unit == null && decValue == 0d) newPtSize = 0d; if (unit == CssUnit.IN) newPtSize = decValue * 72.0d; if (unit == CssUnit.CM) newPtSize = (decValue / 2.54d) * 72.0d; if (unit == CssUnit.MM) newPtSize = (decValue / 25.4d) * 72.0d; if (unit == CssUnit.PC) newPtSize = decValue * 12d; if (unit == CssUnit.PX) newPtSize = decValue * 0.75d; } if (!newPtSize.HasValue) throw new OpenXmlPowerToolsException("Internal error: should not have reached this exception"); CssExpression newExpr = new CssExpression { Terms = new List { new CssTerm { Value = newPtSize.Value.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr; } private static CssExpression ComputeAbsoluteFontSize(XElement element, CssExpression assignedValue, HtmlToWmlConverterSettings settings) { if (assignedValue.Terms.Count != 1) throw new OpenXmlPowerToolsException("Should not have a unit with more than one term, I think"); string value = assignedValue.Terms.First().Value; CssUnit? unit = assignedValue.Terms.First().Unit; if (unit == CssUnit.PT) return assignedValue; if (FontSizeMap.ContainsKey(value)) return new CssExpression { Terms = new List { new CssTerm { Value = FontSizeMap[value].ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; // todo what should the calculation be for computing larger / smaller? if (value == "larger" || value == "smaller") { CssExpression parentFontSize = GetComputedPropertyValue(null, element.Parent, "font-size", settings); double ptSize; if (!double.TryParse(parentFontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out ptSize)) throw new OpenXmlPowerToolsException("did not return a double?"); double newPtSize2 = 0; if (value == "larger") { if (ptSize < 10) newPtSize2 = 10d; if (ptSize == 10 || ptSize == 11) newPtSize2 = 12d; if (ptSize == 12) newPtSize2 = 13.5d; if (ptSize >= 13 && ptSize <= 15) newPtSize2 = 18d; if (ptSize >= 16 && ptSize <= 20) newPtSize2 = 24d; if (ptSize >= 21) newPtSize2 = 36d; } if (value == "smaller") { if (ptSize <= 11) newPtSize2 = 7.5d; if (ptSize == 12) newPtSize2 = 10d; if (ptSize >= 13 && ptSize <= 15) newPtSize2 = 12d; if (ptSize >= 16 && ptSize <= 20) newPtSize2 = 13.5d; if (ptSize >= 21 && ptSize <= 29) newPtSize2 = 18d; if (ptSize >= 30) newPtSize2 = 24d; } return new CssExpression { Terms = new List { new CssTerm { Value = newPtSize2.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; } double decValue; if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue)) throw new OpenXmlPowerToolsException("em value did not parse"); double? newPtSize = null; if (unit == CssUnit.EM || unit == CssUnit.EX || unit == CssUnit.Percent) { CssExpression parentFontSize = GetComputedPropertyValue(null, element.Parent, "font-size", settings); double ptSize; if (!double.TryParse(parentFontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out ptSize)) throw new OpenXmlPowerToolsException("did not return a double?"); if (unit == CssUnit.EM) newPtSize = ptSize * decValue; if (unit == CssUnit.EX) newPtSize = ptSize / 2 * decValue; if (unit == CssUnit.Percent) newPtSize = ptSize * decValue / 100d; } else { if (unit == CssUnit.IN) newPtSize = decValue * 72.0d; if (unit == CssUnit.CM) newPtSize = (decValue / 2.54d) * 72.0d; if (unit == CssUnit.MM) newPtSize = (decValue / 25.4d) * 72.0d; if (unit == CssUnit.PC) newPtSize = decValue * 12d; if (unit == CssUnit.PX) newPtSize = decValue * 0.75d; } if (!newPtSize.HasValue) throw new OpenXmlPowerToolsException("Internal error: should not have reached this exception"); CssExpression newExpr = new CssExpression { Terms = new List { new CssTerm { Value = newPtSize.Value.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr; } private static Dictionary FontSizeMap = new Dictionary() { { "xx-small", 7.5d }, { "x-small", 10d }, { "small", 12d }, { "medium", 13.5d }, { "large", 18d }, { "x-large", 24d }, { "xx-large", 36d }, }; private static void ApplySelector( CssSelector selector, CssRuleSet ruleSet, XElement xHtml, Property.HighOrderPriority notImportantHighOrderSort, Property.HighOrderPriority importantHighOrderSort, ref int propertySequence) { foreach (var element in xHtml.DescendantsAndSelf()) { if (DoesSelectorMatch(selector, element)) { foreach (CssDeclaration declaration in ruleSet.Declarations) { Property prop = new Property() { Name = declaration.Name.ToLower(), Expression = declaration.Expression, HighOrderSort = declaration.Important ? importantHighOrderSort : notImportantHighOrderSort, IdAttributesInSelector = CountIdAttributesInSelector(selector), AttributesInSelector = CountAttributesInSelector(selector), ElementNamesInSelector = CountElementNamesInSelector(selector), SequenceNumber = propertySequence++, }; AddPropertyToElement(element, prop); } } } } private static bool DoesSelectorMatch( CssSelector selector, XElement element) { int currentSimpleSelector = selector.SimpleSelectors.Count() - 1; XElement currentElement = element; while (true) { if (!DoesSimpleSelectorMatch(selector.SimpleSelectors[currentSimpleSelector], currentElement)) return false; if (currentSimpleSelector == 0) return true; if (selector.SimpleSelectors[currentSimpleSelector].Combinator == CssCombinator.ChildOf) { currentElement = currentElement.Parent; if (currentElement == null) return false; currentSimpleSelector--; continue; } if (selector.SimpleSelectors[currentSimpleSelector].Combinator == CssCombinator.PrecededImmediatelyBy) { currentElement = currentElement.ElementsBeforeSelf().Reverse().FirstOrDefault(); if (currentElement == null) return false; currentSimpleSelector--; continue; } if (selector.SimpleSelectors[currentSimpleSelector].Combinator == null) { bool continueOuter = false; foreach (XElement ancestor in element.Ancestors()) { if (DoesSimpleSelectorMatch(selector.SimpleSelectors[currentSimpleSelector - 1], ancestor)) { currentElement = ancestor; currentSimpleSelector--; continueOuter = true; break; } } if (continueOuter) continue; return false; } } } private static bool DoesSimpleSelectorMatch( CssSimpleSelector simpleSelector, XElement element) { bool elemantNameMatch = true; bool classNameMatch = true; bool childSimpleSelectorMatch = true; bool idMatch = true; bool attributeMatch = true; if (simpleSelector.Pseudo != null) return false; if (simpleSelector.ElementName != null && simpleSelector.ElementName != "" && simpleSelector.ElementName != "*") elemantNameMatch = element.Name.ToString() == simpleSelector.ElementName.ToString(); if (elemantNameMatch) { if (simpleSelector.Class != null && simpleSelector.Class != "") classNameMatch = ClassesOf(element).Contains(simpleSelector.Class); if (classNameMatch) { if (simpleSelector.Child != null) childSimpleSelectorMatch = DoesSimpleSelectorMatch(simpleSelector.Child, element); if (childSimpleSelectorMatch) { if (simpleSelector.ID != null && simpleSelector.ID != "") { string id = (string)element.Attribute("ID"); if (id == null) id = (string)element.Attribute("id"); idMatch = simpleSelector.ID == id; } if (idMatch) { if (simpleSelector.Attribute != null) attributeMatch = DoesAttributeMatch(simpleSelector.Attribute, element); } } } } bool result = elemantNameMatch && classNameMatch && childSimpleSelectorMatch && idMatch && attributeMatch; return result; } private static bool DoesAttributeMatch(OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute attribute, XElement element) { string attName = attribute.Operand.ToLower(); string attValue = (string)element.Attribute(attName); if (attValue == null) return false; if (attribute.Operator == null) return true; string value = attribute.Value; switch (attribute.Operator) { case CssAttributeOperator.Equals: return attValue == value; case CssAttributeOperator.BeginsWith: return attValue.StartsWith(value); case CssAttributeOperator.Contains: return attValue.Contains(value); case CssAttributeOperator.EndsWith: return attValue.EndsWith(value); case CssAttributeOperator.InList: return attValue.Split(' ').Contains(value); case CssAttributeOperator.Hyphenated: return attValue.Split('-')[0] == value; default: return false; } } private static int CountIdAttributesInSimpleSelector(CssSimpleSelector simpleSelector) { int count = simpleSelector.ID != null ? 1 : 0 + (simpleSelector.Child != null ? CountIdAttributesInSimpleSelector(simpleSelector.Child) : 0); return count; } private static int CountIdAttributesInSelector(CssSelector selector) { int count = selector.SimpleSelectors.Select(ss => CountIdAttributesInSimpleSelector(ss)).Sum(); return count; } private static int CountAttributesInSimpleSelector(CssSimpleSelector simpleSelector) { int count = (simpleSelector.Attribute != null ? 1 : 0) + ((simpleSelector.Class != null && simpleSelector.Class != "") ? 1 : 0) + (simpleSelector.Child != null ? CountAttributesInSimpleSelector(simpleSelector.Child) : 0); return count; } private static int CountAttributesInSelector(CssSelector selector) { int count = selector.SimpleSelectors.Select(ss => CountAttributesInSimpleSelector(ss)).Sum(); return count; } private static int CountElementNamesInSimpleSelector(CssSimpleSelector simpleSelector) { int count = (simpleSelector.ElementName != null && simpleSelector.ElementName != "" && simpleSelector.ElementName != "*") ? 1 : 0 + (simpleSelector.Child != null ? CountElementNamesInSimpleSelector(simpleSelector.Child) : 0); return count; } private static int CountElementNamesInSelector(CssSelector selector) { int count = selector.SimpleSelectors.Select(ss => CountElementNamesInSimpleSelector(ss)).Sum(); return count; } private static void AddPropertyToElement( XElement element, Property property) { //if (property.Name == "direction") // Console.WriteLine(1); Dictionary propList = element.Annotation>(); if (propList == null) { propList = new Dictionary(); element.AddAnnotation(propList); } if (!propList.ContainsKey(property.Name)) propList.Add(property.Name, property); else { Property current = propList[property.Name]; if (((System.IComparable)property).CompareTo(current) == 1) propList[property.Name] = property; } } private static void AddPropertyToDictionary( Dictionary propList, Property property) { if (!propList.ContainsKey(property.Name)) propList.Add(property.Name, property); else { Property current = propList[property.Name]; if (((System.IComparable)property).CompareTo(current) == 1) propList[property.Name] = property; } } private static string[] ClassesOf(XElement element) { string classesString = (string)element.Attribute("class"); if (classesString == null) return new string[0]; return classesString.Split(' '); } private static void ApplyDeclarationsToElement( CssRuleSet ruleSet, XElement element, Property.HighOrderPriority notImportantHighOrderSort, Property.HighOrderPriority importantHighOrderSort, ref int propertySequence) { foreach (var declaration in ruleSet.Declarations) { Property prop = new Property() { Name = declaration.Name.ToLower(), Expression = declaration.Expression, HighOrderSort = declaration.Important ? importantHighOrderSort : notImportantHighOrderSort, IdAttributesInSelector = 0, AttributesInSelector = 0, ElementNamesInSelector = 0, SequenceNumber = propertySequence++, }; AddPropertyToElement(element, prop); } } private static void ApplyCssToElement( CssDocument cssDoc, XElement element, Property.HighOrderPriority notImportantHighOrderSort, Property.HighOrderPriority importantHighOrderSort, ref int propertySequence) { foreach (var ruleSet in cssDoc.RuleSets) { ApplyDeclarationsToElement(ruleSet, element, notImportantHighOrderSort, importantHighOrderSort, ref propertySequence); } } private static void ApplyStyleAttributes(XElement xHtml, ref int propertySequence) { foreach (var element in xHtml.DescendantsAndSelf()) { XAttribute styleAtt = element.Attribute(XhtmlNoNamespace.style); if (styleAtt != null) { string style = (string)styleAtt; string cssString = element.Name + "{" + style + "}"; cssString = cssString.Replace('\"', '\''); CssParser cssParser = new CssParser(); CssDocument cssDoc = cssParser.ParseText(cssString); ApplyCssToElement( cssDoc, element, Property.HighOrderPriority.StyleAttributeNormal, Property.HighOrderPriority.StyleAttributeHigh, ref propertySequence); } XAttribute dirAtt = element.Attribute(XhtmlNoNamespace.dir); if (dirAtt != null) { string dir = dirAtt.Value.ToLower(); Property prop = new Property() { Name = "direction", Expression = new CssExpression { Terms = new List { new CssTerm { Value = dir, Type = CssTermType.String } } }, HighOrderSort = Property.HighOrderPriority.HtmlAttribute, IdAttributesInSelector = 0, AttributesInSelector = 0, ElementNamesInSelector = 0, SequenceNumber = propertySequence++, }; AddPropertyToElement(element, prop); } } } private enum CssDataType { BorderWidth, BorderStyle, Color, ListStyleType, ListStylePosition, ListStyleImage, BackgroundColor, BackgroundImage, BackgroundRepeat, BackgroundAttachment, BackgroundPosition, FontStyle, FontVarient, FontWeight, FontSize, LineHeight, FontFamily, Length, }; private class ShorthandPropertiesInfo { public string Name; public string Pattern; } private static ShorthandPropertiesInfo[] ShorthandProperties = new[] { new ShorthandPropertiesInfo { Name = "margin", Pattern = "margin-{0}", }, new ShorthandPropertiesInfo { Name = "padding", Pattern = "padding-{0}", }, new ShorthandPropertiesInfo { Name = "border-width", Pattern = "border-{0}-width", }, new ShorthandPropertiesInfo { Name = "border-color", Pattern = "border-{0}-color", }, new ShorthandPropertiesInfo { Name = "border-style", Pattern = "border-{0}-style", }, }; private static void ExpandShorthandProperties(XElement xHtml, HtmlToWmlConverterSettings settings) { foreach (var element in xHtml.DescendantsAndSelf()) { ExpandShorthandPropertiesForElement(element, settings); } } private static void ExpandShorthandPropertiesForElement(XElement element, HtmlToWmlConverterSettings settings) { Dictionary propertyList = element.Annotation>(); if (propertyList == null) { propertyList = new Dictionary(); element.AddAnnotation(propertyList); } foreach (var kvp in propertyList.ToList()) { Property p = kvp.Value; if (p.Name == "border") { CssExpression borderColor; CssExpression borderWidth; CssExpression borderStyle; if (p.Expression.Terms.Count == 1 && p.Expression.Terms.First().Value == "inherit") { borderColor = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; borderWidth = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; borderStyle = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; } else { //borderColor = GetComputedPropertyValue(null, element, "color", settings); //borderWidth = new Expression { Terms = new List { new Term { Value = "medium", Type = TermType.String } } }; //borderStyle = new Expression { Terms = new List { new Term { Value = "none", Type = TermType.String } } }; borderColor = null; borderWidth = null; borderStyle = null; foreach (var term in p.Expression.Terms) { CssDataType dataType = GetDatatypeFromBorderTerm(term); switch (dataType) { case CssDataType.Color: borderColor = new CssExpression { Terms = new List { term } }; break; case CssDataType.BorderWidth: borderWidth = new CssExpression { Terms = new List { term } }; break; case CssDataType.BorderStyle: borderStyle = new CssExpression { Terms = new List { term } }; break; } } } foreach (var side in new[] { "top", "left", "bottom", "right" }) { if (borderWidth != null) { Property bwp = new Property { Name = "border-" + side + "-width", Expression = borderWidth, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bwp); } if (borderStyle != null) { Property bsp = new Property { Name = "border-" + side + "-style", Expression = borderStyle, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bsp); } if (borderColor != null) { Property bc = new Property { Name = "border-" + side + "-color", Expression = borderColor, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bc); } } continue; } if (p.Name == "border-top" || p.Name == "border-right" || p.Name == "border-bottom" || p.Name == "border-left") { CssExpression borderColor; CssExpression borderWidth; CssExpression borderStyle; if (p.Expression.Terms.Count() == 1 && p.Expression.Terms.First().Value == "inherit") { borderColor = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; borderWidth = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; borderStyle = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; } else { //borderColor = GetComputedPropertyValue(null, element, "color", settings); //borderWidth = new Expression { Terms = new List { new Term { Value = "medium", Type = TermType.String } } }; //borderStyle = new Expression { Terms = new List { new Term { Value = "none", Type = TermType.String } } }; borderColor = null; borderWidth = null; borderStyle = null; foreach (var term in p.Expression.Terms) { CssDataType dataType = GetDatatypeFromBorderTerm(term); switch (dataType) { case CssDataType.Color: borderColor = new CssExpression { Terms = new List { term } }; break; case CssDataType.BorderWidth: borderWidth = new CssExpression { Terms = new List { term } }; break; case CssDataType.BorderStyle: borderStyle = new CssExpression { Terms = new List { term } }; break; } } } if (borderWidth != null) { Property bwp = new Property { Name = p.Name + "-width", Expression = borderWidth, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bwp); } if (borderStyle != null) { Property bsp = new Property { Name = p.Name + "-style", Expression = borderStyle, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bsp); } if (borderColor != null) { Property bc = new Property { Name = p.Name + "-color", Expression = borderColor, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bc); } continue; } if (p.Name == "list-style") { CssExpression listStyleType; CssExpression listStylePosition; CssExpression listStyleImage; if (p.Expression.Terms.Count == 1 && p.Expression.Terms.First().Value == "inherit") { listStyleType = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; listStylePosition = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; listStyleImage = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; } else { listStyleType = new CssExpression { Terms = new List { new CssTerm { Value = "disc", Type = CssTermType.String } } }; listStylePosition = new CssExpression { Terms = new List { new CssTerm { Value = "outside", Type = CssTermType.String } } }; listStyleImage = new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }; foreach (var term in p.Expression.Terms) { CssDataType dataType = GetDatatypeFromListStyleTerm(term); switch (dataType) { case CssDataType.ListStyleType: listStyleType = new CssExpression { Terms = new List { term } }; break; case CssDataType.ListStylePosition: listStylePosition = new CssExpression { Terms = new List { term } }; break; case CssDataType.ListStyleImage: listStyleImage = new CssExpression { Terms = new List { term } }; break; } } } Property lst = new Property { Name = "list-style-type", Expression = listStyleType, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lst); Property lsp = new Property { Name = "list-style-position", Expression = listStylePosition, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lsp); Property lsi = new Property { Name = "list-style-image", Expression = listStyleImage, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lsi); continue; } if (p.Name == "background") { CssExpression backgroundColor; CssExpression backgroundImage; CssExpression backgroundRepeat; CssExpression backgroundAttachment; CssExpression backgroundPosition; if (p.Expression.Terms.Count == 1 && p.Expression.Terms.First().Value == "inherit") { backgroundColor = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; backgroundImage = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; backgroundRepeat = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; backgroundAttachment = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; backgroundPosition = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; } else { backgroundColor = new CssExpression { Terms = new List { new CssTerm { Value = "transparent", Type = CssTermType.String } } }; backgroundImage = new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }; backgroundRepeat = new CssExpression { Terms = new List { new CssTerm { Value = "repeat", Type = CssTermType.String } } }; backgroundAttachment = new CssExpression { Terms = new List { new CssTerm { Value = "scroll", Type = CssTermType.String } } }; backgroundPosition = new CssExpression { Terms = new List { new CssTerm { Value = "0", Unit = OpenXmlPowerTools.HtmlToWml.CSS.CssUnit.Percent, Type = CssTermType.Number }, new CssTerm { Value = "0", Unit = OpenXmlPowerTools.HtmlToWml.CSS.CssUnit.Percent, Type = CssTermType.Number }, } }; List backgroundPositionList = new List(); foreach (var term in p.Expression.Terms) { CssDataType dataType = GetDatatypeFromBackgroundTerm(term); switch (dataType) { case CssDataType.BackgroundColor: backgroundColor = new CssExpression { Terms = new List { term } }; break; case CssDataType.BackgroundImage: backgroundImage = new CssExpression { Terms = new List { term } }; break; case CssDataType.BackgroundRepeat: backgroundRepeat = new CssExpression { Terms = new List { term } }; break; case CssDataType.BackgroundAttachment: backgroundAttachment = new CssExpression { Terms = new List { term } }; break; case CssDataType.BackgroundPosition: backgroundPositionList.Add(term); break; } } if (backgroundPositionList.Count() == 1) { backgroundPosition = new CssExpression { Terms = new List { backgroundPositionList.First(), new CssTerm { Value = "center", Type = CssTermType.String }, } }; } if (backgroundPositionList.Count() == 2) { backgroundPosition = new CssExpression { Terms = backgroundPositionList }; } } Property bc = new Property { Name = "background-color", Expression = backgroundColor, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bc); Property bgi = new Property { Name = "background-image", Expression = backgroundImage, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bgi); Property bgr = new Property { Name = "background-repeat", Expression = backgroundRepeat, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bgr); Property bga = new Property { Name = "background-attachment", Expression = backgroundAttachment, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bga); Property bgp = new Property { Name = "background-position", Expression = backgroundPosition, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bgp); continue; } if (p.Name == "font") { CssExpression fontStyle; CssExpression fontVarient; CssExpression fontWeight; CssExpression fontSize; CssExpression lineHeight; CssExpression fontFamily; if (p.Expression.Terms.Count() == 1 && p.Expression.Terms.First().Value == "inherit") { fontStyle = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; fontVarient = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; fontWeight = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; fontSize = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; lineHeight = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; fontFamily = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; } else { fontStyle = new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }; fontVarient = new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }; fontWeight = new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }; fontSize = new CssExpression { Terms = new List { new CssTerm { Value = "medium", Type = CssTermType.String } } }; lineHeight = new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }; fontFamily = new CssExpression { Terms = new List { new CssTerm { Value = "serif", Type = CssTermType.String } } }; List fontFamilyList = new List(); foreach (var term in p.Expression.Terms) { CssDataType dataType = GetDatatypeFromFontTerm(term); switch (dataType) { case CssDataType.FontStyle: fontStyle = new CssExpression { Terms = new List { term } }; break; case CssDataType.FontVarient: fontVarient = new CssExpression { Terms = new List { term } }; break; case CssDataType.FontWeight: fontWeight = new CssExpression { Terms = new List { term } }; break; case CssDataType.FontSize: fontSize = new CssExpression { Terms = new List { term } }; break; case CssDataType.Length: if (term.SeparatorChar == "/") lineHeight = new CssExpression { Terms = new List { term } }; else fontSize = new CssExpression { Terms = new List { term } }; break; case CssDataType.FontFamily: fontFamilyList.Add(term); break; } } if (fontFamilyList.Count > 0) fontFamily = new CssExpression { Terms = fontFamilyList }; } Property fs = new Property { Name = "font-style", Expression = fontStyle, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fs); Property fv = new Property { Name = "font-varient", Expression = fontVarient, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fv); Property fw = new Property { Name = "font-weight", Expression = fontWeight, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fw); Property fsz = new Property { Name = "font-size", Expression = fontSize, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fsz); Property lh = new Property { Name = "line-height", Expression = lineHeight, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lh); Property ff = new Property { Name = "font-family", Expression = fontFamily, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, AttributesInSelector = p.AttributesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ff); continue; } foreach (var shPr in ShorthandProperties) { if (p.Name == shPr.Name) { switch (p.Expression.Terms.Count) { case 1: foreach (var direction in new[] { "top", "right", "bottom", "left" }) { Property ep = new Property() { Name = String.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep); } break; case 2: foreach (var direction in new[] { "top", "bottom" }) { Property ep = new Property() { Name = String.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep); } foreach (var direction in new[] { "left", "right" }) { Property ep = new Property() { Name = String.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(1).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep); } break; case 3: Property ep3 = new Property() { Name = String.Format(shPr.Pattern, "top"), Expression = new CssExpression { Terms = new List { p.Expression.Terms.First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep3); foreach (var direction in new[] { "left", "right" }) { Property ep2 = new Property() { Name = String.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(1).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep2); } Property ep4 = new Property() { Name = String.Format(shPr.Pattern, "bottom"), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(2).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep4); break; case 4: int skip = 0; foreach (var direction in new[] { "top", "right", "bottom", "left" }) { Property ep = new Property() { Name = String.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(skip++).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, AttributesInSelector = p.AttributesInSelector, ElementNamesInSelector = p.ElementNamesInSelector, SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, ep); } break; } } } } } private static string[] BackgroundRepeatValues = new[] { "repeat", "repeat-x", "repeat-y", "no-repeat", }; private static string[] BackgroundAttachmentValues = new[] { "scroll", "fixed", }; private static string[] BackgroundPositionValues = new[] { "left", "right", "top", "bottom", "center", }; private static CssDataType GetDatatypeFromBackgroundTerm(CssTerm term) { if (term.IsColor) return CssDataType.BackgroundColor; if (BackgroundRepeatValues.Contains(term.Value.ToLower())) return CssDataType.BackgroundRepeat; if (BackgroundAttachmentValues.Contains(term.Value.ToLower())) return CssDataType.BackgroundAttachment; if (term.Function != null) return CssDataType.BackgroundImage; if (term.Unit == CssUnit.CM || term.Unit == CssUnit.EM || term.Unit == CssUnit.IN || term.Unit == CssUnit.MM || term.Unit == CssUnit.PT || term.Unit == CssUnit.PX || term.Unit == CssUnit.Percent) return CssDataType.BackgroundPosition; if (BackgroundPositionValues.Contains(term.Value.ToLower())) return CssDataType.BackgroundPosition; return CssDataType.BackgroundPosition; } private static string[] ListStylePositionValues = new[] { "inside", "outside", }; private static string[] BorderStyleValues = new[] { "none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", }; private static CssDataType GetDatatypeFromBorderTerm(CssTerm term) { if (term.IsColor) { return CssDataType.Color; } if (BorderStyleValues.Contains(term.Value.ToLower())) return CssDataType.BorderStyle; return CssDataType.BorderWidth; } private static string[] ListStyleTypeValues = new[] { "disc", "circle", "square", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-latin", "upper-latin", "armenian", "georgian", "lower-alpha", "upper-alpha", }; private static CssDataType GetDatatypeFromListStyleTerm(CssTerm term) { if (ListStyleTypeValues.Contains(term.Value.ToLower())) return CssDataType.ListStyleType; if (ListStylePositionValues.Contains(term.Value.ToLower())) return CssDataType.ListStylePosition; return CssDataType.ListStyleImage; } private static string[] FontStyleValues = new[] { "italic", "oblique", }; private static string[] FontVarientValues = new[] { "small-caps", }; private static string[] FontWeightValues = new[] { "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900", }; private static CssDataType GetDatatypeFromFontTerm(CssTerm term) { if (FontStyleValues.Contains(term.Value.ToLower())) return CssDataType.FontStyle; if (FontVarientValues.Contains(term.Value.ToLower())) return CssDataType.FontVarient; if (FontWeightValues.Contains(term.Value.ToLower())) return CssDataType.FontWeight; if (FontSizeMap.ContainsKey(term.Value.ToLower())) return CssDataType.FontSize; if (term.Unit == CssUnit.CM || term.Unit == CssUnit.EM || term.Unit == CssUnit.IN || term.Unit == CssUnit.MM || term.Unit == CssUnit.PT || term.Unit == CssUnit.PX || term.Unit == CssUnit.Percent) return CssDataType.Length; return CssDataType.FontFamily; } public class PropertyInfo { public string[] Names; public bool Inherits; public Func Includes; public Func InitialValue; public Func ComputedValue; } private static void WriteXHtmlWithAnnotations(XElement element, StringBuilder sb) { int depth = element.Ancestors().Count() * 2; XElement dummyElement = new XElement(element.Name, element.Attributes()); sb.Append(String.Format("{0}{1}", "".PadRight(depth), dummyElement) + Environment.NewLine); Dictionary propList = element.Annotation>(); if (propList != null) { sb.Append("".PadRight(depth + 2) + "Properties from Stylesheets" + Environment.NewLine); sb.Append("".PadRight(depth + 2) + "===========================" + Environment.NewLine); foreach (var kvp in propList.OrderBy(p => p.Key).ThenBy(p => p.Value)) { Property prop = kvp.Value; string propString = String.Format("{0} High:{1} Id:{2} Att:{3} Ell:{4} Seq:{5}", (prop.Name + ":" + prop.Expression + " ").PadRight(50 - depth + 2, '.'), (int)prop.HighOrderSort, prop.IdAttributesInSelector, prop.AttributesInSelector, prop.ElementNamesInSelector, prop.SequenceNumber); sb.Append(String.Format("{0}{1}", "".PadRight(depth + 2), propString) + Environment.NewLine); } sb.Append(Environment.NewLine); } Dictionary computedProperties = element.Annotation>(); if (computedProperties != null) { sb.Append("".PadRight(depth + 2) + "Computed Properties" + Environment.NewLine); sb.Append("".PadRight(depth + 2) + "===================" + Environment.NewLine); foreach (var prop in computedProperties.OrderBy(cp => cp.Key)) { string propString = prop.Key + ":" + prop.Value; sb.Append(String.Format("{0}{1}", "".PadRight(depth + 2), propString) + Environment.NewLine); } sb.Append(Environment.NewLine); } foreach (var child in element.Elements()) WriteXHtmlWithAnnotations(child, sb); } public static string DumpCss(CssDocument css) { StringBuilder sb = new StringBuilder(); int indent = 0; Pr(sb, indent, "CSS Tree Dump"); Pr(sb, indent, "============="); Pr(sb, indent, "Directives count: {0}", css.Directives.Count()); Pr(sb, indent, "RuleSet count: {0}", css.RuleSets.Count()); foreach (var rs in css.RuleSets) DumpRuleSet(sb, indent, rs); Pr(sb, indent, ""); return sb.ToString(); } private static void DumpFunction(StringBuilder sb, int indent, CssFunction f) { Pr(sb, indent, "Function: {0}", f); if (f != null) { indent++; Pr(sb, indent, "Name: {0}", f.Name); DumpExpression(sb, indent, f.Expression); indent--; } } private static void DumpAttribute(StringBuilder sb, int indent, OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute a) { Pr(sb, indent, "Attribute: {0}", a); if (a != null) { indent++; Pr(sb, indent, "Operand: {0}", a.Operand); Pr(sb, indent, "Operator: {0}", a.Operator); Pr(sb, indent, "OperatorString: {0}", a.CssOperatorString); Pr(sb, indent, "Value: {0}", a.Value); indent--; } } private static void DumpSimpleSelector(StringBuilder sb, int indent, CssSimpleSelector s) { indent++; Pr(sb, indent, "SimpleSelector: {0}", s); if (s != null) { indent++; DumpAttribute(sb, indent, s.Attribute); Pr(sb, indent, "Child: {0}", s.Child); DumpSimpleSelector(sb, indent, s.Child); Pr(sb, indent, "Class: {0}", s.Class); Pr(sb, indent, "Combinator: {0}", s.Combinator); Pr(sb, indent, "CombinatorString: {0}", s.CombinatorString); Pr(sb, indent, "ElementName: >{0}<", s.ElementName); DumpFunction(sb, indent, s.Function); Pr(sb, indent, "ID: {0}", s.ID); Pr(sb, indent, "Pseudo: {0}", s.Pseudo); indent--; } indent--; } private static void DumpSelectors(StringBuilder sb, int indent, CssSelector s) { indent++; Pr(sb, indent, "SimpleSelectors count: {0}", s.SimpleSelectors.Count()); foreach (var ss in s.SimpleSelectors) DumpSimpleSelector(sb, indent, ss); indent--; } private static void DumpTerm(StringBuilder sb, int indent, CssTerm t) { Pr(sb, indent, "Term >{0}<", t.ToString()); indent++; DumpFunction(sb, indent, t.Function); Pr(sb, indent, "IsColor: {0}", t.IsColor); Pr(sb, indent, "Separator: {0}", t.Separator); Pr(sb, indent, "SeparatorChar: {0}", t.SeparatorChar); Pr(sb, indent, "Sign: {0}", t.Sign); Pr(sb, indent, "SignChar: {0}", t.SignChar); Pr(sb, indent, "Type: {0}", t.Type); Pr(sb, indent, "Unit: {0}", t.Unit); Pr(sb, indent, "UnitString: {0}", t.UnitString); Pr(sb, indent, "Value: {0}", t.Value); indent--; } private static void DumpExpression(StringBuilder sb, int indent, CssExpression e) { Pr(sb, indent, "Expression >{0}<", e.ToString()); indent++; Pr(sb, indent, "Terms count: {0}", e.Terms.Count()); foreach (var t in e.Terms) DumpTerm(sb, indent, t); indent--; } private static void DumpDeclarations(StringBuilder sb, int indent, CssDeclaration d) { indent++; Pr(sb, indent, "Declaration >{0}<", d.ToString()); indent++; Pr(sb, indent, "Name: {0}", d.Name); DumpExpression(sb, indent, d.Expression); Pr(sb, indent, "Important: {0}", d.Important); indent--; indent--; } private static void DumpRuleSet(StringBuilder sb, int indent, CssRuleSet rs) { indent++; Pr(sb, indent, "RuleSet"); indent++; Pr(sb, indent, "Selectors count: {0}", rs.Selectors.Count()); foreach (var s in rs.Selectors) DumpSelectors(sb, indent, s); Pr(sb, indent, "Declarations count: {0}", rs.Declarations.Count()); foreach (var d in rs.Declarations) DumpDeclarations(sb, indent, d); indent--; indent--; } private static void Pr(StringBuilder sb, int indent, string format, object o) { if (o == null) return; string text = String.Format(format, o); StringBuilder sb2 = new StringBuilder("".PadRight(indent * 2) + text); //sb2.Replace("&", "&"); //sb2.Replace("<", "<"); //sb2.Replace(">", ">"); sb.Append(sb2); sb.Append(Environment.NewLine); //Console.WriteLine(sb2); } private static void Pr(StringBuilder sb, int indent, string text) { StringBuilder sb2 = new StringBuilder("".PadRight(indent * 2) + text); //sb2.Replace("&", "&"); //sb2.Replace("<", "<"); //sb2.Replace(">", ">"); sb.Append(sb2); sb.Append(Environment.NewLine); //Console.WriteLine(sb2); } public class Property : IComparable { public string Name { get; set; } public CssExpression Expression { get; set; } public HighOrderPriority HighOrderSort { get; set; } public int IdAttributesInSelector { get; set; } public int AttributesInSelector { get; set; } public int ElementNamesInSelector { get; set; } public int SequenceNumber { get; set; } public enum HighOrderPriority { InitialValue = 0, Inherited = 1, UserAgentNormal = 2, UserAgentHigh = 3, UserNormal = 4, AuthorNormal = 5, HtmlAttribute = 6, StyleAttributeNormal = 7, StyleAttributeHigh = 8, AuthorHigh = 9, UserHigh = 10, }; int System.IComparable.CompareTo(Property other) { // if this is less than other, return -1 // if this is greater than other, return 1 int gt = 1; int lt = -1; if (this.HighOrderSort < other.HighOrderSort) return lt; if (this.HighOrderSort > other.HighOrderSort) return gt; if (this.IdAttributesInSelector < other.IdAttributesInSelector) return lt; if (this.IdAttributesInSelector > other.IdAttributesInSelector) return gt; if (this.AttributesInSelector < other.AttributesInSelector) return lt; if (this.AttributesInSelector > other.AttributesInSelector) return gt; if (this.ElementNamesInSelector < other.ElementNamesInSelector) return lt; if (this.ElementNamesInSelector > other.ElementNamesInSelector) return gt; return this.SequenceNumber.CompareTo(other.SequenceNumber); } } private static Dictionary ColorMap = new Dictionary() { { "maroon", "800000" }, { "red", "FF0000" }, { "orange", "FFA500" }, { "yellow", "FFFF00" }, { "olive", "808000" }, { "purple", "800080" }, { "fuchsia", "FF00FF" }, { "white", "FFFFFF" }, { "lime", "00FF00" }, { "green", "008000" }, { "navy", "000080" }, { "blue", "0000FF" }, { "mediumblue", "0000CD" }, { "aqua", "00FFFF" }, { "teal", "008080" }, { "black", "000000" }, { "silver", "C0C0C0" }, { "gray", "808080" }, { "darkgray", "A9A9A9" }, { "beige", "F5F5DC" }, { "windowtext", "000000" }, }; public static string GetWmlColorFromExpression(CssExpression color) { // todo have to handle all forms of colors here if (color.Terms.Count() == 1) { CssTerm term = color.Terms.First(); if (term.Type == CssTermType.Function && term.Function.Name.ToUpper() == "RGB" && term.Function.Expression.Terms.Count == 3) { List lt = term.Function.Expression.Terms; if (lt.First().Unit == CssUnit.Percent) { string v1 = lt.First().Value; string v2 = lt.ElementAt(1).Value; string v3 = lt.ElementAt(2).Value; string colorInHex = String.Format("{0:x2}{1:x2}{2:x2}", (int)((float.Parse(v1) / 100.0) * 255), (int)((float.Parse(v2) / 100.0) * 255), (int)((float.Parse(v3) / 100.0) * 255)); return colorInHex; } else { string v1 = lt.First().Value; string v2 = lt.ElementAt(1).Value; string v3 = lt.ElementAt(2).Value; string colorInHex = String.Format("{0:x2}{1:x2}{2:x2}", int.Parse(v1), int.Parse(v2), int.Parse(v3)); return colorInHex; } } string value = term.Value; if (value.Substring(0, 1) == "#" && value.Length == 4) { string e = ConvertSingleDigit(value.Substring(1, 1)) + ConvertSingleDigit(value.Substring(2, 1)) + ConvertSingleDigit(value.Substring(3, 1)); return e; } if (value.Substring(0, 1) == "#") return value.Substring(1); if (ColorMap.ContainsKey(value)) return ColorMap[value]; return value; } return "000000"; } private static string ConvertSingleDigit(string singleDigit) { return singleDigit + singleDigit; } } } #if false color Value: | inherit Initial: depends on UA Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec margin-top, margin-bottom Value: | inherit Initial: 0 Applies to: all elements except elements with table display types other than table-caption, table, and inline-table all elements except th, td, tr Inherited: no Percentages: refer to width of containing block Computed value: the percentage as specified or the absolute length margin-right, margin-left Value: | inherit Initial: 0 Applies to: all elements except elements with table display types other than table-caption, table, and inline-table all elements except th, td, tr Inherited: no Percentages: refer to width of containing block Computed value: the percentage as specified or the absolute length padding-top, padding-right, padding-bottom, padding-left Value: | inherit Initial: 0 Applies to: all elements except table-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column all elements except tr Inherited: no Percentages: refer to width of containing block Computed value: the percentage as specified or the absolute length border-top-width, border-right-width, border-bottom-width, border-left-width Value: | inherit Initial: medium Applies to: all elements Inherited: no Percentages: N/A Computed value: absolute length; '0' if the border style is 'none' or 'hidden' border-top-color, border-right-color, border-bottom-color, border-left-color Value: | transparent | inherit Initial: the value of the color property Applies to: all elements Inherited: no Percentages: N/A Computed value: when taken from the ’color’ property, the computed value of ’color’; otherwise, as specified border-top-style, border-right-style, border-bottom-style, border-left-style Value: | inherit Initial: none Applies to: all elements Inherited: no Percentages: N/A Computed value: as specified display Value: inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | none | inherit Initial: inline Applies to: all elements Inherited: no Percentages: N/A Computed value: see text position Value: static | relative | absolute | fixed | inherit Initial: static Applies to: all elements Inherited: no Percentages: N/A Computed value: as specified top Value: | | auto | inherit Initial: auto Applies to: positioned elements Inherited: no Percentages: refer to height of containing block Computed value: if specified as a length, the corresponding absolute length; if specified as a percentage, the specified value; otherwise, ’auto’. right Value: | | auto | inherit Initial: auto Applies to: positioned elements Inherited: no Percentages: refer to width of containing block Computed value: if specified as a length, the corresponding absolute length; if specified as a percentage, the specified value; otherwise, ’auto’. bottom Value: | | auto | inherit Initial: auto Applies to: positioned elements Inherited: no Percentages: refer to height of containing block Computed value: if specified as a length, the corresponding absolute length; if specified as a percentage, the specified value; otherwise, ’auto’. left Value: | | auto | inherit Initial: auto Applies to: positioned elements Inherited: no Percentages: refer to width of containing block Computed value: if specified as a length, the corresponding absolute length; if specified as a percentage, the specified value; otherwise, ’auto’. float Value: left | right | none | inherit Initial: none Applies to: all, but see 9.7 p. 153 Inherited: no Percentages: N/A Computed value: as specified clear Value: none | left | right | both | inherit Initial: none Applies to: block-level elements Inherited: no Percentages: N/A Computed value: as specified z-index Value: auto | integer | inherit Initial: auto Applies to: positioned elements Inherited: no Percentages: N/A Computed value: as spec direction Value: ltr | rtl | inherit Initial: ltr Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec unicode-bidi Value: normal | embed | bidi-override | inherit Initial: normal Applies to: all elements, but see prose Inherited: no Percentages: N/A Computed value: as spec width Value: | | auto | inherit Initial: auto Applies to: all elements but non-replaced in-line elements, table rows, and row groups Inherited: no Percentages: refer to width of containing block Computed value: the percentage or 'auto' as specified or the absolute length min-width Value: | | inherit Initial: 0 Applies to: all elements but non-replaced in-line elements, table rows, and row groups Inherited: no Percentages: refer to width of containing block Computed value: the percentage as spec or the absolute length max-width Value: | | none | inherit Initial: none Applies to: all elements but non-replaced in-line elements, table rows, and row groups Inherited: no Percentages: refer to width of containing block Computed value: the percentage as spec or the absolute length height Value: | | auto | inherit Initial: auto Applies to: all elements but non-replaced in-line elements, table columns, and column groups Inherited: no Percentages: see prose Computed value: the percentage as spec or the absolute length min-height Value: | | inherit Initial: 0 Applies to: all elements but non-replaced in-line elements, table columns, and column groups Inherited: no Percentages: see prose Computed value: the percentage as spec or the absolute length max-height Value: | | none | inherit Initial: none Applies to: all elements but non-replaced in-line elements, table columns, and column groups Inherited: no Percentages: refer to height of containing block Computed value: the percentage as spec or the absolute length line-height Value: normal | | | | Initial: normal Applies to: all elements Inherited: yes Percentages: refer to the font size of the element itself Computed value: for and the absolute value, otherwise as specified. vertical-align Value: baseline | sub | super | top | text-top | middle | bottom | text-bottom | | | inherit Initial: baseline Applies to: inline-level and 'table-cell' elements Inherited: no Percentages: refer to the line height of the element itself Computed value: for and the absolute length, otherwise as specified. visibility Value: visible | hidden | collapse | inherit Initial: visible Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec list-style-type Value: disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none | inherit Initial: disc Applies to: elements with display: list-item Inherited: yes Percentages: N/A Computed value: as spec list-style-image Value: | none | inherit Initial: none Applies to: elements with ’display: list-item’ Inherited: yes Percentages: N/A Computed value: absolute URI or ’none’ list-style-position Value: inside | outside | inherit Initial: outside Applies to: elements with ’display: list-item’ Inherited: yes Percentages: N/A Computed value: as spec background-color Value: | transparent | inherit Initial: transparent Applies to: all elements Inherited: no Percentages: N/A Computed value: as spec font-family Value: [[ | ] [, | ]* ] | inherit Initial: depends on user agent Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec font-style Value: normal | italic | oblique | inherit Initial: normal Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec font-variant Value: normal | small-caps | inherit Initial: normal Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec font-weight Value: normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit Initial: normal Applies to: all elements Inherited: yes Percentages: N/A Computed value: see text font-size Value: | | | | inherit Initial: medium Applies to: all elements Inherited: yes Percentages: refer to inherited font size Computed value: absolute length text-indent Value: | | inherit Initial: 0 Applies to: block containers Inherited: yes Percentages: refer to width of containing block Computed value: the percentage as specified or the absolute length text-align Value: left | right | center | justify | inherit Initial: a nameless value that acts as ’left’ if ’direction’ is ’ltr’, ’right’ if ’direction’ is ’rtl’ Applies to: block containers Inherited: yes Percentages: N/A Computed value: the initial value or as spec text-decoration Value: none | [ underline || overline || line-through || blink ] | inherit Initial: none Applies to: all elements Inherited: no Percentages: N/A Computed value: as spec letter-spacing Value: normal | | inherit Initial: normal Applies to: all elements Inherited: yes Percentages: N/A Computed value: ’normal’ or absolute length word-spacing Value: normal | | inherit Initial: normal Applies to: all elements Inherited: yes Percentages: N/A Computed value: for ’normal’ the value 0; otherwise the absolute length text-transform Value: capitalize | uppercase | lowercase | none | inherit Initial: none Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec white-space Value: normal | pre | nowrap | pre-wrap | pre-line | inherit Initial: normal Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec caption-side Value: top | bottom | inherit Initial: top Applies to: 'table-caption' elements Inherited: yes Percentages: N/A Computed value: as spec table-layout Value: auto | fixed | inherit Initial: auto Applies to: ’table’ and ’inline-table’ elements Inherited: no Percentages: N/A Computed value: as spec border-collapse Value: collapse | separate | inherit Initial: separate Applies to: ’table’ and ’inline-table’ elements Inherited: yes Percentages: N/A Computed value: as spec border-spacing Value: ? | inherit Initial: 0 Applies to: ’table’ and ’inline-table’ elements Inherited: yes Percentages: N/A Computed value: two absolute lengths empty-cells Value: show | hide | inherit Initial: show Applies to: 'table-cell' elements Inherited: yes Percentages: N/A Computed value: as spec Shorthand Properties margin Value: {1,4} | inherit Initial: see individual props Applies to: all elements except elements with table display types other than table-caption, table, and inline-table Inherited: no Percentages: refer to width of containing block Computed value: see individual props set one - sets all four values set two - first is top/bottom, second is right/left set three - first is top, second is right/left, third is bottom set four - top right bottom left padding Value: {1-4} | inherit Initial: see individual props Applies to: all elements except table-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column Inherited: no Percentages: refer to width of containing block Computed value: see individual props set one - sets all four values set two - first is top/bottom, second is right/left set four - top right bottom left border-width Value: {1-4} | inherit Initial: see individual props Applies to: all elements Inherited: no Percentages: N/A Computed value: see individual props set one - sets all four values set two - first is top/bottom, second is right/left set four - top right bottom left border-color Value: [ | transparent]{1-4} | inherit Initial: see individual props Applies to: all elements Inherited: no Percentages: N/A Computed value: see individual props set one - sets all four values set two - first is top/bottom, second is right/left set four - top right bottom left border-style Value: {1-4} | inherit Initial: see individual props Applies to: all elements Inherited: no Percentages: N/A Computed value: see individual props set one - sets all four values set two - first is top/bottom, second is right/left set four - top right bottom left border-top, border-right, border-bottom, border-left, border Value: [ || || ] | inherit Initial: see individ Applies to: all elements Inherited: no Percentages: N/A Computed value: see individ list-style Value: [ <’list-style-type’> || <’list-style-position’> || <’list-style-image’> ] | inherit Initial: see individual props Applies to: elements with ’display: list-item’ Inherited: yes Percentages: N/A Computed value: see individual props background Value: [<’background-color’> || <’background-image’> || <’background- repeat’> || <’background-attachment’> || <’background- position’>] | inherit Initial: see individual props Applies to: all elements Inherited: no Percentages: allowed on background-position Computed value: see individual props font Value: [ [ <’font-style’> || <’font-variant’> || <’font-weight’> ]? <’font-size’> [ / <’line-height’> ]? <’font-family’> ] | caption | icon | menu | message-box | small-caption | status-bar | inherit Initial: see individual props Applies to: all elements Inherited: yes Percentages: see individual props Computed value: see individual props probably not support overflow Value: visible | hidden | scroll | auto | inherit Initial: visible Applies to: block containers Inherited: no Percentages: N/A Computed value: as spec clip Value: | auto | inherit Initial: auto Applies to: absolutely positioned elements Inherited: no Percentages: N/A Computed value: auto if spec as 'auto', otherwise a rectangle with four values, each of which is 'auto' if spec as 'auto' and the computed length otherwise content Value: normal | none | [ | | | attr() | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit Initial: normal Applies to: :before and :after pseudo elements Inherited: no Percentages: N/A Computed value: On elements, always computes to ’normal’. On :before and :after, if ’normal’ is specified, computes to ’none’. Otherwise, for URI values, the absolute URI; for attr() values, the resulting string; for other keywords, as specified. quotes Value: [ ]+ | none | inherit Initial: depends on user agent Applies to: all elements Inherited: yes Percentages: N/A Computed value: as spec counter-reset Value: [ ? ]+ | none | inherit Initial: none Applies to: all elements Inherited: no Percentages: N/A Computed value: as spec counter-increment Value: [ ? ]+ | none | inherit Initial: none Applies to: all elements Inherited: no Percentages: N/A Computed value: as spec background-image Value: | none | inherit Initial: none Applies to: all elements Inherited: no Percentages: N/A Computed value: absolute URI or none background-repeat Value: repeat | repeat-x | repeat-y | no-repeat | inherit Initial: repeat Applies to: all elements Inherited: no Percentages: N/A Computed value: as spec background-attachment Value: scroll | fixed | inherit Initial: scroll Applies to: all elements Inherited: no Percentages: N/A Computed value: as spec background-position Value: [ [ | | left | center | right ] [ | | top | center | bottom ]? ] | [ [ left | center | right ] || [ top | center | bottom ] ] | inherit Initial: 0% 0% Applies to: all elements Inherited: no Percentages: refer to the size of the box itself Computed value: for the absolute value, otherwise a percentage #endif #if false background-color border-bottom-color border-bottom-style border-bottom-width border-collapse border-left-color border-left-style border-left-width border-right-color border-right-style border-right-width border-spacing border-top-color border-top-style border-top-width bottom caption-side clear color direction display empty-cells float font-family font-size font-style font-variant font-weight height left letter-spacing line-height list-style-image list-style-position list-style-type margin-bottom margin-left margin-right margin-top max-height max-width min-height min-width padding-bottom padding-left padding-right padding-top position right table-layout text-align text-decoration text-indent text-transform top unicode-bidi vertical-align visibility white-space width word-spacing z-index attributes ========== meta style _class href don't know ========== colspan caption title hr border http_equiv content name width height src alt id descr type elements ======== html head body div p h1 h2 h3 h4 h5 h6 h7 h8 h9 a b table tr td br img span blockquote sub sup ol ul li strong em tbody #endif