【发布时间】:2022-09-23 06:54:22
【问题描述】:
完全在后端,没有控制台,没有上下文,没有会话(每隔几秒运行的代理调用的一部分);我需要一种方法将 HTML 的小 sn-p 或整个 HTML 文档转换为图像(位图或其他),然后将其转换为 base64 字符串,以便我可以将 img 呈现为电子邮件模板。
HTML 本身是动态的,并且每次需要时都会更改其中的数据。
- 我尝试过使用不同的库,例如 Aspose (https://products.aspose.com/html/net/),但它不是免费的,而且生成速度很慢。即使对于 HTML 的小 sn-ps
- 我已尝试使用默认的 Webbrowser 方法。这主要是有效的,但不会渲染任何与 HTML 一起使用的 CSS。内联或其他。
使用内联 CSS 将 HTML 渲染到图像/位图/位图图像中的最简单、最快、最简单的方法是什么。任何外部库/Nuget 包必须完全免费。 然后需要将图像转换为 Base64 字符串。 自动裁剪/自动调整大小也将对任何答案产生巨大的好处。
到目前为止,这是我能做的最快和最好的,但它无法为 HTML 渲染 CSS:
public static class UserDataExtensions
{
public static string SignBase64(this string base64, string mediaType, string charSet)
{
return \"data:\" + mediaType + \";charset=\" + charSet + \";base64,\" + base64;
}
}
public class HtmlToImageConverter
{
private string _Html;
private Bitmap _Image;
private const string HTML_START = \"<html><head></head><body>\";
private const string HTML_END = \"</body></html>\";
public HtmlToImageConverter()
{
}
public string ConvertHTML(string html)
{
_Html = HTML_START + html + HTML_END;
return ToBase64(Render()).SignBase64(\"image/png\", \"utf-8\");
}
private string ToBase64(Bitmap bitmap)
{
using (var memory = new MemoryStream())
{
using (var newImage = new Bitmap(bitmap))
{
newImage.Save(memory, ImageFormat.Png);
var SigBase64 = Convert.ToBase64String(memory.GetBuffer()); // Get Base64
return SigBase64;
}
}
}
private Bitmap Render()
{
var thread = new Thread(GenerateInternal);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return _Image;
}
private void GenerateInternal()
{
var webBrowser = new WebBrowser
{
ScrollBarsEnabled = false,
DocumentText = _Html,
ClientSize = new Size(3000, 3000)
};
webBrowser.DocumentCompleted += WebBrowser_DocumentCompleted;
while (webBrowser.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents();
webBrowser.Dispose();
}
private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webBrowser = (WebBrowser)sender;
_Image = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
webBrowser.BringToFront();
webBrowser.DrawToBitmap(_Image, webBrowser.Bounds);
_Image = AutoCrop(_Image);
}
private static byte[][] GetRgb(Bitmap bmp)
{
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
var ptr = bmpData.Scan0;
var numPixels = bmp.Width * bmp.Height;
var numBytes = bmpData.Stride * bmp.Height;
var padding = bmpData.Stride - bmp.Width * 3;
var i = 0;
var ct = 1;
var r = new byte[numPixels];
var g = new byte[numPixels];
var b = new byte[numPixels];
var rgb = new byte[numBytes];
Marshal.Copy(ptr, rgb, 0, numBytes);
for (var x = 0; x < numBytes - 3; x += 3)
{
if (x == (bmpData.Stride * ct - padding))
{
x += padding;
ct++;
}
r[i] = rgb[x];
g[i] = rgb[x + 1];
b[i] = rgb[x + 2]; i++;
}
bmp.UnlockBits(bmpData);
return new[] { r, g, b };
}
private static Bitmap AutoCrop(Bitmap bmp)
{
// Get an array containing the R, G, B components of each pixel
var pixels = GetRgb(bmp);
var h = bmp.Height - 1;
var w = bmp.Width;
var top = 0;
var bottom = h;
var left = bmp.Width;
var right = 0;
var white = 0;
const int tolerance = 95;
var prevColor = false;
for (var i = 0; i < pixels[0].Length; i++)
{
int x = (i % (w)), y = (int)(Math.Floor((decimal)(i / w)));
const int tol = 255 * tolerance / 100;
if (pixels[0][i] >= tol && pixels[1][i] >= tol && pixels[2][i] >= tol)
{
white++;
right = (x > right && white == 1) ? x : right;
}
else
{
left = (x < left && white >= 1) ? x : left;
right = (x == w - 1 && white == 0) ? w - 1 : right;
white = 0;
}
if (white == w)
{
top = (y - top < 3) ? y : top;
bottom = (prevColor && x == w - 1 && y > top + 1) ? y : bottom;
}
left = (x == 0 && white == 0) ? 0 : left;
bottom = (y == h && x == w - 1 && white != w && prevColor) ? h + 1 : bottom;
if (x == w - 1)
{
prevColor = (white < w);
white = 0;
}
}
right = (right == 0) ? w : right;
left = (left == w) ? 0 : left;
// Cropy the image
if (bottom - top > 0)
{
return bmp.Clone(new Rectangle(left, top, right - left + 1, bottom - top), bmp.PixelFormat);
}
return bmp;
}
}
-
等等,您正在尝试将 HTML 呈现为图像,以嵌入到电子邮件中?为什么不将 HTML 作为电子邮件本身发送?每个现代电子邮件程序都会正确呈现它并将其显示给用户。
-
stackoverflow.com/a/60741246/14171304 ...但是为什么呢?如前所述发送 HTML。
-
@MindSwipe 不,不幸的是,我们的很多客户都使用 Outlook 电子邮件帐户。 Outlook 几乎不支持已有 10 年历史的 css/html 技术。我们想要使用的许多更好的样式在 Outlook 中不起作用。这个问题实际上是针对我们的用例的,因为我们公司发送给大量的老电子邮件用户。
标签: c# html image-processing bitmap html-rendering