HTML Renderer 1.1.0.0

This is the first major release of my contribution to the project, the focus of this release is on:
1. Refactor the code for clearer, simpler and more powerful API (work in progress).
2. Improve simple html layout to be more compatible with WinForms WebBrowser control.
3. Add text selection with copy capability.
 
Following the changes list there is a deeper explanation to some of the changes.
 

Changes list

Features

  • Full text selection (with double tap full word selection).
  • Copy selected plain text.
  • Copy selected rich html text .
  • Drag and drop selected text.
  • Added bridge object functionality to call code from html by 'method' and 'property' keys.
  • More extensive HtmlRender static capabilities (added Measure html).
  • Generate html from DOM with or without style.
  • Scroll using keyboard.
  • Add LinkClicked event with handled capability.
  • Supports transparent background.
  • Compile to compact framework.
     

Broke

  • Preformatted text (pre, code tags and pre style), will fix soon.
  • Calling code from html to static property or method using fully qualified name, replaced with providing 'Bridge' object that it's properties and method can be called.
     

Fixes

  • Correct collapsing vertical margins.
  • Correct Line break handling (br tag).
  • Correct text whitespace handling.
  • Support single quoted attributes.
  • 20% performance improvement.
  • MaximumSize calculation in MeasureBounds.
  • Scrollbars on html panel on resize.
  • Inline images using data in img tag src attribute.
  • Font size localization parsing
  • Exception when clicking an anchor that has an URL with an equal sign
  • Scroll to top when setting new html in HtmlPanel
  • Style cascade for fixed blocks
     

Refactoring

  • Renamed assembly and namespace to "HtmlRenderer".
  • Renamed HtmlRenderer class to HtmlRender (namespace conflict)
  • Changed the way code is called from html renderer to use bridge object.
  • Exposed parsed stylesheet data with CssData and CssBlock classes.
  • Removed CssBox inheritance from InitialContainer and renamed it to RootContainer.
  • CssBox and all it's subclasses are internal.
  • Extracted stylesheet parsing code into CssParser class.
  • Extracted html parsing code into HtmlParser class.
  • Created CssBoxProperties class that CssBox inherits from to separate all css properties code.
  • Misc. code refactoring into utility classes (CssUtils, DomUtils, CssDrawingUtils)
     

Demo application

  • Added side by side view with WinForms WebBrowser control.
  • Added open in external browser.
  • Added run performance test.
  • Added "Test Samples" to available html for testing.
  • Improved syntax coloring in editor (attributes).
     

Details

Changing the name of the assembly and namespace

I have changed name of the assembly and namespace to "HtmlRenderer" from "System.Drawing.Html.HtmlRenderer" as it is my strong opinion that custom assembly should not have .NET framework names and namespaces, it should be clear that what you are looking at is custom code. With that I changed the "HtmlRenderer" class to "HtmlRender" to prevent collision with the namespace.
 

More like WinForms WebBrowser control rendering

Not that I'm a big fan of WebBrowser control that is based Internet Explorer ActiveX and MSHTML (this depends on the version of IE you have installed locally), it is probably the first thing you will look into when rendering html in your C# application so I want the Html Renderer to be as close as possible to it so you can copy-paste the html and see almost the same html rendering. It will never be identical as it is pointless to event try and in some cases there is no reason not to do it better by the basic layout and rendering I wanted to match. Running it on my machine with IE 9 and Chrome I saw that for basic html they render almost the same html.
This is why I changed the whitespace, br, hr, and vertical margin collapsing to better match WebBrowser control.
 

Adding bridge object

Previously if you wanted rendered html to execute code (calling method on click, getting image from property) you would specify "method" or "property" depending if you want to call method or property respectively then a colon following by the fully qualified name of the static method or property.
This approach has two problems:
1. It can access only static method\property which can create problems if you have more than one control or you need additional data from the control hosting it.
2. Finding the right method\property requires reflection that includes searching for the right type by name which can be expensive for multiple calls and large assemblies (being performance freak I really didn't like it).

I wanted the same experience you have developing WPF or ASP.NET where you specify the method in the code-behind class. Unfortunately there is no way, that I know about, you can do yourself so I decided to do the closes thing.
You can set "Bridge" property on the RootContainer, HtmlPanel, HtmlLabel or HtmlToolTip with any .net object (hosting control is the obvious choice) and this object will be used to search for the method\property to call. In the html you don't need to provide the full qualified name but only the name of the method\property, the "method:" and "property:" prefix is still required for simplicity and clarity.
 

Code and API refactoring

My goal here is to create clearer code with good separation of responsibility that is easier to modify (CssBox had more than 3,000 lines of code) so I separated the code to css and html parsers, utility classes, create CssBoxProperties to hold and css properties related code so CssBox class will be simpler. Changed the "InitialComponent" class to "RootContainer" that doesn't inherit CssBox but has prived "_root" field that is the root of the DOM CssBoxes tree. This allowed me to change "CssBox" visibility to "internal" so the API os much clearer now with "RootContainer" as the main access point that can be used as is including the complex scroll, mouse and keyboard functionality.
I have also created "CssData" class that holds the parsed stylesheet so it can be reused for performance.
HtmlPanel and HtmlLabel remain the simplest way to use the Html Renderer and I intend to keep it this way only adding ability for more complex scenarios.
 

Removing whitespace support (broking preformatted text)

Part of the html renderer implementation it splits each text word to separate entity that is measured and renderer separately, but also the spaces and newlines where separated into entities and measured as well. This created an issue where space could be moved to new line and was the first box to be rendered created unwanted indent. Also because each white space was measured and rendered separately it effected performance.
I have removed it adding a whitespace padding to each rendered word (like IE does). It improved lines layout and performance but broke the preformatted text as it was relying on it, will fix it soon.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s