Browse Source

WIP: Porting HTMLRenderer support from WPF

Nikita Tsukanov 10 years ago
parent
commit
645d1d8792
29 changed files with 3263 additions and 0 deletions
  1. 4 0
      .gitmodules
  2. 6 0
      Perspex.sln
  3. 33 0
      samples/TestApplication/Program.cs
  4. 5 0
      samples/TestApplication/TestApplication.csproj
  5. 170 0
      samples/TestApplication/html.htm
  6. 47 0
      src/Perspex.HtmlRenderer/Adapters/BrushAdapter.cs
  7. 51 0
      src/Perspex.HtmlRenderer/Adapters/ContextMenuAdapter.cs
  8. 116 0
      src/Perspex.HtmlRenderer/Adapters/ControlAdapter.cs
  9. 106 0
      src/Perspex.HtmlRenderer/Adapters/FontAdapter.cs
  10. 29 0
      src/Perspex.HtmlRenderer/Adapters/FontFamilyAdapter.cs
  11. 277 0
      src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs
  12. 66 0
      src/Perspex.HtmlRenderer/Adapters/GraphicsPathAdapter.cs
  13. 52 0
      src/Perspex.HtmlRenderer/Adapters/ImageAdapter.cs
  14. 93 0
      src/Perspex.HtmlRenderer/Adapters/PenAdapter.cs
  15. 112 0
      src/Perspex.HtmlRenderer/Adapters/PerspexAdapter.cs
  16. 28 0
      src/Perspex.HtmlRenderer/Compat/Attributes.cs
  17. 22 0
      src/Perspex.HtmlRenderer/Compat/ThreadPool.cs
  18. 468 0
      src/Perspex.HtmlRenderer/HtmlContainer.cs
  19. 620 0
      src/Perspex.HtmlRenderer/HtmlControl.cs
  20. 135 0
      src/Perspex.HtmlRenderer/HtmlLabel.cs
  21. 424 0
      src/Perspex.HtmlRenderer/HtmlRender.cs
  22. 198 0
      src/Perspex.HtmlRenderer/Perspex.HtmlRenderer.csproj
  23. 2 0
      src/Perspex.HtmlRenderer/Perspex.HtmlRenderer.csproj.DotSettings
  24. 30 0
      src/Perspex.HtmlRenderer/Properties/AssemblyInfo.cs
  25. 24 0
      src/Perspex.HtmlRenderer/PropertyHelper.cs
  26. 14 0
      src/Perspex.HtmlRenderer/RoutedEventArgsWrapper.cs
  27. 123 0
      src/Perspex.HtmlRenderer/Utilities/Util.cs
  28. 1 0
      src/Perspex.HtmlRenderer/external
  29. 7 0
      src/Perspex.HtmlRenderer/packages.config

+ 4 - 0
.gitmodules

@@ -1,3 +1,7 @@
 [submodule "src/Perspex.ReactiveUI/src"]
 	path = src/Perspex.ReactiveUI/src
 	url = https://github.com/reactiveui/ReactiveUI.git
+[submodule "src/Perspex.HtmlRenderer/external"]
+	path = src/Perspex.HtmlRenderer/external
+	url = https://github.com/Perspex/HTML-Renderer.git
+	branch = perspex-pcl

+ 6 - 0
Perspex.sln

@@ -94,6 +94,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Markup.Xaml.UnitTes
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Cairo.RenderTests", "tests\Perspex.RenderTests\Perspex.Cairo.RenderTests.csproj", "{E106CF37-4066-4615-B684-172A6D30B058}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.HtmlRenderer", "src\Perspex.HtmlRenderer\Perspex.HtmlRenderer.csproj", "{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -224,6 +226,10 @@ Global
 		{E106CF37-4066-4615-B684-172A6D30B058}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E106CF37-4066-4615-B684-172A6D30B058}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E106CF37-4066-4615-B684-172A6D30B058}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 33 - 0
samples/TestApplication/Program.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.IO;
 using System.Reactive.Linq;
 using Perspex;
 using Perspex.Animation;
@@ -219,6 +220,7 @@ namespace TestApplication
                             {
                                 ButtonsTab(),
                                 TextTab(),
+                                HtmlTab(),
                                 ImagesTab(),
                                 ListsTab(),
                                 LayoutTab(),
@@ -365,6 +367,37 @@ namespace TestApplication
             return result;
         }
 
+        private static TabItem HtmlTab()
+        {
+            var htmlText =
+                new StreamReader(typeof (Program).Assembly.GetManifestResourceStream("TestApplication.html.htm"))
+                    .ReadToEnd();
+            return new TabItem
+            {
+                Header = "Html",
+                Content = new ScrollViewer()
+                {
+                    Width = 500,
+                    VerticalScrollBarVisibility = ScrollBarVisibility.Visible,
+                    Content =
+                        new Border
+                        {
+                            Height = 1500,
+                            Child =
+                                new HtmlLabel()
+                                {
+
+                                    Text = htmlText,
+                                    AutoSize = false,
+                                    MaxWidth = 500,
+                                    MaxHeight = 900
+
+                                }
+                        }
+                }
+            };
+        }
+
         private static TabItem TextTab()
         {
             return new TabItem

+ 5 - 0
samples/TestApplication/TestApplication.csproj

@@ -103,6 +103,10 @@
       <Project>{7062AE20-5DCC-4442-9645-8195BDECE63E}</Project>
       <Name>Perspex.Diagnostics</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.HtmlRenderer\Perspex.HtmlRenderer.csproj">
+      <Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
+      <Name>Perspex.HtmlRenderer</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\src\Perspex.Input\Perspex.Input.csproj">
       <Project>{62024B2D-53EB-4638-B26B-85EEAA54866E}</Project>
       <Name>Perspex.Input</Name>
@@ -144,6 +148,7 @@
     <Content Include="github_icon.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <EmbeddedResource Include="html.htm" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\src\Shared\perspex.platform.targets" />

+ 170 - 0
samples/TestApplication/html.htm

@@ -0,0 +1,170 @@
+<html>
+    <head>
+        <title>Additional features</title>
+        <link rel="Stylesheet" href="StyleSheet" />
+        <style>
+        <!--
+            .g1, .g2, .g3, .g4, .g5 {
+                background-color: red;
+                background-gradient: yellow;
+                padding: 22px;
+            }
+
+            .g1 { background-gradient-angle: 0; }
+
+            .g2 { background-gradient-angle: 45; }
+
+            .g3 { background-gradient-angle: 90; }
+
+            .g4 { background-gradient-angle: 135; }
+
+            .g5 { background-gradient-angle: 180; }
+
+            .c1, .c2, .c3, .c4, .c5 {
+                background-color: olive;
+                border: 0px;
+                color: white;
+                vertical-align: middle;
+            }
+
+            .c1 { corner-radius: 0px; }
+
+            .c2 { corner-radius: 10px; }
+
+            .c3 { corner-radius: 0px 10px 10px 0px; }
+
+            .c4 { corner-radius: 18px; }
+
+            .c5 {
+                corner-radius: 10px;
+                border: outset #BBBB00 2px;
+            }
+
+            table { border-style: outset; }
+
+            td, th { border-style: inset; }
+
+            td { text-align: center; }
+        -->
+    </style>
+    </head>
+    <body>
+        <h1>
+            Additional features
+        </h1>
+        <blockquote>
+            <p>
+                There are some additional features that you may already discovered about the renderer
+                core engine.</p>
+            <h2>
+                Graphic features</h2>
+            <p>
+                I have always wanted the W3C to add this features to the CSS spec (and so far, not
+                there yet :)</p>
+            <ul>
+                <li><b>Gradients on backgrounds</b></li>
+                <li><b>Rounded corners</b></li>
+            </ul>
+            <p>
+                And I think many many web designers would agree. Is it so hard or what?.</p>
+            <h3>
+                Background Gradients</h3>
+            <p>
+                It is a simple two color linear gradient, achieved by the adding of two CSS properties:</p>
+            <ol>
+                <li><code>background-gradient: (#Color)</code> - Second color of the gradient background,
+                    the first one is given by <code>background-color</code>. Not inherited.</li>
+                <li><code>background-gradient-angle: (number)</code> - Angle (in degrees, clockwise) of
+                    the gradient. Not inherited. Initial value:90</li>
+            </ol>
+            <b>Some examples</b>
+            <!-- Gradients table -->
+            <table width="300px">
+                <tr>
+                    <td class="g1">
+                    </td>
+                    <td class="g2">
+                    </td>
+                    <td class="g3">
+                    </td>
+                    <td class="g4">
+                    </td>
+                    <td class="g5">
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        0 degrees
+                    </td>
+                    <td>
+                        45 degrees
+                    </td>
+                    <td>
+                        90 degrees
+                    </td>
+                    <td>
+                        135 degrees
+                    </td>
+                    <td>
+                        180 degrees
+                    </td>
+                </tr>
+            </table>
+            <h3>
+                Rounded corners</h3>
+            <p>
+                As you may already know, CSS is based on a <a href="http://www.w3.org/TR/CSS21/box.html">
+                                                               Box Model</a>, where every box has it's own set of properties. Since we are
+                talking abound <b>boxes</b>, why not to make them with rounded corners, almost every
+                website you visit nowadays makes use of rounded corners, where a not very nice trick
+                with images and tables must be used.</p>
+            <p>
+                In this renderer, the rounded corners are achieved by adding this CSS properties:</p>
+            <ul>
+                <li><code>corner-ne-radius: (length)</code> Indicates the radius of the north-east corner.
+                    Not ineritted</li>
+                <li><code>corner-se-radius: (length)</code> Indicates the radius of the south-east corner.
+                    Not ineritted</li>
+                <li><code>corner-sw-radius: (length)</code> Indicates the radius of the south-west corner.
+                    Not ineritted</li>
+                <li><code>corner-nw-radius: (length)</code> Indicates the radius of the north-west corner.
+                    Not ineritted</li>
+                <li><code>corner-radius: (length){1,4}</code> Shorthand for the other corner properties.
+                    Not ineritted</li>
+            </ul>
+            <!-- Corners table -->
+            <b>Some examples</b>
+            <table width="320px" cellspacing="10">
+                <tr>
+                    <td width="1" style="border: 0px">
+                        <p>
+                        </p>
+                        <p>
+                        </p>
+                    </td>
+                    <td class="c1">
+                        c1
+                    </td>
+                    <td class="c2">
+                        c2
+                    </td>
+                    <td class="c3">
+                        c3
+                    </td>
+                    <td class="c4">
+                        c4
+                    </td>
+                    <td class="c5">
+                        c5
+                    </td>
+                </tr>
+            </table>
+            <pre>.c1, .c2, .c3, .c4, .c5 { background-color:olive; border:0px; color:white; vertical-align:middle; }
+.c1  { corner-radius: 0px }
+.c2  { corner-radius: 10px }
+.c3  { corner-radius: 0px 10px 10px 0px }
+.c4  { corner-radius: 18px }
+.c5  { corner-radius: 10px; border: outset #bb0 2px; }</pre>
+        </blockquote>
+    </body>
+</html>

+ 47 - 0
src/Perspex.HtmlRenderer/Adapters/BrushAdapter.cs

@@ -0,0 +1,47 @@
+// "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 Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex brushes.
+    /// </summary>
+    internal sealed class BrushAdapter : RBrush
+    {
+        /// <summary>
+        /// The actual Perspex brush instance.
+        /// </summary>
+        private readonly Brush _brush;
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public BrushAdapter(Brush brush)
+        {
+            _brush = brush;
+        }
+
+        /// <summary>
+        /// The actual Perspex brush instance.
+        /// </summary>
+        public Brush Brush
+        {
+            get { return _brush; }
+        }
+
+        public override void Dispose()
+        { }
+    }
+}

+ 51 - 0
src/Perspex.HtmlRenderer/Adapters/ContextMenuAdapter.cs

@@ -0,0 +1,51 @@
+// "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 TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex context menu for core.
+    /// </summary>
+    internal sealed class NullContextMenuAdapter : RContextMenu
+    {
+        //TODO: actually implement context menu
+
+        private int _itemCount;
+        public override int ItemsCount => _itemCount;
+        public override void AddDivider()
+        {
+            
+        }
+
+        public override void AddItem(string text, bool enabled, EventHandler onClick)
+        {
+            _itemCount++;
+        }
+
+        public override void RemoveLastDivider()
+        {
+            _itemCount++;
+        }
+
+        public override void Show(RControl parent, RPoint location)
+        {
+        }
+
+        public override void Dispose()
+        {
+        }
+    }
+}

+ 116 - 0
src/Perspex.HtmlRenderer/Adapters/ControlAdapter.cs

@@ -0,0 +1,116 @@
+// "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 Perspex.Controls;
+using Perspex.Input;
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core.Utils;
+using TheArtOfDev.HtmlRenderer.Perspex.Utilities;
+// ReSharper disable ConvertPropertyToExpressionBody
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex Control for core.
+    /// </summary>
+    internal sealed class ControlAdapter : RControl
+    {
+        /// <summary>
+        /// the underline Perspex control.
+        /// </summary>
+        private readonly Control _control;
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public ControlAdapter(Control control)
+            : base(PerspexAdapter.Instance)
+        {
+            ArgChecker.AssertArgNotNull(control, "control");
+
+            _control = control;
+        }
+
+        /// <summary>
+        /// Get the underline Perspex control
+        /// </summary>
+        public Control Control
+        {
+            get { return _control; }
+        }
+
+        public override RPoint MouseLocation
+        {
+            get
+            {
+                //TODO: Implement
+                //return Utils.Convert(_control.PointFromScreen(Mouse.GetPosition(_control)));
+                return new RPoint(0, 0);
+            }
+        }
+
+        public override bool LeftMouseButton
+        {
+            get
+            {
+                return false;
+                //TODO: Implement
+                //return Mouse.LeftButton == MouseButtonState.Pressed;
+            }
+        }
+
+        public override bool RightMouseButton
+        {
+            get
+            {
+                return false;
+                //TODO: Implement
+                //return Mouse.RightButton == MouseButtonState.Pressed;
+            }
+        }
+
+        public override void SetCursorDefault()
+        {
+            _control.Cursor = new Cursor(StandardCursorType.Arrow);
+        }
+
+        public override void SetCursorHand()
+        {
+            _control.Cursor = new Cursor(StandardCursorType.Hand);
+        }
+
+        public override void SetCursorIBeam()
+        {
+            _control.Cursor = new Cursor(StandardCursorType.Ibeam);
+        }
+
+        public override void DoDragDropCopy(object dragDropData)
+        {
+            //TODO: Implement
+            //DragDrop.DoDragDrop(_control, dragDropData, DragDropEffects.Copy);
+        }
+
+        public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth)
+        {
+            using (var g = new GraphicsAdapter())
+            {
+                g.MeasureString(str, font, maxWidth, out charFit, out charFitWidth);
+            }
+        }
+
+        public override void Invalidate()
+        {
+            _control.InvalidateVisual();
+        }
+    }
+}

