private void ProcessBody(HtmlEnumerator en)
        {
            List<OpenXmlElement> styleAttributes = new List<OpenXmlElement>();
            ProcessContainerAttributes(en, styleAttributes);

            if (styleAttributes.Count > 0)
                htmlStyles.Runs.BeginTag(en.CurrentTag, styleAttributes.ToArray());
        }
        private void ProcessBlockQuote(HtmlEnumerator en)
        {
            CompleteCurrentParagraph();
            AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());

            // for nested paragraphs:
            htmlStyles.Paragraph.BeginTag(en.CurrentTag, new ParagraphStyleId() { Val = htmlStyles.GetStyle("IntenseQuote") });

            // if the style was not yet defined, we force the indentation
            if (!htmlStyles.DoesStyleExists("IntenseQuote"))
                htmlStyles.Paragraph.BeginTag(en.CurrentTag, new Indentation() { Left = "708" });
        }
        private void ProcessBlockQuote(HtmlEnumerator en)
        {
            CompleteCurrentParagraph(true);

            // for nested paragraphs:
            htmlStyles.Paragraph.BeginTag(en.CurrentTag, new ParagraphStyleId() { Val = htmlStyles.GetStyle("IntenseQuote") });

            // if the style was not yet defined, we force the indentation
            if (!htmlStyles.DoesStyleExists("IntenseQuote"))
                htmlStyles.Paragraph.BeginTag(en.CurrentTag, new Indentation() { Left = "708" });

            // TODO: handle attribute cite and create footnote
        }
        private void ProcessAcronym(HtmlEnumerator en)
        {
            // Transform the inline acronym/abbreviation to a reference to a foot note.

            string title = en.Attributes["title"];
            if (title == null) return;

            AlternateProcessHtmlChunks(en, en.ClosingCurrentTag);

            if (elements.Count > 0 && elements[0] is Run)
            {
                string defaultRefStyle, runStyle;
                FootnoteEndnoteReferenceType reference;

                if (this.AcronymPosition == AcronymPosition.PageEnd)
                {
                    reference = new FootnoteReference() { Id = AddFootnoteReference(title) };
                    defaultRefStyle = "footnote text";
                    runStyle = "footnote reference";
                }
                else
                {
                    reference = new EndnoteReference() { Id = AddEndnoteReference(title) };
                    defaultRefStyle = "endnote text";
                    runStyle = "endnote reference";
                }

                Run run;
                elements.Add(
                    run = new Run(
                        new RunProperties
                        {
                            RunStyle = new RunStyle() { Val = htmlStyles.GetStyle(runStyle, StyleValues.Character) }
                        },
                        reference));

                if (!htmlStyles.DoesStyleExists(defaultRefStyle))
                {
                    // Force the superscript style because if the footnote text style does not exists,
                    // the rendering will be awful.
                    run.InsertInProperties(prop =>
                        prop.VerticalTextAlignment = new VerticalTextAlignment() { Val = VerticalPositionValues.Superscript });
                }
            }
        }
        /// <summary>
        /// Move inside the current tag related to table (td, thead, tr, ...) and converts some common
        /// attributes to their OpenXml equivalence.
        /// </summary>
        /// <param name="styleAttributes">The collection of attributes where to store new discovered attributes.</param>
        public void ProcessCommonRunAttributes(HtmlEnumerator en, IList<OpenXmlElement> styleAttributes)
        {
            if (en.Attributes.Count == 0) return;

            var colorValue = en.StyleAttributes.GetAsColor("color");
            if (colorValue.IsEmpty) colorValue = en.Attributes.GetAsColor("color");
            if (!colorValue.IsEmpty)
                styleAttributes.Add(new Color { Val = colorValue.ToHexString() });

            colorValue = en.StyleAttributes.GetAsColor("background-color");
            if (!colorValue.IsEmpty)
            {
                // change the way the background-color renders. It now uses Shading instead of Highlight.
                // Changes brought by Wude on http://notesforhtml2openxml.codeplex.com/discussions/277570
                styleAttributes.Add(new Shading { Val = ShadingPatternValues.Clear, Fill = colorValue.ToHexString() });
            }

            string attrValue = en.StyleAttributes["text-decoration"];
            if (attrValue == "underline")
            {
                styleAttributes.Add(new Underline { Val = UnderlineValues.Single });
            }
            else if (attrValue == "line-through")
            {
                styleAttributes.Add(new Strike());
            }

            attrValue = en.StyleAttributes["font-style"];
            if (attrValue == "italic" || attrValue == "oblique")
            {
                styleAttributes.Add(new Italic());
            }

            attrValue = en.StyleAttributes["font-weight"];
            if (attrValue == "bold" || attrValue == "bolder")
            {
                styleAttributes.Add(new Bold());
            }

            // We ignore font-family and font-size voluntarily because the user oftenly copy-paste from web pages
            // but don't want to see these font in the report.
        }
        public void BeginList(HtmlEnumerator en)
        {
            int prevAbsNumId = absNumId;

            // lookup for a predefined list style in the template collection
            String type = en.StyleAttributes["list-style-type"];
            bool orderedList = en.CurrentTag.Equals("<ol>", StringComparison.OrdinalIgnoreCase);
            if (type == null || !knonwAbsNumIds.TryGetValue(type.ToLowerInvariant(), out absNumId))
            {
                if (orderedList)
                    absNumId = knonwAbsNumIds["decimal"];
                else
                    absNumId = knonwAbsNumIds["disc"];
            }

            firstItem = true;
            levelDepth++;

            // save a NumberingInstance if the nested list style is the same as its ancestor.
            // this allows us to nest <ol> and restart the indentation to 1.
            int currentInstanceId = this.InstanceID;
            if (levelDepth > 1 && absNumId == prevAbsNumId && orderedList)
            {
                EnsureMultilevel(absNumId);
            }
            else
            {
                currentInstanceId = ++nextInstanceID;
                Numbering numbering = mainPart.NumberingDefinitionsPart.Numbering;
                numbering.Append(
                    new NumberingInstance(
                        new AbstractNumId() { Val = absNumId },
                        new LevelOverride(
                            new StartOverrideNumberingValue() { Val = 1 }
                        ) { LevelIndex = 0, }
                    ) { NumberID = currentInstanceId });
            }

            numInstances.Push(currentInstanceId);
        }
 private void ProcessBold(HtmlEnumerator en)
 {
     htmlStyles.Runs.BeginTag(en.CurrentTag, new Bold());
 }
        private void ProcessTableColumn(HtmlEnumerator en)
        {
            if (!tables.HasContext) return;

            List<OpenXmlElement> styleAttributes = new List<OpenXmlElement>();
            List<OpenXmlElement> runStyleAttributes = new List<OpenXmlElement>();

            Unit unit = en.StyleAttributes.GetAsUnit("width");
            if (!unit.IsValid) unit = en.Attributes.GetAsUnit("width");

            if (unit.IsValid)
            {
                switch (unit.Type)
                {
                    case UnitMetric.Percent:
                        styleAttributes.Add(new TableCellWidth() { Type = TableWidthUnitValues.Pct, Width = (unit.Value * 50).ToString(CultureInfo.InvariantCulture) });
                        break;
                    case UnitMetric.Point:
                        styleAttributes.Add(new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = (unit.Value * 20).ToString(CultureInfo.InvariantCulture) });
                        break;
                    case UnitMetric.Pixel:
                        styleAttributes.Add(new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = (unit.Value).ToString(CultureInfo.InvariantCulture) });
                        break;
                }
            }

            int? colspan = en.Attributes.GetAsInt("colspan");
            if (colspan.HasValue)
            {
                styleAttributes.Add(new GridSpan() { Val = colspan });
            }

            int? rowspan = en.Attributes.GetAsInt("rowspan");
            if (rowspan.HasValue)
            {
                styleAttributes.Add(new VerticalMerge() { Val = MergedCellValues.Restart });
                tables.RowSpan[tables.CellPosition] = rowspan.Value - 1;
            }

            htmlStyles.Runs.ProcessCommonRunAttributes(en, runStyleAttributes);

            // Manage vertical text (only for table cell)
            string direction = en.StyleAttributes["writing-mode"];
            if (direction != null)
            {
                switch (direction)
                {
                    case "tb-lr":
                        styleAttributes.Add(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight });
                        styleAttributes.Add(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center });
                        htmlStyles.Tables.BeginTagForParagraph("<td>", new Justification() { Val = JustificationValues.Center });
                        break;
                    case "tb-rl":
                        styleAttributes.Add(new TextDirection() { Val = TextDirectionValues.TopToBottomRightToLeft });
                        styleAttributes.Add(new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center });
                        htmlStyles.Tables.BeginTagForParagraph("<td>", new Justification() { Val = JustificationValues.Center });
                        break;
                }
            }

            var padding = en.StyleAttributes.GetAsMargin("padding");
            if (!padding.IsEmpty)
            {
                TableCellMargin cellMargin = new TableCellMargin();
                var cellMarginSide = new List<KeyValuePair<Unit, TableWidthType>>();
                cellMarginSide.Add(new KeyValuePair<Unit, TableWidthType>(padding.Top, new TopMargin()));
                cellMarginSide.Add(new KeyValuePair<Unit, TableWidthType>(padding.Right, new RightMargin()));
                cellMarginSide.Add(new KeyValuePair<Unit, TableWidthType>(padding.Bottom, new BottomMargin()));
                cellMarginSide.Add(new KeyValuePair<Unit, TableWidthType>(padding.Left, new LeftMargin()));

                foreach (var pair in cellMarginSide)
                {
                    if (!pair.Key.IsValid || pair.Key.Value == 0) continue;
                    if (pair.Key.Type == UnitMetric.Percent)
                    {
                        pair.Value.Width = (pair.Key.Value * 50).ToString(CultureInfo.InvariantCulture);
                        pair.Value.Type = TableWidthUnitValues.Pct;
                    }
                    else
                    {
                        pair.Value.Width = pair.Key.ValueInDxa.ToString(CultureInfo.InvariantCulture);
                        pair.Value.Type = TableWidthUnitValues.Dxa;
                    }

                    cellMargin.Append(pair.Value);
                }

                styleAttributes.Add(cellMargin);
            }

            htmlStyles.Tables.ProcessCommonAttributes(en, styleAttributes);
            if (runStyleAttributes.Count > 0)
                htmlStyles.Runs.BeginTag(en.CurrentTag, runStyleAttributes.ToArray());

            TableCell cell = new TableCell(
                new TableCellProperties(styleAttributes));
            tables.CurrentTable.GetLastChild<TableRow>().Append(cell);

            if (en.IsSelfClosedTag) // Force a call to ProcessClosingTableColumn
                ProcessClosingTableColumn(en);
            else
            {
                // we create a new currentParagraph to add new runs inside the TableCell
                cell.Append(currentParagraph = new Paragraph());
            }
        }
 private void ProcessSuperscript(HtmlEnumerator en)
 {
     htmlStyles.Runs.BeginTag(en.CurrentTag, new VerticalTextAlignment() { Val = VerticalPositionValues.Superscript });
 }
