132 lines
5.1 KiB
C#
132 lines
5.1 KiB
C#
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
|
|
|
using SixLabors.Fonts;
|
|
using SixLabors.ImageSharp.Drawing.Processing;
|
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
|
using SixLabors.ImageSharp.Formats.Webp;
|
|
|
|
namespace CatherineLynwood.TagHelpers
|
|
{
|
|
[HtmlTargetElement("responsive-image")]
|
|
public class ResponsiveImageTagHelper : TagHelper
|
|
{
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
|
|
public ResponsiveImageTagHelper(IHttpContextAccessor httpContextAccessor)
|
|
{
|
|
_httpContextAccessor = httpContextAccessor;
|
|
}
|
|
|
|
[HtmlAttributeName("src")]
|
|
public string Source { get; set; }
|
|
|
|
[HtmlAttributeName("alt")]
|
|
public string AltText { get; set; }
|
|
|
|
[HtmlAttributeName("class")]
|
|
public string CssClass { get; set; }
|
|
|
|
[HtmlAttributeName("display-width-percentage")]
|
|
public int DisplayWidthPercentage { get; set; } = 100;
|
|
|
|
[HtmlAttributeName("no-index")]
|
|
public bool NoIndex { get; set; }
|
|
|
|
|
|
private readonly string[] _resolutions = { "1920", "1400", "1200", "992", "768", "576" };
|
|
private readonly string _wwwRoot = "wwwroot";
|
|
|
|
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
|
{
|
|
// Adjust for mobile display width if applicable
|
|
if (IsMobile(_httpContextAccessor.HttpContext.Request))
|
|
{
|
|
DisplayWidthPercentage = 100;
|
|
}
|
|
|
|
string rootPath = Path.Combine(Directory.GetCurrentDirectory(), _wwwRoot, "images", Source);
|
|
string directory = Path.GetDirectoryName(rootPath);
|
|
string fileName = Path.GetFileNameWithoutExtension(rootPath);
|
|
string extension = Path.GetExtension(rootPath);
|
|
|
|
string year = DateTime.Now.Year.ToString();
|
|
// Define paths for "webp" and "jpg" subfolders
|
|
string webpDirectory = Path.Combine(directory, "webp");
|
|
string jpgDirectory = Path.Combine(directory, "jpg");
|
|
string watermarkText = $"© Catherine Lynwood {year}";
|
|
|
|
// Ensure the subdirectories exist
|
|
Directory.CreateDirectory(webpDirectory);
|
|
Directory.CreateDirectory(jpgDirectory);
|
|
|
|
// Generate HTML for <picture> tag with WebP sources only
|
|
output.TagName = "picture";
|
|
foreach (string res in _resolutions)
|
|
{
|
|
int effectiveWidth = int.Parse(res) * DisplayWidthPercentage / 100;
|
|
string webpPath = Path.Combine(webpDirectory, $"{fileName}-{effectiveWidth}.webp");
|
|
|
|
if (!File.Exists(webpPath))
|
|
{
|
|
await CreateResizedImage(rootPath, webpPath, effectiveWidth, watermarkText, true); // WebP format
|
|
}
|
|
|
|
output.Content.AppendHtml(
|
|
$"<source srcset='/images/webp/{fileName}-{effectiveWidth}.webp' media='(min-width: {res}px)'>");
|
|
}
|
|
|
|
string webpPathFallback = Path.Combine(webpDirectory, $"{fileName}-400.webp");
|
|
|
|
if (!File.Exists(webpPathFallback))
|
|
{
|
|
await CreateResizedImage(rootPath, webpPathFallback, 400, watermarkText, true); // WebP format
|
|
}
|
|
|
|
// Generate the 400px JPEG for RSS feed or other uses (without adding to the <picture> element)
|
|
string jpgFallbackPath = Path.Combine(jpgDirectory, $"{fileName}-400.jpg");
|
|
if (!File.Exists(jpgFallbackPath))
|
|
{
|
|
await CreateResizedImage(rootPath, jpgFallbackPath, 400, watermarkText, false); // JPEG format
|
|
}
|
|
|
|
// Add the default WebP fallback in the <img> tag (JPEG is not included in HTML)
|
|
string noIndexAttr = NoIndex ? " data-nosnippet" : "";
|
|
output.Content.AppendHtml(
|
|
$"<img src='/images/webp/{fileName}-400.webp' class='{CssClass}' alt='{AltText}'{noIndexAttr}>");
|
|
|
|
}
|
|
|
|
|
|
private async Task CreateResizedImage(string originalPath, string outputPath, int width, string watermarkText, bool isWebP)
|
|
{
|
|
using (var image = await Image.LoadAsync(originalPath))
|
|
{
|
|
image.Mutate(x => x
|
|
.Resize(width, 0)
|
|
.DrawText(watermarkText, SystemFonts.CreateFont("Arial", 12), Color.White, new PointF(30, image.Height - 20)));
|
|
|
|
if (isWebP)
|
|
{
|
|
var webpEncoder = new WebpEncoder { Quality = 75 };
|
|
await image.SaveAsync(outputPath, webpEncoder);
|
|
}
|
|
else
|
|
{
|
|
var jpegEncoder = new JpegEncoder { Quality = 75 };
|
|
await image.SaveAsync(outputPath, jpegEncoder);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool IsMobile(HttpRequest request)
|
|
{
|
|
string userAgent = request.Headers["User-Agent"].ToString().ToLower();
|
|
return userAgent.Contains("mobi") ||
|
|
userAgent.Contains("android") ||
|
|
userAgent.Contains("iphone") ||
|
|
userAgent.Contains("ipad");
|
|
}
|
|
}
|
|
}
|