Browse Source

Various html renderer improvements

pull/136/head
Nikita Tsukanov 11 years ago
parent
commit
18c0d3c655
  1. 1
      samples/TestApplication/Program.cs
  2. 21
      src/Perspex.HtmlRenderer/Adapters/ControlAdapter.cs
  3. 5
      src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs
  4. 2
      src/Perspex.HtmlRenderer/Adapters/ImageAdapter.cs
  5. 33
      src/Perspex.HtmlRenderer/HtmlControl.cs
  6. 7
      src/Perspex.HtmlRenderer/HtmlLabel.cs
  7. 424
      src/Perspex.HtmlRenderer/HtmlRender.cs
  8. 1
      src/Perspex.HtmlRenderer/Perspex.HtmlRenderer.csproj

1
samples/TestApplication/Program.cs

@ -378,6 +378,7 @@ namespace TestApplication
Content = new ScrollViewer()
{
Width = 600,
MaxHeight = 600,
HorizontalAlignment = HorizontalAlignment.Center,
CanScrollHorizontally = false,
VerticalScrollBarVisibility = ScrollBarVisibility.Visible,

21
src/Perspex.HtmlRenderer/Adapters/ControlAdapter.cs

@ -39,6 +39,8 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
ArgChecker.AssertArgNotNull(control, "control");
_control = control;
_control.PointerPressed += delegate { _leftMouseButton = true; };
_control.PointerPressed += delegate { _leftMouseButton = false; };
}
/// <summary>
@ -53,28 +55,19 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
{
get
{
//TODO: Implement
//return Utils.Convert(_control.PointFromScreen(Mouse.GetPosition(_control)));
return new RPoint(0, 0);
return Util.Convert(MouseDevice.Instance.GetPosition(_control));
}
}
public override bool LeftMouseButton
{
get
{
return false;
//TODO: Implement
//return Mouse.LeftButton == MouseButtonState.Pressed;
}
}
private bool _leftMouseButton;
public override bool LeftMouseButton => _leftMouseButton;
public override bool RightMouseButton
{
get
{
return false;
//TODO: Implement
//TODO: Implement right mouse click
//return Mouse.RightButton == MouseButtonState.Pressed;
}
}
@ -96,7 +89,7 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
public override void DoDragDropCopy(object dragDropData)
{
//TODO: Implement
//TODO: Implement DragDropCopy
//DragDrop.DoDragDrop(_control, dragDropData, DragDropEffects.Copy);
}

5
src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs

@ -205,9 +205,8 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
public override void Dispose()
{
//TODO: Do something about Dispose
//if (_releaseGraphics)
// _g.Close();
if (_releaseGraphics)
_g.Dispose();
}

2
src/Perspex.HtmlRenderer/Adapters/ImageAdapter.cs

@ -44,7 +44,7 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
public override void Dispose()
{
//TODO: Implement
//TODO: Implement image disposal
/*if (_image.StreamSource != null)
_image.StreamSource.Dispose();*/
}

33
src/Perspex.HtmlRenderer/HtmlControl.cs