Example #10
0
        /// <summary>
        /// Move inside the current tag related to table (td, thead, tr, ...) and converts some common
        /// attributes to their OpenXml equivalence.
        /// </summary>
        /// <param name="en">The Html enumerator positionned on a <i>table (or related)</i> tag.</param>
        /// <param name="runStyleAttributes">The collection of attributes where to store new discovered attributes.</param>
        public void ProcessCommonAttributes(HtmlEnumerator en, IList <OpenXmlElement> runStyleAttributes)
        {
            List <OpenXmlElement> containerStyleAttributes = new List <OpenXmlElement>();

            var colorValue = en.StyleAttributes.GetAsColor("background-color");

            // "background-color" is also handled by RunStyleCollection which duplicate this attribute (bug #13212).
            // Also apply on <th> (issue #20).
            // As on 05 Jan 2018, the duplication was due to the wrong argument passed during the td/th processing.
            // It was the runStyle and not the containerStyle that was provided. The code has been removed as no more useful
            if (colorValue.IsEmpty)
            {
                colorValue = en.Attributes.GetAsColor("bgcolor");
            }
            if (!colorValue.IsEmpty)
            {
                containerStyleAttributes.Add(
                    new Shading()
                {
                    Val = ShadingPatternValues.Clear, Color = "auto", Fill = colorValue.ToHexString()
                });
            }

            var htmlAlign = en.StyleAttributes["vertical-align"];

            if (htmlAlign == null)
            {
                htmlAlign = en.Attributes["valign"];
            }
            if (htmlAlign != null)
            {
                TableVerticalAlignmentValues?valign = Converter.ToVAlign(htmlAlign);
                if (valign.HasValue)
                {
                    containerStyleAttributes.Add(new TableCellVerticalAlignment()
                    {
                        Val = valign
                    });
                }
            }

            htmlAlign = en.StyleAttributes["text-align"];
            if (htmlAlign == null)
            {
                htmlAlign = en.Attributes["align"];
            }
            if (htmlAlign != null)
            {
                JustificationValues?halign = Converter.ToParagraphAlign(htmlAlign);
                if (halign.HasValue)
                {
                    this.BeginTagForParagraph(en.CurrentTag, new KeepNext(), new Justification {
                        Val = halign
                    });
                }
            }

            // implemented by ddforge
            String[] classes = en.Attributes.GetAsClass();
            if (classes != null)
            {
                for (int i = 0; i < classes.Length; i++)
                {
                    string className = documentStyle.GetStyle(classes[i], StyleValues.Table, ignoreCase: true);
                    if (className != null)                     // only one Style can be applied in OpenXml and dealing with inheritance is out of scope
                    {
                        containerStyleAttributes.Add(new RunStyle()
                        {
                            Val = className
                        });
                        break;
                    }
                }
            }

            this.BeginTag(en.CurrentTag, containerStyleAttributes);

            // Process general run styles
            documentStyle.Runs.ProcessCommonAttributes(en, runStyleAttributes);
        }
        private void ProcessPre(HtmlEnumerator en)
        {
            CompleteCurrentParagraph();
            currentParagraph = htmlStyles.Paragraph.NewParagraph();

            // Oftenly, <pre> tag are used to renders some code examples. They look better inside a table
            if (this.RenderPreAsTable)
            {
                Table currentTable = new Table(
                    new TableProperties(
                        new TableStyle() { Val = htmlStyles.GetStyle("Table Grid", false) },
                        new TableWidth() { Type = TableWidthUnitValues.Pct, Width = "5000" }), // 100% * 50
                    new TableGrid(
                        new GridColumn() { Width = "5610" }),
                    new TableRow(
                        new TableCell(
                    // Ensure the border lines are visible (regardless of the style used)
                            new TableCellProperties(
                                new TableCellBorders(
                                    new TopBorder() { Val = BorderValues.Single },
                                    new LeftBorder() { Val = BorderValues.Single },
                                    new BottomBorder() { Val = BorderValues.Single },
                                    new RightBorder() { Val = BorderValues.Single })),
                            currentParagraph))
                );

                AddParagraph(currentTable);
                tables.NewContext(currentTable);
            }
            else
            {
                AddParagraph(currentParagraph);
            }

            // Process the entire <pre> tag and append it to the document
            List<OpenXmlElement> styleAttributes = new List<OpenXmlElement>();
            ProcessContainerAttributes(en, styleAttributes);

            if (styleAttributes.Count > 0)
                htmlStyles.Runs.BeginTag(en.CurrentTag, styleAttributes.ToArray());

            AlternateProcessHtmlChunks(en, "</pre>");

            if (styleAttributes.Count > 0)
                htmlStyles.Runs.EndTag(en.CurrentTag);

            if (RenderPreAsTable)
                tables.CloseContext();

            currentParagraph.Append(elements);
            elements.Clear();
        }
        private void ProcessSpan(HtmlEnumerator en)
        {
            // A span style attribute can contains many information: font color, background color, font size,
            // font family, ...
            // We'll check for each of these and add apply them to the next build runs.

            List<OpenXmlElement> styleAttributes = new List<OpenXmlElement>();
            bool newParagraph = ProcessContainerAttributes(en, styleAttributes);

            if (styleAttributes.Count > 0)
                htmlStyles.Runs.MergeTag(en.CurrentTag, styleAttributes);

            if (newParagraph)
            {
                AlternateProcessHtmlChunks(en, en.ClosingCurrentTag);

                CompleteCurrentParagraph();
                AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
            }
        }
        private void ProcessParagraph(HtmlEnumerator en)
        {
            CompleteCurrentParagraph();
            AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());

            // Respect this order: this is the way the browsers apply them
            String attrValue = en.StyleAttributes["text-align"];
            if (attrValue == null) attrValue = en.Attributes["align"];

            if (attrValue != null)
            {
                JustificationValues? align = ConverterUtility.FormatParagraphAlign(attrValue);
                if (align.HasValue)
                {
                    currentParagraph.InsertInProperties(new Justification { Val = align });
                }
            }

            List<OpenXmlElement> styleAttributes = new List<OpenXmlElement>();
            bool newParagraph = ProcessContainerAttributes(en, styleAttributes);

            if (styleAttributes.Count > 0)
                htmlStyles.Runs.BeginTag(en.CurrentTag, styleAttributes.ToArray());

            if (newParagraph)
            {
                AlternateProcessHtmlChunks(en, en.ClosingCurrentTag);
                ProcessClosingParagraph(en);
            }
        }
        private void ProcessLink(HtmlEnumerator en)
        {
            String att = en.Attributes["href"];
            Hyperlink h = null;
            Uri uri = null;

            if (!String.IsNullOrEmpty(att))
            {
                // is it an anchor?
                if (att[0] == '#' && att.Length > 1)
                {
                    // Always accept _top anchor
                    if (!this.ExcludeLinkAnchor || att == "#_top")
                    {
                        h = new Hyperlink(
                            ) { History = true, Anchor = att.Substring(1) };
                    }
                }
                // ensure the links does not start with javascript:
                else if (Uri.TryCreate(att, UriKind.Absolute, out uri) && uri.Scheme != "javascript")
                {
                    HyperlinkRelationship extLink = mainPart.AddHyperlinkRelationship(uri, true);

                    h = new Hyperlink(
                        ) { History = true, Id = extLink.Id };
                }
            }

            if (h == null)
            {
                // link to a broken url, simply process the content of the tag
                ProcessHtmlChunks(en, "</a>");
                return;
            }

            AlternateProcessHtmlChunks(en, "</a>");

            if (elements.Count > 0)
            {
                // Let's see whether the link tag include an image inside its body.
                // If so, the Hyperlink OpenXmlElement is lost and we'll keep only the images
                // and applied a HyperlinkOnClick attribute.
                List<OpenXmlElement> imageInLink = elements.FindAll(e => { return e.HasChild<Drawing>(); });
                if (imageInLink.Count != 0)
                {
                    for (int i = 0; i < imageInLink.Count; i++)
                    {
                        // Retrieves the "alt" attribute of the image and apply it as the link's tooltip
                        Drawing d = imageInLink[i].GetFirstChild<Drawing>();
                        var enDp = d.Descendants<pic.NonVisualDrawingProperties>().GetEnumerator();
                        String alt;
                        if (enDp.MoveNext()) alt = enDp.Current.Description;
                        else alt = null;

                        d.InsertInDocProperties(
                             new a.HyperlinkOnClick() { Id = h.Id ?? h.Anchor, Tooltip = alt });
                    }

                    CompleteCurrentParagraph();
                    AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
                }
                else
                {
                    // Append the processed elements and put them to the Run of the Hyperlink
                    h.Append(elements);

                    if (!htmlStyles.DoesStyleExists("Hyperlink"))
                    {
                        htmlStyles.AddStyle("Hyperlink", new Style(
                            new StyleName() { Val = "Hyperlink" },
                            new UnhideWhenUsed(),
                            new StyleRunProperties(
                                new DocumentFormat.OpenXml.Wordprocessing.Color() { Val = "0000FF", ThemeColor = ThemeColorValues.Hyperlink },
                                new Underline() { Val = UnderlineValues.Single }
                            )
                        ) { Type = StyleValues.Character, StyleId = "Hyperlink" });
                    }

                    h.GetFirstChild<Run>().InsertInProperties(
                        new RunStyle() { Val = htmlStyles.GetStyle("Hyperlink", true) });

                    this.elements.Clear();

                    // Append the hyperlink
                    elements.Add(h);
                }
            }
        }
        public int ProcessItem(HtmlEnumerator en)
        {
            if (!firstItem)
            {
                return(this.InstanceID);
            }

            firstItem = false;

            // in case a margin has been specifically specified, we need to create a new list template
            // on the fly with a different AbsNumId, in order to let Word doesn't merge the style with its predecessor.
            Margin margin = en.StyleAttributes.GetAsMargin("margin");

            if (margin.Left.Value > 0 && margin.Left.Type == UnitMetric.Pixel)
            {
                Numbering numbering = mainPart.NumberingDefinitionsPart.Numbering;
                foreach (AbstractNum absNum in numbering.Elements <AbstractNum>())
                {
                    if (absNum.AbstractNumberId == numInstances.Peek().Value)
                    {
                        Level lvl          = absNum.GetFirstChild <Level>();
                        Int32 currentNumId = ++nextInstanceID;

                        numbering.Append(
                            new AbstractNum(
                                new MultiLevelType()
                        {
                            Val = MultiLevelValues.SingleLevel
                        },
                                new Level {
                            StartNumberingValue = new StartNumberingValue()
                            {
                                Val = 1
                            },
                            NumberingFormat = new NumberingFormat()
                            {
                                Val = lvl.NumberingFormat.Val
                            },
                            LevelIndex = 0,
                            LevelText  = new LevelText()
                            {
                                Val = lvl.LevelText.Val
                            }
                        }
                                )
                        {
                            AbstractNumberId = currentNumId
                        });
                        numbering.Save(mainPart.NumberingDefinitionsPart);
                        numbering.Append(
                            new NumberingInstance(
                                new AbstractNumId()
                        {
                            Val = currentNumId
                        }
                                )
                        {
                            NumberID = currentNumId
                        });
                        numbering.Save(mainPart.NumberingDefinitionsPart);
                        mainPart.NumberingDefinitionsPart.Numbering.Reload();
                        break;
                    }
                }
            }

            return(this.InstanceID);
        }
        /// <summary>
        /// Converts some common styling attributes to their OpenXml equivalence.
        /// </summary>
        /// <param name="en">The Html parser.</param>
        /// <param name="styleAttributes">The collection of attributes where to store new discovered attributes.</param>
        public void ProcessCommonAttributes(HtmlEnumerator en, IList <OpenXmlElement> styleAttributes)
        {
            if (en.Attributes.Count == 0)
            {
                return;
            }

            var colorValue = en.StyleAttributes.GetAsColor("color");

            if (colorValue.IsEmpty)
            {
                colorValue = en.Attributes.GetAsColor("color");
            }
            if (!colorValue.IsEmpty)
            {
                styleAttributes.Add(new Color {
                    Val = colorValue.ToHexString()
                });
            }

            colorValue = en.StyleAttributes.GetAsColor("background-color");
            if (!colorValue.IsEmpty)
            {
                // change the way the background-color renders. It now uses Shading instead of Highlight.
                // Changes brought by Wude on http://html2openxml.codeplex.com/discussions/277570
                styleAttributes.Add(new Shading {
                    Val = ShadingPatternValues.Clear, Fill = colorValue.ToHexString()
                });
            }

            var decorations = Converter.ToTextDecoration(en.StyleAttributes["text-decoration"]);

            if ((decorations & TextDecoration.Underline) != 0)
            {
                styleAttributes.Add(new Underline {
                    Val = UnderlineValues.Single
                });
            }
            if ((decorations & TextDecoration.LineThrough) != 0)
            {
                styleAttributes.Add(new Strike());
            }

            String[] classes = en.Attributes.GetAsClass();
            if (classes != null)
            {
                for (int i = 0; i < classes.Length; i++)
                {
                    string className = documentStyle.GetStyle(classes[i], StyleValues.Character, ignoreCase: true);
                    if (className != null)                     // only one Style can be applied in OpenXml and dealing with inheritance is out of scope
                    {
                        styleAttributes.Add(new RunStyle()
                        {
                            Val = className
                        });
                        break;
                    }
                }
            }

            HtmlFont font = en.StyleAttributes.GetAsFont("font");

            if (!font.IsEmpty)
            {
                if (font.Style == FontStyle.Italic)
                {
                    styleAttributes.Add(new Italic());
                }

                if (font.Weight == FontWeight.Bold || font.Weight == FontWeight.Bolder)
                {
                    styleAttributes.Add(new Bold());
                }

                if (font.Variant == FontVariant.SmallCaps)
                {
                    styleAttributes.Add(new SmallCaps());
                }

                if (font.Family != null)
                {
                    styleAttributes.Add(new RunFonts()
                    {
                        Ascii = font.Family, HighAnsi = font.Family
                    });
                }

                // size are half-point font size
                if (font.Size.IsFixed)
                {
                    styleAttributes.Add(new FontSize()
                    {
                        Val = (font.Size.ValueInPoint * 2).ToString(CultureInfo.InvariantCulture)
                    });
                }
            }
        }
