/// <summary> /// Replaces a tag inside a paragraph (a:p) with parsed HTML /// </summary> /// <param name="p">The paragraph (a:p).</param> /// <param name="tag">The tag to replace by newText, if null or empty do nothing; tag is a regex string.</param> /// <param name="newText">The new text to replace the tag with, if null replaced by empty string and not visible.</param> /// <param name="fontName">Font name</param> /// <param name="fontSize">Font size. E.g. 800 is 8pt (small) font. If value is less than 100 it will be multiplied by 100 to keep up with PPT notation.</param> /// <param name="hyperlinks">URL Relationships dictionary. Relationship has to be defined on slide level.</param> /// <returns><c>true</c> if a tag has been found and replaced, <c>false</c> otherwise.</returns> internal static bool ReplaceTagWithHtml(A.Paragraph p, string tag, string newText, string fontName = null, int fontSize = 0, IDictionary <string, string> hyperlinks = null) { newText = CorrectUnhandledHtmlTags(newText); // e.g. deal with ul/li html tags bool isFirstLine = true; // avoiding unintentional empty line at the begining of the text bool replaced = false; string[] closingTags = new string[] { "div", "p" }; // tags that force line break in PPTX paragraph if (string.IsNullOrEmpty(tag)) { return(replaced); } if (newText == null) { newText = string.Empty; } newText = RemoveInvalidXMLChars(newText); while (true) { // Search for the tag Match match = Regex.Match(GetTexts(p), tag); if (!match.Success) { break; } p.RemoveAllChildren(); // // remove exisitng children then add new HtmlParser hp = new HtmlParser(newText); MariGold.HtmlParser.IHtmlNode nodes = null; try { nodes = hp.FindBodyOrFirstElement(); } catch { Console.WriteLine(String.Format("WARNING: HTML is empty or HTML schema has errors. Parsed HTML[]: [{0}]", newText)); } while (nodes != null) { foreach (var item in nodes.Children) { bool skipLineBreak = false; A.Run r = new A.Run(); r.RunProperties = new A.RunProperties(); if (item.Html.Contains("<b>") || item.Html.Contains("<strong>")) { r.RunProperties.Bold = new DocumentFormat.OpenXml.BooleanValue(true); } if (item.Html.Contains("<i>") || item.Html.Contains("<em>")) { r.RunProperties.Italic = new DocumentFormat.OpenXml.BooleanValue(true); } if (item.Html.Contains("<u>") || item.Html.Contains("text-decoration: underline")) { r.RunProperties.Underline = new DocumentFormat.OpenXml.EnumValue <A.TextUnderlineValues>(A.TextUnderlineValues.Dash); } if (item.Html.Contains("<a")) { string uriId = null; try { string url = PptxSlide.ParseHttpUrls(item.Html).First().Value; uriId = hyperlinks.Where(q => q.Value == url).FirstOrDefault().Key; } catch (Exception ex) { Console.WriteLine("URL is no available"); } if (uriId != null) { A.HyperlinkOnClick link = new A.HyperlinkOnClick() { Id = uriId }; r.RunProperties.AppendChild(link); } } A.Text at = new A.Text(PptxTemplater.TextTransformationHelper.HtmlToPlainTxt(item.InnerHtml) + " "); // clear not interpreted html tags if (at.InnerText.Trim() == "" && isFirstLine) { at = new A.Text(); // avoid excessive new lines isFirstLine = false; } r.AppendChild(at); p.Append(r); // LINE BREAK -- if outer tag is div add line break if (closingTags.Contains(item.Parent.Tag) && skipLineBreak == false) { p.Append(new A.Break()); } } // remove parsed html part newText = newText.Substring(nodes.Html.Length); if (newText.Trim() == "") { break; } hp = new HtmlParser(newText); nodes = hp.FindBodyOrFirstElement(); } var run = p.Descendants <A.Run>(); foreach (var item in run) { if (fontName != null) { item.RunProperties.RemoveAllChildren <A.LatinFont>(); var latinFont = new A.LatinFont(); latinFont.Typeface = fontName; item.RunProperties.AppendChild(latinFont); } if (fontSize > 0) { item.RunProperties.FontSize = (fontSize > 99) ? fontSize : fontSize * 100; // e.g. translate value 8 (Power Point UI font size) to 800 for API } } replaced = true; } return(replaced); }