+ 106 - 0
src/Perspex.HtmlRenderer/Adapters/FontAdapter.cs

@@ -0,0 +1,106 @@
+// "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 Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex Font.
+    /// </summary>
+    internal sealed class FontAdapter : RFont
+    {
+        public RFontStyle Style { get; }
+
+        #region Fields and Consts
+
+
+        /// <summary>
+        /// the size of the font
+        /// </summary>
+        private readonly double _size;
+
+        /// <summary>
+        /// the vertical offset of the font underline location from the top of the font.
+        /// </summary>
+        private readonly double _underlineOffset = -1;
+
+        /// <summary>
+        /// Cached font height.
+        /// </summary>
+        private readonly double _height = -1;
+
+        /// <summary>
+        /// Cached font whitespace width.
+        /// </summary>
+        private double _whitespaceWidth = -1;
+        
+
+        #endregion
+
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public FontAdapter(string fontFamily, double size, RFontStyle style)
+        {
+            Style = style;
+            Name = fontFamily;
+            _size = size;
+            //TODO: Somehow get proper line spacing and underlinePosition
+            var lineSpacing = 2;
+            var underlinePosition = 1;
+
+            _height = 96d / 72d * _size * lineSpacing;
+            _underlineOffset = 96d / 72d * _size * (lineSpacing + underlinePosition);
+
+        }
+
+        public string Name { get; set; }
+
+
+        public override double Size
+        {
+            get { return _size; }
+        }
+
+        public override double UnderlineOffset
+        {
+            get { return _underlineOffset; }
+        }
+
+        public override double Height
+        {
+            get { return _height; }
+        }
+
+        public override double LeftPadding
+        {
+            get { return _height / 6f; }
+        }
+
+        public override double GetWhitespaceWidth(RGraphics graphics)
+        {
+            if (_whitespaceWidth < 0)
+            {
+                _whitespaceWidth = graphics.MeasureString(" ", this).Width;
+            }
+            return _whitespaceWidth;
+        }
+
+        public FontStyle FontStyle => Style.HasFlag(RFontStyle.Italic) ? FontStyle.Italic : FontStyle.Normal;
+
+        public FontWeight Weight => Style.HasFlag(RFontStyle.Bold) ? FontWeight.Bold : FontWeight.Normal;
+    }
+}

+ 29 - 0
src/Perspex.HtmlRenderer/Adapters/FontFamilyAdapter.cs

@@ -0,0 +1,29 @@
+// "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 TheArtOfDev.HtmlRenderer.Adapters;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex Font family object for core.
+    /// </summary>
+    internal sealed class FontFamilyAdapter : RFontFamily
+    {
+        public FontFamilyAdapter(string fontFamily)
+        {
+            Name = fontFamily;
+        }
+        
+        public override string Name { get; }
+    }
+}

+ 277 - 0
src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs

@@ -0,0 +1,277 @@
+// "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.Globalization;
+using Perspex;
+using Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core.Utils;
+using TheArtOfDev.HtmlRenderer.Perspex.Utilities;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex Graphics.
+    /// </summary>
+    internal sealed class GraphicsAdapter : RGraphics
+    {
+        #region Fields and Consts
+
+        /// <summary>
+        /// The wrapped Perspex graphics object
+        /// </summary>
+        private readonly IDrawingContext _g;
+
+        /// <summary>
+        /// if to release the graphics object on dispose
+        /// </summary>
+        private readonly bool _releaseGraphics;
+
+        #endregion
+
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        /// <param name="g">the Perspex graphics object to use</param>
+        /// <param name="initialClip">the initial clip of the graphics</param>
+        /// <param name="releaseGraphics">optional: if to release the graphics object on dispose (default - false)</param>
+        public GraphicsAdapter(IDrawingContext g, RRect initialClip, bool releaseGraphics = false)
+            : base(PerspexAdapter.Instance, initialClip)
+        {
+            ArgChecker.AssertArgNotNull(g, "g");
+
+            _g = g;
+            _releaseGraphics = releaseGraphics;
+        }
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public GraphicsAdapter()
+            : base(PerspexAdapter.Instance, RRect.Empty)
+        {
+            _g = null;
+            _releaseGraphics = false;
+        }
+        
+        public override void PopClip()
+        {
+            /*
+            _g.Pop();
+            _clipStack.Pop();
+            */
+        }
+
+        public override void PushClip(RRect rect)
+        {
+            //_clipStack.Push(rect);
+            //_g.PushClip(new RectangleGeometry(Utils.Convert(rect)));
+        }
+
+        public override void PushClipExclude(RRect rect)
+        {
+            //var geometry = new CombinedGeometry();
+            //geometry.Geometry1 = new RectangleGeometry(Utils.Convert(_clipStack.Peek()));
+            //geometry.Geometry2 = new RectangleGeometry(Utils.Convert(rect));
+            //geometry.GeometryCombineMode = GeometryCombineMode.Exclude;
+
+            //_clipStack.Push(_clipStack.Peek());
+            //_g.PushClip(geometry);
+        }
+
+        public override Object SetAntiAliasSmoothingMode()
+        {
+            return null;
+        }
+
+        public override void ReturnPreviousSmoothingMode(Object prevMode)
+        { }
+
+        public override RSize MeasureString(string str, RFont font)
+        {
+            var text = GetText(str, font);
+            var measure = text.Measure();
+            return new RSize(measure.Width, measure.Height);
+            
+        }
+
+        FormattedText GetText(string str, RFont font)
+        {
+            var f = ((FontAdapter)font);
+            return new FormattedText(str, f.Name, font.Size, f.FontStyle, TextAlignment.Left, f.Weight);
+        }
+
+        public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth)
+        {
+            var text = GetText(str, font);
+            charFit = str.Length;
+            charFitWidth = text.Measure().Width;
+        }
+        
+        public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl)
+        {
+            var text = GetText(str, font);
+            text.Constraint = Util.Convert(size);
+            _g.DrawText(new SolidColorBrush(Util.Convert(color)), Util.Convert(point), text);
+
+            //var colorConv = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
+
+            //bool glyphRendered = false;
+            //GlyphTypeface glyphTypeface = ((FontAdapter)font).GlyphTypeface;
+            //if (glyphTypeface != null)
+            //{
+            //    double width = 0;
+            //    ushort[] glyphs = new ushort[str.Length];
+            //    double[] widths = new double[str.Length];
+
+            //    int i = 0;
+            //    for (; i < str.Length; i++)
+            //    {
+            //        ushort glyph;
+            //        if (!glyphTypeface.CharacterToGlyphMap.TryGetValue(str[i], out glyph))
+            //            break;
+
+            //        glyphs[i] = glyph;
+            //        width += glyphTypeface.AdvanceWidths[glyph];
+            //        widths[i] = 96d / 72d * font.Size * glyphTypeface.AdvanceWidths[glyph];
+            //    }
+
+            //    if (i >= str.Length)
+            //    {
+            //        point.Y += glyphTypeface.Baseline * font.Size * 96d / 72d;
+            //        point.X += rtl ? 96d / 72d * font.Size * width : 0;
+
+            //        glyphRendered = true;
+            //        var glyphRun = new GlyphRun(glyphTypeface, rtl ? 1 : 0, false, 96d / 72d * font.Size, glyphs, Utils.ConvertRound(point), widths, null, null, null, null, null, null);
+            //        _g.DrawGlyphRun(colorConv, glyphRun);
+            //    }
+            //}
+
+            //if (!glyphRendered)
+            //{
+            //    var formattedText = new FormattedText(str, CultureInfo.CurrentCulture, rtl ? FlowDirection.RightToLeft : FlowDirection.LeftToRight, ((FontAdapter)font).Font, 96d / 72d * font.Size, colorConv);
+            //    point.X += rtl ? formattedText.Width : 0;
+            //    _g.DrawText(formattedText, Utils.ConvertRound(point));
+            //}
+        }
+
+        public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
+        {
+            //TODO: Implement texture brush
+            return PerspexAdapter.Instance.GetSolidBrush(Util.Convert(Colors.Magenta));
+
+            //var brush = new ImageBrush(((ImageAdapter)image).Image);
+            //brush.Stretch = Stretch.None;
+            //brush.TileMode = TileMode.Tile;
+            //brush.Viewport = Utils.Convert(dstRect);
+            //brush.ViewportUnits = BrushMappingMode.Absolute;
+            //brush.Transform = new TranslateTransform(translateTransformLocation.X, translateTransformLocation.Y);
+            //brush.Freeze();
+            //return new BrushAdapter(brush);
+        }
+        
+        public override RGraphicsPath GetGraphicsPath()
+        {
+            return new GraphicsPathAdapter();
+        }
+
+        public override void Dispose()
+        {
+            //TODO: Do something about Dispose
+            //if (_releaseGraphics)
+            //    _g.Close();
+        }
+
+    
+        #region Delegate graphics methods
+
+        public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2)
+        {
+            x1 = (int)x1;
+            x2 = (int)x2;
+            y1 = (int)y1;
+            y2 = (int)y2;
+
+            var adj = pen.Width;
+            if (Math.Abs(x1 - x2) < .1 && Math.Abs(adj % 2 - 1) < .1)
+            {
+                x1 += .5;
+                x2 += .5;
+            }
+            if (Math.Abs(y1 - y2) < .1 && Math.Abs(adj % 2 - 1) < .1)
+            {
+                y1 += .5;
+                y2 += .5;
+            }
+
+            _g.DrawLine(((PenAdapter)pen).CreatePen(), new Point(x1, y1), new Point(x2, y2));
+        }
+        
+        public override void DrawRectangle(RPen pen, double x, double y, double width, double height)
+        {
+            var adj = pen.Width;
+            if (Math.Abs(adj % 2 - 1) < .1)
+            {
+                x += .5;
+                y += .5;
+            }
+            _g.DrawRectange(((PenAdapter) pen).CreatePen(), new Rect(x, y, width, height));
+        }
+
+        public override void DrawRectangle(RBrush brush, double x, double y, double width, double height)
+        {
+            _g.FillRectange(((BrushAdapter) brush).Brush, new Rect(x, y, width, height));
+        }
+
+        public override void DrawImage(RImage image, RRect destRect, RRect srcRect)
+        {
+            _g.DrawImage(((ImageAdapter) image).Image, 1, Util.Convert(srcRect), Util.Convert(destRect));
+        }
+
+        public override void DrawImage(RImage image, RRect destRect)
+        {
+            _g.DrawImage(((ImageAdapter) image).Image, 1, new Rect(0, 0, image.Width, image.Height),
+                Util.Convert(destRect));
+        }
+
+        public override void DrawPath(RPen pen, RGraphicsPath path)
+        {
+            _g.DrawGeometry(null, ((PenAdapter)pen).CreatePen(), ((GraphicsPathAdapter)path).GetClosedGeometry());
+        }
+
+        public override void DrawPath(RBrush brush, RGraphicsPath path)
+        {
+            _g.DrawGeometry(((BrushAdapter)brush).Brush, null, ((GraphicsPathAdapter)path).GetClosedGeometry());
+        }
+
+        public override void DrawPolygon(RBrush brush, RPoint[] points)
+        {
+            if (points != null && points.Length > 0)
+            {
+                var g = new StreamGeometry();
+                using (var context = g.Open())
+                {
+                    context.BeginFigure(Util.Convert(points[0]), true);
+                    for (int i = 1; i < points.Length; i++)
+                        context.LineTo(Util.Convert(points[i]));
+                }
+
+                _g.DrawGeometry(((BrushAdapter)brush).Brush, null, g);
+            }
+        }
+
+        #endregion
+    }
+}

