Skip to content

Commit

Permalink
Merge pull request #5 from ErikHen/feature/add_img_width
Browse files Browse the repository at this point in the history
Possible to set width attribute on img element.
  • Loading branch information
ErikHen authored Aug 24, 2022
2 parents 9f2d810 + 5288abf commit 5b2f95d
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 21 deletions.
22 changes: 14 additions & 8 deletions PictureRenderer/Picture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,19 @@ public static string Render(string[] imagePaths, PictureProfileBase profile, str
return Render(imagePaths, profile, altText, LazyLoading.Browser, focalPoints: default, cssClass: cssClass);
}

public static string Render(string imagePath, PictureProfileBase profile, string altText = "", LazyLoading lazyLoading = LazyLoading.Browser, (double x, double y) focalPoint = default, string cssClass = "")
public static string Render(string imagePath, PictureProfileBase profile, string altText = "", LazyLoading lazyLoading = LazyLoading.Browser, (double x, double y) focalPoint = default, string cssClass = "", string imgWidth = "")
{
var pictureData = PictureUtils.GetPictureData(imagePath, profile, altText, focalPoint, cssClass);

var sourceElement = RenderSourceElement(pictureData);





var sourceElementWebp = string.Empty;
if (!string.IsNullOrEmpty(pictureData.SrcSetWebp))
{
sourceElementWebp = RenderSourceElement(pictureData, ImageFormat.Webp);
}

var imgElement = RenderImgElement(pictureData, profile, lazyLoading);
var imgElement = RenderImgElement(pictureData, profile, lazyLoading, imgWidth);

//Webp source element must be rendered first. Browser selects the first version it supports.
return $"<picture>{sourceElementWebp}{sourceElement}{imgElement}</picture>";
Expand All @@ -82,16 +78,26 @@ public static string Render(string[] imagePaths, PictureProfileBase profile, str
return $"<picture>{sourceElements}{imgElement}</picture>";
}

private static string RenderImgElement(PictureData pictureData, PictureProfileBase profile, LazyLoading lazyLoading)
private static string RenderImgElement(PictureData pictureData, PictureProfileBase profile, LazyLoading lazyLoading, string imgWidth = "")
{
var widthAndHeightAttributes = profile.ImgWidthHeight ? $"width=\"{profile.FallbackWidth}\"height=\"{Math.Round(profile.FallbackWidth / profile.AspectRatio)}\"" : string.Empty;
var widthAndHeightAttributes = GetImgWidthAndHeightAttributes(profile, imgWidth);
var loadingAttribute = lazyLoading == LazyLoading.Browser ? "loading=\"lazy\"" : string.Empty;
var classAttribute = string.IsNullOrEmpty(pictureData.CssClass) ? string.Empty : $"class=\"{HttpUtility.HtmlEncode(pictureData.CssClass)}\"";
var decodingAttribute = profile.ImageDecoding == ImageDecoding.None ? string.Empty : $"decoding=\"{Enum.GetName(typeof(ImageDecoding), profile.ImageDecoding)?.ToLower()}\"";

return $"<img alt=\"{HttpUtility.HtmlEncode(pictureData.AltText)}\"src=\"{pictureData.ImgSrc}\"{widthAndHeightAttributes}{loadingAttribute}{decodingAttribute}{classAttribute}/>";
}

private static string GetImgWidthAndHeightAttributes(PictureProfileBase profile, string imgWidth)
{
if (!string.IsNullOrEmpty(imgWidth))
{
return $"width=\"{imgWidth}\"";
}

return profile.ImgWidthHeight ? $"width=\"{profile.FallbackWidth}\"height=\"{Math.Round(profile.FallbackWidth / profile.AspectRatio)}\"" : string.Empty;
}

private static string RenderSourceElement(PictureData pictureData, string format = "")
{
var srcSet = pictureData.SrcSet;
Expand Down
2 changes: 1 addition & 1 deletion PictureRenderer/PictureRenderer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>3.2.0</Version>
<Version>3.3.0</Version>
<Authors>Erik Henningson</Authors>
<Company />
<Product />
Expand Down
29 changes: 17 additions & 12 deletions PictureRendererTests/PictureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public class PictureTests
public void RenderWithoutWebpTest()
{
const string expected = "<picture><source srcset=\"/myImage.jpg?width=375&height=211&quality=80 375w, /myImage.jpg?width=750&height=422&quality=80 750w, /myImage.jpg?width=980&height=551&quality=80 980w, /myImage.jpg?width=1500&height=844&quality=80 1500w\"sizes=\"(max-width: 980px) calc((100vw - 40px)), (max-width: 1200px) 368px, 750px\"/><img alt=\"\"src=\"/myImage.jpg?width=1500&height=844&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";

var profile = new ImageSharpProfile()
{
SrcSetWidths = new[] { 375, 750, 980, 1500 },
Expand All @@ -30,9 +29,7 @@ public void RenderWithoutWebpTest()
public void RenderWithWebpTest()
{
const string expected = "<picture><source srcset=\"/myImage.jpg?format=webp&width=150&height=150&quality=80 150w, /myImage.jpg?format=webp&width=300&height=300&quality=80 300w\"sizes=\"150px\"type=\"image/webp\"/><source srcset=\"/myImage.jpg?width=150&height=150&quality=80 150w, /myImage.jpg?width=300&height=300&quality=80 300w\"sizes=\"150px\"/><img alt=\"alt text\"src=\"/myImage.jpg?width=400&height=400&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";
//const string expected = "<picture><source srcset=\"/myImage.jpg?format=webp&width=150&height=150&quality=80 150w, /myImage.jpg?format=webp&width=300&height=300&quality=80 300w\"sizes=\"150px\"type=\"image/webp\"/><source srcset=\"/myImage.jpg?width=150&height=150&quality=80 150w, /myImage.jpg?width=300&height=300&quality=80 300w\"sizes=\"150px\"/><img alt=\"alt text\"src=\"/myImage.jpg?width=200&height=200&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";

var profile = GetMultiImageProfile();
var profile = GetTestImageProfile();

var result = PictureRenderer.Picture.Render("/myImage.jpg", profile, "alt text");

Expand All @@ -51,7 +48,6 @@ public void RenderWithCssClassAndImageDecodingAuto()
ImageDecoding = ImageDecoding.Auto,
};


var result = PictureRenderer.Picture.Render("/myImage.jpg", profile, "alt text", "my-css-class");

Assert.Equal(expected, result);
Expand All @@ -61,7 +57,6 @@ public void RenderWithCssClassAndImageDecodingAuto()
public void RenderWithWidthAndHeightAndNoDecoding()
{
const string expected = "<picture><source srcset=\"/myImage.jpg?format=webp&width=150&height=150&quality=80 150w, /myImage.jpg?format=webp&width=300&height=300&quality=80 300w\"sizes=\"150px\"type=\"image/webp\"/><source srcset=\"/myImage.jpg?width=150&height=150&quality=80 150w, /myImage.jpg?width=300&height=300&quality=80 300w\"sizes=\"150px\"/><img alt=\"alt text\"src=\"/myImage.jpg?width=300&height=300&quality=80\"width=\"300\"height=\"300\"loading=\"lazy\"/></picture>";

var profile = new ImageSharpProfile()
{
SrcSetWidths = new[] { 150, 300 },
Expand All @@ -71,7 +66,6 @@ public void RenderWithWidthAndHeightAndNoDecoding()
ImageDecoding = ImageDecoding.None,
};


var result = PictureRenderer.Picture.Render("/myImage.jpg", profile, "alt text");

Assert.Equal(expected, result);
Expand All @@ -81,7 +75,7 @@ public void RenderWithWidthAndHeightAndNoDecoding()
public void RenderMultiImageTest()
{
const string expected = "<picture><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?format=webp&width=400&height=400&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?width=400&height=400&quality=80\"/><source media=\"(min-width: 600px)\" srcset=\"/myImage2.png?width=200&height=200&quality=80\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage3.jpg?format=webp&width=100&height=100&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage3.jpg?width=100&height=100&quality=80\"/><img alt=\"\"src=\"/myImage.jpg?width=400&height=400&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";
var result = Picture.Render(new []{"/myImage.jpg", "/myImage2.png", "/myImage3.jpg" }, GetMultiImageProfile());
var result = Picture.Render(new []{"/myImage.jpg", "/myImage2.png", "/myImage3.jpg" }, GetTestImageProfile());

Assert.Equal(expected, result);
}
Expand All @@ -90,7 +84,7 @@ public void RenderMultiImageTest()
public void RenderMultiImageMissingImageTest()
{
const string expected = "<picture><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?format=webp&width=400&height=400&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?width=400&height=400&quality=80\"/><source media=\"(min-width: 600px)\" srcset=\"/myImage2.jpg?format=webp&width=200&height=200&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 600px)\" srcset=\"/myImage2.jpg?width=200&height=200&quality=80\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage2.jpg?format=webp&width=100&height=100&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage2.jpg?width=100&height=100&quality=80\"/><img alt=\"alt text\"src=\"/myImage.jpg?width=400&height=400&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";
var result = Picture.Render(new[] { "/myImage.jpg", "/myImage2.jpg" }, GetMultiImageProfile(), "alt text");
var result = Picture.Render(new[] { "/myImage.jpg", "/myImage2.jpg" }, GetTestImageProfile(), "alt text");

Assert.Equal(expected, result);
}
Expand All @@ -100,7 +94,7 @@ public void RenderMultiImageWithFocalPointsTest()
{
const string expected = "<picture><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?format=webp&width=400&height=400&rxy=0.1%2c0.1&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?width=400&height=400&rxy=0.1%2c0.1&quality=80\"/><source media=\"(min-width: 600px)\" srcset=\"/myImage2.png?width=200&height=200&rxy=0.2%2c0.2&quality=80\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage3.jpg?format=webp&width=100&height=100&rxy=0.3%2c0.3&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage3.jpg?width=100&height=100&rxy=0.3%2c0.3&quality=80\"/><img alt=\"\"src=\"/myImage.jpg?width=400&height=400&rxy=0.1%2c0.1&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";

var result = Picture.Render(new[] { "/myImage.jpg", "/myImage2.png", "/myImage3.jpg" }, GetMultiImageProfile(), focalPoints: new [] { (0.1, 0.1), (0.2, 0.2), (0.3, 0.3) });
var result = Picture.Render(new[] { "/myImage.jpg", "/myImage2.png", "/myImage3.jpg" }, GetTestImageProfile(), focalPoints: new [] { (0.1, 0.1), (0.2, 0.2), (0.3, 0.3) });

Assert.Equal(expected, result);
}
Expand All @@ -110,12 +104,23 @@ public void RenderMultiImageWithEmptyFocalPointsTest()
{
const string expected = "<picture><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?format=webp&width=400&height=400&rxy=0.1%2c0.1&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 1200px)\" srcset=\"/myImage.jpg?width=400&height=400&rxy=0.1%2c0.1&quality=80\"/><source media=\"(min-width: 600px)\" srcset=\"/myImage2.png?width=200&height=200&quality=80\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage3.jpg?format=webp&width=100&height=100&rxy=0.3%2c0.3&quality=80\" type=\"image/webp\"/><source media=\"(min-width: 300px)\" srcset=\"/myImage3.jpg?width=100&height=100&rxy=0.3%2c0.3&quality=80\"/><img alt=\"\"src=\"/myImage.jpg?width=400&height=400&rxy=0.1%2c0.1&quality=80\"loading=\"lazy\"decoding=\"async\"/></picture>";

var result = Picture.Render(new[] { "/myImage.jpg", "/myImage2.png", "/myImage3.jpg" }, GetMultiImageProfile(), focalPoints: new[] { (0.1, 0.1), default, (0.3, 0.3) });
var result = Picture.Render(new[] { "/myImage.jpg", "/myImage2.png", "/myImage3.jpg" }, GetTestImageProfile(), focalPoints: new[] { (0.1, 0.1), default, (0.3, 0.3) });

Assert.Equal(expected, result);
}

[Fact()]
public void RenderWithImgWidthTest()
{
const string expected = "<picture><source srcset=\"/myImage.jpg?format=webp&width=150&height=150&quality=80 150w, /myImage.jpg?format=webp&width=300&height=300&quality=80 300w\"sizes=\"150px\"type=\"image/webp\"/><source srcset=\"/myImage.jpg?width=150&height=150&quality=80 150w, /myImage.jpg?width=300&height=300&quality=80 300w\"sizes=\"150px\"/><img alt=\"alt text\"src=\"/myImage.jpg?width=400&height=400&quality=80\"width=\"50%\"loading=\"lazy\"decoding=\"async\"/></picture>";
var profile = GetTestImageProfile();

var result = PictureRenderer.Picture.Render("/myImage.jpg", profile, "alt text", LazyLoading.Browser, default, "", "50%");

Assert.Equal(expected, result);
}

private static ImageSharpProfile GetMultiImageProfile()
private static ImageSharpProfile GetTestImageProfile()
{
//use this to test with both single and multiple images
return new ImageSharpProfile()
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ See also [sample projects](https://github.com/ErikHen/PictureRenderer.Samples).
<br><br>

## Version history
* **3.3** Adding possibility to set width attribute on img element. Needed for rare edge case in Optimizely CMS.
* **3.2** Changing some things reported as warnings by Sonarcloud.
* **3.1** Making PictureRenderer.ImageFormat public. Don't understand why it was internal in the first place 😊.

Expand Down

0 comments on commit 5b2f95d

Please sign in to comment.