From 6c9c962a2c6f9d8be365c1778a442b6e11fd568f Mon Sep 17 00:00:00 2001 From: Alfie Brimblecombe Date: Thu, 4 Apr 2024 20:51:08 +1000 Subject: [PATCH] automatically determine image height if one isn't provided --- .../SkiaSvgConverter.cs | 6 +- .../HtmlRenderer.SkiaSharp/ImageGenerator.cs | 93 +++++++++---------- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SkiaSvgConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/SkiaSvgConverter.cs index a9a897c19..eaf4b36fa 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/SkiaSvgConverter.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/SkiaSvgConverter.cs @@ -19,16 +19,18 @@ public SkiaSvgConverter(string sampleRunIdentifier, string basePath) : base(samp public async Task GenerateSampleAsync(HtmlSample sample) { var size = new SKSize(500, 1000); + int width = 500; + int height = 50; using (var fileStream = File.Open(GetSamplePath(sample, ".svg"), FileMode.CreateNew)) { - await ImageGenerator.GenerateSvgAsync(sample.Html, fileStream, size, imageLoad: OnImageLoaded); + await ImageGenerator.GenerateSvgAsync(sample.Html, fileStream, width, height, imageLoad: OnImageLoaded); fileStream.Flush(); } using (var fileStream = File.Open(GetSamplePath(sample, ".png"), FileMode.CreateNew)) { - await ImageGenerator.GenerateBitmapAsync(sample.Html, fileStream, size, SKEncodedImageFormat.Png, 100, imageLoad: OnImageLoaded); + await ImageGenerator.GenerateBitmapAsync(sample.Html, fileStream, SKEncodedImageFormat.Png, width, imageLoad: OnImageLoaded); fileStream.Flush(); } } diff --git a/Source/HtmlRenderer.SkiaSharp/ImageGenerator.cs b/Source/HtmlRenderer.SkiaSharp/ImageGenerator.cs index 2add0b57c..7cd653006 100644 --- a/Source/HtmlRenderer.SkiaSharp/ImageGenerator.cs +++ b/Source/HtmlRenderer.SkiaSharp/ImageGenerator.cs @@ -67,92 +67,89 @@ public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static async Task GenerateSvgAsync( + public static async Task GenerateSvgAsync( string html, Stream outputStream, - SKSize size, + int width, + int? height = null, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { - // create svg document to render the HTML into - var canvas = SKSvgCanvas.Create(new SKRect(0, 0, size.Width, size.Height), outputStream); - - // add rendered image - await DrawSvgAsync(canvas, html, size, cssData, stylesheetLoad, imageLoad); - canvas.Dispose(); - - return canvas; + using (var container = await CreateHtmlContainer(html, width, cssData, stylesheetLoad, imageLoad)) + { + var size = new SKSize(width, height ?? container.ActualSize.Height); + var canvas = SKSvgCanvas.Create(new SKRect(0, 0, width, height ?? container.ActualSize.Height), outputStream); + await DrawHtmlToCanvas(container, canvas); + canvas.Dispose(); + } } /// /// Writes html to a bitmap image /// /// HTML source to create image from - /// The size of the image /// The file format used to encode the image. /// The quality level to use for the image. Quality range from 0-100. Higher values correspond to improved visual quality, but less compression. /// optional: the style to use for html rendering (default - use W3 default style) /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// - public static async Task GenerateBitmapAsync( + public static async Task GenerateBitmapAsync( string html, Stream outputStream, - SKSize size, SKEncodedImageFormat imageFormat, - int quality, + int width, + int? height = null, + int quality = 100, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { + using (var container = await CreateHtmlContainer(html, width, cssData, stylesheetLoad, imageLoad)) + { + var bitmap = new SKBitmap(width, height ?? (int)container.ActualSize.Height); + var canvas = new SKCanvas(bitmap); + await DrawHtmlToCanvas(container, canvas); + bitmap.Encode(outputStream, imageFormat, quality); - var bitmap = new SKBitmap((int)size.Width, (int)size.Height); - var canvas = new SKCanvas(bitmap); - - // add rendered image - await DrawSvgAsync(canvas, html, size, cssData, stylesheetLoad, imageLoad); - bitmap.Encode(outputStream, imageFormat, quality); - - return canvas; + } } /// - /// Create image pages from given HTML and appends them to the provided image document.
+ /// Creates a html container for the suupplied content. Given a width, this will + /// determine the actual height. ///
- /// canvas to draw to - /// HTML source to create image from - /// optional: the style to use for html rendering (default - use W3 default style) - /// optional: can be used to overwrite stylesheet resolution logic - /// optional: can be used to overwrite image resolution logic - /// the generated image of the html - public static async Task DrawSvgAsync( - SKCanvas canvas, - string html, - SKSize size, - CssData cssData = null, - EventHandler stylesheetLoad = null, - EventHandler imageLoad = null) + private static async Task CreateHtmlContainer(string html, + int width, + CssData? cssData = null, + EventHandler? stylesheetLoad = null, + EventHandler? imageLoad = null) { - using var container = new HtmlContainer(); + var container = new HtmlContainer(); + if (stylesheetLoad != null) container.StylesheetLoad += stylesheetLoad; if (imageLoad != null) container.ImageLoad += imageLoad; - container.Location = new SKPoint(0, 0); - //container.MaxSize = size; - container.MaxSize = new SKSize(size.Width, 0); - container.PageSize = size; - container.MarginBottom = 0; - container.MarginLeft = 0; - container.MarginRight = 0; - container.MarginTop = 0; - container.ScrollOffset = new SKPoint(0, 0); + container.MaxSize = new SKSize(width, 0); + await container.SetHtml(html, cssData!); + + // determine the actual height of the html we're rendering. + var docImageInfo = new SKImageInfo(width, width); + using (var s = SKSurface.Create(docImageInfo)) + using (var g = s.Canvas) + { + await container.PerformLayout(g); + } - await container.SetHtml(html, cssData); + container.PageSize = container.ActualSize; + return container; + } - // layout the HTML with the page width restriction to know how many pages are required + private static async Task DrawHtmlToCanvas(HtmlContainer container, SKCanvas canvas) + { await container.PerformLayout(canvas); await container.PerformPaint(canvas); }