+ 66 - 0
src/Perspex.HtmlRenderer/Adapters/GraphicsPathAdapter.cs

@@ -0,0 +1,66 @@
+// "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 Perspex;
+using Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex graphics path object for core.
+    /// </summary>
+    internal sealed class GraphicsPathAdapter : RGraphicsPath
+    {
+        /// <summary>
+        /// The actual Perspex graphics geometry instance.
+        /// </summary>
+        private readonly StreamGeometry _geometry = new StreamGeometry();
+
+        /// <summary>
+        /// The context used in Perspex geometry to render path
+        /// </summary>
+        private readonly StreamGeometryContext _geometryContext;
+
+        public GraphicsPathAdapter()
+        {
+            _geometryContext = _geometry.Open();
+        }
+
+        public override void Start(double x, double y)
+        {
+            _geometryContext.BeginFigure(new Point(x, y), true);
+        }
+
+        public override void LineTo(double x, double y)
+        {
+            _geometryContext.LineTo(new Point(x, y));
+        }
+
+        public override void ArcTo(double x, double y, double size, Corner corner)
+        {
+            _geometryContext.ArcTo(new Point(x, y), new Size(size, size), 0, false, SweepDirection.Clockwise);
+        }
+
+        /// <summary>
+        /// Close the geometry to so no more path adding is allowed and return the instance so it can be rendered.
+        /// </summary>
+        public StreamGeometry GetClosedGeometry()
+        {
+            _geometryContext.EndFigure(true);
+            return _geometry;
+        }
+
+        public override void Dispose()
+        { }
+    }
+}

+ 52 - 0
src/Perspex.HtmlRenderer/Adapters/ImageAdapter.cs

@@ -0,0 +1,52 @@
+// "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 Perspex.Media.Imaging;
+using TheArtOfDev.HtmlRenderer.Adapters;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex Image object for core.
+    /// </summary>
+    internal sealed class ImageAdapter : RImage
+    {
+        /// <summary>
+        /// the underline Perspex image.
+        /// </summary>
+        private readonly Bitmap _image;
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public ImageAdapter(Bitmap image)
+        {
+            _image = image;
+        }
+
+        /// <summary>
+        /// the underline Perspex image.
+        /// </summary>
+        public Bitmap Image => _image;
+
+        public override double Width => _image.PixelWidth;
+
+        public override double Height => _image.PixelHeight;
+
+        public override void Dispose()
+        {
+            //TODO: Implement
+            /*if (_image.StreamSource != null)
+                _image.StreamSource.Dispose();*/
+        }
+    }
+}

+ 93 - 0
src/Perspex.HtmlRenderer/Adapters/PenAdapter.cs

@@ -0,0 +1,93 @@
+// "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 Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    /// <summary>
+    /// Adapter for Perspex pens objects for core.
+    /// </summary>
+    internal sealed class PenAdapter : RPen
+    {
+        /// <summary>
+        /// The actual Perspex brush instance.
+        /// </summary>
+        private readonly Brush _brush;
+
+        /// <summary>
+        /// the width of the pen
+        /// </summary>
+        private double _width;
+
+        /// <summary>
+        /// the dash style of the pen
+        /// </summary>
+        //private DashStyle _dashStyle = DashStyles.Solid;
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public PenAdapter(Brush brush)
+        {
+            _brush = brush;
+        }
+
+        public override double Width
+        {
+            get { return _width; }
+            set { _width = value; }
+        }
+
+        public override RDashStyle DashStyle
+        {
+            set
+            {
+                //TODO: Implement DashStyles
+                /*
+                switch (value)
+                {
+                    case RDashStyle.Solid:
+                        _dashStyle = DashStyles.Solid;
+                        break;
+                    case RDashStyle.Dash:
+                        _dashStyle = DashStyles.Dash;
+                        break;
+                    case RDashStyle.Dot:
+                        _dashStyle = DashStyles.Dot;
+                        break;
+                    case RDashStyle.DashDot:
+                        _dashStyle = DashStyles.DashDot;
+                        break;
+                    case RDashStyle.DashDotDot:
+                        _dashStyle = DashStyles.DashDotDot;
+                        break;
+                    default:
+                        _dashStyle = DashStyles.Solid;
+                        break;
+                }*/
+            }
+        }
+
+        /// <summary>
+        /// Create the actual Perspex pen instance.
+        /// </summary>
+        public Pen CreatePen()
+        {
+            var pen = new Pen(_brush, _width);
+            //pen.DashStyle = _dashStyle;
+            return pen;
+        }
+    }
+}

+ 112 - 0
src/Perspex.HtmlRenderer/Adapters/PerspexAdapter.cs

@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Perspex;
+using Perspex.Media;
+using Perspex.Media.Imaging;
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Perspex.Utilities;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
+{
+    class PerspexAdapter : RAdapter
+    {
+        public static PerspexAdapter Instance { get; } = new PerspexAdapter();
+
+        /// <summary>
+        /// List of valid predefined color names in lower-case
+        /// </summary>
+        private static readonly Dictionary<string, Color> ColorNameDic = new Dictionary<string, Color>();
+        
+
+        static PerspexAdapter()
+        {
+            foreach (var colorProp in typeof(Colors).GetRuntimeProperties()
+                .Where(p=>p.PropertyType == typeof(Color)))
+            {
+                ColorNameDic[colorProp.Name.ToLower()] = (Color)colorProp.GetValue(null);
+            }
+        }
+
+        protected override RColor GetColorInt(string colorName)
+        {
+            Color c;
+            if(!ColorNameDic.TryGetValue(colorName.ToLower(), out c))
+                return RColor.Empty;
+            return Util.Convert(c);
+        }
+
+        protected override RPen CreatePen(RColor color)
+        {
+            return new PenAdapter(GetSolidColorBrush(color));
+        }
+
+        /// <summary>
+        /// Get solid color brush for the given color.
+        /// </summary>
+        private static Brush GetSolidColorBrush(RColor color)
+        {
+            Brush solidBrush;
+            if (color == RColor.White)
+                solidBrush = Brushes.White;
+            else if (color == RColor.Black)
+                solidBrush = Brushes.Black;
+            else if (color.A < 1)
+                solidBrush = Brushes.Transparent;
+            else
+                solidBrush = new SolidColorBrush(Util.Convert(color));
+            return solidBrush;
+        }
+
+        protected override RBrush CreateSolidBrush(RColor color)
+        {
+            return new BrushAdapter(GetSolidColorBrush(color));
+        }
+
+        protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle)
+        {
+            var startColor = angle <= 180 ? Util.Convert(color1) : Util.Convert(color2);
+            var endColor = angle <= 180 ? Util.Convert(color2) : Util.Convert(color1);
+            angle = angle <= 180 ? angle : angle - 180;
+            double x = angle < 135 ? Math.Max((angle - 45) / 90, 0) : 1;
+            double y = angle <= 45 ? Math.Max(0.5 - angle / 90, 0) : angle > 135 ? Math.Abs(1.5 - angle / 90) : 0;
+            return new BrushAdapter(new LinearGradientBrush
+            {
+                StartPoint = new Point(x, y), 
+                EndPoint = new Point(1 - x, 1 - y),
+                GradientStops =
+                {
+                    new GradientStop(startColor, 0),
+                    new GradientStop(endColor, 1)
+                }
+            });
+
+        }
+
+        protected override RImage ConvertImageInt(object image)
+        {
+            return image != null ? new ImageAdapter((Bitmap)image) : null;
+        }
+
+        protected override RImage ImageFromStreamInt(Stream memoryStream)
+        {
+            //TODO: Implement bitmap loader
+            return null;
+        }
+
+        protected override RFont CreateFontInt(string family, double size, RFontStyle style)
+        {
+            return new FontAdapter(family, size, style);
+        }
+
+        protected override RFont CreateFontInt(RFontFamily family, double size, RFontStyle style)
+        {
+            return new FontAdapter(family.Name, size, style);
+        }
+    }
+}

