/*
         * 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;
        }
        /*
         * 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 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 DetermineRunMarks(XElement run, XElement rPr, Dictionary<string, string> style, out XEntity runStartMark, out XEntity runEndMark)
        {
            runStartMark = null;
            runEndMark = null;

            // Only do the following for text runs.
            if (run.Element(W.t) == null) return;

            // Can't add directional marks if the font-family is symbol - they are visible, and display as a ?
            var addDirectionalMarks = true;
            if (style.ContainsKey("font-family"))
            {
                if (style["font-family"].ToLower() == "symbol")
                    addDirectionalMarks = false;
            }
            if (!addDirectionalMarks) return;

            var isRtl = rPr.Element(W.rtl) != null;
            if (isRtl)
            {
                runStartMark = new XEntity("#x200f"); // RLM
                runEndMark = new XEntity("#x200f"); // RLM
            }
            else
            {
                var paragraph = run.Ancestors(W.p).First();
                var paraIsBidi = paragraph
                    .Elements(W.pPr)
                    .Elements(W.bidi)
                    .Any(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true);

                if (paraIsBidi)
                {
                    runStartMark = new XEntity("#x200e"); // LRM
                    runEndMark = new XEntity("#x200e"); // LRM
                }
            }
        }