private static void CreateStyleFromShd(Dictionary<string, string> style, XElement shd)
 {
     if (shd == null)
         return;
     var shadeType = (string)shd.Attribute(W.val);
     var color = (string)shd.Attribute(W.color);
     var fill = (string)shd.Attribute(W.fill);
     if (ShadeMapper.ContainsKey(shadeType))
     {
         color = ShadeMapper[shadeType](color, fill);
     }
     if (color != null)
     {
         var cvtColor = ConvertColor(color);
         if (cvtColor != null && cvtColor != "")
             style.AddIfMissing("background", cvtColor);
     }
 }
        /*
         * Handle:
         * - b
         * - bdr
         * - caps
         * - color
         * - dstrike
         * - highlight
         * - i
         * - position
         * - rFonts
         * - shd
         * - smallCaps
         * - spacing
         * - strike
         * - sz
         * - u
         * - vanish
         * - vertAlign
         *
         * Don't handle:
         * - em
         * - emboss
         * - fitText
         * - imprint
         * - kern
         * - outline
         * - shadow
         * - w
         * 
         */

        private static object ConvertRun(WordprocessingDocument wordDoc, HtmlConverterSettings settings, XElement run)
        {
            var style = new Dictionary<string, string>();
            var sn = (string)run.Attribute(PtOpenXml.StyleName);
            if (sn != null)
                style.Add("PtStyleName", sn);

            var rPr = run.Element(W.rPr);

            if (rPr == null)
            {
                object content2 = run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m));
                return content2;
            }

            // hide all content that contains the w:rPr/w:webHidden element
            if (rPr.Element(W.webHidden) != null)
                return null;

            // W.bdr
            if (rPr.Element(W.bdr) != null && (string)rPr.Elements(W.bdr).Attributes(W.val).FirstOrDefault() != "none")
            {
                style.AddIfMissing("border", "solid windowtext 1.0pt");
                style.AddIfMissing("padding", "0in");
            }

            // W.color
            string color = (string)rPr.Elements(W.color).Attributes(W.val).FirstOrDefault();
            if (color != null)
                CreateColorProperty("color", color, style);

            // W.highlight
            string highlight = (string)rPr.Elements(W.highlight).Attributes(W.val).FirstOrDefault();
            if (highlight != null)
                CreateColorProperty("background", highlight, style);

            // W.shd
            string shade = (string)rPr.Elements(W.shd).Attributes(W.fill).FirstOrDefault();
            if (shade != null)
                CreateColorProperty("background", shade, style);

            // Pt.FontName
            string font = null;
            if (run.Element(W.sym) != null)
                font = (string)run.Elements(W.sym).Attributes(W.font).FirstOrDefault();
            else
                font = (string)run.Attributes(PtOpenXml.FontName).FirstOrDefault();
            if (font != null)
                CreateFontCssProperty(font, style);

            // W.sz
            var languageType = (string)run.Attribute(PtOpenXml.LanguageType);
            decimal? sz = null;
            if (languageType == "bidi")
                sz = (decimal?)rPr.Elements(W.szCs).Attributes(W.val).FirstOrDefault();
            else
                sz = (decimal?)rPr.Elements(W.sz).Attributes(W.val).FirstOrDefault();
            if (sz != null)
                style.AddIfMissing("font-size", string.Format("{0}pt", sz / 2.0m));

            // W.caps
            if (getBoolProp(rPr, W.caps))
                style.AddIfMissing("text-transform", "uppercase");

            // W.smallCaps
            if (getBoolProp(rPr, W.smallCaps))
                style.AddIfMissing("font-variant", "small-caps");

            // W.spacing
            decimal? spacingInTwips = (decimal?)rPr.Elements(W.spacing).Attributes(W.val).FirstOrDefault();
            if (spacingInTwips != null)
                style.AddIfMissing("letter-spacing", string.Format("{0}pt", spacingInTwips / 20));

            // W.position
            decimal? position = (decimal?)rPr.Elements(W.position).Attributes(W.val).FirstOrDefault();
            if (position != null)
            {
                style.AddIfMissing("position", "relative");
                style.AddIfMissing("top", string.Format("{0}pt", -(position / 2)));
            }

            // W.vanish
            if (getBoolProp(rPr, W.vanish))
                style.AddIfMissing("display", "none");

            object content = run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m));

            // W.u
            if (rPr.Element(W.u) != null && (string)rPr.Elements(W.u).Attributes(W.val).FirstOrDefault() != "none")
            {
                var newContent = new XElement(Xhtml.u, content);
                if (newContent.Nodes().Any())
                    content = newContent;
                style.AddIfMissing("text-decoration", "underline");
            }

            // W.i
            if (getBoolProp(rPr, W.i))
            {
                var newContent = new XElement(Xhtml.i, content);
                if (newContent.Nodes().Any())
                    content = newContent;
                style.AddIfMissing("font-style", "italic");
            }

            // W.b
            if (getBoolProp(rPr, W.b))
            {
                var newContent = new XElement(Xhtml.b, content);
                if (newContent.Nodes().Any())
                    content = newContent;
                style.AddIfMissing("font-weight", "bold");
            }
            else
            {
                style.AddIfMissing("font-weight", "normal");
            }

            // W.strike
            if (getBoolProp(rPr, W.strike) || getBoolProp(rPr, W.dstrike))
            {
                var newContent = new XElement(Xhtml.s, content);
                if (newContent.Nodes().Any())
                    content = newContent;
                style.AddIfMissing("text-decoration", "line-through");
            }

            // W.vertAlign
            if (rPr.Element(W.vertAlign) != null && (string)rPr.Elements(W.vertAlign).Attributes(W.val).FirstOrDefault() == "superscript")
            {
                var newContent = new XElement(Xhtml.sup, content);
                if (newContent.Nodes().Any())
                    content = newContent;
            }

            if (rPr.Element(W.vertAlign) != null && (string)rPr.Elements(W.vertAlign).Attributes(W.val).FirstOrDefault() == "subscript")
            {
                var newContent = new XElement(Xhtml.sub, content);
                if (newContent.Nodes().Any())
                    content = newContent;
            }

            var rtl = rPr.Element(W.rtl);
            var isRtl = rtl != null;

            var paragraph = run.Ancestors(W.p).FirstOrDefault();
            var paraBidi = paragraph
                .Elements(W.pPr)
                .Elements(W.bidi)
                .Where(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true)
                .FirstOrDefault();
            var paraIsBidi = paraBidi != null;

            string lang = null;
            if (languageType == "western")
                lang = (string)rPr.Elements(W.lang).Attributes(W.val).FirstOrDefault();
            else if (languageType == "bidi")
                lang = (string)rPr.Elements(W.lang).Attributes(W.bidi).FirstOrDefault();
            else if (languageType == "eastAsia")
                lang = (string)rPr.Elements(W.lang).Attributes(W.eastAsia).FirstOrDefault();

            // only do the following for text runs.
            XEntity runStartMark = null;
            XEntity runEndMark = null;

            // Can't add directional marks if the font-family is symbol - they are visible, and display as a ?
            bool addDirectionalMarks = true;
            if (style.ContainsKey("font-family"))
            {
                if (style["font-family"].ToLower() == "symbol")
                    addDirectionalMarks = false;
            }
            if (addDirectionalMarks)
            {
                if (run.Element(W.t) != null)
                {
                    if (isRtl)
                    {
                        runStartMark = new XEntity("#x200f"); // RLM
                        runEndMark = new XEntity("#x200f"); // RLM
                    }
                    else
                    {
                        if (paraIsBidi)
                        {
                            runStartMark = new XEntity("#x200e"); // LRM
                            runEndMark = new XEntity("#x200e"); // LRM
                        }
                    }
                }
            }

            string defaultLanguage = "en-US"; // todo need to get defaultLanguage
            if (lang == null)
                lang = defaultLanguage;
            XAttribute langAttribute = new XAttribute("lang", lang);
            if (lang == defaultLanguage)
                langAttribute = null;

            if (style.Any() || isRtl || langAttribute != null)
            {
                style.AddIfMissing("margin", "0in");
                style.AddIfMissing("padding", "0in");
                var xe = new XElement(Xhtml.span,
                    langAttribute,
                    runStartMark,
                    content,
                    runEndMark);

                xe.AddAnnotation(style);
                content = xe;
            }
            return content;
        }
        private static object CreateBorderDivs(WordprocessingDocument wordDoc, HtmlConverterSettings settings, IEnumerable<XElement> elements)
        {
            return elements.GroupAdjacent(e =>
                {
                    if (e.Elements(W.pPr).Elements(W.pBdr).Any())
                    {
                        var pBdr = e.Element(W.pPr).Element(W.pBdr);
                        var indStr = "";
                        var ind = e.Element(W.pPr).Element(W.ind);
                        if (ind != null)
                            indStr = ind.ToString(SaveOptions.DisableFormatting);
                        return pBdr.ToString(SaveOptions.DisableFormatting) + indStr;
                    }
                    else if (e.Name == W.tbl)
                    {
                        return "table";
                    }
                    else
                    {
                        return ""; // empty string means no pBdr
                    }
                })
                .Select(g =>
                {
                    if (g.Key == "")
                    {
                        var o = GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, 0m);
                        return (object)o;
                    }
                    if (g.Key == "table")
                    {
                        var o = g.Select(gc => ConvertToHtmlTransform(wordDoc,
                            settings, gc, false, 0));
                        return o;
                    }
                    var pPr = g.First().Element(W.pPr);
                    var pBdr = pPr.Element(W.pBdr);
                    Dictionary<string, string> style = new Dictionary<string, string>();
                    GenerateBorderStyle(pBdr, W.top, style, BorderType.Paragraph);
                    GenerateBorderStyle(pBdr, W.right, style, BorderType.Paragraph);
                    GenerateBorderStyle(pBdr, W.bottom, style, BorderType.Paragraph);
                    GenerateBorderStyle(pBdr, W.left, style, BorderType.Paragraph);
                    var ind = pPr.Element(W.ind);
                    decimal currentMarginLeft = 0m;
                    if (ind != null)
                    {
                        decimal? left = (decimal?)ind.Attribute(W.left);
                        decimal leftInInches = 0;
                        if (left != null)
                            leftInInches = (decimal)left / 1440;
                        decimal? hanging = (decimal?)ind.Attribute(W.hanging);
                        decimal hangingInInches = 0;
                        if (hanging != null)
                            hangingInInches = -(decimal)hanging / 1440;
                        currentMarginLeft = leftInInches + hangingInInches;
                        style.AddIfMissing("margin-left", string.Format("{0:0.00}in", currentMarginLeft));
                    }

                    var div = new XElement(Xhtml.div,
                        GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, currentMarginLeft));

                    div.AddAnnotation(style);
                    return div;
                })
            .ToList();
        }
        private static object ProcessTableCell(WordprocessingDocument wordDoc, HtmlConverterSettings settings, XElement element)
        {
            var style = new Dictionary<string, string>();
            XAttribute colSpan = null;
            XAttribute rowSpan = null;

            var tcPr = element.Element(W.tcPr);
            if (tcPr != null)
            {
                if ((string) tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart")
                {
                    var currentRow = element.Parent.ElementsBeforeSelf(W.tr).Count();
                    var currentCell = element.ElementsBeforeSelf(W.tc).Count();
                    var tbl = element.Parent.Parent;
                    int rowSpanCount = 1;
                    currentRow += 1;
                    while (true)
                    {
                        var row = tbl.Elements(W.tr).Skip(currentRow).FirstOrDefault();
                        if (row == null)
                            break;
                        var cell2 = row.Elements(W.tc).Skip(currentCell).FirstOrDefault();
                        if (cell2 == null)
                            break;
                        if (cell2.Elements(W.tcPr).Elements(W.vMerge).FirstOrDefault() == null)
                            break;
                        if ((string) cell2.Elements(W.tcPr).Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart")
                            break;
                        currentRow += 1;
                        rowSpanCount += 1;
                    }
                    rowSpan = new XAttribute("rowspan", rowSpanCount);
                }

                if (tcPr.Element(W.vMerge) != null &&
                    (string) tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() != "restart")
                    return null;

                if (tcPr.Element(W.vAlign) != null)
                {
                    var vAlignVal = (string) tcPr.Elements(W.vAlign).Attributes(W.val).FirstOrDefault();
                    if (vAlignVal == "top")
                        style.AddIfMissing("vertical-align", "top");
                    else if (vAlignVal == "center")
                        style.AddIfMissing("vertical-align", "middle");
                    else if (vAlignVal == "bottom")
                        style.AddIfMissing("vertical-align", "bottom");
                    else
                        style.AddIfMissing("vertical-align", "middle");
                }
                style.AddIfMissing("vertical-align", "top");

                if ((string) tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "dxa")
                {
                    decimal width = (int) tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault();
                    style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width/20m));
                }
                if ((string) tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "pct")
                {
                    decimal width = (int) tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault();
                    style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", width/50m));
                }

                var tcBorders = tcPr.Element(W.tcBorders);
                GenerateBorderStyle(tcBorders, W.top, style, BorderType.Cell);
                GenerateBorderStyle(tcBorders, W.right, style, BorderType.Cell);
                GenerateBorderStyle(tcBorders, W.bottom, style, BorderType.Cell);
                GenerateBorderStyle(tcBorders, W.left, style, BorderType.Cell);

                CreateStyleFromShd(style, tcPr.Element(W.shd));

                var gridSpan = tcPr.Elements(W.gridSpan).Attributes(W.val).Select(a => (int?) a).FirstOrDefault();
                if (gridSpan != null)
                    colSpan = new XAttribute("colspan", (int) gridSpan);
            }
            style.AddIfMissing("padding-top", "0");
            style.AddIfMissing("padding-bottom", "0");

            var cell = new XElement(Xhtml.td,
                rowSpan,
                colSpan,
                CreateBorderDivs(wordDoc, settings, element.Elements()));
            cell.AddAnnotation(style);
            return cell;
        }
 private static object ProcessTableRow(WordprocessingDocument wordDoc, HtmlConverterSettings settings, XElement element,
     decimal currentMarginLeft)
 {
     var style = new Dictionary<string, string>();
     int? trHeight = (int?) element.Elements(W.trPr).Elements(W.trHeight).Attributes(W.val).FirstOrDefault();
     if (trHeight != null)
         style.AddIfMissing("height",
             string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", (decimal) trHeight/1440m));
     var htmlRow = new XElement(Xhtml.tr,
         element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
     if (style.Any())
         htmlRow.AddAnnotation(style);
     return htmlRow;
 }
        private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc,
            HtmlConverterSettings settings, XNode node,
            bool suppressTrailingWhiteSpace,
            decimal currentMarginLeft)
        {
            XElement element = node as XElement;
            if (element != null)
            {
                if (element.Name == W.document)
                    return new XElement(Xhtml.html,
                        new XElement(Xhtml.head,
                            new XElement(Xhtml.meta,
                                new XAttribute("http-equiv", "Content-Type"),
                                new XAttribute("content", "text/html; charset=utf-8")),
                            new XElement(Xhtml.meta,
                                new XAttribute("name", "Generator"),
                                new XAttribute("content", "PowerTools for Open XML")),
                            settings.PageTitle != null ? new XElement(Xhtml.title,
                                new XText(settings.PageTitle)) : null
                        ),
                        element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft))
                    );

                // Transform the w:body element to the XHTML h:body element.
                if (element.Name == W.body)
                {
                    var sectionDivContent = new XElement(Xhtml.body,
                        CreateSectionDivs(wordDoc, settings, element));
                    return sectionDivContent;
                }

                if (element.Name == W.p)
                {
                    var bidi = element
                        .Elements(W.pPr)
                        .Elements(W.bidi)
                        .Where(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true)
                        .FirstOrDefault();
                    var isBidi = bidi != null;
                    string styleId = (string)element.Elements(W.pPr).Elements(W.pStyle)
                        .Attributes(W.val).FirstOrDefault();
                    if (styleId != null)
                    {
                        XElement style = wordDoc.MainDocumentPart.StyleDefinitionsPart
                            .GetXDocument().Root.Elements(W.style)
                            .Where(s => (string)s.Attribute(W.styleId) == styleId)
                            .FirstOrDefault();
                        if (style != null)
                        {
                            int? outlineLevel = (int?)style.Elements(W.pPr)
                                .Elements(W.outlineLvl).Attributes(W.val).FirstOrDefault();
                            if (outlineLevel != null && outlineLevel <= 5)
                            {
                                XName elementName = Xhtml.xhtml + string.Format("h{0}",
                                    outlineLevel + 1);
                                return ConvertParagraph(wordDoc, settings, element, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi);
                            }
                            else
                            {
                                XName elementName = Xhtml.p;
                                var o = ConvertParagraph(wordDoc, settings, element, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi);
                                return o;
                            }
                        }
                    }
                    else
                    {
                        XName elementName = Xhtml.p;
                        var o = ConvertParagraph(wordDoc, settings, element, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi);
                        return o;
                    }
                }

                // Transform hyperlinks to the XHTML h:A element.
                if (element.Name == W.hyperlink && element.Attribute(R.id) != null)
                {
                    try
                    {
                        return new XElement(Xhtml.A,
                            new XAttribute("href",
                                wordDoc.MainDocumentPart
                                    .HyperlinkRelationships
                                    .Where(x => x.Id == (string)element.Attribute(R.id))
                                    .First()
                                    .Uri
                            ),
                            element.Elements(W.r).Select(run => ConvertRun(wordDoc, settings, run))
                        );
                    }
                    catch (UriFormatException)
                    {
                        return element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft));
                    }
                }

                // Transform hyperlinks to bookmarks to the XHTML h:A element.
                if (element.Name == W.hyperlink && element.Attribute(W.anchor) != null)
                {
                    var style = new Dictionary<string, string>();
                    var a = new XElement(Xhtml.A,
                        new XAttribute("href", "#" + (string)element.Attribute(W.anchor)),
                        element
                            .Elements(W.r)
                            .Select(run => ConvertRun(wordDoc, settings, run)));
                    style.Add("text-decoration", "none");
                    a.AddAnnotation(style);
                    return a;
                }

                // Transform contents of runs.
                if (element.Name == W.r)
                    return ConvertRun(wordDoc, settings, element);

                // Transform w:bookmarkStart into anchor
                if (element.Name == W.bookmarkStart)
                {
                    var name = (string)element.Attribute(W.name);
                    if (name != null)
                    {
                        var style = new Dictionary<string, string>();
                        var a = new XElement(Xhtml.A,
                            new XAttribute("id", name),
                            new XText(""));
                        style.Add("text-decoration", "none");
                        a.AddAnnotation(style);
                        return a;
                    }
                }

                // Transform every w:t element to a text node.
                if (element.Name == W.t)
                {
                    var textWithEntities = ConvertEntities(element.Value);
                    return textWithEntities;
                }

                // Transform symbols to spans
                if (element.Name == W.sym)
                {
                    var cs = (string)element.Attribute(W._char);
                    var c = Convert.ToInt32(cs, 16);
                    var symbolSpan = new XElement(Xhtml.span, new XEntity(string.Format("#{0}", ((int)c).ToString())));
                    return symbolSpan;
                }

                // Transform tabs that have the pt:TabWidth attribute set
                if (element.Name == W.tab)
                {
                    var tabWidthAtt = element.Attribute(PtOpenXml.TabWidth);
                    if (tabWidthAtt != null)
                    {
                        var leader = (string)element.Attribute(PtOpenXml.Leader);
                        var tabWidth = (decimal)tabWidthAtt;
                        var style = new Dictionary<string, string>();
                        XElement span;
                        if (leader != null)
                        {
                            var leaderChar = ".";
                            if (leader == "hyphen")
                                leaderChar = "-";
                            else if (leader == "dot")
                                leaderChar = ".";
                            else if (leader == "underscore")
                                leaderChar = "_";

                            var runContainingTabToReplace = element.Ancestors(W.r).First();
                            var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.pt + "FontName");
                            if (fontNameAtt == null)
                                fontNameAtt = runContainingTabToReplace.Ancestors(W.p).First()
                                    .Attribute(PtOpenXml.pt + "FontName");

                            var dummyRun = new XElement(W.r,
                                fontNameAtt,
                                runContainingTabToReplace.Elements(W.rPr),
                                new XElement(W.t, leaderChar));

                            var widthOfLeaderChar = CalcWidthOfRunInTwips(dummyRun);

                            bool forceArial = false;
                            if (widthOfLeaderChar == 0)
                            {
                                dummyRun = new XElement(W.r,
                                    new XAttribute(PtOpenXml.FontName, "Arial"),
                                    runContainingTabToReplace.Elements(W.rPr),
                                    new XElement(W.t, leaderChar));
                                widthOfLeaderChar = CalcWidthOfRunInTwips(dummyRun);
                                forceArial = true;
                            }

                            if (widthOfLeaderChar != 0)
                            {
                                var numberOfLeaderChars = (int)(Math.Floor((tabWidth * 1440) / widthOfLeaderChar));
                                if (numberOfLeaderChars < 0)
                                    numberOfLeaderChars = 0;
                                span = new XElement(Xhtml.span,
                                    " " + "".PadRight(numberOfLeaderChars, leaderChar[0]) + " ");
                                style.Add("margin", "0 0 0 0");
                                style.Add("padding", "0 0 0 0");
                                style.Add("width", string.Format("{0:0.00}in", tabWidth));
                                style.Add("text-align", "center");
                                if (forceArial)
                                    style.Add("font-family", "Arial");
                            }
                            else
                            {
                                span = new XElement(Xhtml.span, " ");
                                style.Add("margin", "0 0 0 0");
                                style.Add("padding", "0 0 0 0");
                                style.Add("width", string.Format("{0:0.00}in", tabWidth));
                                style.Add("text-align", "center");
                                if (leader == "underscore")
                                {
                                    style.Add("text-decoration", "underline");
                                }
                            }
                        }
                        else
                        {
#if false
                            var bidi = element
                                .Ancestors(W.p)
                                .Take(1)
                                .Elements(W.pPr)
                                .Elements(W.bidi)
                                .Where(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true)
                                .FirstOrDefault();
                            var isBidi = bidi != null;
                            if (isBidi)
                                span = new XElement(Xhtml.span, new XEntity("#x200f")); // RLM
                            else
                                span = new XElement(Xhtml.span, new XEntity("#x200e")); // LRM
#else
                            span = new XElement(Xhtml.span, new XEntity("nbsp"));
#endif
                            style.Add("margin", string.Format("0 0 0 {0:0.00}in", tabWidth));
                            style.Add("padding", "0 0 0 0");
                        }
                        span.AddAnnotation(style);
                        return span;
                    }
                }

                // Transform w:br to h:br.
                if (element.Name == W.br || element.Name == W.cr)
                {
                    XElement span = null;
                    var tabWidth = (decimal?)element.Attribute(PtOpenXml.TabWidth);
                    if (tabWidth != null)
                    {
                        span = new XElement(Xhtml.span);
                        var style = new Dictionary<string, string>();
                        style.Add("margin", string.Format("0 0 0 {0:0.00}in", tabWidth));
                        style.Add("padding", "0 0 0 0");
                        span.AddAnnotation(style);
                    }
                    var paragraph = element.Ancestors(W.p).FirstOrDefault();
                    bool isBidi = false;
                    if (paragraph != null)
                    {
                        var bidi = paragraph
                            .Elements(W.pPr)
                            .Elements(W.bidi)
                            .Where(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true)
                            .FirstOrDefault();
                        isBidi = bidi != null;
                    }
                    var br = new XElement(Xhtml.br);
                    XEntity zeroWidthChar = null;
                    if (isBidi)
                        zeroWidthChar = new XEntity("#x200f"); // RLM
                    else
                        zeroWidthChar = new XEntity("#x200e"); // LRM
                    return new object[] {
                        br,
                        zeroWidthChar,
                        span,
                    };
                }

                // Transform w:noBreakHyphen to '-'
                if (element.Name == W.noBreakHyphen)
                    return new XText("-");

                // Transform w:tbl to h:tbl.
                if (element.Name == W.tbl)
                {
                    var style = new Dictionary<string, string>();
                    style.AddIfMissing("border-collapse", "collapse");
                    style.AddIfMissing("border", "none");
                    var bidiVisual = element.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault();
                    var tblW = element.Elements(W.tblPr).Elements(W.tblW).FirstOrDefault();
                    if (tblW != null)
                    {
                        var type = (string)tblW.Attribute(W.type);
                        if (type != null && type == "pct")
                        {
                            var w = (int)tblW.Attribute(W._w);
                            style.AddIfMissing("width", (w / 50).ToString() + "%");
                        }
                    }
                    var tblInd = element.Elements(W.tblPr).Elements(W.tblInd).FirstOrDefault();
                    if (tblInd != null)
                    {
                        var tblIndType = (string)tblInd.Attribute(W.type);
                        if (tblIndType != null)
                        {
                            if (tblIndType == "dxa")
                            {
                                var width = (decimal?)tblInd.Attribute(W._w);
                                if (width != null)
                                {
                                    style.AddIfMissing("margin-left", string.Format("{0}pt", width / 20m));
                                }
                            }
                        }
                    }
                    XAttribute tableDirection = null;
                    if (bidiVisual != null)
                    {
                        tableDirection = new XAttribute("dir", "rtl");
                    }
                    else
                    {
                        tableDirection = new XAttribute("dir", "ltr");
                    }
                    style.AddIfMissing("margin-bottom", ".001pt");
                    var table = new XElement(Xhtml.table,
                        new XAttribute("border", "1"),
                        new XAttribute("cellspacing", 0),
                        new XAttribute("cellpadding", 0),
                        tableDirection,
                        element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
                    table.AddAnnotation(style);
                    var jc = (string)element.Elements(W.tblPr).Elements(W.jc).Attributes(W.val).FirstOrDefault();
                    if (jc == null)
                        jc = "left";
                    XAttribute dir = null;
                    XAttribute jcToUse = null;
                    if (bidiVisual != null)
                    {
                        dir = new XAttribute("dir", "rtl");
                        if (jc == "left")
                            jcToUse = new XAttribute("align", "right");
                        else if (jc == "right")
                            jcToUse = new XAttribute("align", "left");
                        else if (jc == "center")
                            jcToUse = new XAttribute("align", "center");
                    }
                    else
                    {
                        jcToUse = new XAttribute("align", jc);
                    }
                    var tableDiv = new XElement(Xhtml.div,
                        dir,
                        jcToUse,
                        table);
                    return tableDiv;
                }

                // Transform w:tr to h:tr.
                if (element.Name == W.tr)
                {
                    var style = new Dictionary<string, string>();
                    int? trHeight = (int?)element.Elements(W.trPr).Elements(W.trHeight).Attributes(W.val).FirstOrDefault();
                    if (trHeight != null)
                        style.AddIfMissing("height", string.Format("{0}in", (decimal)trHeight / 1440m));
                    var htmlRow = new XElement(Xhtml.tr,
                        element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
                    if (style.Any())
                        htmlRow.AddAnnotation(style);
                    return htmlRow;
                }

                // Transform w:tc to h:td.
                if (element.Name == W.tc)
                {
                    var style = new Dictionary<string, string>();
                    XAttribute colSpan = null;
                    XAttribute rowSpan = null;

                    var tcPr = element.Element(W.tcPr);
                    if (tcPr != null)
                    {
                        if ((string)tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart")
                        {
                            var currentRow = element.Parent.ElementsBeforeSelf(W.tr).Count();
                            var currentCell = element.ElementsBeforeSelf(W.tc).Count();
                            var tbl = element.Parent.Parent;
                            int rowSpanCount = 1;
                            currentRow += 1;
                            while (true)
                            {
                                var row = tbl.Elements(W.tr).Skip(currentRow).FirstOrDefault();
                                if (row == null)
                                    break;
                                var cell2 = row.Elements(W.tc).Skip(currentCell).FirstOrDefault();
                                if (cell2 == null)
                                    break;
                                if (cell2.Elements(W.tcPr).Elements(W.vMerge).FirstOrDefault() == null)
                                    break;
                                if ((string)cell2.Elements(W.tcPr).Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart")
                                    break;
                                currentRow += 1;
                                rowSpanCount += 1;
                            }
                            rowSpan = new XAttribute("rowspan", rowSpanCount);
                        }

                        if (tcPr.Element(W.vMerge) != null && (string)tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() != "restart")
                            return null;

                        if (tcPr.Element(W.vAlign) != null)
                        {
                            var vAlignVal = (string)tcPr.Elements(W.vAlign).Attributes(W.val).FirstOrDefault();
                            if (vAlignVal == "top")
                                style.AddIfMissing("vertical-align", "top");
                            else if (vAlignVal == "center")
                                style.AddIfMissing("vertical-align", "middle");
                            else if (vAlignVal == "bottom")
                                style.AddIfMissing("vertical-align", "bottom");
                            else
                                style.AddIfMissing("vertical-align", "middle");
                        }
                        style.AddIfMissing("vertical-align", "top");

                        if ((string)tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "dxa")
                        {
                            decimal width = (int)tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault();
                            style.AddIfMissing("width", string.Format("{0}pt", width / 20m));
                        }
                        if ((string)tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "pct")
                        {
                            decimal width = (int)tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault();
                            style.AddIfMissing("width", string.Format("{0:0.0}%", width / 50m));
                        }

                        var tcBorders = tcPr.Element(W.tcBorders);
                        GenerateBorderStyle(tcBorders, W.top, style, BorderType.Cell);
                        GenerateBorderStyle(tcBorders, W.right, style, BorderType.Cell);
                        GenerateBorderStyle(tcBorders, W.bottom, style, BorderType.Cell);
                        GenerateBorderStyle(tcBorders, W.left, style, BorderType.Cell);

                        CreateStyleFromShd(style, tcPr.Element(W.shd));

                        var gridSpan = (int?)tcPr.Elements(W.gridSpan).Attributes(W.val).Select(a => (int?)a).FirstOrDefault();
                        if (gridSpan != null)
                            colSpan = new XAttribute("colspan", (int)gridSpan);
                    }
                    style.AddIfMissing("padding-top", "0in");
                    style.AddIfMissing("padding-bottom", "0in");

                    var cell = new XElement(Xhtml.td,
                        rowSpan,
                        colSpan,
                        CreateBorderDivs(wordDoc, settings, element.Elements()));
                    cell.AddAnnotation(style);
                    return cell;
                }

                // Transform images
                if (element.Name == W.drawing || element.Name == W.pict || element.Name == W._object)
                {
                    if (settings.ImageHandler == null)
                        return null;
                    return ProcessImage(wordDoc, element, settings.ImageHandler);
                }

                if (element.Name == W.sdt)
                {
                    var relevantAncestors = element.Ancestors().TakeWhile(a => a.Name != W.txbxContent);
                    var isRunLevelContentControl = relevantAncestors.Any(a => a.Name == W.p);
                    if (isRunLevelContentControl)
                    {
                        var o = element.Element(W.sdtContent).Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft))
                            .ToList();
                        return o;
                    }
                    else
                    {
                        var o = CreateBorderDivs(wordDoc, settings, element.Element(W.sdtContent).Elements());
                        return o;
                    }
                }

                if (element.Name == W.smartTag || element.Name == W.fldSimple)
                {
                    var o = CreateBorderDivs(wordDoc, settings, element.Elements());
                    return o;
                }

                return null;
            }
            return null;
        }
        private static void CreateStyleFromInd(Dictionary<string, string> style, XElement ind, XName elementName,
            decimal currentMarginLeft, bool isBidi)
        {
            if (ind == null) return;

            var left = (decimal?) ind.Attribute(W.left);
            if (left != null && elementName != Xhtml.span)
            {
                var leftInInches = (decimal) left/1440 - currentMarginLeft;
                style.AddIfMissing(isBidi ? "margin-right" : "margin-left",
                    leftInInches > 0m
                        ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", leftInInches)
                        : "0");
            }

            var right = (decimal?) ind.Attribute(W.right);
            if (right != null)
            {
                var rightInInches = (decimal) right/1440;
                style.AddIfMissing(isBidi ? "margin-left" : "margin-right",
                    rightInInches > 0m
                        ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", rightInInches)
                        : "0");
            }

            var firstLine = (decimal?) ind.Attribute(W.firstLine);
            if (firstLine != null && elementName != Xhtml.span)
            {
                var firstLineInInches = (decimal) firstLine/1440m;
                style.AddIfMissing("text-indent",
                    string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", firstLineInInches));
            }

            var hanging = (decimal?) ind.Attribute(W.hanging);
            if (hanging != null && elementName != Xhtml.span)
            {
                var hangingInInches = (decimal) -hanging/1440m;
                style.AddIfMissing("text-indent",
                    string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", hangingInInches));
            }
        }
        private static Dictionary<string, string> DefineRunStyle(XElement run)
        {
            var style = new Dictionary<string, string>();

            var rPr = run.Elements(W.rPr).First();

            var styleName = (string) run.Attribute(PtOpenXml.StyleName);
            if (styleName != null)
                style.Add("PtStyleName", styleName);

            // W.bdr
            if (rPr.Element(W.bdr) != null && (string) rPr.Elements(W.bdr).Attributes(W.val).FirstOrDefault() != "none")
            {
                style.AddIfMissing("border", "solid windowtext 1.0pt");
                style.AddIfMissing("padding", "0");
            }

            // W.color
            var color = (string) rPr.Elements(W.color).Attributes(W.val).FirstOrDefault();
            if (color != null)
                CreateColorProperty("color", color, style);

            // W.highlight
            var highlight = (string) rPr.Elements(W.highlight).Attributes(W.val).FirstOrDefault();
            if (highlight != null)
                CreateColorProperty("background", highlight, style);

            // W.shd
            var shade = (string) rPr.Elements(W.shd).Attributes(W.fill).FirstOrDefault();
            if (shade != null)
                CreateColorProperty("background", shade, style);

            // Pt.FontName
            var sym = run.Element(W.sym);
            var font = sym != null
                ? (string) sym.Attributes(W.font).FirstOrDefault()
                : (string) run.Attributes(PtOpenXml.FontName).FirstOrDefault();
            if (font != null)
                CreateFontCssProperty(font, style);

            // W.sz
            var languageType = (string)run.Attribute(PtOpenXml.LanguageType);
            var sz = GetFontSize(languageType, rPr);
            if (sz != null)
                style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz/2.0m));

            // W.caps
            if (GetBoolProp(rPr, W.caps))
                style.AddIfMissing("text-transform", "uppercase");

            // W.smallCaps
            if (GetBoolProp(rPr, W.smallCaps))
                style.AddIfMissing("font-variant", "small-caps");

            // W.spacing
            var spacingInTwips = (decimal?) rPr.Elements(W.spacing).Attributes(W.val).FirstOrDefault();
            if (spacingInTwips != null)
                style.AddIfMissing("letter-spacing",
                    spacingInTwips > 0m
                        ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingInTwips/20)
                        : "0");

            // W.position
            var position = (decimal?) rPr.Elements(W.position).Attributes(W.val).FirstOrDefault();
            if (position != null)
            {
                style.AddIfMissing("position", "relative");
                style.AddIfMissing("top", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", -(position/2)));
            }

            // W.vanish
            if (GetBoolProp(rPr, W.vanish) && !GetBoolProp(rPr, W.specVanish))
                style.AddIfMissing("display", "none");

            // W.u
            if (rPr.Element(W.u) != null && (string) rPr.Elements(W.u).Attributes(W.val).FirstOrDefault() != "none")
                style.AddIfMissing("text-decoration", "underline");

            // W.i
            style.AddIfMissing("font-style", GetBoolProp(rPr, W.i) ? "italic" : "normal");

            // W.b
            style.AddIfMissing("font-weight", GetBoolProp(rPr, W.b) ? "bold" : "normal");

            // W.strike
            if (GetBoolProp(rPr, W.strike) || GetBoolProp(rPr, W.dstrike))
                style.AddIfMissing("text-decoration", "line-through");

            return style;
        }
        private static object CreateBorderDivs(WordprocessingDocument wordDoc, HtmlConverterSettings settings, IEnumerable<XElement> elements)
        {
            return elements.GroupAdjacent(e =>
                {
                    var pBdr = e.Elements(W.pPr).Elements(W.pBdr).FirstOrDefault();
                    if (pBdr != null)
                    {
                        var indStr = string.Empty;
                        var ind = e.Elements(W.pPr).Elements(W.ind).FirstOrDefault();
                        if (ind != null)
                            indStr = ind.ToString(SaveOptions.DisableFormatting);
                        return pBdr.ToString(SaveOptions.DisableFormatting) + indStr;
                    }
                    return e.Name == W.tbl ? "table" : string.Empty;
                })
                .Select(g =>
                {
                    if (g.Key == string.Empty)
                    {
                        return (object) GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, 0m);
                    }
                    if (g.Key == "table")
                    {
                        return g.Select(gc => ConvertToHtmlTransform(wordDoc, settings, gc, false, 0));
                    }
                    var pPr = g.First().Elements(W.pPr).First();
                    var pBdr = pPr.Element(W.pBdr);
                    var style = new Dictionary<string, string>();
                    GenerateBorderStyle(pBdr, W.top, style, BorderType.Paragraph);
                    GenerateBorderStyle(pBdr, W.right, style, BorderType.Paragraph);
                    GenerateBorderStyle(pBdr, W.bottom, style, BorderType.Paragraph);
                    GenerateBorderStyle(pBdr, W.left, style, BorderType.Paragraph);

                    var currentMarginLeft = 0m;
                    var ind = pPr.Element(W.ind);
                    if (ind != null)
                    {
                        var leftInInches = (decimal?) ind.Attribute(W.left)/1440m ?? 0;
                        var hangingInInches = -(decimal?) ind.Attribute(W.hanging)/1440m ?? 0;
                        currentMarginLeft = leftInInches + hangingInInches;

                        style.AddIfMissing("margin-left",
                            currentMarginLeft > 0m
                                ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", currentMarginLeft)
                                : "0");
                    }

                    var div = new XElement(Xhtml.div,
                        GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, currentMarginLeft));
                    div.AddAnnotation(style);
                    return div;
                })
            .ToList();
        }
 private static void DefineFontSize(Dictionary<string, string> style, XElement paragraph)
 {
     var sz = paragraph
         .DescendantsTrimmed(W.txbxContent)
         .Where(e => e.Name == W.r)
         .Select(GetFontSize)
         .Max();
     if (sz != null)
         style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz / 2.0m));
 }
 private static void DefineLineHeight(Dictionary<string, string> style, XElement paragraph)
 {
     var allRunsAreUniDirectional = paragraph
         .DescendantsTrimmed(W.txbxContent)
         .Where(e => e.Name == W.r)
         .Select(run => (string)run.Attribute(PtOpenXml.LanguageType))
         .All(lt => lt != "bidi");
     if (allRunsAreUniDirectional)
         style.AddIfMissing("line-height", "108%");
 }
        private static void CreateStyleFromTextAlignment(Dictionary<string, string> style, XElement textAlignment)
        {
            if (textAlignment == null) return;

            var verticalTextAlignment = (string)textAlignment.Attributes(W.val).FirstOrDefault();
            if (verticalTextAlignment == null || verticalTextAlignment == "auto") return;

            if (verticalTextAlignment == "top")
                style.AddIfMissing("vertical-align", "top");
            else if (verticalTextAlignment == "center")
                style.AddIfMissing("vertical-align", "middle");
            else if (verticalTextAlignment == "baseline")
                style.AddIfMissing("vertical-align", "baseline");
            else if (verticalTextAlignment == "bottom")
                style.AddIfMissing("vertical-align", "bottom");
        }
        private static void CreateStyleFromSpacing(Dictionary<string, string> style, XElement spacing, XName elementName,
            bool suppressTrailingWhiteSpace)
        {
            if (spacing == null) return;

            var spacingBefore = (decimal?) spacing.Attribute(W.before);
            if (spacingBefore != null && elementName != Xhtml.span)
                style.AddIfMissing("margin-top",
                    spacingBefore > 0m
                        ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingBefore/20.0m)
                        : "0");

            var lineRule = (string) spacing.Attribute(W.lineRule);
            if (lineRule == "auto")
            {
                var line = (decimal) spacing.Attribute(W.line);
                if (line != 240m)
                {
                    var pct = (line/240m)*100m;
                    style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", pct));
                }
            }
            if (lineRule == "exact")
            {
                var line = (decimal) spacing.Attribute(W.line);
                var points = line/20m;
                style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", points));
            }
            if (lineRule == "atLeast")
            {
                var line = (decimal) spacing.Attribute(W.line);
                var points = line/20m;
                if (points >= 14m)
                    style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", points));
            }

            var spacingAfter = suppressTrailingWhiteSpace ? 0m : (decimal?) spacing.Attribute(W.after);
            if (spacingAfter != null)
                style.AddIfMissing("margin-bottom",
                    spacingAfter > 0m
                        ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingAfter/20.0m)
                        : "0");
        }
 private static void CreateStyleFromJc(Dictionary<string, string> style, XElement jc, bool isBidi)
 {
     if (jc != null)
     {
         var jcVal = (string)jc.Attributes(W.val).FirstOrDefault() ?? "left";
         if (jcVal == "left")
             style.AddIfMissing("text-align", isBidi ? "right" : "left");
         else if (jcVal == "right")
             style.AddIfMissing("text-align", isBidi ? "left" : "right");
         else if (jcVal == "center")
             style.AddIfMissing("text-align", "center");
         else if (jcVal == "both")
             style.AddIfMissing("text-align", "justify");
     }
 }
 private static void CreateColorProperty(string propertyName, string color, Dictionary<string, string> style)
 {
     if (color == null)
         return;
     if (namedColors.ContainsKey(color))
     {
         var lc = namedColors[color];
         if (lc == "")
             return;
         style.AddIfMissing(propertyName, lc);
         return;
     }
     style.AddIfMissing(propertyName, "#" + color);
 }
        private static void CreateColorProperty(string propertyName, string color, Dictionary<string, string> style)
        {
            if (color == null)
                return;

            // "auto" color is black for "color" and white for "background" property.
            if (color == "auto")
                color = propertyName == "color" ? "black" : "white";

            if (NamedColors.ContainsKey(color))
            {
                var lc = NamedColors[color];
                if (lc == "")
                    return;
                style.AddIfMissing(propertyName, lc);
                return;
            }
            style.AddIfMissing(propertyName, "#" + color);
        }
 private static void CreateFontCssProperty(string font, Dictionary<string, string> style)
 {
     if (FontFallback.ContainsKey(font))
     {
         style.AddIfMissing("font-family", string.Format(FontFallback[font], font));
         return;
     }
     style.AddIfMissing("font-family", font);
 }
 private static object ProcessTable(WordprocessingDocument wordDoc, HtmlConverterSettings settings, XElement element, decimal currentMarginLeft)
 {
     var style = new Dictionary<string, string>();
     style.AddIfMissing("border-collapse", "collapse");
     style.AddIfMissing("border", "none");
     var bidiVisual = element.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault();
     var tblW = element.Elements(W.tblPr).Elements(W.tblW).FirstOrDefault();
     if (tblW != null)
     {
         var type = (string)tblW.Attribute(W.type);
         if (type != null && type == "pct")
         {
             var w = (int)tblW.Attribute(W._w);
             style.AddIfMissing("width", (w / 50) + "%");
         }
     }
     var tblInd = element.Elements(W.tblPr).Elements(W.tblInd).FirstOrDefault();
     if (tblInd != null)
     {
         var tblIndType = (string)tblInd.Attribute(W.type);
         if (tblIndType != null)
         {
             if (tblIndType == "dxa")
             {
                 var width = (decimal?)tblInd.Attribute(W._w);
                 if (width != null)
                 {
                     style.AddIfMissing("margin-left",
                         width > 0m
                             ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width / 20m)
                             : "0");
                 }
             }
         }
     }
     var tableDirection = bidiVisual != null ? new XAttribute("dir", "rtl") : new XAttribute("dir", "ltr");
     style.AddIfMissing("margin-bottom", ".001pt");
     var table = new XElement(Xhtml.table,
         // TODO: Revisit and make sure the omission is covered by appropriate CSS.
         // new XAttribute("border", "1"),
         // new XAttribute("cellspacing", 0),
         // new XAttribute("cellpadding", 0),
         tableDirection,
         element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)));
     table.AddAnnotation(style);
     var jc = (string)element.Elements(W.tblPr).Elements(W.jc).Attributes(W.val).FirstOrDefault() ?? "left";
     XAttribute dir = null;
     XAttribute jcToUse = null;
     if (bidiVisual != null)
     {
         dir = new XAttribute("dir", "rtl");
         if (jc == "left")
             jcToUse = new XAttribute("align", "right");
         else if (jc == "right")
             jcToUse = new XAttribute("align", "left");
         else if (jc == "center")
             jcToUse = new XAttribute("align", "center");
     }
     else
     {
         jcToUse = new XAttribute("align", jc);
     }
     var tableDiv = new XElement(Xhtml.div,
         dir,
         jcToUse,
         table);
     return tableDiv;
 }
        /*
         * Notes on line spacing
         * 
         * the w:line and w:lineRule attributes control spacing between lines - including between lines within a paragraph
         * 
         * If w:spacing w:lineRule="auto" then
         *   w:spacing w:line is a percentage where 240 == 100%
         *   
         *   (line value / 240) * 100 = percentage of line 
         *   
         * If w:spacing w:lineRule="exact" or w:lineRule="atLeast" then
         *   w:spacing w:line is in twips
         *   1440 = exactly one inch from line to line
         *
         * Handle
         * - ind
         * - jc
         * - numPr
         * - pBdr
         * - shd
         * - spacing
         * - textAlignment
         *  
         * Don't Handle (yet)
         * - adjustRightInd?
         * - autoSpaceDE
         * - autoSpaceDN
         * - bidi
         * - contextualSpacing
         * - divId
         * - framePr
         * - keepLines
         * - keepNext
         * - kinsoku
         * - mirrorIndents
         * - overflowPunct
         * - pageBreakBefore
         * - snapToGrid
         * - suppressAutoHyphens
         * - suppressLineNumbers
         * - suppressOverlap
         * - tabs
         * - textBoxTightWrap
         * - textDirection
         * - topLinePunct
         * - widowControl
         * - wordWrap
         * 
         */

        private static object ConvertParagraph(WordprocessingDocument wordDoc, HtmlConverterSettings settings,
            XElement paragraph, XName elementName, bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi)
        {
            var style = new Dictionary<string, string>();
            var sn = (string)paragraph.Attribute(PtOpenXml.StyleName);
            if (sn != null)
                style.Add("PtStyleName", sn);

            XElement pPr = paragraph.Element(W.pPr);
            if (pPr != null)
            {
                var spacing = pPr.Element(W.spacing);
                if (spacing != null)
                {
                    var spacingBefore = (decimal?)spacing.Attribute(W.before);
                    if (spacingBefore != null)
                        style.AddIfMissing("margin-top", string.Format("{0}pt", spacingBefore / 20.0m));

                    var lineRule = (string)spacing.Attribute(W.lineRule);
                    if (lineRule == "auto")
                    {
                        var line = (decimal)spacing.Attribute(W.line);
                        if (line != 240m)
                        {
                            var pct = (line / 240m) * 100m;
                            style.Add("line-height", string.Format("{0:0.0}%", pct));
                        }
                    }
                    if (lineRule == "exact")
                    {
                        var line = (decimal)spacing.Attribute(W.line);
                        var points = line / 20m;
                        style.Add("line-height", string.Format("{0:0.0}pt", points));
                    }
                    if (lineRule == "atLeast")
                    {
                        var line = (decimal)spacing.Attribute(W.line);
                        var points = line / 20m;
                        if (points >= 14m)
                            style.Add("line-height", string.Format("{0:0.0}pt", points));
                    }

                    decimal? spacingAfter;
                    if (suppressTrailingWhiteSpace)
                        spacingAfter = 0;
                    else
                        spacingAfter = (decimal?)spacing.Attribute(W.after) /*+ addToSpacing*/;

                    if (spacingAfter != null)
                        style.AddIfMissing("margin-bottom", string.Format("{0}pt", spacingAfter / 20.0m));

                }

                var ind = pPr.Element(W.ind);
                if (ind != null)
                {
                    decimal? left = (decimal?)ind.Attribute(W.left);
                    if (left != null)
                    {
                        decimal leftInInches = (decimal)left / 1440 - currentMarginLeft;
                        style.AddIfMissing(isBidi ? "margin-right" : "margin-left", 
                            string.Format("{0:0.00}in", leftInInches));
                    }

                    decimal? right = (decimal?)ind.Attribute(W.right);
                    if (right != null)
                    {
                        decimal rightInInches = (decimal)right / 1440;
                        style.AddIfMissing(isBidi ? "margin-left" : "margin-right",
                            string.Format("{0:0.00}in", rightInInches));
                    }

                    decimal? firstLine = (decimal?)ind.Attribute(W.firstLine);
                    if (firstLine != null)
                    {
                        decimal firstLineInInches = (decimal)firstLine / 1440m;
                        style.AddIfMissing("text-indent", string.Format("{0:0.00}in", firstLineInInches));
                    }
                    decimal? hanging = (decimal?)ind.Attribute(W.hanging);
                    if (hanging != null)
                    {
                        decimal hangingInInches = (decimal)-hanging / 1440m;
                        style.AddIfMissing("text-indent", string.Format("{0:0.00}in", hangingInInches));
                    }
                }

                // todo need to handle
                // - both
                // - mediumKashida
                // - distribute
                // - numTab
                // - highKashida
                // - lowKashida
                // - thaiDistribute

                var jcVal = (string)pPr.Elements(W.jc).Attributes(W.val).FirstOrDefault();
                if (jcVal == null)
                {
                    jcVal = "left";
                }
                if (jcVal == "left")
                {
                    if (isBidi)
                        style.AddIfMissing("text-align", "right");
                    else
                        style.AddIfMissing("text-align", "left");
                }
                else if (jcVal == "right")
                {
                    if (isBidi)
                        style.AddIfMissing("text-align", "left");
                    else
                        style.AddIfMissing("text-align", "right");
                }
                else if (jcVal == "center")
                    style.AddIfMissing("text-align", "center");
                else if (jcVal == "both")
                    style.AddIfMissing("text-align", "justify");

                CreateStyleFromShd(style, pPr.Element(W.shd));

                // Pt.FontName
                string font = (string)paragraph.Attributes(PtOpenXml.FontName).FirstOrDefault();
                if (font != null)
                    CreateFontCssProperty(font, style);

                // W.sz
                decimal? sz = null;

                var languageType = (string)paragraph.Attribute(PtOpenXml.LanguageType);
                if (languageType == "bidi")
                    sz = (decimal?)pPr.Elements(W.rPr).Elements(W.szCs).Attributes(W.val).FirstOrDefault();
                else
                    sz = (decimal?)pPr.Elements(W.rPr).Elements(W.sz).Attributes(W.val).FirstOrDefault();

                var sizesOfAllRunsInParagraph = paragraph
                    .DescendantsTrimmed(W.txbxContent)
                    .Select(run =>
                    {
                        if (run.Name != W.r)
                            return null;
                        var runLanguageType = (string)run.Attribute(PtOpenXml.LanguageType);
                        if (runLanguageType == "bidi")
                        {
                            var runCsSz = (decimal?)run
                                .Elements(W.rPr)
                                .Elements(W.szCs)
                                .Attributes(W.val)
                                .FirstOrDefault();
                            return runCsSz;
                        }
                        else
                        {
                            var runSz = (decimal?)run
                                .Elements(W.rPr)
                                .Elements(W.sz)
                                .Attributes(W.val)
                                .FirstOrDefault();
                            return runSz;
                        }
                    })
                    .Where(runSz => runSz != null);

                if (sizesOfAllRunsInParagraph.Any())
                    sz = sizesOfAllRunsInParagraph.Cast<decimal>().Max();

                if (sz != null)
                    style.AddIfMissing("font-size", string.Format("{0}pt", sz / 2.0m));

                var languageTypeOfAllRunsInParagraph = paragraph
                    .DescendantsTrimmed(W.txbxContent)
                    .Select(run =>
                    {
                        if (run.Name != W.r)
                            return null;
                        var runLanguageType = (string)run.Attribute(PtOpenXml.LanguageType);
                        return runLanguageType;
                    })
                    .Where(runSz => runSz != null);

                if (!languageTypeOfAllRunsInParagraph.Any(lt => lt == "bidi"))
                    style.AddIfMissing("line-height", "108%");

                // vertical text alignment as of December 2013 does not work in any major browsers.
                var verticalTextAlignment = (string)pPr.Elements(W.textAlignment).Attributes(W.val).FirstOrDefault();
                if (verticalTextAlignment != null && verticalTextAlignment != "auto")
                {
                    if (verticalTextAlignment == "top")
                        style.AddIfMissing("vertical-align", "top");
                    else if (verticalTextAlignment == "center")
                        style.AddIfMissing("vertical-align", "middle");
                    else if (verticalTextAlignment == "baseline")
                        style.AddIfMissing("vertical-align", "baseline");
                    else if (verticalTextAlignment == "bottom")
                        style.AddIfMissing("vertical-align", "bottom");
                }

                style.AddIfMissing("margin-top", "0pt");
                style.AddIfMissing("margin-left", "0pt");
                style.AddIfMissing("margin-right", "0pt");
                style.AddIfMissing("margin-bottom", ".001pt");

            }
            XAttribute rtl = null;
            XEntity firstMark = null;
            if (isBidi)
            {
                rtl = new XAttribute("dir", "rtl");
                firstMark = new XEntity("#x200f"); // RLM
            }
            else
            {
                rtl = new XAttribute("dir", "ltr");
            }
            var paraElement = new XElement(elementName,
                rtl,
                firstMark,
                ConvertContentThatCanContainFields(wordDoc, settings, paragraph.Elements()));
            paraElement.AddAnnotation(style);
            return paraElement;
        }
        private static Dictionary<string, string> DefineParagraphStyle(XElement paragraph, XName elementName,
            bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi)
        {
            var style = new Dictionary<string, string>();

            var styleName = (string) paragraph.Attribute(PtOpenXml.StyleName);
            if (styleName != null)
                style.Add("PtStyleName", styleName);

            var pPr = paragraph.Element(W.pPr);
            if (pPr == null) return style;

            CreateStyleFromSpacing(style, pPr.Element(W.spacing), elementName, suppressTrailingWhiteSpace);
            CreateStyleFromInd(style, pPr.Element(W.ind), elementName, currentMarginLeft, isBidi);

            // todo need to handle
            // - both
            // - mediumKashida
            // - distribute
            // - numTab
            // - highKashida
            // - lowKashida
            // - thaiDistribute

            CreateStyleFromJc(style, pPr.Element(W.jc), isBidi);
            CreateStyleFromShd(style, pPr.Element(W.shd));

            // Pt.FontName
            var font = (string) paragraph.Attributes(PtOpenXml.FontName).FirstOrDefault();
            if (font != null)
                CreateFontCssProperty(font, style);

            DefineFontSize(style, paragraph);
            DefineLineHeight(style, paragraph);

            // vertical text alignment as of December 2013 does not work in any major browsers.
            CreateStyleFromTextAlignment(style, pPr.Element(W.textAlignment));

            style.AddIfMissing("margin-top", "0");
            style.AddIfMissing("margin-left", "0");
            style.AddIfMissing("margin-right", "0");
            style.AddIfMissing("margin-bottom", ".001pt");

            return style;
        }