Wednesday 7 December 2011

Wrapping Images using the Html Agility Pack, TinyMce, Umbraco


Here is an Umbraco related rendering task that most people would probably do in the DOM.

You have an image that was inserted in tinymce. You want to wrap that image tag in some custom markup.

I used the Html Agility pack to do just that (code below)!

I coded in <figure> but you can use anything you like.

In a razor script I just did this:
var newBodyHtml = WrapImages(bodyHtml, "your outer css class", "your inner css class")

/// <summary>
/// Wraps body images in custom markup
/// E.g.
/// Turns <img src="" /> into  <figure class="<cssclassouter>"><a href=""><img src="" class="<cssclassinner>" /></a></figure>
/// </summary>
/// <param name="body"></param>
/// <returns></returns>
string WrapImages(string bodyHtml, string cssClassOuter, string cssClassInner)
{
 var doc = new HtmlAgilityPack.HtmlDocument();
 doc.LoadHtml(bodyHtml);

 // traverse html to find and replace image node
 var q = new Queue<HtmlNode>();
 q.Enqueue(doc.DocumentNode);
 while (q.Count > 0)
 {
  var item = q.Dequeue();
  HtmlNode node = null;

  // found image, need to wrap it!
  if (item.Name == "img")
  {
   // get image src from item
   string imageUrl = item.Attributes["src"].Value;

   // create new node
   node = HtmlTextNode.CreateNode("<figure class=\" + cssClassOuter + "\"><a href=\"" + imageUrl + "\" ><img src=\"" + imageUrl + "\" class=\" + cssClassInner + \" /></a></figure>");

   // set new html
   item.ParentNode.InnerHtml = node.OuterHtml;
  }
  else
  {
   // traverse children
   foreach (var child in item.ChildNodes) { q.Enqueue(child); }
  }
 }
 return doc.DocumentNode.OuterHtml;
}

3 comments:

  1. excellent, I had neeed for this just last week, didnt want to go down the route of creating img class for the surrounding DIV as it would have affected every image in that DIV...

    ReplyDelete
  2. Lol, did exactly the same thing for a project I've worked on at the end of 2010:
    public static string BeautifyImages(string text)
    {
    if (text.Contains("<img "))
    {
    HtmlDocument htmlDoc = new HtmlDocument();
    htmlDoc.LoadHtml(text);
    foreach (HtmlNode node in htmlDoc.DocumentNode.SelectNodes("//img"))
    {
    HtmlNode div = HtmlNode.CreateNode("<div class=\"picture\"><div class=\"top\"></div></div>");
    HtmlNode divBottom = HtmlNode.CreateNode("<div class=\"bottom\"></div>");
    HtmlNode parent = node.ParentNode;
    node.Attributes["src"].Value = MediaHelper.GetImageGenUrlByImageUrl(node.Attributes["src"].Value, 268);
    node.Attributes.Remove("width");
    node.Attributes.Remove("height");
    div.AppendChild(node.CloneNode(false));
    div.AppendChild(divBottom);
    parent.ReplaceChild(div, node);
    }
    return htmlDoc.DocumentNode.WriteContentTo();
    }
    else
    return text;
    }

    ReplyDelete
  3. I haven't always been a fan of agility pack but its powerful and really does the job when all you have to go on is HTML markup. I did wish we could hook up into the RTE HTML generation process earlier though, which could avoid having to use helper methods like these. On the other hand its nice to have a tool/extensions available in the script/view to do some last minute processing :)

    ReplyDelete