+ 28 - 0
src/Perspex.HtmlRenderer/Compat/Attributes.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+internal class CategoryAttribute : Attribute
+{
+    public CategoryAttribute(string s)
+    {
+        
+    }
+}
+internal class DescriptionAttribute : Attribute
+{
+    public DescriptionAttribute(string s)
+    {
+
+    }
+}
+
+internal class BrowsableAttribute : Attribute
+{
+    public BrowsableAttribute(bool b)
+    {
+
+    }
+}

+ 22 - 0
src/Perspex.HtmlRenderer/Compat/ThreadPool.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TheArtOfDev.HtmlRenderer.Core.Handlers;
+
+namespace System.Threading
+{
+    class ThreadPool
+    {
+        public static void QueueUserWorkItem(Action<object> cb, object state)
+        {
+            Task.Factory.StartNew(() => cb(state));
+        }
+
+        public static void QueueUserWorkItem(Action<object> cb)
+        {
+            Task.Factory.StartNew(() => cb(null));
+        }
+    }
+}

+ 468 - 0
src/Perspex.HtmlRenderer/HtmlContainer.cs

@@ -0,0 +1,468 @@
+// "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.Collections.Generic;
+using Perspex.Input;
+using Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core;
+using TheArtOfDev.HtmlRenderer.Core.Entities;
+using TheArtOfDev.HtmlRenderer.Core.Parse;
+using TheArtOfDev.HtmlRenderer.Core.Utils;
+using TheArtOfDev.HtmlRenderer.Perspex.Adapters;
+using TheArtOfDev.HtmlRenderer.Perspex.Utilities;
+
+namespace Perspex.Controls
+{
+    /// <summary>
+    /// Low level handling of Html Renderer logic, this class is used by <see cref="HtmlParser"/>, 
+    /// <see cref="HtmlLabel"/>, <see cref="HtmlToolTip"/> and <see cref="HtmlRender"/>.<br/>
+    /// </summary>
+    /// <seealso cref="HtmlContainerInt"/>
+    public sealed class HtmlContainer : IDisposable
+    {
+        #region Fields and Consts
+
+        /// <summary>
+        /// The internal core html container
+        /// </summary>
+        private readonly HtmlContainerInt _htmlContainerInt;
+
+        #endregion
+
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        public HtmlContainer()
+        {
+            _htmlContainerInt = new HtmlContainerInt(PerspexAdapter.Instance);
+        }
+
+        /// <summary>
+        /// Raised when the set html document has been fully loaded.<br/>
+        /// Allows manipulation of the html dom, scroll position, etc.
+        /// </summary>
+        public event EventHandler LoadComplete
+        {
+            add { _htmlContainerInt.LoadComplete += value; }
+            remove { _htmlContainerInt.LoadComplete -= value; }
+        }
+
+        /// <summary>
+        /// Raised when the user clicks on a link in the html.<br/>
+        /// Allows canceling the execution of the link.
+        /// </summary>
+        public event EventHandler<HtmlLinkClickedEventArgs> LinkClicked
+        {
+            add { _htmlContainerInt.LinkClicked += value; }
+            remove { _htmlContainerInt.LinkClicked -= value; }
+        }
+
+        /// <summary>
+        /// Raised when html renderer requires refresh of the control hosting (invalidation and re-layout).
+        /// </summary>
+        /// <remarks>
+        /// There is no guarantee that the event will be raised on the main thread, it can be raised on thread-pool thread.
+        /// </remarks>
+        public event EventHandler<HtmlRefreshEventArgs> Refresh
+        {
+            add { _htmlContainerInt.Refresh += value; }
+            remove { _htmlContainerInt.Refresh -= value; }
+        }
+
+        /// <summary>
+        /// Raised when Html Renderer request scroll to specific location.<br/>
+        /// This can occur on document anchor click.
+        /// </summary>
+        public event EventHandler<HtmlScrollEventArgs> ScrollChange
+        {
+            add { _htmlContainerInt.ScrollChange += value; }
+            remove { _htmlContainerInt.ScrollChange -= value; }
+        }
+
+        /// <summary>
+        /// Raised when an error occurred during html rendering.<br/>
+        /// </summary>
+        /// <remarks>
+        /// There is no guarantee that the event will be raised on the main thread, it can be raised on thread-pool thread.
+        /// </remarks>
+        public event EventHandler<HtmlRenderErrorEventArgs> RenderError
+        {
+            add { _htmlContainerInt.RenderError += value; }
+            remove { _htmlContainerInt.RenderError -= value; }
+        }
+
+        /// <summary>
+        /// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
+        /// This event allows to provide the stylesheet manually or provide new source (file or Uri) to load from.<br/>
+        /// If no alternative data is provided the original source will be used.<br/>
+        /// </summary>
+        public event EventHandler<HtmlStylesheetLoadEventArgs> StylesheetLoad
+        {
+            add { _htmlContainerInt.StylesheetLoad += value; }
+            remove { _htmlContainerInt.StylesheetLoad -= value; }
+        }
+
+        /// <summary>
+        /// Raised when an image is about to be loaded by file path or URI.<br/>
+        /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
+        /// </summary>
+        public event EventHandler<HtmlImageLoadEventArgs> ImageLoad
+        {
+            add { _htmlContainerInt.ImageLoad += value; }
+            remove { _htmlContainerInt.ImageLoad -= value; }
+        }
+
+        /// <summary>
+        /// The internal core html container
+        /// </summary>
+        internal HtmlContainerInt HtmlContainerInt
+        {
+            get { return _htmlContainerInt; }
+        }
+
+        /// <summary>
+        /// the parsed stylesheet data used for handling the html
+        /// </summary>
+        public CssData CssData
+        {
+            get { return _htmlContainerInt.CssData; }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating if image asynchronous loading should be avoided (default - false).<br/>
+        /// True - images are loaded synchronously during html parsing.<br/>
+        /// False - images are loaded asynchronously to html parsing when downloaded from URL or loaded from disk.<br/>
+        /// </summary>
+        /// <remarks>
+        /// Asynchronously image loading allows to unblock html rendering while image is downloaded or loaded from disk using IO 
+        /// ports to achieve better performance.<br/>
+        /// Asynchronously image loading should be avoided when the full html content must be available during render, like render to image.
+        /// </remarks>
+        public bool AvoidAsyncImagesLoading
+        {
+            get { return _htmlContainerInt.AvoidAsyncImagesLoading; }
+            set { _htmlContainerInt.AvoidAsyncImagesLoading = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).<br/>
+        /// True - images are loaded as soon as the html is parsed.<br/>
+        /// False - images that are not visible because of scroll location are not loaded until they are scrolled to.
+        /// </summary>
+        /// <remarks>
+        /// Images late loading improve performance if the page contains image outside the visible scroll area, especially if there is large 
+        /// amount of images, as all image loading is delayed (downloading and loading into memory).<br/>
+        /// Late image loading may effect the layout and actual size as image without set size will not have actual size until they are loaded
+        /// resulting in layout change during user scroll.<br/>
+        /// Early image loading may also effect the layout if image without known size above the current scroll location are loaded as they
+        /// will push the html elements down.
+        /// </remarks>
+        public bool AvoidImagesLateLoading
+        {
+            get { return _htmlContainerInt.AvoidImagesLateLoading; }
+            set { _htmlContainerInt.AvoidImagesLateLoading = value; }
+        }
+
+        /// <summary>
+        /// Is content selection is enabled for the rendered html (default - true).<br/>
+        /// If set to 'false' the rendered html will be static only with ability to click on links.
+        /// </summary>
+        public bool IsSelectionEnabled
+        {
+            get { return _htmlContainerInt.IsSelectionEnabled; }
+            set { _htmlContainerInt.IsSelectionEnabled = value; }
+        }
+
+        /// <summary>
+        /// Is the build-in context menu enabled and will be shown on mouse right click (default - true)
+        /// </summary>
+        public bool IsContextMenuEnabled
+        {
+            get { return _htmlContainerInt.IsContextMenuEnabled; }
+            set { _htmlContainerInt.IsContextMenuEnabled = value; }
+        }
+
+        /// <summary>
+        /// The scroll offset of the html.<br/>
+        /// This will adjust the rendered html by the given offset so the content will be "scrolled".<br/>
+        /// </summary>
+        /// <example>
+        /// Element that is rendered at location (50,100) with offset of (0,200) will not be rendered as it
+        /// will be at -100 therefore outside the client Rect.
+        /// </example>
+        public Point ScrollOffset
+        {
+            get { return Util.Convert(_htmlContainerInt.ScrollOffset); }
+            set { _htmlContainerInt.ScrollOffset = Util.Convert(value); }
+        }
+
+        /// <summary>
+        /// The top-left most location of the rendered html.<br/>
+        /// This will offset the top-left corner of the rendered html.
+        /// </summary>
+        public Point Location
+        {
+            get { return Util.Convert(_htmlContainerInt.Location); }
+            set { _htmlContainerInt.Location = Util.Convert(value); }
+        }
+
+        /// <summary>
+        /// The max width and height of the rendered html.<br/>
+        /// The max width will effect the html layout wrapping lines, resize images and tables where possible.<br/>
+        /// The max height does NOT effect layout, but will not render outside it (clip).<br/>
+        /// <see cref="ActualSize"/> can be exceed the max size by layout restrictions (unwrappable line, set image size, etc.).<br/>
+        /// Set zero for unlimited (width\height separately).<br/>
+        /// </summary>
+        public Size MaxSize
+        {
+            get { return Util.Convert(_htmlContainerInt.MaxSize); }
+            set { _htmlContainerInt.MaxSize = Util.Convert(value); }
+        }
+
+        /// <summary>
+        /// The actual size of the rendered html (after layout)
+        /// </summary>
+        public Size ActualSize
+        {
+            get { return Util.Convert(_htmlContainerInt.ActualSize); }
+            internal set { _htmlContainerInt.ActualSize = Util.Convert(value); }
+        }
+
+        /// <summary>
+        /// Get the currently selected text segment in the html.
+        /// </summary>
+        public string SelectedText
+        {
+            get { return _htmlContainerInt.SelectedText; }
+        }
+
+        /// <summary>
+        /// Copy the currently selected html segment with style.
+        /// </summary>
+        public string SelectedHtml
+        {
+            get { return _htmlContainerInt.SelectedHtml; }
+        }
+
+        /// <summary>
+        /// Clear the current selection.
+        /// </summary>
+        public void ClearSelection()
+        {
+            HtmlContainerInt.ClearSelection();
+        }
+
+        /// <summary>
+        /// Init with optional document and stylesheet.
+        /// </summary>
+        /// <param name="htmlSource">the html to init with, init empty if not given</param>
+        /// <param name="baseCssData">optional: the stylesheet to init with, init default if not given</param>
+        public void SetHtml(string htmlSource, CssData baseCssData = null)
+        {
+            _htmlContainerInt.SetHtml(htmlSource, baseCssData);
+        }
+
+        /// <summary>
+        /// Clear the content of the HTML container releasing any resources used to render previously existing content.
+        /// </summary>
+        public void Clear()
+        {
+            _htmlContainerInt.Clear();
+        }
+
+        /// <summary>
+        /// Get html from the current DOM tree with style if requested.
+        /// </summary>
+        /// <param name="styleGen">Optional: controls the way styles are generated when html is generated (default: <see cref="HtmlGenerationStyle.Inline"/>)</param>
+        /// <returns>generated html</returns>
+        public string GetHtml(HtmlGenerationStyle styleGen = HtmlGenerationStyle.Inline)
+        {
+            return _htmlContainerInt.GetHtml(styleGen);
+        }
+
+        /// <summary>
+        /// Get attribute value of element at the given x,y location by given key.<br/>
+        /// If more than one element exist with the attribute at the location the inner most is returned.
+        /// </summary>
+        /// <param name="location">the location to find the attribute at</param>
+        /// <param name="attribute">the attribute key to get value by</param>
+        /// <returns>found attribute value or null if not found</returns>
+        public string GetAttributeAt(Point location, string attribute)
+        {
+            return _htmlContainerInt.GetAttributeAt(Util.Convert(location), attribute);
+        }
+
+        /// <summary>
+        /// Get all the links in the HTML with the element Rect and href data.
+        /// </summary>
+        /// <returns>collection of all the links in the HTML</returns>
+        public List<LinkElementData<Rect>> GetLinks()
+        {
+            var linkElements = new List<LinkElementData<Rect>>();
+            foreach (var link in HtmlContainerInt.GetLinks())
+            {
+                linkElements.Add(new LinkElementData<Rect>(link.Id, link.Href, Util.Convert(link.Rectangle)));
+            }
+            return linkElements;
+        }
+
+        /// <summary>
+        /// Get css link href at the given x,y location.
+        /// </summary>
+        /// <param name="location">the location to find the link at</param>
+        /// <returns>css link href if exists or null</returns>
+        public string GetLinkAt(Point location)
+        {
+            return _htmlContainerInt.GetLinkAt(Util.Convert(location));
+        }
+
+        /// <summary>
+        /// Get the Rect of html element as calculated by html layout.<br/>
+        /// Element if found by id (id attribute on the html element).<br/>
+        /// Note: to get the screen Rect you need to adjust by the hosting control.<br/>
+        /// </summary>
+        /// <param name="elementId">the id of the element to get its Rect</param>
+        /// <returns>the Rect of the element or null if not found</returns>
+        public Rect? GetElementRectangle(string elementId)
+        {
+            var r = _htmlContainerInt.GetElementRectangle(elementId);
+            return r.HasValue ? Util.Convert(r.Value) : (Rect?)null;
+        }
+
+        /// <summary>
+        /// Measures the bounds of box and children, recursively.
+        /// </summary>
+        public void PerformLayout()
+        {
+            using (var ig = new GraphicsAdapter())
+            {
+                _htmlContainerInt.PerformLayout(ig);
+            }
+        }
+
+        /// <summary>
+        /// Render the html using the given device.
+        /// </summary>
+        /// <param name="g">the device to use to render</param>
+        /// <param name="clip">the clip rectangle of the html container</param>
+        public void PerformPaint(IDrawingContext g, Rect clip)
+        {
+            ArgChecker.AssertArgNotNull(g, "g");
+
+            using (var ig = new GraphicsAdapter(g, Util.Convert(clip)))
+            {
+                _htmlContainerInt.PerformPaint(ig);
+            }
+        }
+
+        /// <summary>
+        /// Handle mouse down to handle selection.
+        /// </summary>
+        /// <param name="parent">the control hosting the html to invalidate</param>
+        /// <param name="e">the mouse event args</param>
+        public void HandleLeftMouseDown(Control parent, PointerEventArgs e)
+        {
+            ArgChecker.AssertArgNotNull(parent, "parent");
+            ArgChecker.AssertArgNotNull(e, "e");
+
+            _htmlContainerInt.HandleMouseDown(new ControlAdapter(parent), Util.Convert(e.GetPosition(parent)));
+        }
+
+        /// <summary>
+        /// Handle mouse up to handle selection and link click.
+        /// </summary>
+        /// <param name="parent">the control hosting the html to invalidate</param>
+        /// <param name="e">the mouse event args</param>
+        public void HandleLeftMouseUp(Control parent, PointerEventArgs e)
+        {
+            ArgChecker.AssertArgNotNull(parent, "parent");
+            ArgChecker.AssertArgNotNull(e, "e");
+
+            var mouseEvent = new RMouseEvent(true);
+            _htmlContainerInt.HandleMouseUp(new ControlAdapter(parent), Util.Convert(e.GetPosition(parent)), mouseEvent);
+        }
+
+        /// <summary>
+        /// Handle mouse double click to select word under the mouse.
+        /// </summary>
+        /// <param name="parent">the control hosting the html to set cursor and invalidate</param>
+        /// <param name="e">mouse event args</param>
+        public void HandleMouseDoubleClick(Control parent, PointerEventArgs e)
+        {
+            ArgChecker.AssertArgNotNull(parent, "parent");
+            ArgChecker.AssertArgNotNull(e, "e");
+
+            _htmlContainerInt.HandleMouseDoubleClick(new ControlAdapter(parent), Util.Convert(e.GetPosition(parent)));
+        }
+
+        /// <summary>
+        /// Handle mouse move to handle hover cursor and text selection.
+        /// </summary>
+        /// <param name="parent">the control hosting the html to set cursor and invalidate</param>
+        /// <param name="mousePos">the mouse event args</param>
+        public void HandleMouseMove(Control parent, Point mousePos)
+        {
+            ArgChecker.AssertArgNotNull(parent, "parent");
+
+            _htmlContainerInt.HandleMouseMove(new ControlAdapter(parent), Util.Convert(mousePos));
+        }
+
+        /// <summary>
+        /// Handle mouse leave to handle hover cursor.
+        /// </summary>
+        /// <param name="parent">the control hosting the html to set cursor and invalidate</param>
+        public void HandleMouseLeave(Control parent)
+        {
+            ArgChecker.AssertArgNotNull(parent, "parent");
+
+            _htmlContainerInt.HandleMouseLeave(new ControlAdapter(parent));
+        }
+
+        /// <summary>
+        /// Handle key down event for selection and copy.
+        /// </summary>
+        /// <param name="parent">the control hosting the html to invalidate</param>
+        /// <param name="e">the pressed key</param>
+        public void HandleKeyDown(Control parent, KeyEventArgs e)
+        {
+            ArgChecker.AssertArgNotNull(parent, "parent");
+            ArgChecker.AssertArgNotNull(e, "e");
+
+            _htmlContainerInt.HandleKeyDown(new ControlAdapter(parent), CreateKeyEevent(e));
+        }
+
+        /// <summary>
+        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+        /// </summary>
+        public void Dispose()
+        {
+            _htmlContainerInt.Dispose();
+        }
+
+
+        #region Private methods
+
+        /// <summary>
+        /// Create HtmlRenderer key event from Perspex key event.
+        /// </summary>
+        private static RKeyEvent CreateKeyEevent(KeyEventArgs e)
+        {
+            var control = (e.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
+            return new RKeyEvent(control, e.Key == Key.A, e.Key == Key.C);
+        }
+
+        #endregion
+    }
+}

+ 620 - 0
src/Perspex.HtmlRenderer/HtmlControl.cs

@@ -0,0 +1,620 @@
+// "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.Controls.Primitives;
+using Perspex.HtmlRenderer;
+using Perspex.Input;
+using Perspex.Interactivity;
+using Perspex.Media;
+using Perspex.Threading;
+using TheArtOfDev.HtmlRenderer.Core;
+using TheArtOfDev.HtmlRenderer.Core.Entities;
+
+namespace Perspex.Controls
+{
+    /// <summary>
+    /// Provides HTML rendering using the text property.<br/>
+    /// Perspex control that will render html content in it's client rectangle.<br/>
+    /// The control will handle mouse and keyboard events on it to support html text selection, copy-paste and mouse clicks.<br/>
+    /// <para>
+    /// The major differential to use HtmlPanel or HtmlLabel is size and scrollbars.<br/>
+    /// If the size of the control depends on the html content the HtmlLabel should be used.<br/>
+    /// If the size is set by some kind of layout then HtmlPanel is more suitable, also shows scrollbars if the html contents is larger than the control client rectangle.<br/>
+    /// </para>
+    /// <para>
+    /// <h4>LinkClicked event:</h4>
+    /// Raised when the user clicks on a link in the html.<br/>
+    /// Allows canceling the execution of the link.
+    /// </para>
+    /// <para>
+    /// <h4>StylesheetLoad event:</h4>
+    /// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
+    /// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.<br/>
+    /// If no alternative data is provided the original source will be used.<br/>
+    /// </para>
+    /// <para>
+    /// <h4>ImageLoad event:</h4>
+    /// Raised when an image is about to be loaded by file path or URI.<br/>
+    /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
+    /// </para>
+    /// <para>
+    /// <h4>RenderError event:</h4>
+    /// Raised when an error occurred during html rendering.<br/>
+    /// </para>
+    /// </summary>
+    
+    public class HtmlControl : Control
+    {
+        #region Fields and Consts
+
+        /// <summary>
+        /// Underline html container instance.
+        /// </summary>
+        protected readonly HtmlContainer _htmlContainer;
+
+        /// <summary>
+        /// the base stylesheet data used in the control
+        /// </summary>
+        protected CssData _baseCssData;
+
+        /// <summary>
+        /// The last position of the scrollbars to know if it has changed to update mouse
+        /// </summary>
+        protected Point _lastScrollOffset;
+
+        #endregion
+
+
+        #region Dependency properties / routed events
+
+        public static readonly PerspexProperty AvoidImagesLateLoadingProperty = 
+            PropertyHelper.Register<HtmlControl, bool>("AvoidImagesLateLoading", false, OnPerspexProperty_valueChanged);
+        public static readonly PerspexProperty IsSelectionEnabledProperty =
+            PropertyHelper.Register<HtmlControl, bool>("IsSelectionEnabled", true, OnPerspexProperty_valueChanged);
+        public static readonly PerspexProperty IsContextMenuEnabledProperty =
+            PropertyHelper.Register<HtmlControl, bool>("IsContextMenuEnabled", true, OnPerspexProperty_valueChanged);
+
+        public static readonly PerspexProperty BaseStylesheetProperty =
+            PropertyHelper.Register<HtmlControl, string>("BaseStylesheet", null, OnPerspexProperty_valueChanged);
+
+        public static readonly PerspexProperty TextProperty =
+            PropertyHelper.Register<HtmlControl, string>("Text", null, OnPerspexProperty_valueChanged);
+
+        public static readonly PerspexProperty BackgroundProperty =
+            PerspexProperty.Register<HtmlControl, Color>("Background", Colors.White);
+
+        public static readonly PerspexProperty BorderThicknessProperty =
+            PerspexProperty.Register<HtmlControl, Thickness>("BorderThickness", new Thickness(0));
+
+        public static readonly PerspexProperty PaddingProperty =
+            PerspexProperty.Register<HtmlControl, Thickness>("Padding", new Thickness(0));
+
+        public static readonly RoutedEvent LoadCompleteEvent =
+            RoutedEvent.Register<RoutedEventArgs>("LoadComplete",  RoutingStrategies.Bubble, typeof(HtmlControl));
+        public static readonly RoutedEvent LinkClickedEvent =
+            RoutedEvent.Register<RoutedEventArgsWrapper<HtmlLinkClickedEventArgs>>("LinkClicked", RoutingStrategies.Bubble, typeof(HtmlControl));
+        public static readonly RoutedEvent RenderErrorEvent 
+            = RoutedEvent.Register<RoutedEventArgsWrapper<HtmlRenderErrorEventArgs>>("RenderError", RoutingStrategies.Bubble, typeof(HtmlControl));
+        public static readonly RoutedEvent RefreshEvent 
+            = RoutedEvent.Register< RoutedEventArgsWrapper<HtmlRefreshEventArgs>>("Refresh", RoutingStrategies.Bubble, typeof(HtmlControl));
+        public static readonly RoutedEvent StylesheetLoadEvent 
+            = RoutedEvent.Register<RoutedEventArgsWrapper<HtmlStylesheetLoadEventArgs>>("StylesheetLoad", RoutingStrategies.Bubble, typeof(HtmlControl));
+
+        public static readonly RoutedEvent ImageLoadEvent
+            = RoutedEvent.Register<RoutedEventArgsWrapper<HtmlImageLoadEventArgs>>("ImageLoad", RoutingStrategies.Bubble,
+                typeof (HtmlControl));
+
+        #endregion
+
+
+        /// <summary>
+        /// Creates a new HtmlPanel and sets a basic css for it's styling.
+        /// </summary>
+        protected HtmlControl()
+        {
+            _htmlContainer = new HtmlContainer();/*
+            _htmlContainer.LoadComplete += OnLoadComplete;
+            _htmlContainer.LinkClicked += OnLinkClicked;
+            _htmlContainer.RenderError += OnRenderError;
+            _htmlContainer.Refresh += OnRefresh;
+            _htmlContainer.StylesheetLoad += OnStylesheetLoad;
+            _htmlContainer.ImageLoad += OnImageLoad;*/
+        }
+
+        /// <summary>
+        /// Raised when the set html document has been fully loaded.<br/>
+        /// Allows manipulation of the html dom, scroll position, etc.
+        /// </summary>
+        public event EventHandler LoadComplete
+        {
+            add { AddHandler(LoadCompleteEvent, value); }
+            remove { RemoveHandler(LoadCompleteEvent, value); }
+        }
+
+        /// <summary>
+        /// Raised when the user clicks on a link in the html.<br/>
+        /// Allows canceling the execution of the link.
+        /// </summary>
+        public event EventHandler<HtmlLinkClickedEventArgs> LinkClicked
+        {
+            add { AddHandler(LinkClickedEvent, value); }
+            remove { RemoveHandler(LinkClickedEvent, value); }
+        }
+
+        /// <summary>
+        /// Raised when an error occurred during html rendering.<br/>
+        /// </summary>
+        public event EventHandler<HtmlRenderErrorEventArgs> RenderError
+        {
+            add { AddHandler(RenderErrorEvent, value); }
+            remove { RemoveHandler(RenderErrorEvent, value); }
+        }
+
+        /// <summary>
+        /// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
+        /// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.<br/>
+        /// If no alternative data is provided the original source will be used.<br/>
+        /// </summary>
+        public event EventHandler<HtmlStylesheetLoadEventArgs> StylesheetLoad
+        {
+            add { AddHandler(StylesheetLoadEvent, value); }
+            remove { RemoveHandler(StylesheetLoadEvent, value); }
+        }
+
+        /// <summary>
+        /// Raised when an image is about to be loaded by file path or URI.<br/>
+        /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
+        /// </summary>
+        public event EventHandler<HtmlImageLoadEventArgs> ImageLoad
+        {
+            add { AddHandler(ImageLoadEvent, value); }
+            remove { RemoveHandler(ImageLoadEvent, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).<br/>
+        /// True - images are loaded as soon as the html is parsed.<br/>
+        /// False - images that are not visible because of scroll location are not loaded until they are scrolled to.
+        /// </summary>
+        /// <remarks>
+        /// Images late loading improve performance if the page contains image outside the visible scroll area, especially if there is large 
+        /// amount of images, as all image loading is delayed (downloading and loading into memory).<br/>
+        /// Late image loading may effect the layout and actual size as image without set size will not have actual size until they are loaded
+        /// resulting in layout change during user scroll.<br/>
+        /// Early image loading may also effect the layout if image without known size above the current scroll location are loaded as they
+        /// will push the html elements down.
+        /// </remarks>
+        [Category("Behavior")]
+        [Description("If image loading only when visible should be avoided")]
+        public bool AvoidImagesLateLoading
+        {
+            get { return (bool)GetValue(AvoidImagesLateLoadingProperty); }
+            set { SetValue(AvoidImagesLateLoadingProperty, value); }
+        }
+
+        /// <summary>
+        /// Is content selection is enabled for the rendered html (default - true).<br/>
+        /// If set to 'false' the rendered html will be static only with ability to click on links.
+        /// </summary>
+        [Category("Behavior")]
+        [Description("Is content selection is enabled for the rendered html.")]
+        public bool IsSelectionEnabled
+        {
+            get { return (bool)GetValue(IsSelectionEnabledProperty); }
+            set { SetValue(IsSelectionEnabledProperty, value); }
+        }
+
+        /// <summary>
+        /// Is the build-in context menu enabled and will be shown on mouse right click (default - true)
+        /// </summary>
+        [Category("Behavior")]
+        [Description("Is the build-in context menu enabled and will be shown on mouse right click.")]
+        public bool IsContextMenuEnabled
+        {
+            get { return (bool)GetValue(IsContextMenuEnabledProperty); }
+            set { SetValue(IsContextMenuEnabledProperty, value); }
+        }
+
+        /// <summary>
+        /// Set base stylesheet to be used by html rendered in the panel.
+        /// </summary>
+        [Category("Appearance")]
+        [Description("Set base stylesheet to be used by html rendered in the control.")]
+        public string BaseStylesheet
+        {
+            get { return (string)GetValue(BaseStylesheetProperty); }
+            set { SetValue(BaseStylesheetProperty, value); }
+        }
+
+        /// <summary>
+        /// Gets or sets the text of this panel
+        /// </summary>
+        [Description("Sets the html of this control.")]
+        public string Text
+        {
+            get { return (string)GetValue(TextProperty); }
+            set { SetValue(TextProperty, value); }
+        }
+
+        public Thickness BorderThickness
+        {
+            get { return (Thickness) GetValue(BorderThicknessProperty); }
+            set { SetValue(BorderThicknessProperty, value); }
+        }
+
+        public Thickness Padding
+        {
+            get { return (Thickness)GetValue(PaddingProperty); }
+            set { SetValue(PaddingProperty, value); }
+        }
+
+        public Color Background
+        {
+            get { return (Color?) GetValue(BackgroundProperty) ?? Colors.White; }
+            set { SetValue(BackgroundProperty, value);}
+        }
+
+        /// <summary>
+        /// Get the currently selected text segment in the html.
+        /// </summary>
+        [Browsable(false)]
+        public virtual string SelectedText
+        {
+            get { return _htmlContainer.SelectedText; }
+        }
+
+        /// <summary>
+        /// Copy the currently selected html segment with style.
+        /// </summary>
+        [Browsable(false)]
+        public virtual string SelectedHtml
+        {
+            get { return _htmlContainer.SelectedHtml; }
+        }
+
+        /// <summary>
+        /// Get html from the current DOM tree with inline style.
+        /// </summary>
+        /// <returns>generated html</returns>
+        public virtual string GetHtml()
+        {
+            return _htmlContainer != null ? _htmlContainer.GetHtml() : null;
+        }
+
+        /// <summary>
+        /// Get the rectangle of html element as calculated by html layout.<br/>
+        /// Element if found by id (id attribute on the html element).<br/>
+        /// Note: to get the screen rectangle you need to adjust by the hosting control.<br/>
+        /// </summary>
+        /// <param name="elementId">the id of the element to get its rectangle</param>
+        /// <returns>the rectangle of the element or null if not found</returns>
+        public virtual Rect? GetElementRectangle(string elementId)
+        {
+            return _htmlContainer != null ? _htmlContainer.GetElementRectangle(elementId) : null;
+        }
+
+        /// <summary>
+        /// Clear the current selection.
+        /// </summary>
+        public void ClearSelection()
+        {
+            if (_htmlContainer != null)
+                _htmlContainer.ClearSelection();
+        }
+
+
+        #region Private methods
+
+        //HACK: We don't have support for RenderSize for now
+        private Size RenderSize => new Size(Bounds.Width, Bounds.Height);
+
+        
+        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));
+                */
+
+            if (BorderThickness != new Thickness(0))
+            {
+                //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));
+                if (BorderThickness.Bottom > 0)
+                    context.FillRectange(brush, new Rect(0, RenderSize.Height - BorderThickness.Bottom, RenderSize.Width, BorderThickness.Bottom));
+                if (BorderThickness.Left > 0)
+                    context.FillRectange(brush, new Rect(0, 0, BorderThickness.Left, RenderSize.Height));
+                if (BorderThickness.Right > 0)
+                    context.FillRectange(brush, new Rect(RenderSize.Width - BorderThickness.Right, 0, BorderThickness.Right, RenderSize.Height));
+            }
+
+            var htmlWidth = HtmlWidth(RenderSize);
+            var htmlHeight = HtmlHeight(RenderSize);
+            if (_htmlContainer != null && htmlWidth > 0 && htmlHeight > 0)
+            {
+                /*
+                //TODO: Rever antialiasing fixes
+                var windows = Window.GetWindow(this);
+                if (windows != null)
+                {
+                    // adjust render location to round point so we won't get anti-alias smugness
+                    var wPoint = TranslatePoint(new Point(0, 0), windows);
+                    wPoint.Offset(-(int)wPoint.X, -(int)wPoint.Y);
+                    var xTrans = wPoint.X < .5 ? -wPoint.X : 1 - wPoint.X;
+                    var yTrans = wPoint.Y < .5 ? -wPoint.Y : 1 - wPoint.Y;
+                    context.PushTransform(new TranslateTransform(xTrans, yTrans));
+                }*/
+
+                using (context.PushClip(new Rect(Padding.Left + BorderThickness.Left, Padding.Top + BorderThickness.Top,
+                    htmlWidth, (int) htmlHeight)))
+                {
+                    _htmlContainer.Location = new Point(Padding.Left + BorderThickness.Left,
+                        Padding.Top + BorderThickness.Top);
+                    _htmlContainer.PerformPaint(context,
+                        new Rect(Padding.Left + BorderThickness.Left, Padding.Top + BorderThickness.Top, htmlWidth,
+                            htmlHeight));
+                }
+
+                if (!_lastScrollOffset.Equals(_htmlContainer.ScrollOffset))
+                {
+                    _lastScrollOffset = _htmlContainer.ScrollOffset;
+                    InvokeMouseMove();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Handle mouse move to handle hover cursor and text selection. 
+        /// </summary>
+        protected override void OnPointerMoved(PointerEventArgs e)
+        {
+            base.OnPointerMoved(e);
+            if (_htmlContainer != null)
+                _htmlContainer.HandleMouseMove(this, e.GetPosition(this));
+        }
+        /// <summary>
+        /// Handle mouse leave to handle cursor change.
+        /// </summary>
+        protected override void OnPointerLeave(PointerEventArgs e)
+        {
+            base.OnPointerLeave(e);
+            if (_htmlContainer != null)
+                _htmlContainer.HandleMouseLeave(this);
+        }
+
+        /// <summary>
+        /// Handle mouse down to handle selection. 
+        /// </summary>
+        protected override void OnPointerPressed(PointerPressEventArgs e)
+        {
+            base.OnPointerPressed(e);
+            _htmlContainer?.HandleLeftMouseDown(this, e);
+        }
+
+        
+
+        /// <summary>
+        /// Handle mouse up to handle selection and link click. 
+        /// </summary>
+        protected override void OnPointerReleased(PointerEventArgs e)
+        {
+            base.OnPointerReleased(e);
+            if (_htmlContainer != null)
+                _htmlContainer.HandleLeftMouseUp(this, e);
+        }
+
+        //TODO: Implement double click
+        /*
+        /// <summary>
+        /// Handle mouse double click to select word under the mouse. 
+        /// </summary>
+        protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
+        {
+            base.OnMouseDoubleClick(e);
+            if (_htmlContainer != null)
+                _htmlContainer.HandleMouseDoubleClick(this, e);
+        }
+        */
+        /// <summary>
+        /// Handle key down event for selection, copy and scrollbars handling.
+        /// </summary>
+        protected override void OnKeyDown(KeyEventArgs e)
+        {
+            base.OnKeyDown(e);
+            if (_htmlContainer != null)
+                _htmlContainer.HandleKeyDown(this, e);
+        }
+
+        /// <summary>
+        /// Propagate the LoadComplete event from root container.
+        /// </summary>
+        protected virtual void OnLoadComplete(EventArgs e)
+        {
+            //RoutedEventArgs newEventArgs = new RoutedEventArgs<EventArgs>(LoadCompleteEvent, this, e);
+            //RaiseEvent(newEventArgs);
+        }
+
+        /// <summary>
+        /// Propagate the LinkClicked event from root container.
+        /// </summary>
+        protected virtual void OnLinkClicked(HtmlLinkClickedEventArgs e)
+        {
+            //RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlLinkClickedEventArgs>(LinkClickedEvent, this, e);
+            //RaiseEvent(newEventArgs);
+        }
+
+        /// <summary>
+        /// Propagate the Render Error event from root container.
+        /// </summary>
+        protected virtual void OnRenderError(HtmlRenderErrorEventArgs e)
+        {
+            //RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlRenderErrorEventArgs>(RenderErrorEvent, this, e);
+            //RaiseEvent(newEventArgs);
+        }
+
+        /// <summary>
+        /// Propagate the stylesheet load event from root container.
+        /// </summary>
+        protected virtual void OnStylesheetLoad(HtmlStylesheetLoadEventArgs e)
+        {
+            //RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlStylesheetLoadEventArgs>(StylesheetLoadEvent, this, e);
+            //RaiseEvent(newEventArgs);
+        }
+
+        /// <summary>
+        /// Propagate the image load event from root container.
+        /// </summary>
+        protected virtual void OnImageLoad(HtmlImageLoadEventArgs e)
+        {
+            //RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlImageLoadEventArgs>(ImageLoadEvent, this, e);
+            //RaiseEvent(newEventArgs);
+        }
+
+        /// <summary>
+        /// Handle html renderer invalidate and re-layout as requested.
+        /// </summary>
+        protected virtual void OnRefresh(HtmlRefreshEventArgs e)
+        {
+            if (e.Layout)
+                InvalidateMeasure();
+            InvalidateVisual();
+        }
+
+        /// <summary>
+        /// Get the width the HTML has to render in (not including vertical scroll iff it is visible)
+        /// </summary>
+        protected virtual double HtmlWidth(Size size)
+        {
+            return size.Width - Padding.Left - Padding.Right - BorderThickness.Left - BorderThickness.Right;
+        }
+
+        /// <summary>
+        /// Get the width the HTML has to render in (not including vertical scroll iff it is visible)
+        /// </summary>
+        protected virtual double HtmlHeight(Size size)
+        {
+            return size.Height - Padding.Top - Padding.Bottom - BorderThickness.Top - BorderThickness.Bottom;
+        }
+
+        /// <summary>
+        /// call mouse move to handle paint after scroll or html change affecting mouse cursor.
+        /// </summary>
+        protected virtual void InvokeMouseMove()
+        {
+            
+            _htmlContainer.HandleMouseMove(this, MouseDevice.Instance.GetPosition(this));
+        }
+
+        /// <summary>
+        /// Handle when dependency property value changes to update the underline HtmlContainer with the new value.
+        /// </summary>
+        private static void OnPerspexProperty_valueChanged(PerspexObject PerspexObject, PerspexPropertyChangedEventArgs e)
+        {
+            var control = PerspexObject as HtmlControl;
+            if (control != null)
+            {
+                var htmlContainer = control._htmlContainer;
+                if (e.Property == AvoidImagesLateLoadingProperty)
+                {
+                    htmlContainer.AvoidImagesLateLoading = (bool)e.NewValue;
+                }
+                else if (e.Property == IsSelectionEnabledProperty)
+                {
+                    htmlContainer.IsSelectionEnabled = (bool)e.NewValue;
+                }
+                else if (e.Property == IsContextMenuEnabledProperty)
+                {
+                    htmlContainer.IsContextMenuEnabled = (bool)e.NewValue;
+                }
+                else if (e.Property == BaseStylesheetProperty)
+                {
+                    var baseCssData = HtmlRender.ParseStyleSheet((string)e.NewValue);
+                    control._baseCssData = baseCssData;
+                    htmlContainer.SetHtml(control.Text, baseCssData);
+                }
+                else if (e.Property == TextProperty)
+                {
+                    htmlContainer.ScrollOffset = new Point(0, 0);
+                    htmlContainer.SetHtml((string)e.NewValue, control._baseCssData);
+                    control.InvalidateMeasure();
+                    control.InvalidateVisual();
+                    control.InvokeMouseMove();
+                }
+            }
+        }
+
+
+        #region Private event handlers
+
+
+        /* TODO: Implement events
+        private void OnLoadComplete(object sender, EventArgs e)
+        {
+
+            if (CheckAccess())
+                OnLoadComplete(e);
+            else
+                Dispatcher.Invoke(new Action<HtmlLinkClickedEventArgs>(OnLinkClicked), e);
+
+        }
+
+        private void OnLinkClicked(object sender, HtmlLinkClickedEventArgs e)
+        {
+            if (CheckAccess())
+                OnLinkClicked(e);
+            else
+                Dispatcher.Invoke(new Action<HtmlLinkClickedEventArgs>(OnLinkClicked), e);
+        }
+
+        private void OnRenderError(object sender, HtmlRenderErrorEventArgs e)
+        {
+            if (CheckAccess())
+                OnRenderError(e);
+            else
+                Dispatcher.Invoke(new Action<HtmlRenderErrorEventArgs>(OnRenderError), e);
+        }
+
+        private void OnStylesheetLoad(object sender, HtmlStylesheetLoadEventArgs e)
+        {
+            if (CheckAccess())
+                OnStylesheetLoad(e);
+            else
+                Dispatcher.Invoke(new Action<HtmlStylesheetLoadEventArgs>(OnStylesheetLoad), e);
+        }
+
+        private void OnImageLoad(object sender, HtmlImageLoadEventArgs e)
+        {
+            if (CheckAccess())
+                OnImageLoad(e);
+            else
+                Dispatcher.Invoke(new Action<HtmlImageLoadEventArgs>(OnImageLoad), e);
+        }
+
+        private void OnRefresh(object sender, HtmlRefreshEventArgs e)
+        {
+            if (CheckAccess())
+                OnRefresh(e);
+            else
+                Dispatcher.Invoke(new Action<HtmlRefreshEventArgs>(OnRefresh), e);
+        }
+        */
+
+        #endregion
+
+
+        #endregion
+    }
+}

+ 135 - 0
src/Perspex.HtmlRenderer/HtmlLabel.cs

@@ -0,0 +1,135 @@
+// "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.HtmlRenderer;
+using Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core;
+using TheArtOfDev.HtmlRenderer.Perspex.Adapters;
+
+namespace Perspex.Controls
+{
+    /// <summary>
+    /// Provides HTML rendering using the text property.<br/>
+    /// WPF control that will render html content in it's client rectangle.<br/>
+    /// Using <see cref="AutoSize"/> and <see cref="AutoSizeHeightOnly"/> client can control how the html content effects the
+    /// size of the label. Either case scrollbars are never shown and html content outside of client bounds will be clipped.
+    /// MaxWidth/MaxHeight and MinWidth/MinHeight with AutoSize can limit the max/min size of the control<br/>
+    /// The control will handle mouse and keyboard events on it to support html text selection, copy-paste and mouse clicks.<br/>
+    /// </summary>
+    /// <remarks>
+    /// See <see cref="HtmlControl"/> for more info.
+    /// </remarks>
+    public class HtmlLabel : HtmlControl
+    {
+        #region Dependency properties
+
+        public static readonly PerspexProperty AutoSizeProperty = PropertyHelper.Register<HtmlLabel, bool>("AutoSize", true, OnPerspexProperty_valueChanged);
+        public static readonly PerspexProperty AutoSizeHeightOnlyProperty = PropertyHelper.Register<HtmlLabel, bool>("AutoSizeHeightOnly", false, OnPerspexProperty_valueChanged);
+
+        #endregion
+
+
+        /// <summary>
+        /// Init.
+        /// </summary>
+        static HtmlLabel()
+        {
+            //BackgroundProperty.OverrideDefaultValue<HtmlLabel>(Brushes.Transparent);
+        }
+
+        /// <summary>
+        /// Automatically sets the size of the label by content size
+        /// </summary>
+        [Category("Layout")]
+        [Description("Automatically sets the size of the label by content size.")]
+        public bool AutoSize
+        {
+            get { return (bool)GetValue(AutoSizeProperty); }
+            set { SetValue(AutoSizeProperty, value); }
+        }
+
+        /// <summary>
+        /// Automatically sets the height of the label by content height (width is not effected).
+        /// </summary>
+        [Category("Layout")]
+        [Description("Automatically sets the height of the label by content height (width is not effected)")]
+        public virtual bool AutoSizeHeightOnly
+        {
+            get { return (bool)GetValue(AutoSizeHeightOnlyProperty); }
+            set { SetValue(AutoSizeHeightOnlyProperty, value); }
+        }
+
+
+        #region Private methods
+
+        /// <summary>
+        /// Perform the layout of the html in the control.
+        /// </summary>
+        protected override Size MeasureOverride(Size constraint)
+        {
+            if (_htmlContainer != null)
+            {
+                using (var ig = new GraphicsAdapter())
+                {
+                    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 newSize = HtmlRendererUtils.Layout(ig, _htmlContainer.HtmlContainerInt, size, minSize, maxSize, AutoSize, AutoSizeHeightOnly);
+
+                    constraint = new Size(newSize.Width + horizontal, newSize.Height + vertical);
+                }
+            }
+
+            if (double.IsPositiveInfinity(constraint.Width) || double.IsPositiveInfinity(constraint.Height))
+                constraint = Size.Empty;
+
+            return constraint;
+        }
+
+        /// <summary>
+        /// Handle when dependency property value changes to update the underline HtmlContainer with the new value.
+        /// </summary>
+        private static void OnPerspexProperty_valueChanged(PerspexObject PerspexObject, PerspexPropertyChangedEventArgs e)
+        {
+            var control = PerspexObject as HtmlLabel;
+            if (control != null)
+            {
+                if (e.Property == AutoSizeProperty)
+                {
+                    if ((bool)e.NewValue)
+                    {
+                        PerspexObject.SetValue(AutoSizeHeightOnlyProperty, false);
+                        control.InvalidateMeasure();
+                        control.InvalidateVisual();
+                    }
+                }
+                else if (e.Property == AutoSizeHeightOnlyProperty)
+                {
+                    if ((bool)e.NewValue)
+                    {
+                        PerspexObject.SetValue(AutoSizeProperty, false);
+                        control.InvalidateMeasure();
+                        control.InvalidateVisual();
+                    }
+                }
+            }
+        }
+
+        #endregion
+    }
+}

+ 424 - 0
src/Perspex.HtmlRenderer/HtmlRender.cs

@@ -0,0 +1,424 @@
+// "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
+        */
+    }
+}

+ 198 - 0
src/Perspex.HtmlRenderer/Perspex.HtmlRenderer.csproj

@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Perspex.HtmlRenderer</RootNamespace>
+    <AssemblyName>Perspex.HtmlRenderer</AssemblyName>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;PCL</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <!-- A reference to the entire .NET Framework is automatically included -->
+    <Content Include="external\Source\HtmlRenderer\Core\Utils\ImageError.png" />
+    <Content Include="external\Source\HtmlRenderer\Core\Utils\ImageLoad.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Adapters\BrushAdapter.cs" />
+    <Compile Include="Adapters\ContextMenuAdapter.cs" />
+    <Compile Include="Adapters\ControlAdapter.cs" />
+    <Compile Include="Adapters\FontAdapter.cs" />
+    <Compile Include="Adapters\FontFamilyAdapter.cs" />
+    <Compile Include="Adapters\GraphicsAdapter.cs" />
+    <Compile Include="Adapters\GraphicsPathAdapter.cs" />
+    <Compile Include="Adapters\ImageAdapter.cs" />
+    <Compile Include="Adapters\PenAdapter.cs" />
+    <Compile Include="Adapters\PerspexAdapter.cs" />
+    <Compile Include="Compat\Attributes.cs" />
+    <Compile Include="Compat\ThreadPool.cs" />
+    <Compile Include="external\Source\HtmlRenderer.Pcl\PclCompat.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RColor.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RDashStyle.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RFontStyle.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RKeyEvent.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RMouseEvent.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RPoint.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RRect.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RSize.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RAdapter.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RBrush.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RContextMenu.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RControl.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RFont.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RFontFamily.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RGraphics.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RGraphicsPath.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RImage.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Adapters\RPen.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\CssData.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\CssDefaults.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\Border.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBox.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxFrame.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxHr.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxImage.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxProperties.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLayoutEngine.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLayoutEngineTable.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLength.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLineBox.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssRect.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssRectImage.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssRectWord.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssSpacingBox.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\CssUnit.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\HoverBoxBlock.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Dom\HtmlTag.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\CssBlock.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\CssBlockSelectorItem.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlGenerationStyle.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlImageLoadEventArgs.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlLinkClickedEventArgs.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlLinkClickedException.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlRefreshEventArgs.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlRenderErrorEventArgs.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlRenderErrorType.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlScrollEventArgs.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlStylesheetLoadEventArgs.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Entities\LinkElementData.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\BackgroundImageDrawHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\BordersDrawHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\ContextMenuHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\FontsHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\ImageDownloader.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\ImageLoadHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\SelectionHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Handlers\StylesheetLoadHandler.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\HtmlContainerInt.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\HtmlRendererUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Parse\CssParser.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Parse\CssValueParser.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Parse\DomParser.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Parse\HtmlParser.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Parse\RegexParserHelper.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Parse\RegexParserUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\ArgChecker.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\CommonUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\CssConstants.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\CssUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\DomUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\HtmlConstants.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\HtmlUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\RenderUtils.cs" />
+    <Compile Include="external\Source\HtmlRenderer\Core\Utils\SubString.cs" />
+    <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" />
+    <Compile Include="Utilities\Util.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Perspex.Animation\Perspex.Animation.csproj">
+      <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
+      <Name>Perspex.Animation</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.Base\Perspex.Base.csproj">
+      <Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
+      <Name>Perspex.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.Controls\Perspex.Controls.csproj">
+      <Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
+      <Name>Perspex.Controls</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.Input\Perspex.Input.csproj">
+      <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
+      <Name>Perspex.Input</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.Interactivity\Perspex.Interactivity.csproj">
+      <Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
+      <Name>Perspex.Interactivity</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.Layout\Perspex.Layout.csproj">
+      <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
+      <Name>Perspex.Layout</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
+      <Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project>
+      <Name>Perspex.SceneGraph</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Perspex.Styling\Perspex.Styling.csproj">
+      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
+      <Name>Perspex.Styling</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Interfaces.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+  <Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
+  <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
+    <Error Condition="!Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
+    <Error Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
+  </Target>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 2 - 0
src/Perspex.HtmlRenderer/Perspex.HtmlRenderer.csproj.DotSettings

@@ -0,0 +1,2 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp60</s:String></wpf:ResourceDictionary>

+ 30 - 0
src/Perspex.HtmlRenderer/Properties/AssemblyInfo.cs

@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Perspex.HtmlRenderer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Perspex.HtmlRenderer")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 24 - 0
src/Perspex.HtmlRenderer/PropertyHelper.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Perspex.HtmlRenderer
+{
+    static class PropertyHelper
+    {
+
+        public static PerspexProperty Register<TOwner, T>(string name, T def, Action<PerspexObject, PerspexPropertyChangedEventArgs> changed) where TOwner : PerspexObject
+        {
+            var pp = PerspexProperty.Register<TOwner, T>(name, def);
+            Action<PerspexPropertyChangedEventArgs> cb = args =>
+            {
+                changed(args.Sender, args);
+            };
+
+            pp.Changed.Subscribe(cb);
+            return pp;
+        }
+    }
+}

+ 14 - 0
src/Perspex.HtmlRenderer/RoutedEventArgsWrapper.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Perspex.Interactivity;
+
+namespace Perspex.HtmlRenderer
+{
+    public class RoutedEventArgsWrapper<T> : RoutedEventArgs
+    {
+        public T Event { get; set; }
+    }
+}

+ 123 - 0
src/Perspex.HtmlRenderer/Utilities/Util.cs

@@ -0,0 +1,123 @@
+// "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 Perspex;
+using Perspex.Media;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+
+namespace TheArtOfDev.HtmlRenderer.Perspex.Utilities
+{
+    /// <summary>
+    /// Utilities for converting WPF entities to HtmlRenderer core entities.
+    /// </summary>
+    internal static class Util
+    {
+        /// <summary>
+        /// Convert from WPF point to core point.
+        /// </summary>
+        public static RPoint Convert(Point p)
+        {
+            return new RPoint(p.X, p.Y);
+        }
+
+        /// <summary>
+        /// Convert from WPF point to core point.
+        /// </summary>
+        public static Point[] Convert(RPoint[] points)
+        {
+            Point[] myPoints = new Point[points.Length];
+            for (int i = 0; i < points.Length; i++)
+                myPoints[i] = Convert(points[i]);
+            return myPoints;
+        }
+
+        /// <summary>
+        /// Convert from core point to WPF point.
+        /// </summary>
+        public static Point Convert(RPoint p)
+        {
+            return new Point(p.X, p.Y);
+        }
+
+        /// <summary>
+        /// Convert from core point to WPF point.
+        /// </summary>
+        public static Point ConvertRound(RPoint p)
+        {
+            return new Point((int)p.X, (int)p.Y);
+        }
+
+        /// <summary>
+        /// Convert from WPF size to core size.
+        /// </summary>
+        public static RSize Convert(Size s)
+        {
+            return new RSize(s.Width, s.Height);
+        }
+
+        /// <summary>
+        /// Convert from core size to WPF size.
+        /// </summary>
+        public static Size Convert(RSize s)
+        {
+            return new Size(s.Width, s.Height);
+        }
+
+        /// <summary>
+        /// Convert from core point to WPF point.
+        /// </summary>
+        public static Size ConvertRound(RSize s)
+        {
+            return new Size((int)s.Width, (int)s.Height);
+        }
+
+        /// <summary>
+        /// Convert from WPF rectangle to core rectangle.
+        /// </summary>
+        public static RRect Convert(Rect r)
+        {
+            return new RRect(r.X, r.Y, r.Width, r.Height);
+        }
+
+        /// <summary>
+        /// Convert from core rectangle to WPF rectangle.
+        /// </summary>
+        public static Rect Convert(RRect r)
+        {
+            return new Rect(r.X, r.Y, r.Width, r.Height);
+        }
+
+        /// <summary>
+        /// Convert from core rectangle to WPF rectangle.
+        /// </summary>
+        public static Rect ConvertRound(RRect r)
+        {
+            return new Rect((int)r.X, (int)r.Y, (int)r.Width, (int)r.Height);
+        }
+
+        /// <summary>
+        /// Convert from WPF color to core color.
+        /// </summary>
+        public static RColor Convert(Color c)
+        {
+            return RColor.FromArgb(c.A, c.R, c.G, c.B);
+        }
+
+        /// <summary>
+        /// Convert from core color to WPF color.
+        /// </summary>
+        public static Color Convert(RColor c)
+        {
+            return Color.FromArgb(c.A, c.R, c.G, c.B);
+        }
+    }
+}

+ 1 - 0
src/Perspex.HtmlRenderer/external

@@ -0,0 +1 @@
+Subproject commit 8ae3973d6b766b2b286a8869c66470e6eba74ab1

+ 7 - 0
src/Perspex.HtmlRenderer/packages.config

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Microsoft.Bcl" version="1.1.10" targetFramework="portable45-net45+win8" />
+  <package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="portable45-net45+win8" />
+  <package id="Rx-Core" version="2.2.5" targetFramework="portable45-net45+win8" />
+  <package id="Rx-Interfaces" version="2.2.5" targetFramework="portable45-net45+win8" />
+</packages>