@ -19,6 +19,7 @@ using Perspex.Media;
using Perspex.Threading;
using TheArtOfDev.HtmlRenderer.Core;
using TheArtOfDev.HtmlRenderer.Core.Entities;
using TheArtOfDev.HtmlRenderer.Perspex.Adapters;
namespace Perspex.Controls
{
@ -91,11 +92,14 @@ namespace Perspex.Controls
PropertyHelper.Register<HtmlControl, string>("Text", null, OnPerspexProperty_valueChanged);
public static readonly PerspexProperty BackgroundProperty =
PerspexProperty.Register<HtmlControl, Color>("Background", Colors.White);
PerspexProperty.Register<HtmlControl, Brush>("Background", Brushes.White);
public static readonly PerspexProperty BorderThicknessProperty =
PerspexProperty.Register<HtmlControl, Thickness>("BorderThickness", new Thickness(0));
public static readonly PerspexProperty BorderBrushProperty =
PerspexProperty.Register<HtmlControl, Brush>("BorderBrush");
public static readonly PerspexProperty PaddingProperty =
PerspexProperty.Register<HtmlControl, Thickness>("Padding", new Thickness(0));
@ -252,15 +256,21 @@ namespace Perspex.Controls
set { SetValue(BorderThicknessProperty, value); }
}
public Brush BorderBrush
{
get { return (Brush)GetValue(BorderBrushProperty); }
set { SetValue(BorderThicknessProperty, value); }
}
public Thickness Padding
{
get { return (Thickness)GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}
public Color Background
public Brush Background
{
get { return (Color?) GetValue(BackgroundProperty) ?? Colors.White; }
get { return (Brush) GetValue(BackgroundProperty); }
set { SetValue(BackgroundProperty, value);}
}
@ -321,15 +331,10 @@ namespace Perspex.Controls
public override void Render(IDrawingContext context)
{
//TODO: Use actual background
context.FillRectange(new SolidColorBrush(Colors.White), new Rect(RenderSize));
/*if (Background.Opacity > 0)
context.DrawRectangle(Background, null, new Rect(RenderSize));
*/
context.FillRectange(Background, new Rect(RenderSize));
if (BorderThickness != new Thickness(0))
if (BorderThickness != new Thickness(0) && BorderBrush != null)
{
//TODO: Get default border brush from theme or something
var brush = new SolidColorBrush(Colors.Black);
if (BorderThickness.Top > 0)
context.FillRectange(brush, new Rect(0, 0, RenderSize.Width, BorderThickness.Top));
@ -346,7 +351,7 @@ namespace Perspex.Controls
if (_htmlContainer != null && htmlWidth > 0 && htmlHeight > 0)
{
/*
//TODO: Rever antialiasing fixes
//TODO: Revert antialiasing fixes
var windows = Window.GetWindow(this);
if (windows != null)
{
@ -514,8 +519,8 @@ namespace Perspex.Controls
/// </summary>
protected virtual void InvokeMouseMove()
{
_htmlContainer.HandleMouseMove(this, MouseDevice.Instance.GetPosition(this));
_htmlContainer.HandleMouseMove(this, MouseDevice.Instance?.GetPosition(this) ?? default(Point));
}
/// <summary>
@ -541,7 +546,7 @@ namespace Perspex.Controls
}
else if (e.Property == BaseStylesheetProperty)
{
var baseCssData = HtmlRender.ParseStyleSheet((string)e.NewValue);
var baseCssData = CssData.Parse(PerspexAdapter.Instance, (string)e.NewValue);
control._baseCssData = baseCssData;
htmlContainer.SetHtml(control.Text, baseCssData);
}

7
src/Perspex.HtmlRenderer/HtmlLabel.cs

@ -45,7 +45,7 @@ namespace Perspex.Controls
/// </summary>
static HtmlLabel()
{
//BackgroundProperty.OverrideDefaultValue<HtmlLabel>(Brushes.Transparent);
BackgroundProperty.OverrideDefaultValue<HtmlLabel>(Brushes.Transparent);
}
#region Private methods
@ -62,9 +62,8 @@ namespace Perspex.Controls
var horizontal = Padding.Left + Padding.Right + BorderThickness.Left + BorderThickness.Right;
var vertical = Padding.Top + Padding.Bottom + BorderThickness.Top + BorderThickness.Bottom;
var size = new RSize(constraint.Width < Double.PositiveInfinity ? constraint.Width - horizontal : 0, constraint.Height < Double.PositiveInfinity ? constraint.Height - vertical : 0);
//var minSize = new RSize(MinWidth < Double.PositiveInfinity ? MinWidth - horizontal : 0, MinHeight < Double.PositiveInfinity ? MinHeight - vertical : 0);
var maxSize = new RSize(MaxWidth < Double.PositiveInfinity ? MaxWidth - horizontal : 0, MaxHeight < Double.PositiveInfinity ? MaxHeight - vertical : 0);
var size = new Size(Math.Min(MaxWidth, constraint.Width), Math.Min(MaxHeight, constraint.Height));
var maxSize = new RSize(size.Width < Double.PositiveInfinity ? size.Width - horizontal : 0, size.Height < Double.PositiveInfinity ? size.Height - vertical : 0);
_htmlContainer.HtmlContainerInt.MaxSize = maxSize;
_htmlContainer.HtmlContainerInt.PerformLayout(ig);

424
src/Perspex.HtmlRenderer/HtmlRender.cs

@ -1,424 +0,0 @@
// "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 Perspex.Media;
using Perspex.Media.Imaging;
using TheArtOfDev.HtmlRenderer.Core;
using TheArtOfDev.HtmlRenderer.Core.Entities;
using TheArtOfDev.HtmlRenderer.Core.Utils;
using TheArtOfDev.HtmlRenderer.Perspex.Adapters;
namespace Perspex.Controls
{
/// <summary>
/// Standalone static class for simple and direct HTML rendering.<br/>
/// For Perspex UI prefer using HTML controls: <see cref="HtmlPanel"/> or <see cref="HtmlLabel"/>.<br/>
/// For low-level control and performance consider using <see cref="HtmlContainer"/>.<br/>
/// </summary>
/// <remarks>
/// <para>
/// <b>Rendering to image</b><br/>
/// // TODO:a update!
/// See https://htmlrenderer.codeplex.com/wikipage?title=Image%20generation <br/>
/// Because of GDI text rendering issue with alpha channel clear type text rendering rendering to image requires special handling.<br/>
/// <u>Solid color background -</u> 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)<br/>
/// <u>Image background -</u> 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)<br/>
/// <u>Transparent background -</u> render html to empty image using GDI+ text rendering, the generated image can be transparent.
/// </para>
/// <para>
/// <b>Overwrite stylesheet resolution</b><br/>
/// Exposed by optional "stylesheetLoad" delegate argument.<br/>
/// Invoked when a stylesheet is about to be loaded by file path or URL in 'link' element.<br/>
/// Allows to overwrite the loaded stylesheet by providing the stylesheet data manually, or different source (file or URL) to load from.<br/>
/// 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<br/>
/// If no alternative data is provided the original source will be used.<br/>
/// </para>
/// <para>
/// <b>Overwrite image resolution</b><br/>
/// Exposed by optional "imageLoad" delegate argument.<br/>
/// Invoked when an image is about to be loaded by file path, URL or inline data in 'img' element or background-image CSS style.<br/>
/// Allows to overwrite the loaded image by providing the image object manually, or different source (file or URL) to load from.<br/>
/// Example: image 'src' can be non-valid string that is interpreted in the overwrite delegate by custom logic to resource image object<br/>
/// Example: image 'src' in the html is relative - the overwrite intercepts the load and provide full source URL to load the image from<br/>
/// 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.<br/>
/// If no alternative data is provided the original source will be used.<br/>
/// Note: Cannot use asynchronous scheme overwrite scheme.<br/>
/// </para>
/// </remarks>
/// <example>
/// <para>
/// <b>Simple rendering</b><br/>
/// HtmlRender.Render(g, "<![CDATA[<div>Hello <b>World</b></div>]]>");<br/>
/// HtmlRender.Render(g, "<![CDATA[<div>Hello <b>World</b></div>]]>", 10, 10, 500, CssData.Parse("body {font-size: 20px}")");<br/>
/// </para>
/// <para>
/// <b>Image rendering</b><br/>
/// HtmlRender.RenderToImage("<![CDATA[<div>Hello <b>World</b></div>]]>", new Size(600,400));<br/>
/// HtmlRender.RenderToImage("<![CDATA[<div>Hello <b>World</b></div>]]>", 600);<br/>
/// HtmlRender.RenderToImage(existingImage, "<![CDATA[<div>Hello <b>World</b></div>]]>");<br/>
/// </para>
/// </example>
public static class HtmlRender
{
/// <summary>
/// Adds a font family to be used in html rendering.<br/>
/// The added font will be used by all rendering function including <see cref="HtmlContainer"/> and all Perspex controls.
/// </summary>
/// <remarks>
/// The given font family instance must be remain alive while the renderer is in use.<br/>
/// If loaded from file then the file must not be deleted.
/// </remarks>
/// <param name="fontFamily">The font family to add.</param>
public static void AddFontFamily(string fontFamily)
{
ArgChecker.AssertArgNotNull(fontFamily, "fontFamily");
PerspexAdapter.Instance.AddFontFamily(new FontFamilyAdapter(fontFamily));
}
/// <summary>
/// Adds a font mapping from <paramref name="fromFamily"/> to <paramref name="toFamily"/> iff the <paramref name="fromFamily"/> is not found.<br/>
/// When the <paramref name="fromFamily"/> font is used in rendered html and is not found in existing
/// fonts (installed or added) it will be replaced by <paramref name="toFamily"/>.<br/>
/// </summary>
/// <remarks>
/// This fonts mapping can be used as a fallback in case the requested font is not installed in the client system.
/// </remarks>
/// <param name="fromFamily">the font family to replace</param>
/// <param name="toFamily">the font family to replace with</param>
public static void AddFontFamilyMapping(string fromFamily, string toFamily)
{
ArgChecker.AssertArgNotNullOrEmpty(fromFamily, "fromFamily");
ArgChecker.AssertArgNotNullOrEmpty(toFamily, "toFamily");
PerspexAdapter.Instance.AddFontFamilyMapping(fromFamily, toFamily);
}
/// <summary>
/// Parse the given stylesheet to <see cref="CssData"/> object.<br/>
/// If <paramref name="combineWithDefault"/> 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.
/// </summary>
/// <seealso cref="http://www.w3.org/TR/CSS21/sample.html"/>
/// <param name="stylesheet">the stylesheet source to parse</param>
/// <param name="combineWithDefault">true - combine the parsed css data with default css data, false - return only the parsed css data</param>
/// <returns>the parsed css data</returns>
public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault = true)
{
return CssData.Parse(PerspexAdapter.Instance, stylesheet, combineWithDefault);
}
/// <summary>
/// Measure the size (width and height) required to draw the given html under given max width restriction.<br/>
/// 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.<br/>
/// </summary>
/// <param name="html">HTML source to render</param>
/// <param name="maxWidth">optional: bound the width of the html to render in (default - 0, unlimited)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the size required for the html</returns>
public static Size Measure(string html, double maxWidth = 0, CssData cssData = null,
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null)
{
Size actualSize = Size.Empty;
if (!string.IsNullOrEmpty(html))
{
using (var container = new HtmlContainer())
{
container.MaxSize = new Size(maxWidth, 0);
container.AvoidAsyncImagesLoading = true;
container.AvoidImagesLateLoading = true;
if (stylesheetLoad != null)
container.StylesheetLoad += stylesheetLoad;
if (imageLoad != null)
container.ImageLoad += imageLoad;
container.SetHtml(html, cssData);
container.PerformLayout();
actualSize = container.ActualSize;
}
}
return actualSize;
}
/*
/// <summary>
/// Renders the specified HTML source on the specified location and max width restriction.<br/>
/// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
/// wrap as specified in the html<br/>
/// Returned is the actual width and height of the rendered html.<br/>
/// </summary>
/// <param name="g">Device to render with</param>
/// <param name="html">HTML source to render</param>
/// <param name="left">optional: the left most location to start render the html at (default - 0)</param>
/// <param name="top">optional: the top most location to start render the html at (default - 0)</param>
/// <param name="maxWidth">optional: bound the width of the html to render in (default - 0, unlimited)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the actual size of the rendered html</returns>
public static Size Render(IDrawingContext g, string html, double left = 0, double top = 0, double maxWidth = 0, CssData cssData = null,
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null)
{
ArgChecker.AssertArgNotNull(g, "g");
return RenderClip(g, html, new Point(left, top), new Size(maxWidth, 0), cssData, stylesheetLoad, imageLoad);
}
/// <summary>
/// Renders the specified HTML source on the specified location and max size restriction.<br/>
/// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
/// wrap as specified in the html<br/>
/// If <paramref name="maxSize"/>.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.<br/>
/// Returned is the actual width and height of the rendered html.<br/>
/// </summary>
/// <param name="g">Device to render with</param>
/// <param name="html">HTML source to render</param>
/// <param name="location">the top-left most location to start render the html at</param>
/// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the actual size of the rendered html</returns>
public static Size Render(IDrawingContext g, string html, Point location, Size maxSize, CssData cssData = null,
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null)
{
ArgChecker.AssertArgNotNull(g, "g");
return RenderClip(g, html, location, maxSize, cssData, stylesheetLoad, imageLoad);
}
/// <summary>
/// Renders the specified HTML into a new image of the requested size.<br/>
/// The HTML will be layout by the given size but will be clipped if cannot fit.<br/>
/// </summary>
/// <param name="html">HTML source to render</param>
/// <param name="size">The size of the image to render into, layout html by width and clipped by height</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the generated image of the html</returns>
public static BitmapFrame RenderToImage(string html, Size size, CssData cssData = null,
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null)
{
var renderTarget = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
if (!string.IsNullOrEmpty(html))
{
// render HTML into the visual
DrawingVisual drawingVisual = new DrawingVisual();
using (IDrawingContext g = drawingVisual.RenderOpen())
{
RenderHtml(g, html, new Point(), size, cssData, stylesheetLoad, imageLoad);
}
// render visual into target bitmap
renderTarget.Render(drawingVisual);
}
return BitmapFrame.Create(renderTarget);
}
/// <summary>
/// Renders the specified HTML into a new image of unknown size that will be determined by max width/height and HTML layout.<br/>
/// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
/// wrap as specified in the html<br/>
/// If <paramref name="maxHeight"/> 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.<br/>
/// <p>
/// Limitation: The image cannot have transparent background, by default it will be white.<br/>
/// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
/// </p>
/// </summary>
/// <param name="html">HTML source to render</param>
/// <param name="maxWidth">optional: the max width of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
/// <param name="maxHeight">optional: the max height of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
/// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the generated image of the html</returns>
public static BitmapFrame RenderToImage(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null,
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null)
{
return RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad);
}
/// <summary>
/// Renders the specified HTML into a new image of unknown size that will be determined by min/max width/height and HTML layout.<br/>
/// If <paramref name="maxSize.Width"/> is zero the html will use all the required width, otherwise it will perform line
/// wrap as specified in the html<br/>
/// If <paramref name="maxSize.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.<br/>
/// If <paramref name="minSize"/> (Width/Height) is above zero the rendered image will not be smaller than the given min size.<br/>
/// <p>
/// Limitation: The image cannot have transparent background, by default it will be white.<br/>
/// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
/// </p>
/// </summary>
/// <param name="html">HTML source to render</param>
/// <param name="minSize">optional: the min size of the rendered html (zero - not limit the width/height)</param>
/// <param name="maxSize">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)</param>
/// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the generated image of the html</returns>
public static BitmapFrame RenderToImage(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null,
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null)
{
RenderTargetBitmap renderTarget;
if (!string.IsNullOrEmpty(html))
{
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;
renderTarget = new RenderTargetBitmap((int)finalSize.Width, (int)finalSize.Height, 96, 96, PixelFormats.Pbgra32);
// render HTML into the visual
DrawingVisual drawingVisual = new DrawingVisual();
using (IDrawingContext g = drawingVisual.RenderOpen())
{
container.PerformPaint(g, new Rect(new Size(maxSize.Width > 0 ? maxSize.Width : double.MaxValue, maxSize.Height > 0 ? maxSize.Height : double.MaxValue)));
}
// render visual into target bitmap
renderTarget.Render(drawingVisual);
}
}
else
{
renderTarget = new RenderTargetBitmap(0, 0, 96, 96, PixelFormats.Pbgra32);
}
return BitmapFrame.Create(renderTarget);
}
#region Private methods
/// <summary>
/// Measure the size of the html by performing layout under the given restrictions.
/// </summary>
/// <param name="htmlContainer">the html to calculate the layout for</param>
/// <param name="minSize">the minimal size of the rendered html (zero - not limit the width/height)</param>
/// <param name="maxSize">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)</param>
/// <returns>return: the size of the html to be rendered within the min/max limits</returns>
private static Size MeasureHtmlByRestrictions(HtmlContainer htmlContainer, Size minSize, Size maxSize)
{
// use desktop created graphics to measure the HTML
using (var mg = new GraphicsAdapter())
{
var sizeInt = HtmlRendererUtil.MeasureHtmlByRestrictions(mg, htmlContainer.HtmlContainerInt, Util.Convert(minSize), Util.Convert(maxSize));
if (maxSize.Width < 1 && sizeInt.Width > 4096)
sizeInt.Width = 4096;
return Util.ConvertRound(sizeInt);
}
}
/// <summary>
/// Renders the specified HTML source on the specified location and max size restriction.<br/>
/// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
/// wrap as specified in the html<br/>
/// If <paramref name="maxSize"/>.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.<br/>
/// Clip the graphics so the html will not be rendered outside the max height bound given.<br/>
/// Returned is the actual width and height of the rendered html.<br/>
/// </summary>
/// <param name="g">Device to render with</param>
/// <param name="html">HTML source to render</param>
/// <param name="location">the top-left most location to start render the html at</param>
/// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the actual size of the rendered html</returns>
private static Size RenderClip(IDrawingContext g, string html, Point location, Size maxSize, CssData cssData, EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad, EventHandler<HtmlImageLoadEventArgs> imageLoad)
{
if (maxSize.Height > 0)
g.PushClip(new RectangleGeometry(new Rect(location, maxSize)));
var actualSize = RenderHtml(g, html, location, maxSize, cssData, stylesheetLoad, imageLoad);
if (maxSize.Height > 0)
g.Pop();
return actualSize;
}
/// <summary>
/// Renders the specified HTML source on the specified location and max size restriction.<br/>
/// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
/// wrap as specified in the html<br/>
/// If <paramref name="maxSize"/>.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.<br/>
/// Returned is the actual width and height of the rendered html.<br/>
/// </summary>
/// <param name="g">Device to render with</param>
/// <param name="html">HTML source to render</param>
/// <param name="location">the top-left most location to start render the html at</param>
/// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
/// <returns>the actual size of the rendered html</returns>
private static Size RenderHtml(IDrawingContext g, string html, Point location, Size maxSize, CssData cssData, EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad, EventHandler<HtmlImageLoadEventArgs> imageLoad)
{
Size actualSize = Size.Empty;
if (!string.IsNullOrEmpty(html))
{
using (var container = new HtmlContainer())
{
container.Location = location;
container.MaxSize = maxSize;
container.AvoidAsyncImagesLoading = true;
container.AvoidImagesLateLoading = true;
if (stylesheetLoad != null)
container.StylesheetLoad += stylesheetLoad;
if (imageLoad != null)
container.ImageLoad += imageLoad;
container.SetHtml(html, cssData);
container.PerformLayout();
container.PerformPaint(g, new Rect(0, 0, double.MaxValue, double.MaxValue));
actualSize = container.ActualSize;
}
}
return actualSize;
}
#endregion
*/
}
}

1
src/Perspex.HtmlRenderer/Perspex.HtmlRenderer.csproj

@ -129,7 +129,6 @@
<Compile Include="HtmlContainer.cs" />
<Compile Include="HtmlControl.cs" />
<Compile Include="HtmlLabel.cs" />
<Compile Include="HtmlRender.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PropertyHelper.cs" />
<Compile Include="RoutedEventArgsWrapper.cs" />

Loading…
Cancel
Save