Recursive AI

Convert HTML to PNG

All onerender recipes

Render HTML and CSS to a PNG in C# with OneRender: a 1200 by 630 social card with a gradient and a logo, and a badge on a transparent background at three times the pixel density.

C#
using OneRender.Image;
using OneRender.Painting;
 
string title = "Shipping weekly: 444 orders";
string html = $$"""
<!DOCTYPE html>
<html lang="en">
<style>
  body { margin: 0; width: 1200px; height: 630px; display: flex; flex-direction: column; justify-content: space-between;
         box-sizing: border-box; padding: 72px; font-family: sans-serif; color: #fff;
         background: linear-gradient(135deg, #16202c, #1d4ed8) }
  h1 { font-size: 64px; line-height: 1.1; margin: 0; max-width: 900px }
  footer { display: flex; align-items: center; gap: 20px; font-size: 28px; opacity: .85 }
</style>
<body>
  <h1>{{System.Net.WebUtility.HtmlEncode(title)}}</h1>
  <footer><img src="logo.png" width="64" height="64" alt=""> example.com/blog</footer>
</body>
</html>
""";
 
RgbaImage card = HtmlRender.ToImage(html, new HtmlRenderOptions
{
    Width = 1200,
    Height = 630,
    BaseUrl = "file:///cards/",
    Resources = url => url == "file:///cards/logo.png" ? File.ReadAllBytes("logo.png") : null,
});
File.WriteAllBytes("card.png", card.ToPng());
Console.WriteLine($"card.png: {card.Width}x{card.Height}");
 
// A badge on a transparent background, at 3x for high-density screens.
RgbaImage badge = HtmlRender.ToImage(
    "<body style='margin:0'><span style='display:inline-block;padding:4px 10px;border-radius:999px;background:#16a34a;color:#fff;font:600 14px sans-serif'>Passing</span>",
    new HtmlRenderOptions { Width = 80, Height = 27, DevicePixelRatio = 3, PageColor = PaintColor.Transparent });
File.WriteAllBytes("badge.png", badge.ToPng());
Console.WriteLine($"badge.png: {badge.Width}x{badge.Height}, corner alpha {badge.Pixels[3]}");
Text
card.png: 1200x630
badge.png: 240x81, corner alpha 0

The options

  • Width and Height are the viewport in CSS pixels, from 1 to 16,384. The image shows exactly the viewport; content below it is cut off. To capture a whole page, measure it first: see Replace a headless browser.
  • DevicePixelRatio scales the pixels, not the layout: the badge is laid out at 80 by 27 and drawn at 240 by 81.
  • PageColor is painted where the document paints no background. The default is opaque white; PaintColor.Transparent leaves those pixels clear.
  • Resources supplies images, style sheets and fonts by absolute address, and BaseUrl is what relative references resolve against. The engine fetches nothing itself.
  • AnimationTime shows CSS animations and transitions as they look that many milliseconds in. Without it, the page is drawn with no animation applied.

The image

RgbaImage holds Width * Height * 4 bytes of 8-bit RGBA in Pixels, row by row from the top left, ready to hand to another library or a texture upload. ToPng() encodes it.

Good to know

  • Images in the page are decoded by OneImage (PNG, JPEG, WebP, GIF, TIFF, BMP and ICO), and inline <svg> is drawn. An <img> pointing to an SVG file isn’t drawn in 1.0.0; inline the SVG instead.
  • Rendering is deterministic: the same HTML, resources and fonts give the same pixels on every machine, so snapshot tests can compare bytes.
  • Text uses the bundled fonts unless you add more: see Fonts.

The rasterizer and everything it draws are described in the overview and the display list.