Example #17
0
        /// <summary>
        /// There is a few attributes shared by a large number of tags. This method will check them for a limited
        /// number of tags (&lt;p&gt;, &lt;pre&gt;, &lt;div&gt;, &lt;span&gt; and &lt;body&gt;).
        /// </summary>
        /// <returns>Returns true if the processing of this tag should generate a new paragraph.</returns>
        public bool ProcessCommonAttributes(HtmlEnumerator en, IList <OpenXmlElement> styleAttributes)
        {
            if (en.Attributes.Count == 0)
            {
                return(false);
            }

            bool newParagraph = false;
            List <OpenXmlElement> containerStyleAttributes = new List <OpenXmlElement>();

            string attrValue = en.Attributes["lang"];

            if (attrValue != null && attrValue.Length > 0)
            {
                try
                {
#if !NET_CORE
                    var ci = System.Globalization.CultureInfo.GetCultureInfo(attrValue);
#else
                    var ci = new System.Globalization.CultureInfo(attrValue);
#endif
                    bool rtl = ci.TextInfo.IsRightToLeft;

                    Languages lang = new Languages()
                    {
                        Val = ci.TwoLetterISOLanguageName
                    };
                    if (rtl)
                    {
                        lang.Bidi = ci.Name;
                        styleAttributes.Add(new Languages()
                        {
                            Bidi = ci.Name
                        });

                        // notify table
                        documentStyle.Tables.BeginTag(en.CurrentTag, new TableJustification()
                        {
                            Val = TableRowAlignmentValues.Right
                        });
                    }

                    containerStyleAttributes.Add(new ParagraphMarkRunProperties(lang));
                    containerStyleAttributes.Add(new BiDi()
                    {
                        Val = OnOffValue.FromBoolean(rtl)
                    });
                }
                catch (ArgumentException exc)
                {
                    // lang not valid, ignore it
                    if (Logging.On)
                    {
                        Logging.PrintError($"lang attribute {attrValue} not recognized: " + exc.Message, exc);
                    }
                }
            }


            attrValue = en.StyleAttributes["text-align"];
            if (attrValue != null && en.CurrentTag != "<font>")
            {
                JustificationValues?align = Converter.ToParagraphAlign(attrValue);
                if (align.HasValue)
                {
                    containerStyleAttributes.Add(new Justification {
                        Val = align
                    });
                }
            }

            // according to w3c, dir should be used in conjonction with lang. But whatever happens, we'll apply the RTL layout
            attrValue = en.Attributes["dir"];
            if (attrValue != null)
            {
                if (attrValue.Equals("rtl", StringComparison.OrdinalIgnoreCase))
                {
                    styleAttributes.Add(new RightToLeftText());
                    containerStyleAttributes.Add(new Justification()
                    {
                        Val = JustificationValues.Right
                    });
                }
                else if (attrValue.Equals("ltr", StringComparison.OrdinalIgnoreCase))
                {
                    containerStyleAttributes.Add(new Justification()
                    {
                        Val = JustificationValues.Left
                    });
                }
            }

            // <span> and <font> are considered as semi-container attribute. When converted to OpenXml, there are Runs but not Paragraphs
            if (en.CurrentTag == "<p>" || en.CurrentTag == "<div>" || en.CurrentTag == "<pre>")
            {
                var border = en.StyleAttributes.GetAsBorder("border");
                if (!border.IsEmpty)
                {
                    ParagraphBorders borders = new ParagraphBorders();
                    if (border.Top.IsValid)
                    {
                        borders.Append(
                            new TopBorder()
                        {
                            Val = border.Top.Style, Color = border.Top.Color.ToHexString(), Size = (uint)border.Top.Width.ValueInPx * 4, Space = 1U
                        });
                    }
                    if (border.Left.IsValid)
                    {
                        borders.Append(
                            new LeftBorder()
                        {
                            Val = border.Left.Style, Color = border.Left.Color.ToHexString(), Size = (uint)border.Left.Width.ValueInPx * 4, Space = 1U
                        });
                    }
                    if (border.Bottom.IsValid)
                    {
                        borders.Append(
                            new BottomBorder()
                        {
                            Val = border.Bottom.Style, Color = border.Bottom.Color.ToHexString(), Size = (uint)border.Bottom.Width.ValueInPx * 4, Space = 1U
                        });
                    }
                    if (border.Right.IsValid)
                    {
                        borders.Append(
                            new RightBorder()
                        {
                            Val = border.Right.Style, Color = border.Right.Color.ToHexString(), Size = (uint)border.Right.Width.ValueInPx * 4, Space = 1U
                        });
                    }

                    containerStyleAttributes.Add(borders);
                    newParagraph = true;
                }
            }
            else if (en.CurrentTag == "<span>" || en.CurrentTag == "<font>")
            {
                // OpenXml limits the border to 4-side of the same color and style.
                SideBorder border = en.StyleAttributes.GetAsSideBorder("border");
                if (border.IsValid)
                {
                    styleAttributes.Add(new DocumentFormat.OpenXml.Wordprocessing.Border()
                    {
                        Val   = border.Style,
                        Color = border.Color.ToHexString(),
                        Size  = (uint)border.Width.ValueInPx * 4,
                        Space = 1U
                    });
                }
            }

            String[] classes = en.Attributes.GetAsClass();
            if (classes != null)
            {
                for (int i = 0; i < classes.Length; i++)
                {
                    string className = documentStyle.GetStyle(classes[i], StyleValues.Paragraph, ignoreCase: true);
                    if (className != null)
                    {
                        containerStyleAttributes.Add(new ParagraphStyleId()
                        {
                            Val = className
                        });
                        newParagraph = true;
                        break;
                    }
                }
            }

            Margin      margin      = en.StyleAttributes.GetAsMargin("margin");
            Indentation indentation = null;
            if (!margin.IsEmpty)
            {
                if (margin.Top.IsFixed || margin.Bottom.IsFixed)
                {
                    SpacingBetweenLines spacing = new SpacingBetweenLines();
                    if (margin.Top.IsFixed)
                    {
                        spacing.Before = margin.Top.ValueInDxa.ToString(CultureInfo.InvariantCulture);
                    }
                    if (margin.Bottom.IsFixed)
                    {
                        spacing.After = margin.Bottom.ValueInDxa.ToString(CultureInfo.InvariantCulture);
                    }
                    containerStyleAttributes.Add(spacing);
                }
                if (margin.Left.IsFixed || margin.Right.IsFixed)
                {
                    indentation = new Indentation();
                    if (margin.Left.IsFixed)
                    {
                        indentation.Left = margin.Left.ValueInDxa.ToString(CultureInfo.InvariantCulture);
                    }
                    if (margin.Right.IsFixed)
                    {
                        indentation.Right = margin.Right.ValueInDxa.ToString(CultureInfo.InvariantCulture);
                    }
                    containerStyleAttributes.Add(indentation);
                }
            }

            // implemented by giorand (feature #13787)
            Unit textIndent = en.StyleAttributes.GetAsUnit("text-indent");
            if (textIndent.IsValid && (en.CurrentTag == "<p>" || en.CurrentTag == "<div>"))
            {
                if (indentation == null)
                {
                    indentation = new Indentation();
                }
                indentation.FirstLine = textIndent.ValueInDxa.ToString(CultureInfo.InvariantCulture);
                containerStyleAttributes.Add(indentation);
            }

            this.BeginTag(en.CurrentTag, containerStyleAttributes);

            // Process general run styles
            documentStyle.Runs.ProcessCommonAttributes(en, styleAttributes);

            return(newParagraph);
        }
        private void ProcessLi(HtmlEnumerator en)
        {
            CompleteCurrentParagraph();

            int numberingId = htmlStyles.NumberingList.ProcessItem(en);
            int level = htmlStyles.NumberingList.LevelIndex;

            // Save the new paragraph reference to support nested numbering list.
            Paragraph p = htmlStyles.Paragraph.NewParagraph();
            currentParagraph = p;
            currentParagraph.InsertInProperties(
                new ParagraphStyleId() { Val = htmlStyles.GetStyle("ListParagraph", false) },
                new SpacingBetweenLines() { After = "0" },
                new Indentation() { Hanging = "357", Left = ((level - 1) * 357).ToString(CultureInfo.InvariantCulture) },
                new NumberingProperties(
                    new NumberingLevelReference() { Val = level - 1 },
                    new NumberingId() { Val = numberingId }
                )
            );

            // Restore the original elements list
            AddParagraph(currentParagraph);

            // Continue to process the html until we found </li>
            AlternateProcessHtmlChunks(en, "</li>");
            p.Append(elements);
            this.elements.Clear();
        }
 private void ProcessDefinitionList(HtmlEnumerator en)
 {
     ProcessParagraph(en);
     currentParagraph.InsertInProperties(
          new SpacingBetweenLines() { After = "0" });
 }
 private void ProcessNumberingList(HtmlEnumerator en)
 {
     htmlStyles.NumberingList.BeginList(en);
 }
        private void ProcessDefinitionListItem(HtmlEnumerator en)
        {
            AlternateProcessHtmlChunks(en, "</dd>");

            currentParagraph = htmlStyles.Paragraph.NewParagraph();
            currentParagraph.Append(elements);
            currentParagraph.InsertInProperties(
                   new Indentation() { FirstLine = "708" },
                   new SpacingBetweenLines() { After = "0" }
            );

            // Restore the original elements list
            AddParagraph(currentParagraph);
            this.elements.Clear();
        }
        private void ProcessDiv(HtmlEnumerator en)
        {
            // The way the browser consider <div> is like a simple Break. But in case of any attributes that targets
            // the paragraph, we don't want to apply the style on the old paragraph but on a new one.
            if (en.Attributes.Count == 0 || (en.StyleAttributes["text-align"] == null && en.Attributes["align"] == null && en.StyleAttributes.GetAsBorder("border").IsEmpty))
            {
                CompleteCurrentParagraph();
                Paragraph previousParagraph = currentParagraph;
                currentParagraph = htmlStyles.Paragraph.NewParagraph();

                List<OpenXmlElement> runStyleAttributes = new List<OpenXmlElement>();
                bool newParagraph = ProcessContainerAttributes(en, runStyleAttributes);

                if (runStyleAttributes.Count > 0)
                    htmlStyles.Runs.BeginTag(en.CurrentTag, runStyleAttributes);

                // Any changes that requires a new paragraph?
                if (!newParagraph && previousParagraph.HasChild<Run>())
                {
                    ProcessBr(en);
                    currentParagraph = previousParagraph;
                }
                else
                {
                    if (newParagraph)
                    {
                        // Insert before the break, complete this paragraph and start a new one
                        this.paragraphs.Insert(this.paragraphs.Count - 1, currentParagraph);
                        AlternateProcessHtmlChunks(en, en.ClosingCurrentTag);
                        CompleteCurrentParagraph();
                        AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
                    }
                    else
                    {
                        AddParagraph(currentParagraph);
                    }
                }
            }
            else
            {
                ProcessParagraph(en);
            }
        }
        private void ProcessFigureCaption(HtmlEnumerator en)
        {
            this.CompleteCurrentParagraph();
            EnsureCaptionStyle();

            AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
            currentParagraph.Append(
                    new ParagraphProperties(
                        new ParagraphStyleId() { Val = htmlStyles.GetStyle("caption", false) },
                        new KeepNext()
                    ),
                    new Run(
                        new Text("Figure ") { Space = SpaceProcessingModeValues.Preserve }
                    ),
                    new SimpleField(
                        new Run(
                            new Text(AddFigureCaption().ToString(CultureInfo.InvariantCulture)))
                    ) { Instruction = " SEQ Figure \\* ARABIC " }
                );

            ProcessHtmlChunks(en, "</figcaption>");

            if (elements.Count > 0) // any caption?
            {
                Text t = (elements[0] as Run).GetFirstChild<Text>();
                t.Text = " " + t.InnerText; // append a space after the numero of the picture
            }

            this.CompleteCurrentParagraph();
            AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
        }
        private void ProcessQuote(HtmlEnumerator en)
        {
            // The browsers render the quote tag between a kind of separators.
            // We add the Quote style to the nested runs to match more Word.

            htmlStyles.Runs.BeginTag(en.CurrentTag, new RunStyle() { Val = htmlStyles.GetStyle("Quote", true) });

            Run run = new Run(
                new Text(" " + HtmlStyles.QuoteCharacters.chars[0]) { Space = SpaceProcessingModeValues.Preserve }
            );

            htmlStyles.Runs.ApplyTags(run);
            elements.Add(run);
        }
        private void ProcessFont(HtmlEnumerator en)
        {
            List<OpenXmlElement> styleAttributes = new List<OpenXmlElement>();
            ProcessContainerAttributes(en, styleAttributes);

            string attrValue = en.Attributes["size"];
            if (attrValue != null)
            {
                uint fontSize = ConverterUtility.ConvertToFontSize(attrValue);
                if (fontSize != 0L)
                    styleAttributes.Add(new FontSize { Val = fontSize.ToString(CultureInfo.InvariantCulture) });
            }

            attrValue = en.Attributes["face"];
            if (attrValue != null)
            {
                // Set HightAnsi. Bug fixed by xjpmauricio on http://notesforhtml2openxml.codeplex.com/discussions/285439
                // where characters with accents were always using fallback font
                styleAttributes.Add(new RunFonts { Ascii = attrValue, HighAnsi = attrValue });
            }

            if (styleAttributes.Count > 0)
                htmlStyles.Runs.MergeTag(en.CurrentTag, styleAttributes);
        }
 private void ProcessStrike(HtmlEnumerator en)
 {
     htmlStyles.Runs.BeginTag(en.CurrentTag, new Strike());
 }
        private void ProcessHeading(HtmlEnumerator en)
        {
            char level = en.Current[2];

            AlternateProcessHtmlChunks(en, "</h" + level + ">");
            Paragraph p = new Paragraph(elements);
            p.InsertInProperties(
                new ParagraphStyleId() { Val = htmlStyles.GetStyle("heading " + level, false) });

            this.elements.Clear();
            AddParagraph(p);
            AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
        }
        private void ProcessTable(HtmlEnumerator en)
        {
            IList<OpenXmlElement> properties = new List<OpenXmlElement>();

            int? border = en.Attributes.GetAsInt("border");
            if (border.HasValue && border.Value > 0)
            {
                // If the border has been specified, we display the Table Grid style which display
                // its grid lines. Otherwise the default table style hides the grid lines.
                if (htmlStyles.DoesStyleExists("Table Grid"))
                    properties.Add(new TableStyle() { Val = htmlStyles.GetStyle("Table Grid", false) });
                else
                {
                    properties.Add(new TableBorders(
                        new TopBorder { Val = BorderValues.Single },
                        new LeftBorder { Val = BorderValues.Single },
                        new RightBorder { Val = BorderValues.Single },
                        new BottomBorder { Val = BorderValues.Single },
                        new InsideHorizontalBorder { Val = BorderValues.Single },
                        new InsideVerticalBorder { Val = BorderValues.Single }
                    ));
                }
            }

            Unit unit = en.StyleAttributes.GetAsUnit("width");
            if (!unit.IsValid) unit = en.Attributes.GetAsUnit("width");

            if (unit.IsValid)
            {
                switch (unit.Type)
                {
                    case UnitMetric.Percent:
                        properties.Add(new TableWidth() { Type = TableWidthUnitValues.Pct, Width = (unit.Value * 50).ToString(CultureInfo.InvariantCulture) }); break;
                    case UnitMetric.Point:
                        properties.Add(new TableWidth() { Type = TableWidthUnitValues.Dxa, Width = unit.ValueInDxa.ToString(CultureInfo.InvariantCulture) }); break;
                    case UnitMetric.Pixel:
                        properties.Add(new TableWidth() { Type = TableWidthUnitValues.Dxa, Width = unit.ValueInDxa.ToString(CultureInfo.InvariantCulture) }); break;
                }
            }
            else
            {
                properties.Add(new TableWidth() { Type = TableWidthUnitValues.Pct, Width = "5000" }); // 100% * 50
            }

            string align = en.Attributes["align"];
            if (align != null)
            {
                JustificationValues? halign = ConverterUtility.FormatParagraphAlign(align);
                if (halign.HasValue)
                    properties.Add(new TableJustification() { Val = halign.Value.ToTableRowAlignment() });
            }

            // only if the table is left aligned, we can handle some left margin indentation
            // Right margin + Right align has no equivalent in OpenXml
            if (align == null || align == "left")
            {
                Margin margin = en.StyleAttributes.GetAsMargin("margin");

                // OpenXml doesn't support left table margin in Percent, but Html does
                if (margin.Left.IsValid && margin.Left.Type != UnitMetric.Percent)
                {
                    properties.Add(new TableIndentation() { Width = (int) margin.Left.ValueInDxa, Type = TableWidthUnitValues.Dxa });
                }
            }

            List<OpenXmlElement> runStyleAttributes = new List<OpenXmlElement>();
            htmlStyles.Runs.ProcessCommonRunAttributes(en, runStyleAttributes);
            if (runStyleAttributes.Count > 0)
                htmlStyles.Runs.BeginTag(en.CurrentTag, runStyleAttributes.ToArray());

            Table currentTable = new Table(
                new TableProperties(properties));

            if (tables.HasContext)
            {
                TableCell currentCell = tables.CurrentTable.GetLastChild<TableRow>().GetLastChild<TableCell>();
                currentCell.Append(new Paragraph(elements));
                currentCell.Append(currentTable);
                elements.Clear();
            }
            else
            {
                CompleteCurrentParagraph();
                this.paragraphs.Add(currentTable);
            }

            tables.NewContext(currentTable);
        }
        private void ProcessHorizontalLine(HtmlEnumerator en)
        {
            // Insert an horizontal line as it stands in many emails.

            CompleteCurrentParagraph();

            UInt32 hrSize = 4U;

            // If the previous paragraph contains a bottom border, we should toggle the size of this <hr> to 0U or 4U
            // or Word will display only the last border.
            // (see Remarks: http://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.bottomborder%28office.14%29.aspx)
            if (paragraphs.Count > 1)
            {
                ParagraphProperties prop = paragraphs[paragraphs.Count - 2].GetFirstChild<ParagraphProperties>();
                if (prop != null)
                {
                    ParagraphBorders borders = prop.GetFirstChild<ParagraphBorders>();
                    if (borders != null && borders.HasChild<BottomBorder>())
                    {
                        if (borders.GetFirstChild<BottomBorder>().Size == 4U) hrSize = 0U;
                        else hrSize = 4U;
                    }
                }
            }

            currentParagraph.InsertInProperties(
                new ParagraphBorders(
                    new BottomBorder() { Val = BorderValues.Single, Size = hrSize }));
            AddParagraph(currentParagraph = htmlStyles.Paragraph.NewParagraph());
        }
        private void ProcessTableCaption(HtmlEnumerator en)
        {
            if (!tables.HasContext) return;

            string att = en.StyleAttributes["text-align"];
            if (att == null) att = en.Attributes["align"];

            ProcessHtmlChunks(en, "</caption>");

            var runStyleId = htmlStyles.GetStyle("Subtle Reference", true);
            var legend = new Paragraph(
                    new ParagraphProperties(
                        new ParagraphStyleId() { Val = htmlStyles.GetStyle("caption", false) },
                        new ParagraphMarkRunProperties(
                            new RunStyle() { Val = runStyleId })),
                    new Run(
                        new RunProperties(
                            new RunStyle() { Val = runStyleId }),
                        new FieldChar() { FieldCharType = FieldCharValues.Begin }),
                    new Run(
                        new RunProperties(
                            new RunStyle() { Val = runStyleId }),
                        new FieldCode(" SEQ Tableau \\* ARABIC ") { Space = SpaceProcessingModeValues.Preserve }),
                    new Run(
                        new RunProperties(
                            new RunStyle() { Val = runStyleId }),
                        new FieldChar() { FieldCharType = FieldCharValues.End })
                );
            legend.Append(elements);
            elements.Clear();

            if (att != null)
            {
                JustificationValues? align = ConverterUtility.FormatParagraphAlign(att);
                if (align.HasValue)
                {
                    legend.InsertInProperties(new Justification { Val = align });
                }
            }
            else
            {
                // If no particular alignement has been specified for the legend, we will align the legend
                // relative to the owning table
                TableProperties props = tables.CurrentTable.GetFirstChild<TableProperties>();
                if (props != null)
                {
                    TableJustification justif = props.GetFirstChild<TableJustification>();
                    if (justif != null) legend.InsertInProperties(new Justification { Val = justif.Val.Value.ToJustification() });
                }
            }

            if (this.TableCaptionPosition == CaptionPositionValues.Above)
            {
                this.paragraphs.Insert(this.paragraphs.Count - 1, legend);
            }
            else
            {
                this.paragraphs.Add(legend);
            }

            EnsureCaptionStyle();
        }
        private void ProcessImage(HtmlEnumerator en)
        {
            if (this.ImageProcessing == ImageProcessing.Ignore) return;

            Drawing drawing = null;
            wBorder border = new wBorder() { Val = BorderValues.None };
            string src = en.Attributes["src"];
            Uri uri;

            if (src != null && Uri.TryCreate(src, UriKind.RelativeOrAbsolute, out uri))
            {
                string alt = en.Attributes["alt"];
                bool process = true;

                if (!uri.IsAbsoluteUri && this.BaseImageUrl != null)
                    uri = new Uri(this.BaseImageUrl, uri);

                Size preferredSize = Size.Empty;
                if (en.Attributes["width"] != null || en.Attributes["height"] != null)
                {
                    Unit wu = en.Attributes.GetAsUnit("width");
                    Unit hu = en.Attributes.GetAsUnit("height");

                    // % is not supported
                    if (wu.IsValid && wu.Value > 0 && wu.Type != UnitMetric.Percent)
                    {
                        preferredSize.Width = wu.ValueInPx;
                    }
                    if (hu.IsValid && hu.Value > 0 && wu.Type != UnitMetric.Percent)
                    {
                        // Image perspective skewed. Bug fixed by ddeforge on http://notesforhtml2openxml.codeplex.com/discussions/350500
                        preferredSize.Height = hu.ValueInPx;
                    }
                }

                SideBorder attrBorder = en.StyleAttributes.GetAsSideBorder("border");
                if (attrBorder.IsValid)
                {
                    border.Val = attrBorder.Style;
                    border.Color = attrBorder.Color.ToHexString();
                    border.Size = (uint) attrBorder.Width.ValueInPx * 4;
                }

                if (process)
                    drawing = AddImagePart(uri, src, alt, preferredSize);
            }

            if (drawing != null)
            {
                Run run = new Run(drawing);
                if (border.Val != BorderValues.None) run.InsertInProperties(border);
                elements.Add(run);
            }
        }
 private void ProcessBr(HtmlEnumerator en)
 {
     elements.Add(new Run(new Break()));
 }
 private void ProcessItalic(HtmlEnumerator en)
 {
     htmlStyles.Runs.BeginTag(en.CurrentTag, new Italic());
 }
 private void ProcessCite(HtmlEnumerator en)
 {
     htmlStyles.Runs.BeginTag(en.CurrentTag, new RunStyle() { Val = htmlStyles.GetStyle("Quote", true) });
 }
        public void BeginList(HtmlEnumerator en)
        {
            int prevAbsNumId = numInstances.Peek().Value;
            var absNumId     = -1;

            // lookup for a predefined list style in the template collection
            String type        = en.StyleAttributes["list-style-type"];
            bool   orderedList = en.CurrentTag.Equals("<ol>", StringComparison.OrdinalIgnoreCase);

            if (type == null || !knonwAbsNumIds.TryGetValue(type.ToLowerInvariant(), out absNumId))
            {
                if (orderedList)
                {
                    absNumId = knonwAbsNumIds["decimal"];
                }
                else
                {
                    absNumId = knonwAbsNumIds["disc"];
                }
            }

            firstItem = true;
            levelDepth++;
            if (levelDepth > maxlevelDepth)
            {
                maxlevelDepth = levelDepth;
            }

            // save a NumberingInstance if the nested list style is the same as its ancestor.
            // this allows us to nest <ol> and restart the indentation to 1.
            int currentInstanceId = this.InstanceID;

            if (levelDepth > 1 && absNumId == prevAbsNumId && orderedList)
            {
                EnsureMultilevel(absNumId);
            }
            else
            {
                // For unordered lists (<ul>), create only one NumberingInstance per level
                // (MS Word does not tolerate hundreds of identical NumberingInstances)
                if (orderedList || (levelDepth >= maxlevelDepth))
                {
                    currentInstanceId = ++nextInstanceID;
                    Numbering numbering = mainPart.NumberingDefinitionsPart.Numbering;
                    numbering.Append(
                        new NumberingInstance(
                            new AbstractNumId()
                    {
                        Val = absNumId
                    },
                            new LevelOverride(
                                new StartOverrideNumberingValue()
                    {
                        Val = 1
                    }
                                )
                    {
                        LevelIndex = 0,
                    }
                            )
                    {
                        NumberID = currentInstanceId
                    });
                }
            }

            numInstances.Push(new KeyValuePair <int, int>(currentInstanceId, absNumId));
        }