// "Therefore those skilled at the unorthodox // are infinite as heaven and earth, // inexhaustible as the great rivers. // When they come to an end, // they begin again, // like the days and months; // they die and are reborn, // like the four seasons." // // - Sun Tsu, // "The Art of War" using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; using TheArtOfDev.HtmlRenderer.WinForms.Adapters; using TheArtOfDev.HtmlRenderer.WinForms.Utilities; namespace TheArtOfDev.HtmlRenderer.WinForms { /// /// Standalone static class for simple and direct HTML rendering.
/// For WinForms UI prefer using HTML controls: or .
/// For low-level control and performance consider using .
///
/// /// /// GDI vs. GDI+ text rendering
/// Windows supports two text rendering technologies: GDI and GDI+.
/// GDI is older, has better performance and looks better on standard monitors but doesn't support alpha channel for transparency.
/// GDI+ is newer, device independent so work better for printers but is slower and looks worse on monitors.
/// HtmlRender supports both GDI and GDI+ text rendering to accommodate different needs, GDI+ text rendering methods have "GdiPlus" suffix /// in their name where GDI do not.
///
/// /// Rendering to image
/// See https://htmlrenderer.codeplex.com/wikipage?title=Image%20generation
/// Because of GDI text rendering issue with alpha channel clear type text rendering rendering to image requires special handling.
/// Solid color background - generate an image where the background is filled with solid color and all the html is rendered on top /// of the background color, GDI text rendering will be used. (RenderToImage method where the first argument is html string)
/// Image background - render html on top of existing image with whatever currently exist but it cannot have transparent pixels, /// GDI text rendering will be used. (RenderToImage method where the first argument is Image object)
/// Transparent background - render html to empty image using GDI+ text rendering, the generated image can be transparent. /// Text rendering can be controlled using , note that /// doesn't render well on transparent background. (RenderToImageGdiPlus method)
///
/// /// Overwrite stylesheet resolution
/// Exposed by optional "stylesheetLoad" delegate argument.
/// Invoked when a stylesheet is about to be loaded by file path or URL in 'link' element.
/// Allows to overwrite the loaded stylesheet by providing the stylesheet data manually, or different source (file or URL) to load from.
/// Example: The stylesheet 'href' can be non-valid URI string that is interpreted in the overwrite delegate by custom logic to pre-loaded stylesheet object
/// If no alternative data is provided the original source will be used.
///
/// /// Overwrite image resolution
/// Exposed by optional "imageLoad" delegate argument.
/// Invoked when an image is about to be loaded by file path, URL or inline data in 'img' element or background-image CSS style.
/// Allows to overwrite the loaded image by providing the image object manually, or different source (file or URL) to load from.
/// Example: image 'src' can be non-valid string that is interpreted in the overwrite delegate by custom logic to resource image object
/// Example: image 'src' in the html is relative - the overwrite intercepts the load and provide full source URL to load the image from
/// Example: image download requires authentication - the overwrite intercepts the load, downloads the image to disk using custom code and provide /// file path to load the image from.
/// If no alternative data is provided the original source will be used.
/// Note: Cannot use asynchronous scheme overwrite scheme.
///
///
/// /// /// Simple rendering
/// HtmlRender.Render(g, "Hello World]]>");
/// HtmlRender.Render(g, "Hello World]]>", 10, 10, 500, CssData.Parse("body {font-size: 20px}")");
///
/// /// Image rendering
/// HtmlRender.RenderToImage("Hello World]]>", new Size(600,400));
/// HtmlRender.RenderToImage("Hello World]]>", 600);
/// HtmlRender.RenderToImage(existingImage, "Hello World]]>");
///
///
public static class HtmlRender { /// /// Adds a font family to be used in html rendering.
/// The added font will be used by all rendering function including and all WinForms controls. ///
/// /// The given font family instance must be remain alive while the renderer is in use.
/// If loaded to then the collection must be alive.
/// If loaded from file then the file must not be deleted. ///
/// The font family to add. public static void AddFontFamily(FontFamily fontFamily) { ArgChecker.AssertArgNotNull(fontFamily, "fontFamily"); WinFormsAdapter.Instance.AddFontFamily(new FontFamilyAdapter(fontFamily)); } /// /// Adds a font mapping from to iff the is not found.
/// When the font is used in rendered html and is not found in existing /// fonts (installed or added) it will be replaced by .
///
/// /// This fonts mapping can be used as a fallback in case the requested font is not installed in the client system. /// /// the font family to replace /// the font family to replace with public static void AddFontFamilyMapping(string fromFamily, string toFamily) { ArgChecker.AssertArgNotNullOrEmpty(fromFamily, "fromFamily"); ArgChecker.AssertArgNotNullOrEmpty(toFamily, "toFamily"); WinFormsAdapter.Instance.AddFontFamilyMapping(fromFamily, toFamily); } /// /// Parse the given stylesheet to object.
/// If is true the parsed css blocks are added to the /// default css data (as defined by W3), merged if class name already exists. If false only the data in the given stylesheet is returned. ///
/// the stylesheet source to parse /// true - combine the parsed css data with default css data, false - return only the parsed css data /// the parsed css data public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault = true) { return CssData.Parse(WinFormsAdapter.Instance, stylesheet, combineWithDefault); } #if !MONO /// /// Measure the size (width and height) required to draw the given html under given max width restriction.
/// If no max width restriction is given the layout will use the maximum possible width required by the content, /// it can be the longest text line or full image width.
/// Use GDI text rendering, note has no effect. ///
/// Device to use for measure /// HTML source to render /// optional: bound the width of the html to render in (default - 0, unlimited) /// 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 size required for the html public static SizeF Measure(Graphics g, string html, float maxWidth = 0, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(g, "g"); return Measure(g, html, maxWidth, cssData, false, stylesheetLoad, imageLoad); } #endif /// /// Measure the size (width and height) required to draw the given html under given max width restriction.
/// If no max width restriction is given the layout will use the maximum possible width required by the content, /// it can be the longest text line or full image width.
/// Use GDI+ text rending, use to control text rendering. ///
/// Device to use for measure /// HTML source to render /// optional: bound the width of the html to render in (default - 0, unlimited) /// 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 size required for the html public static SizeF MeasureGdiPlus(Graphics g, string html, float maxWidth = 0, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(g, "g"); return Measure(g, html, maxWidth, cssData, true, stylesheetLoad, imageLoad); } #if !MONO /// /// Renders the specified HTML source on the specified location and max width restriction.
/// Use GDI text rendering, note has no effect.
/// If is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// Returned is the actual width and height of the rendered html.
///
/// Device to render with /// HTML source to render /// optional: the left most location to start render the html at (default - 0) /// optional: the top most location to start render the html at (default - 0) /// optional: bound the width of the html to render in (default - 0, unlimited) /// 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 actual size of the rendered html public static SizeF Render(Graphics g, string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(g, "g"); return RenderClip(g, html, new PointF(left, top), new SizeF(maxWidth, 0), cssData, false, stylesheetLoad, imageLoad); } /// /// Renders the specified HTML source on the specified location and max size restriction.
/// Use GDI text rendering, note has no effect.
/// If .Width is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If .Height is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// Returned is the actual width and height of the rendered html.
///
/// Device to render with /// HTML source to render /// the top-left most location to start render the html at /// the max size of the rendered html (if height above zero it will be clipped) /// 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 actual size of the rendered html public static SizeF Render(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(g, "g"); return RenderClip(g, html, location, maxSize, cssData, false, stylesheetLoad, imageLoad); } #endif /// /// Renders the specified HTML source on the specified location and max size restriction.
/// Use GDI+ text rending, use to control text rendering.
/// If is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// Returned is the actual width and height of the rendered html.
///
/// Device to render with /// HTML source to render /// optional: the left most location to start render the html at (default - 0) /// optional: the top most location to start render the html at (default - 0) /// optional: bound the width of the html to render in (default - 0, unlimited) /// 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 actual size of the rendered html public static SizeF RenderGdiPlus(Graphics g, string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(g, "g"); return RenderClip(g, html, new PointF(left, top), new SizeF(maxWidth, 0), cssData, true, stylesheetLoad, imageLoad); } /// /// Renders the specified HTML source on the specified location and max size restriction.
/// Use GDI+ text rending, use to control text rendering.
/// If .Width is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If .Height is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// Returned is the actual width and height of the rendered html.
///
/// Device to render with /// HTML source to render /// the top-left most location to start render the html at /// the max size of the rendered html (if height above zero it will be clipped) /// 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 actual size of the rendered html public static SizeF RenderGdiPlus(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(g, "g"); return RenderClip(g, html, location, maxSize, cssData, true, stylesheetLoad, imageLoad); } #if !MONO /// /// Renders the specified HTML on top of the given image.
/// will contain the rendered html in it on top of original content.
/// must not contain transparent pixels as it will corrupt the rendered html text.
/// The HTML will be layout by the given image size but may be clipped if cannot fit.
/// See "Rendering to image" remarks section on .
///
/// the image to render the html on /// HTML source to render /// optional: the top-left most location to start render the html at (default - 0,0) /// 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 void RenderToImage(Image image, string html, PointF location = new PointF(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(image, "image"); var maxSize = new SizeF(image.Size.Width - location.X, image.Size.Height - location.Y); RenderToImage(image, html, location, maxSize, cssData, stylesheetLoad, imageLoad); } /// /// Renders the specified HTML on top of the given image.
/// will contain the rendered html in it on top of original content.
/// must not contain transparent pixels as it will corrupt the rendered html text.
/// See "Rendering to image" remarks section on .
///
/// the image to render the html on /// HTML source to render /// the top-left most location to start render the html at /// the max size of the rendered html (if height above zero it will be clipped) /// 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 void RenderToImage(Image image, string html, PointF location, SizeF maxSize, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { ArgChecker.AssertArgNotNull(image, "image"); if (!string.IsNullOrEmpty(html)) { // create memory buffer from desktop handle that supports alpha channel IntPtr dib; var memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib); try { // create memory buffer graphics to use for HTML rendering using (var memoryGraphics = Graphics.FromHdc(memoryHdc)) { // draw the image to the memory buffer to be the background of the rendered html memoryGraphics.DrawImageUnscaled(image, 0, 0); // render HTML into the memory buffer RenderHtml(memoryGraphics, html, location, maxSize, cssData, false, stylesheetLoad, imageLoad); } // copy from memory buffer to image CopyBufferToImage(memoryHdc, image); } finally { Win32Utils.ReleaseMemoryHdc(memoryHdc, dib); } } } /// /// Renders the specified HTML into a new image of the requested size.
/// The HTML will be layout by the given size but will be clipped if cannot fit.
///

/// Limitation: The image cannot have transparent background, by default it will be white.
/// See "Rendering to image" remarks section on .
///

///
/// HTML source to render /// The size of the image to render into, layout html by width and clipped by height /// optional: the color to fill the image with (default - white) /// 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 /// if is . public static Image RenderToImage(string html, Size size, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { if (backgroundColor == Color.Transparent) throw new ArgumentOutOfRangeException("backgroundColor", "Transparent background in not supported"); // create the final image to render into var image = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb); if (!string.IsNullOrEmpty(html)) { // create memory buffer from desktop handle that supports alpha channel IntPtr dib; var memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib); try { // create memory buffer graphics to use for HTML rendering using (var memoryGraphics = Graphics.FromHdc(memoryHdc)) { memoryGraphics.Clear(backgroundColor != Color.Empty ? backgroundColor : Color.White); // render HTML into the memory buffer RenderHtml(memoryGraphics, html, PointF.Empty, size, cssData, true, stylesheetLoad, imageLoad); } // copy from memory buffer to image CopyBufferToImage(memoryHdc, image); } finally { Win32Utils.ReleaseMemoryHdc(memoryHdc, dib); } } return image; } /// /// Renders the specified HTML into a new image of unknown size that will be determined by max width/height and HTML layout.
/// If is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
///

/// Limitation: The image cannot have transparent background, by default it will be white.
/// See "Rendering to image" remarks section on .
///

///
/// HTML source to render /// optional: the max width of the rendered html, if not zero and html cannot be layout within the limit it will be clipped /// optional: the max height of the rendered html, if not zero and html cannot be layout within the limit it will be clipped /// optional: the color to fill the image with (default - white) /// 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 /// if is . public static Image RenderToImage(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { return RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); } /// /// Renders the specified HTML into a new image of unknown size that will be determined by min/max width/height and HTML layout.
/// If is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// If (Width/Height) is above zero the rendered image will not be smaller than the given min size.
///

/// Limitation: The image cannot have transparent background, by default it will be white.
/// See "Rendering to image" remarks section on .
///

///
/// HTML source to render /// optional: the min size of the rendered html (zero - not limit the width/height) /// optional: the max size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height) /// optional: the color to fill the image with (default - white) /// 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 /// if is . public static Image RenderToImage(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { if (backgroundColor == Color.Transparent) throw new ArgumentOutOfRangeException("backgroundColor", "Transparent background in not supported"); if (string.IsNullOrEmpty(html)) return new Bitmap(0, 0, PixelFormat.Format32bppArgb); using (var container = new HtmlContainer()) { container.AvoidAsyncImagesLoading = true; container.AvoidImagesLateLoading = true; if (stylesheetLoad != null) container.StylesheetLoad += stylesheetLoad; if (imageLoad != null) container.ImageLoad += imageLoad; container.SetHtml(html, cssData); var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); container.MaxSize = finalSize; // create the final image to render into by measured size var image = new Bitmap(finalSize.Width, finalSize.Height, PixelFormat.Format32bppArgb); // create memory buffer from desktop handle that supports alpha channel IntPtr dib; var memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib); try { // render HTML into the memory buffer using (var memoryGraphics = Graphics.FromHdc(memoryHdc)) { memoryGraphics.Clear(backgroundColor != Color.Empty ? backgroundColor : Color.White); container.PerformPaint(memoryGraphics); } // copy from memory buffer to image CopyBufferToImage(memoryHdc, image); } finally { Win32Utils.ReleaseMemoryHdc(memoryHdc, dib); } return image; } } #endif /// /// Renders the specified HTML into a new image of the requested size.
/// The HTML will be layout by the given size but will be clipped if cannot fit.
/// The generated image have transparent background that the html is rendered on.
/// GDI+ text rending can be controlled by providing .
/// See "Rendering to image" remarks section on .
///
/// HTML source to render /// The size of the image to render into, layout html by width and clipped by height /// optional: (default - SingleBitPerPixelGridFit) /// 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 Image RenderToImageGdiPlus(string html, Size size, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { var image = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb); using (var g = Graphics.FromImage(image)) { g.TextRenderingHint = textRenderingHint; RenderHtml(g, html, PointF.Empty, size, cssData, true, stylesheetLoad, imageLoad); } return image; } /// /// Renders the specified HTML into a new image of unknown size that will be determined by max width/height and HTML layout.
/// If is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// The generated image have transparent background that the html is rendered on.
/// GDI+ text rending can be controlled by providing .
/// See "Rendering to image" remarks section on .
///
/// HTML source to render /// optional: the max width of the rendered html, if not zero and html cannot be layout within the limit it will be clipped /// optional: the max height of the rendered html, if not zero and html cannot be layout within the limit it will be clipped /// optional: (default - SingleBitPerPixelGridFit) /// 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 Image RenderToImageGdiPlus(string html, int maxWidth = 0, int maxHeight = 0, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { return RenderToImageGdiPlus(html, Size.Empty, new Size(maxWidth, maxHeight), textRenderingHint, cssData, stylesheetLoad, imageLoad); } /// /// Renders the specified HTML into a new image of unknown size that will be determined by min/max width/height and HTML layout.
/// If is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// If (Width/Height) is above zero the rendered image will not be smaller than the given min size.
/// The generated image have transparent background that the html is rendered on.
/// GDI+ text rending can be controlled by providing .
/// See "Rendering to image" remarks section on .
///
/// HTML source to render /// optional: the min size of the rendered html (zero - not limit the width/height) /// optional: the max size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height) /// optional: (default - SingleBitPerPixelGridFit) /// 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 Image RenderToImageGdiPlus(string html, Size minSize, Size maxSize, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { if (string.IsNullOrEmpty(html)) return new Bitmap(0, 0, PixelFormat.Format32bppArgb); using (var container = new HtmlContainer()) { container.AvoidAsyncImagesLoading = true; container.AvoidImagesLateLoading = true; container.UseGdiPlusTextRendering = true; if (stylesheetLoad != null) container.StylesheetLoad += stylesheetLoad; if (imageLoad != null) container.ImageLoad += imageLoad; container.SetHtml(html, cssData); var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); container.MaxSize = finalSize; // create the final image to render into by measured size var image = new Bitmap(finalSize.Width, finalSize.Height, PixelFormat.Format32bppArgb); // render HTML into the image using (var g = Graphics.FromImage(image)) { g.TextRenderingHint = textRenderingHint; container.PerformPaint(g); } return image; } } #region Private methods /// /// Measure the size (width and height) required to draw the given html under given width and height restrictions.
///
/// Device to use for measure /// HTML source to render /// optional: bound the width of the html to render in (default - 0, unlimited) /// optional: the style to use for html rendering (default - use W3 default style) /// true - use GDI+ text rendering, false - use GDI text rendering /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the size required for the html private static SizeF Measure(Graphics g, string html, float maxWidth, CssData cssData, bool useGdiPlusTextRendering, EventHandler stylesheetLoad, EventHandler imageLoad) { SizeF actualSize = SizeF.Empty; if (!string.IsNullOrEmpty(html)) { using (var container = new HtmlContainer()) { container.MaxSize = new SizeF(maxWidth, 0); container.AvoidAsyncImagesLoading = true; container.AvoidImagesLateLoading = true; container.UseGdiPlusTextRendering = useGdiPlusTextRendering; if (stylesheetLoad != null) container.StylesheetLoad += stylesheetLoad; if (imageLoad != null) container.ImageLoad += imageLoad; container.SetHtml(html, cssData); container.PerformLayout(g); actualSize = container.ActualSize; } } return actualSize; } /// /// Measure the size of the html by performing layout under the given restrictions. /// /// the html to calculate the layout for /// the minimal size of the rendered html (zero - not limit the width/height) /// the maximum size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height) /// return: the size of the html to be rendered within the min/max limits private static Size MeasureHtmlByRestrictions(HtmlContainer htmlContainer, Size minSize, Size maxSize) { // use desktop created graphics to measure the HTML using (var g = Graphics.FromHwnd(IntPtr.Zero)) using (var mg = new GraphicsAdapter(g, htmlContainer.UseGdiPlusTextRendering)) { var sizeInt = HtmlRendererUtils.MeasureHtmlByRestrictions(mg, htmlContainer.HtmlContainerInt, Utils.Convert(minSize), Utils.Convert(maxSize)); return Utils.ConvertRound(sizeInt); } } /// /// Renders the specified HTML source on the specified location and max size restriction.
/// If .Width is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If .Height is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// Clip the graphics so the html will not be rendered outside the max height bound given.
/// Returned is the actual width and height of the rendered html.
///
/// Device to render with /// HTML source to render /// the top-left most location to start render the html at /// the max size of the rendered html (if height above zero it will be clipped) /// optional: the style to use for html rendering (default - use W3 default style) /// true - use GDI+ text rendering, false - use GDI text rendering /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html private static SizeF RenderClip(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData, bool useGdiPlusTextRendering, EventHandler stylesheetLoad, EventHandler imageLoad) { Region prevClip = null; if (maxSize.Height > 0) { prevClip = g.Clip; g.SetClip(new RectangleF(location, maxSize)); } var actualSize = RenderHtml(g, html, location, maxSize, cssData, useGdiPlusTextRendering, stylesheetLoad, imageLoad); if (prevClip != null) { g.SetClip(prevClip, CombineMode.Replace); } return actualSize; } /// /// Renders the specified HTML source on the specified location and max size restriction.
/// If .Width is zero the html will use all the required width, otherwise it will perform line /// wrap as specified in the html
/// If .Height is zero the html will use all the required height, otherwise it will clip at the /// given max height not rendering the html below it.
/// Returned is the actual width and height of the rendered html.
///
/// Device to render with /// HTML source to render /// the top-left most location to start render the html at /// the max size of the rendered html (if height above zero it will be clipped) /// optional: the style to use for html rendering (default - use W3 default style) /// true - use GDI+ text rendering, false - use GDI text rendering /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html private static SizeF RenderHtml(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData, bool useGdiPlusTextRendering, EventHandler stylesheetLoad, EventHandler imageLoad) { SizeF actualSize = SizeF.Empty; if (!string.IsNullOrEmpty(html)) { using (var container = new HtmlContainer()) { container.Location = location; container.MaxSize = maxSize; container.AvoidAsyncImagesLoading = true; container.AvoidImagesLateLoading = true; container.UseGdiPlusTextRendering = useGdiPlusTextRendering; if (stylesheetLoad != null) container.StylesheetLoad += stylesheetLoad; if (imageLoad != null) container.ImageLoad += imageLoad; container.SetHtml(html, cssData); container.PerformLayout(g); container.PerformPaint(g); actualSize = container.ActualSize; } } return actualSize; } #if !MONO /// /// Copy all the bitmap bits from memory bitmap buffer to the given image. /// /// the source memory bitmap buffer to copy from /// the destination bitmap image to copy to private static void CopyBufferToImage(IntPtr memoryHdc, Image image) { using (var imageGraphics = Graphics.FromImage(image)) { var imgHdc = imageGraphics.GetHdc(); Win32Utils.BitBlt(imgHdc, 0, 0, image.Width, image.Height, memoryHdc, 0, 0, Win32Utils.BitBltCopy); imageGraphics.ReleaseHdc(imgHdc); } } #endif #endregion } }