Last active
April 20, 2019 20:43
-
-
Save SurinderBhomra/f4d03cab9af096d8d13629b891aefbb7 to your computer and use it in GitHub Desktop.
Responsive Images: Convert Image To Picture Tag
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using HtmlAgilityPack; | |
using Site.Common.Kentico; | |
using System; | |
using System.Collections.Generic; | |
using System.Collections.Specialized; | |
using System.Linq; | |
using System.Text; | |
using System.Web; | |
namespace SurinderBhomra.Common.Extensions | |
{ | |
public static class ContentManipulatorExtensions | |
{ | |
/// <summary> | |
/// Transforms all image tags to a picture tag inside parsed HTML. | |
/// All source image URL's need to contain a "width" query parameter in order to have a resize starting point. | |
/// </summary> | |
/// <param name="content"></param> | |
/// <param name="percentageReduction"></param> | |
/// <param name="minimumWidth">The minimum width an image has to be to warrant resizing.</param> | |
/// <param name="viewPorts"></param> | |
/// <returns></returns> | |
public static string ConvertImageToPictureTag(this string content, int percentageReduction = 10, int minimumWidth = 200, params int[] viewPorts) | |
{ | |
if (viewPorts?.Length == 0) | |
throw new Exception("Viewport parameter is required."); | |
if (!string.IsNullOrEmpty(content)) | |
{ | |
//Create a new document parser object. | |
HtmlDocument document = new HtmlDocument(); | |
//Load the content. | |
document.LoadHtml(content); | |
//Get all image tags. | |
List<HtmlNode> imageNodes = document.DocumentNode.Descendants("img").ToList(); | |
if (imageNodes.Any()) | |
{ | |
// Loop through all image tags. | |
foreach (HtmlNode imgNode in imageNodes) | |
{ | |
// Make sure there is an image source and it is not externally linked. | |
if (imgNode.Attributes.Contains("src") && !imgNode.Attributes["src"].Value.StartsWith("http", StringComparison.Ordinal)) | |
{ | |
#region Image Attributes - src, class, alt, style | |
string imageSrc = imgNode.Attributes["src"].Value.Replace("~", string.Empty); | |
string imageClass = imgNode.Attributes.Contains("class") ? imgNode.Attributes["class"].Value : string.Empty; | |
string imageAlt = imgNode.Attributes.Contains("alt") ? imgNode.Attributes["alt"].Value : string.Empty; | |
string imageStyle = imgNode.Attributes.Contains("style") ? imgNode.Attributes["style"].Value : string.Empty; | |
#endregion | |
#region If Image Source has a width query parameter, this will be used as the starting size to reduce images | |
int imageWidth = 0; | |
UriBuilder imageSrcUri = new UriBuilder($"http://www.surinderbhomra.com{imageSrc}"); | |
NameValueCollection imageSrcQueryParams = HttpUtility.ParseQueryString(imageSrcUri.Query); | |
if (imageSrcQueryParams?.Count > 0 && !string.IsNullOrEmpty(imageSrcQueryParams["width"])) | |
imageWidth = int.Parse(imageSrcQueryParams["width"]); | |
// If there is no width parameter, then we cannot resize this image. | |
// Might be an older non-responsive image link. | |
if (imageWidth == 0 || imageWidth <= minimumWidth) | |
continue; | |
// Clear the query string from image source. | |
imageSrc = imageSrc.ClearQueryStrings(); | |
#endregion | |
// Create picture tag. | |
HtmlNode pictureNode = document.CreateElement("picture"); | |
if (!string.IsNullOrEmpty(imageStyle)) | |
pictureNode.Attributes.Add("style", imageStyle); | |
#region Add multiple source tags | |
StringBuilder sourceHtml = new StringBuilder(); | |
int newImageWidth = imageWidth; | |
for (int vp = 0; vp < viewPorts.Length; vp++) | |
{ | |
int viewPort = viewPorts[vp]; | |
// We do not not want to apply the percentage reduction to the first viewport size. | |
// The first image should always be the original size. | |
if (vp != 0) | |
newImageWidth = newImageWidth - (newImageWidth * percentageReduction / 100); | |
sourceHtml.Append($"<source srcset=\"{imageSrc}?width={newImageWidth}\" data-srcset=\"{imageSrc}?width={newImageWidth}\" media=\"(min-width: {viewPort}px)\">"); | |
} | |
// Add fallback image. | |
sourceHtml.Append($"<img src=\"{imageSrc}?width=50\" style=\"width: {imageWidth}px\" class=\"{imageClass} lazyload\" alt=\"{imageAlt}\" />"); | |
pictureNode.InnerHtml = sourceHtml.ToString(); | |
#endregion | |
// Replace the image node with the new picture node. | |
imgNode.ParentNode.ReplaceChild(pictureNode, imgNode); | |
} | |
} | |
return document.DocumentNode.OuterHtml; | |
} | |
} | |
return content; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I am referencing a
ClearQueryStrings
method. The code for this is below: