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 }); }
/// <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) }); } } }
/// <summary> /// There is a few attributes shared by a large number of tags. This method will check them for a limited /// number of tags (<p>, <pre>, <div>, <span> and <body>). /// </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)); }