diff --git a/HtmlRenderer.nupkg b/HtmlRenderer.nupkg index 0b2804e23..90c579250 100644 Binary files a/HtmlRenderer.nupkg and b/HtmlRenderer.nupkg differ diff --git a/README.md b/README.md index 3c55b7bde..7a63925df 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,20 @@ The library is **100% managed code** without any external dependencies, the only 1. 100% managed code. 3. No external dependencies. 3. Single and small dll (250K). - 4. Supports .NET 2.0 and 3.0, 3.5, 4.0 client profile. + 4. Supports .NET 2.0 and 3.0, 3.5, 4.0 client profile, 4.5 or higher. **What it is not** * -**Installing** +**NuGet package install** + 1. PM> Install-Package HtmlRenderer.WinForms + 2. Or from NuGet UI + +**Manual install** 1. Download the binaries. 2. Reference the proper .NET release (2.0, 3.0, 3.5, 4.0, 4.5) in your project. **Usage** - 1. Drag-n-drop HtmlPanel, HtmlLabel or HtmlToolTip from the Toolbox. - 2. Set the *Text* property with your html. + 1. Add HtmlRenderer to Visual Studio Toolbox (drag-drop the dll on it). + 2. Drag-n-drop HtmlPanel, HtmlLabel or HtmlToolTip from the Toolbox. + 3. Set the *Text* property with your html. diff --git a/Source/Demo/DemoForm.cs b/Source/Demo/DemoForm.cs index 0bde836c9..e79aea832 100644 --- a/Source/Demo/DemoForm.cs +++ b/Source/Demo/DemoForm.cs @@ -123,7 +123,9 @@ private void LoadSamples() { using (StreamReader sreader = new StreamReader(resourceStream, Encoding.Default)) { - _samples[name] = sreader.ReadToEnd(); + var html = sreader.ReadToEnd(); + html = html.Replace("$$Release$$", _htmlPanel.GetType().Assembly.GetName().Version.ToString()); + _samples[name] = html; } var node = new TreeNode(shortName); diff --git a/Source/Demo/HtmlRenderer.Demo.csproj b/Source/Demo/HtmlRenderer.Demo.csproj index 1dc779880..c3d246fb3 100644 --- a/Source/Demo/HtmlRenderer.Demo.csproj +++ b/Source/Demo/HtmlRenderer.Demo.csproj @@ -148,25 +148,25 @@ - + - + - + - + - + - + @@ -176,7 +176,7 @@ Designer SampleForm.cs - + @@ -186,7 +186,7 @@ - + @@ -221,21 +221,21 @@ - + - - - + + + - + diff --git a/Source/Demo/Samples/00.Intro.htm b/Source/Demo/Samples/00.Intro.htm index 1c699dabe..d0eb45f60 100644 --- a/Source/Demo/Samples/00.Intro.htm +++ b/Source/Demo/Samples/00.Intro.htm @@ -8,7 +8,7 @@

HTML Renderer Project
- Release 1.4.6.2 + Release $$Release$$

@@ -19,23 +19,23 @@

Everything you see on this panel (see samples on the left) is custom-painted - by the HTML Renderer. Including tables, images, links and videos.
- This project allows you can have the rich format power of HTML on you desktop applications - without WebBrowser control and MSHTML.
- The library is 100% managed code without any external libraries dependencies, - the only requirement is .NET 2.0 or higher. + by the HTML Renderer, including tables, images, links and videos.
+ This project allows you to have the rich format power of HTML on your desktop applications + without WebBrowser control or MSHTML.
+ The library is 100% managed code without any external dependencies, the only + requirement is .NET 2.0 or higher, including support for Client Profile.

- Text selection (copy-paste) + Text selection (copy to clipboard)

The rendered html has full support for text selection including drag-and-drop and copy to clipboard of rich html and plain text to handle paste operation to editor that support rich or/and plain text.
- Addtionally there is context-menu with select all, copy text, copy image, + Additionally there is a context-menu with select all, copy text, copy image, save image, open link, copy link url, open video, copy video url.

@@ -48,7 +48,7 @@

rounded corners.

- WinForms support + WinForms controls

It comes with handy WinForms controls (see Sample Form): @@ -59,18 +59,23 @@

auto size.
  • HtmlToolTip - For ToolTip with rich html.
  • +

    + Benefits +

    +
      +
    • 100% managed code and no external dependencies.
    • +
    • Supports .NET 2.0 or higher including Client Profile.
    • +
    • Handles "real world" malformed HTML, it doesn't have to be XHTML.
    • +
    • Lightweight single dll (~250K).
    • +
    • High performance and low memory footprint.
    • +
    • Extendable and configurable.
    • +

    Limitations

    -
    - Here are some warnings that you should know when using the capabilities of the HTML - Renderer: -
    • All HTML end tags marked as optional should be there. No problem with tags marked as forbidden.
    • -
    • When using rounded corners with borders, you must prevent the corner radius to be - larger than the border thickness

    diff --git a/Source/Demo/Samples/01.Background.htm b/Source/Demo/Samples/01.History.htm similarity index 69% rename from Source/Demo/Samples/01.Background.htm rename to Source/Demo/Samples/01.History.htm index 740dc5736..e0ad690de 100644 --- a/Source/Demo/Samples/01.Background.htm +++ b/Source/Demo/Samples/01.History.htm @@ -4,17 +4,17 @@

    - Background + History

    For years, I (Jose) have been planning for a project like this. I prepared - my self quite well. I went through the entire CSS Level 2 specifiation along with + my self quite well. I went through the entire CSS Level 2 specification along with the HTML 4.01 specification.

    One of the most interesting things I found is this: Drawing HTML is no more than - laying out a bunch of boxes with borders margins and paddings. Once you overpass + laying out a bunch of boxes with borders margins and padding's. Once you overpass this paradigm, everything else is to help the code actually place the boxes on the right place, and then paint the string each box contains.

    @@ -29,10 +29,20 @@

    all of the code on the library is managed code and the methods it use to paint are quite basic. It draws lines, rectangles, curves and text.

    -
    +

    In October 2012 I (Arthur) was looking to replace the usage of WinForms WebBrowser - control by fully managed code... + control by something that can render complex html and have good performance and stability + characteristics. Obviously I was looking for fully managed solution preferably one that + I will have full control over. +

    +

    + HTML Renderer project showed great promise but had significant performance issues, + lacked many features (primary text selection) and wasn't updated for more than 3 years. + Realizing there is no alternative I embraced the project making it my baby. +

    +

    +

    diff --git a/Source/Demo/Samples/02.Text.htm b/Source/Demo/Samples/02.Text.htm new file mode 100644 index 000000000..49a5486e6 --- /dev/null +++ b/Source/Demo/Samples/02.Text.htm @@ -0,0 +1,183 @@ + + + Text + + + + +

    Text +

    +
    +

    Formatting +

    +

    + You can use all the well known tags and CSS properties to format text, fonts and + colors. +

    +
      +
    • Colors, Colors, + Colors
    • +
    • Back colors, Back colors, Back colors
    • +
    • Font style, Font style, Font style, Font style, Font style
    • +
    +

    + Lorem ipsum dolor sit amet, + consectetur adipiscing elit. Curabitur ornare mollis elit. Integer sagittis. + Fusce elementum commodo felis. Vivamus lacinia eleifend libero. + Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. + Mauris a dolor eu elit rutrum commodo. Nam + iaculis turpis non augue. Nullam lobortis egestas risus. Nulla elementum dolor ac + mauris. Ut tristique. In varius volutpat metus. Integer leo dolor, tristique a, + dignissim ac, iaculis eget, elit. + Donec arcu. +

    +
    +

    Custom fonts +

    +

    + This is a custom font that is not installed on the system. +

    +
    +

    Alignment +

    +

    + Simple paragraphs can be used to create a document. Alignment can be used as you + already know. +

    +

    Left aligned +

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis + elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend + libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. + Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue. +

    +

    Center aligned

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis + elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend + libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. + Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue. +

    +

    Right aligned

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis + elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend + libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. + Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue. +

    +

    Justifed

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis + elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend + libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. + Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue. +

    +
    +

    Breakable lines

    + http://www.google.com/sjkdhgfasdfgfg/asdfadfsgaefg/adfgafdgadfg/asdfgaedfgsdfg/dasfgasfdgasdfg/adfgadfgasfdg/adfsgafgafg/afdgaddfgadfg/afsdgafdgaddfg/afsdgafgadqfdgaeddfg +
    +

    Transparent text

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit +

    +
    +

    Preformatted text

    +

    + The preformatted text is fully supported, like this C# code demo: +

    +
    +//Example of code using preformatted text
    +public class HelloWorld
    +{
    +    public HelloWorld()
    +    {
    +        MessageBox.Show("Hello World");
    +    }
    +}
    +
    +
    +

    Lists

    +

    + Both UL and OL tags are supported, though, all the CSS properties related with lists + are not still fully supported.. The maximum you will get is bullets and numbers. + Image bullets and better numbering support may be added by next release. +

    +

    Unordered list +

    +
      +
    • Item one
    • +
    • Item two +
        +
      • Sub item one
      • +
      • Sub item two
      • +
          +
        • Sub-sub item two
        • +
        • Sub-sub item two
        • +
        +
      +
    • +
    • Item three
    • +
    • Item four
    • +
    +

    Ordered list +

    +
      +
    1. Item one
    2. +
    3. Item two
    4. +
    5. Item three
    6. +
    +

    List nesting

    +
      +
    1. Item one
    2. +
    3. Item two
    4. +
    5. Item three +
        +
      • Item one
      • +
      • Item two
      • +
      • Item three
      • +
      +
    6. +
    7. Item four
    8. +
    9. Item five
    10. +
    +
    +
    +

    Right to left direction +

    +

    + It may not behave exactly like the specification says, but it can be useful if you + use it for right-to-left languages. All you have to do is to alter the direction + property like this: +

    +
    .myparagraph { direction:rtl; }
    +

    Left aligned +

    +

    + בניגוד לטענה הרווחת, Lorem Ipsum אינו סתם טקסט רנדומלי. יש לו שורשים וחלקים מתוך הספרות הלטינית הקלאסית מאז 45 לפני הספירה. מה שהופך אותו לעתיק מעל 2000 שנה. ריצ'רד מקלינטוק, פרופסור לטיני בקולג' של המפדן-סידני בורג'יניה, חיפש את אחת המילים המעורפלות ביותר בלטינית - consectetur - מתוך פסקאות של Lorem Ipsum ודרך ציטוטים של המילה מתוך הספרות הקלאסית, הוא גילה מקור בלתי ניתן לערעור. Lorem Ipsum מגיע מתוך מקטע 1.10.32 ו- 1.10.33 של "de Finibus Bonorum et Malorum" (הקיצוניות של הטוב והרע) שנכתב על ידי קיקרו ב-45 לפני הספירה. ספר זה הוא מאמר על תאוריית האתיקה, שהיה מאוד מפורסם בתקופת הרנסנס. השורה הראשונה של "Lorem ipsum dolor sit amet", שמופיעה בטקסטים של Lorem Ipsum, באה משורה במקטע 1.10.32 +

    +

    Center aligned +

    +

    + בניגוד לטענה הרווחת, Lorem Ipsum אינו סתם טקסט רנדומלי. יש לו שורשים וחלקים מתוך הספרות הלטינית הקלאסית מאז 45 לפני הספירה. מה שהופך אותו לעתיק מעל 2000 שנה. ריצ'רד מקלינטוק, פרופסור לטיני בקולג' של המפדן-סידני בורג'יניה, חיפש את אחת המילים המעורפלות ביותר בלטינית - consectetur - מתוך פסקאות של Lorem Ipsum ודרך ציטוטים של המילה מתוך הספרות הקלאסית, הוא גילה מקור בלתי ניתן לערעור. Lorem Ipsum מגיע מתוך מקטע 1.10.32 ו- 1.10.33 של "de Finibus Bonorum et Malorum" (הקיצוניות של הטוב והרע) שנכתב על ידי קיקרו ב-45 לפני הספירה. ספר זה הוא מאמר על תאוריית האתיקה, שהיה מאוד מפורסם בתקופת הרנסנס. השורה הראשונה של "Lorem ipsum dolor sit amet", שמופיעה בטקסטים של Lorem Ipsum, באה משורה במקטע 1.10.32 +

    +

    Right aligned +

    +

    + בניגוד לטענה הרווחת, Lorem Ipsum אינו סתם טקסט רנדומלי. יש לו שורשים וחלקים מתוך הספרות הלטינית הקלאסית מאז 45 לפני הספירה. מה שהופך אותו לעתיק מעל 2000 שנה. ריצ'רד מקלינטוק, פרופסור לטיני בקולג' של המפדן-סידני בורג'יניה, חיפש את אחת המילים המעורפלות ביותר בלטינית - consectetur - מתוך פסקאות של Lorem Ipsum ודרך ציטוטים של המילה מתוך הספרות הקלאסית, הוא גילה מקור בלתי ניתן לערעור. Lorem Ipsum מגיע מתוך מקטע 1.10.32 ו- 1.10.33 של "de Finibus Bonorum et Malorum" (הקיצוניות של הטוב והרע) שנכתב על ידי קיקרו ב-45 לפני הספירה. ספר זה הוא מאמר על תאוריית האתיקה, שהיה מאוד מפורסם בתקופת הרנסנס. השורה הראשונה של "Lorem ipsum dolor sit amet", שמופיעה בטקסטים של Lorem Ipsum, באה משורה במקטע 1.10.32 +

    +

    Justifed +

    +

    + בניגוד לטענה הרווחת, Lorem Ipsum אינו סתם טקסט רנדומלי. יש לו שורשים וחלקים מתוך הספרות הלטינית הקלאסית מאז 45 לפני הספירה. מה שהופך אותו לעתיק מעל 2000 שנה. ריצ'רד מקלינטוק, פרופסור לטיני בקולג' של המפדן-סידני בורג'יניה, חיפש את אחת המילים המעורפלות ביותר בלטינית - consectetur - מתוך פסקאות של Lorem Ipsum ודרך ציטוטים של המילה מתוך הספרות הקלאסית, הוא גילה מקור בלתי ניתן לערעור. Lorem Ipsum מגיע מתוך מקטע 1.10.32 ו- 1.10.33 של "de Finibus Bonorum et Malorum" (הקיצוניות של הטוב והרע) שנכתב על ידי קיקרו ב-45 לפני הספירה. ספר זה הוא מאמר על תאוריית האתיקה, שהיה מאוד מפורסם בתקופת הרנסנס. השורה הראשונה של "Lorem ipsum dolor sit amet", שמופיעה בטקסטים של Lorem Ipsum, באה משורה במקטע 1.10.32 +

    +
    +
    +
    + + diff --git a/Source/Demo/Samples/04.Tables.htm b/Source/Demo/Samples/03.Tables.htm similarity index 100% rename from Source/Demo/Samples/04.Tables.htm rename to Source/Demo/Samples/03.Tables.htm diff --git a/Source/Demo/Samples/03.Text.htm b/Source/Demo/Samples/03.Text.htm deleted file mode 100644 index bb6f00c32..000000000 --- a/Source/Demo/Samples/03.Text.htm +++ /dev/null @@ -1,200 +0,0 @@ - - - Text - - - - -

    - Text -

    -
    -

    - Formatting -

    -

    - You can use all the well known tags and CSS properties to format text, fonts and - colors. -

    -
      -
    • Colors, Colors, - Colors
    • -
    • Back colors, - Back colors, Back colors
    • -
    • Font style, - Font style, Font style, - Font style, Font style
    • -
    -

    - Lorem ipsum dolor sit amet, - consectetur adipiscing elit. Curabitur ornare mollis elit. Integer sagittis. - Fusce elementum commodo felis. Vivamus lacinia eleifend libero. - Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam - iaculis turpis non augue. Nullam lobortis egestas risus. Nulla elementum dolor ac - mauris. Ut tristique. In varius volutpat metus. Integer leo dolor, tristique a, - dignissim ac, iaculis eget, elit. - Donec arcu.

    -
    -

    - Custom fonts -

    -

    - This is a custom font that is not installed on the system. -

    -
    -

    - Alignment -

    -

    - Simple paragraphs can be used to create a document. Alignment can be used as you - already know. -

    -

    - Left aligned -

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -

    - Center aligned

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -

    - Right aligned

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -

    - Justifed

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -
    -

    Breakable lines

    - http://www.google.com/sjkdhgfasdfgfg/asdfadfsgaefg/adfgafdgadfg/asdfgaedfgsdfg/dasfgasfdgasdfg/adfgadfgasfdg/adfsgafgafg/afdgaddfgadfg/afsdgafdgaddfg/afsdgafgadqfdgaeddfg -
    -

    - Preformatted text

    -

    - The preformatted text is fully supported, like this C# code demo:

    -
    -//Example of code using preformatted text
    -public class HelloWorld
    -{
    -    public HelloWorld()
    -    {
    -        MessageBox.Show("Hello World");
    -    }
    -}
    -
    -
    -

    - Lists

    -

    - Both UL and OL tags are supported, though, all the CSS properties related with lists - are not still fully supported.. The maximum you will get is bullets and numbers. - Image bullets and better numbering support may be added by next release.

    -

    - Unordered list -

    -
      -
    • Item one
    • -
    • Item two -
        -
      • Sub item one
      • -
      • Sub item two
      • -
          -
        • Sub-sub item two
        • -
        • Sub-sub item two
        • -
        -
      -
    • -
    • Item three
    • -
    • Item four
    • -
    -

    - Ordered list -

    -
      -
    1. Item one
    2. -
    3. Item two
    4. -
    5. Item three
    6. -
    -

    - List nesting

    -
      -
    1. Item one
    2. -
    3. Item two
    4. -
    5. Item three -
        -
      • Item one
      • -
      • Item two
      • -
      • Item three
      • -
      -
    6. -
    7. Item four
    8. -
    9. Item five
    10. -
    -
    -
    -

    - Right to left direction -

    -

    - It may not behave exactly like the specification says, but it can be useful if you - use it for right-to-left languages. All you have to do is to alter the direction - property like this:

    -
    .myparagraph { direction:rtl; }
    -

    - Left aligned -

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -

    - Center aligned -

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -

    - Right aligned -

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -

    - Justifed -

    -

    - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis - elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend - libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. - Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue.

    -
    -
    -
    - - diff --git a/Source/Demo/Samples/04.Links.htm b/Source/Demo/Samples/04.Links.htm new file mode 100644 index 000000000..8485a0914 --- /dev/null +++ b/Source/Demo/Samples/04.Links.htm @@ -0,0 +1,68 @@ + + + Links + + + +

    Links +

    +
    +

    + HTML Renderer supports all html hyperlinks specification and code interception. +

    +

    +

    URI href

    + Any valid URI path will activate the default internet browser. +
    + Check the context menu options on the link as well by right clicking on it. +
    + This is a URI link to HTML Renderer (href="https://htmlrenderer.codeplex.com/") +

    +

    +

    File path href

    + Any file system path, It will be started as if you typed it on the Run Windows dialog. +
    + This is a link to hard drive (href="C:\") +

    +

    +

    Anchors href

    + Link to elements on the page using element id will scroll to that element so it will be at the top. +
    + This is a link to anchor at the bottom of the page (href="#anchor") +

    +

    +

    Intercept

    + Any link click can be intercepted by LinkClicked event, analyzed by link 'href' value or + other attributes of the link element and cancel the default processing those allowing custom handling. +
    + This link is intercepted to show message box Hello! (href="SayHello") + +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + Anchor here +

    +
    + + diff --git a/Source/Demo/Samples/05.Images.htm b/Source/Demo/Samples/05.Images.htm index 6a58648b5..bd76e5b3a 100644 --- a/Source/Demo/Samples/05.Images.htm +++ b/Source/Demo/Samples/05.Images.htm @@ -3,89 +3,100 @@ -

    - Images +

    Images

    - Of course, there's the capability of displaying images. In this release, images - lack of the expected alignment features to behave just as expected on an HTML context, - but they will do just fine for inline contexts, tables and some others.

    -

    - Inserting images + HTML Renderer supports img tag as well as CSS background-image property. +
    + Image data can be provided by URI, file path, base64 encoded and code interception. + +

    + +

    Loading image

    - We all know that the src attribute of the img HTML tag - can be a relative or absolute path to an image on the same or any other server. - Here things are slightly different. Images can come from other sources:

    -
      -
    • File Paths: If you place a path to a file, the Renderer will try to load - it using Image.FromFile
    • -
    • Static Properties: Obtains the image from a static property that returns - an Image object
    • -
    • Static Methods: Obtains the image from a static method that takes no arguments - and returns an Image
    • -
    -

    - Positioning Images -

    +

    URI

    +

    - You can use Images anyway you like, apply borders margin and padding as for any - other box. +

    File path

    +

    +

    +

    Base64 encoded

    - Some examples of image positioning: +

    Intercept

    +

    +

    img tag

    +

    + You can use Images anyway you like, apply borders margin and padding as for any + other box. +

    + Limitation +
    + Image align attribute and CSS float property are not yet supported. +

    - - - - -
    - Just an image: + Just an image:
    - Image with border and background: + Image with border and background:
    - Stretched Image: + Stretched Image:
    - Huge padding and border: + Huge padding and border:
    - Image in line + Image in line with the text
    + +

    Background images +

    +
    diff --git a/Source/Demo/Samples/08.Embeded video.htm b/Source/Demo/Samples/06.Embeded video.htm similarity index 94% rename from Source/Demo/Samples/08.Embeded video.htm rename to Source/Demo/Samples/06.Embeded video.htm index 8ef95f5cf..6abf0f656 100644 --- a/Source/Demo/Samples/08.Embeded video.htm +++ b/Source/Demo/Samples/06.Embeded video.htm @@ -11,7 +11,7 @@

    Embeded video is an 'iframe' with 'src' pointing to the video hosted on the server.
    Obviously the Html Renderer doesn't support iframes so don't expect to see ActiveX in the control that will stream the video, but the video thumbnail image, caption - string and play visualization is shown where clicking on it will open the video + string and play visualization is shown. Clicking on it will open the video page on its site.

    diff --git a/Source/Demo/Samples/09.Non standard.htm b/Source/Demo/Samples/07.Additional features.htm similarity index 93% rename from Source/Demo/Samples/09.Non standard.htm rename to Source/Demo/Samples/07.Additional features.htm index e3d922971..5f49136c3 100644 --- a/Source/Demo/Samples/09.Non standard.htm +++ b/Source/Demo/Samples/07.Additional features.htm @@ -1,6 +1,6 @@ - Non standard features + Additional features -

    Case 1

    +

    Case 1

    Warning:P tags must be closed. In fact all tags with the end tag marked as optional, must be closed. It may be fixed by next release.


    -

    Text align justify with background colors

    +

    Text align justify with background colors

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing - elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua tempor incididunt ut labore et dolore magna aliqua incididunt ut labore et dolore magna aliqua. +
    + Lorem ipsum dolor sit amet, consectetur adipisicing + elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua tempor incididunt ut labore et dolore magna aliqua incididunt ut labore et dolore magna aliqua.


    -

    Transparent text

    +

    Right align adjusts

    + + + + + + + +
    978
    32
    +
    +

    Transparent text

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend libero. Donec lacus. Nam sit amet urna. Nullam nulla. Donec accumsan porta magna. Mauris a dolor eu elit rutrum commodo. Nam iaculis turpis non augue. Nullam lobortis egestas risus. Nulla elementum dolor ac mauris. Ut tristique. In varius volutpat metus. Integer leo dolor, tristique a, dignissim ac, iaculis eget, elit. Donec arcu.

    - +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis elit. Integer sagittis. Fusce elementum commodo felis. Vivamus lacinia eleifend libero. Donec @@ -46,6 +65,18 @@

    Transparent text

    elementum dolor ac mauris. Ut tristique. In varius volutpat metus. Integer leo dolor, tristique a, dignissim ac, iaculis eget, elit. Donec arcu.

    -
    +
    +

    RTL text

    +

    +

    + בדיקה של טקסט ימין לשמאל +

    +

    + בדיקה של טקסט ימין לשמאל normal text +

    +

    + בדיקה של טקסט ימין לשמאל normal text +

    +

    diff --git a/Source/Demo/TestSamples/13.Tables.htm b/Source/Demo/TestSamples/13.Tables.htm index ec48b136c..124d98a0b 100644 --- a/Source/Demo/TestSamples/13.Tables.htm +++ b/Source/Demo/TestSamples/13.Tables.htm @@ -1,55 +1,94 @@
    + + + + + + + + +
    Cell One Cell One Cell this text is not visible +
    Cell Three + Cell Four +
    +
    + + + + +
    + + + + + +
    Left Inner Cell OneRight Inner Cell Two
    +
    +
    + + + + + +
    + Fixed width column + + column without a width so should take all the space but be close to the left column +
    +
    - - - @@ -67,11 +106,9 @@
    df - -
    A: - 1 + 1
    B: - 2 + 2
    C: - 3 + 3
    - sdfsdf + sdfsdf - sdfsd + sdfsd
    @@ -79,14 +116,11 @@ - - - @@ -95,22 +129,17 @@
    - a + a - LongStringLongStringLongStringLongStringLongStringLongStringLongString + LongStringLongStringLongStringLongStringLongStringLongStringLongString - text + text
    - - - - @@ -118,44 +147,35 @@
    - Title + Title
    - Word 1 + Word 1 - Word 2 + Word 2 - Word 3 + Word 3
    - - -
    - Word1Word1 Word1Word1Word1 + Word1Word1 Word1Word1Word1 - Word 2 + Word 2 - Word3Word3Word3Word3 Word3Word3Word3Word3Word3Word3Word3Word3Word3 + Word3Word3Word3Word3 Word3Word3Word3Word3Word3Word3Word3Word3Word3

    - - -
    - Word1 Word1Word1 + Word1 Word1Word1 - Word 2 + Word 2 - Word3Word3Word3 Word3 + Word3Word3Word3 Word3

    - - -
    - Container + Container
    - Type + Type - $Type + $Type
    diff --git a/Source/HtmlRenderer/Dom/CssBox.cs b/Source/HtmlRenderer/Dom/CssBox.cs index bfbeeb74f..726159382 100644 --- a/Source/HtmlRenderer/Dom/CssBox.cs +++ b/Source/HtmlRenderer/Dom/CssBox.cs @@ -565,22 +565,22 @@ public virtual void Dispose() /// Device context to use protected virtual void PerformLayoutImp(IGraphics g) { - if (Display != CssConstants.None) + if( Display != CssConstants.None ) { RectanglesReset(); MeasureWordsSize(g); } - if (IsBlock || Display == CssConstants.ListItem || Display == CssConstants.Table || Display == CssConstants.InlineTable || Display == CssConstants.TableCell) + if( IsBlock || Display == CssConstants.ListItem || Display == CssConstants.Table || Display == CssConstants.InlineTable || Display == CssConstants.TableCell ) { // Because their width and height are set by CssTable - if (Display != CssConstants.TableCell && Display != CssConstants.Table) + if( Display != CssConstants.TableCell && Display != CssConstants.Table ) { float width = ContainingBlock.Size.Width - ContainingBlock.ActualPaddingLeft - ContainingBlock.ActualPaddingRight - ContainingBlock.ActualBorderLeftWidth - ContainingBlock.ActualBorderRightWidth; - if (Width != CssConstants.Auto && !string.IsNullOrEmpty(Width)) + if( Width != CssConstants.Auto && !string.IsNullOrEmpty(Width) ) { width = CssValueParser.ParseLength(Width, width, this); } @@ -591,31 +591,31 @@ protected virtual void PerformLayoutImp(IGraphics g) Size = new SizeF(width - ActualMarginLeft - ActualMarginRight, Size.Height); } - if (Display != CssConstants.TableCell) + if( Display != CssConstants.TableCell ) { var prevSibling = DomUtils.GetPreviousSibling(this); float left = ContainingBlock.Location.X + ContainingBlock.ActualPaddingLeft + ActualMarginLeft + ContainingBlock.ActualBorderLeftWidth; - float top = (prevSibling == null && ParentBox != null ? ParentBox.ClientTop : ParentBox == null ? Location.Y : 0) + MarginTopCollapse(prevSibling) + (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0); + float top = ( prevSibling == null && ParentBox != null ? ParentBox.ClientTop : ParentBox == null ? Location.Y : 0 ) + MarginTopCollapse(prevSibling) + ( prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0 ); Location = new PointF(left, top); ActualBottom = top; } //If we're talking about a table here.. - if (Display == CssConstants.Table || Display == CssConstants.InlineTable) + if( Display == CssConstants.Table || Display == CssConstants.InlineTable ) { CssLayoutEngineTable.PerformLayout(g, this); } else { //If there's just inline boxes, create LineBoxes - if (DomUtils.ContainsInlinesOnly(this)) + if( DomUtils.ContainsInlinesOnly(this) ) { ActualBottom = Location.Y; CssLayoutEngine.CreateLineBoxes(g, this); //This will automatically set the bottom of this block } - else if (_boxes.Count > 0) + else if( _boxes.Count > 0 ) { - foreach (var childBox in Boxes) + foreach(var childBox in Boxes) { childBox.PerformLayout(g); } @@ -627,9 +627,9 @@ protected virtual void PerformLayoutImp(IGraphics g) else { var prevSibling = DomUtils.GetPreviousSibling(this); - if (prevSibling != null) + if( prevSibling != null ) { - if (Location == PointF.Empty) + if( Location == PointF.Empty ) Location = prevSibling.Location; ActualBottom = prevSibling.ActualBottom; } @@ -921,59 +921,81 @@ internal float GetMaximumBottom(CssBox startBox, float currentMaxBottom) } /// - /// Get the width of the box at full width (No line breaks) + /// Get the and width of the box content.
    ///
    - /// - internal void GetFullWidth(IGraphics g, out float minWidth, out float maxWidth) + /// The minimum width the content must be so it won't overflow (largest word + padding). + /// The total width the content can take without line wrapping (with padding). + internal void GetMinMaxWidth(out float minWidth, out float maxWidth) { - float sum = 0f; float min = 0f; - float paddingsum = 0f; - GetFullWidth_WordsWith(this, ref sum, ref min, ref paddingsum); + float maxSum = 0f; + float paddingSum = 0f; + float marginSum = 0f; + GetMinMaxSumWords(this, ref min, ref maxSum, ref paddingSum, ref marginSum); - maxWidth = paddingsum + sum; - minWidth = paddingsum + (min < 90999 ? min : 0); + maxWidth = paddingSum + maxSum; + minWidth = paddingSum + (min < 90999 ? min : 0); } /// - /// Gets the longest word (in width) inside the box, deeply. + /// Get the and of the box words content and .
    ///
    - /// - /// - /// - /// + /// the box to calculate for + /// the width that allows for each word to fit (width of the longest word) + /// the max width a single line of words can take without wrapping + /// the total amount of padding the content has + /// /// - private static void GetFullWidth_WordsWith(CssBox b, ref float sum, ref float min, ref float paddingsum) + private static void GetMinMaxSumWords(CssBox box, ref float min, ref float maxSum, ref float paddingSum, ref float marginSum) { float? oldSum = null; - if (b.Display != CssConstants.Inline) + + // not inline (block) boxes start a new line so we need to reset the max sum + if (box.Display != CssConstants.Inline && box.Display != CssConstants.TableCell && box.WhiteSpace != CssConstants.NoWrap) { - oldSum = sum; - sum = 0; + oldSum = maxSum; + maxSum = marginSum; } - paddingsum += b.ActualBorderLeftWidth + b.ActualBorderRightWidth + b.ActualPaddingRight + b.ActualPaddingLeft; + // add the padding + paddingSum += box.ActualBorderLeftWidth + box.ActualBorderRightWidth + box.ActualPaddingRight + box.ActualPaddingLeft; - if (b.Words.Count > 0) + + // for tables the padding also contains the spacing between cells + if( box.Display == CssConstants.Table ) + paddingSum += CssLayoutEngineTable.GetTableSpacing(box); + + if (box.Words.Count > 0) { - foreach (CssRect word in b.Words) + // calculate the min and max sum for all the words in the box + foreach (CssRect word in box.Words) { - sum += word.FullWidth; + maxSum += word.FullWidth; min = Math.Max(min, word.Width); } - + + // remove the last word padding + if( box.Words.Count > 0 ) + maxSum -= box.Words[box.Words.Count - 1].ActualWordSpacing; } else { - foreach (CssBox bb in b.Boxes) + // recursively on all the child boxes + foreach (CssBox childBox in box.Boxes) { - GetFullWidth_WordsWith(bb, ref sum, ref min, ref paddingsum); + marginSum += childBox.ActualMarginLeft + childBox.ActualMarginRight; + + //maxSum += childBox.ActualMarginLeft + childBox.ActualMarginRight; + GetMinMaxSumWords(childBox, ref min, ref maxSum, ref paddingSum, ref marginSum); + + marginSum -= childBox.ActualMarginLeft + childBox.ActualMarginRight; } } + // max sum is max of all the lines in the box if (oldSum.HasValue) { - sum = Math.Max(sum, oldSum.Value); + maxSum = Math.Max(maxSum, oldSum.Value); } } @@ -983,12 +1005,7 @@ private static void GetFullWidth_WordsWith(CssBox b, ref float sum, ref float mi /// internal bool HasJustInlineSiblings() { - if (ParentBox == null) - { - return false; - } - - return DomUtils.ContainsInlinesOnly(ParentBox); + return ParentBox != null && DomUtils.ContainsInlinesOnly(ParentBox); } /// @@ -996,7 +1013,7 @@ internal bool HasJustInlineSiblings() /// /// Rectangles where content should be placed /// - /// Inline boxes can be splitted across different LineBoxes, that's why this method + /// Inline boxes can be split across different LineBoxes, that's why this method /// Delivers a rectangle for each LineBox related to this box, if inline. /// diff --git a/Source/HtmlRenderer/Dom/CssBoxProperties.cs b/Source/HtmlRenderer/Dom/CssBoxProperties.cs index 6bfc2ce95..86a5604cd 100644 --- a/Source/HtmlRenderer/Dom/CssBoxProperties.cs +++ b/Source/HtmlRenderer/Dom/CssBoxProperties.cs @@ -489,7 +489,7 @@ public string Position public string LineHeight { get { return _lineHeight; } - set { _lineHeight = string.Format(NumberFormatInfo.InvariantInfo, "{0}px", CssValueParser.ParseLength(value, Size.Height, this)); } + set { _lineHeight = string.Format(NumberFormatInfo.InvariantInfo, "{0}px", CssValueParser.ParseLength(value, Size.Height, this, CssConstants.Em)); } } public string VerticalAlign @@ -1240,7 +1240,7 @@ public Font ActualFont case CssConstants.Larger: fsize = parentSize + 2; break; default: - fsize = CssValueParser.ParseLength(FontSize, parentSize, parentSize, true, true); + fsize = CssValueParser.ParseLength(FontSize, parentSize, parentSize, null, true, true); break; } @@ -1431,6 +1431,7 @@ protected void InheritStyle(CssBox p, bool everything) _listStyle = p._listStyle; _lineHeight = p._lineHeight; _wordBreak = p.WordBreak; + _direction = p._direction; if (everything) { @@ -1458,7 +1459,6 @@ protected void InheritStyle(CssBox p, bool everything) _cornerSERadius = p._cornerSERadius; _cornerSWRadius = p._cornerSWRadius; _cornerRadius = p._cornerRadius; - _direction = p._direction; _display = p._display; _float = p._float; _height = p._height; diff --git a/Source/HtmlRenderer/Dom/CssLayoutEngine.cs b/Source/HtmlRenderer/Dom/CssLayoutEngine.cs index 3372c3fd9..e8274d522 100644 --- a/Source/HtmlRenderer/Dom/CssLayoutEngine.cs +++ b/Source/HtmlRenderer/Dom/CssLayoutEngine.cs @@ -96,14 +96,14 @@ public static void MeasureImageSize(CssRectImage imageWord) // If only the width was set in the html tag, ratio the height. if ((hasImageTagWidth && !hasImageTagHeight) || scaleImageHeight) { - // Devide the given tag width with the actual image width, to get the ratio. + // Divide the given tag width with the actual image width, to get the ratio. float ratio = imageWord.Width / imageWord.Image.Width; imageWord.Height = imageWord.Image.Height * ratio; } // If only the height was set in the html tag, ratio the width. else if (hasImageTagHeight && !hasImageTagWidth) { - // Devide the given tag height with the actual image height, to get the ratio. + // Divide the given tag height with the actual image height, to get the ratio. float ratio = imageWord.Height / imageWord.Image.Height; imageWord.Width = imageWord.Image.Width * ratio; } @@ -152,14 +152,18 @@ public static void CreateLineBoxes(IGraphics g, CssBox blockBox) foreach (var linebox in blockBox.LineBoxes) { ApplyAlignment(g, linebox); - if (blockBox.Direction == CssConstants.Rtl) - ApplyRightToLeft(linebox); - + ApplyRightToLeft(blockBox, linebox); BubbleRectangles(blockBox, linebox); linebox.AssignRectanglesToBoxes(); } blockBox.ActualBottom = maxBottom + blockBox.ActualPaddingBottom + blockBox.ActualBorderBottomWidth; + + // handle limiting block height when overflow is hidden + if( blockBox.Height != null && blockBox.Height != CssConstants.Auto && blockBox.Overflow == CssConstants.Hidden && blockBox.ActualBottom - blockBox.Location.Y > blockBox.ActualHeight ) + { + blockBox.ActualBottom = blockBox.Location.Y + blockBox.ActualHeight; + } } /// @@ -255,7 +259,7 @@ private static void FlowBox(IGraphics g, CssBox blockbox, CssBox box, float limi if (b.Words.Count > 0) { bool wrapNoWrapBox = false; - if (b.WhiteSpace == CssConstants.Nowrap && curx > startx) + if (b.WhiteSpace == CssConstants.NoWrap && curx > startx) { var boxRight = curx; foreach(var word in b.Words) @@ -272,13 +276,13 @@ private static void FlowBox(IGraphics g, CssBox blockbox, CssBox box, float limi if (maxbottom - cury < box.ActualLineHeight) maxbottom += box.ActualLineHeight - (maxbottom - cury); - if ((b.WhiteSpace != CssConstants.Nowrap && b.WhiteSpace != CssConstants.Pre && curx + word.Width + rightspacing > limitRight) || word.IsLineBreak || wrapNoWrapBox) + if ((b.WhiteSpace != CssConstants.NoWrap && b.WhiteSpace != CssConstants.Pre && curx + word.Width + rightspacing > limitRight) || word.IsLineBreak || wrapNoWrapBox) { wrapNoWrapBox = false; curx = startx; // handle if line is wrapped for the first text element where parent has left margin\padding - if (b == box.Boxes[0] && word == b.Words[0] && !word.IsLineBreak) + if (b == box.Boxes[0] && !word.IsLineBreak && (word == b.Words[0] || box.ParentBox.IsBlock)) curx += box.ActualMarginLeft + box.ActualBorderLeftWidth + box.ActualPaddingLeft; cury = maxbottom + linespacing; @@ -441,11 +445,34 @@ private static void ApplyAlignment(IGraphics g, CssLineBox lineBox) /// /// Applies right to left direction to words /// - /// - private static void ApplyRightToLeft(CssLineBox line) + /// + /// + private static void ApplyRightToLeft(CssBox blockBox, CssLineBox lineBox) { - float left = line.OwnerBox.ClientLeft; - float right = line.OwnerBox.ClientRight; + if( blockBox.Direction == CssConstants.Rtl ) + { + ApplyRightToLeftOnLine(lineBox); + } + else + { + foreach(var box in lineBox.RelatedBoxes) + { + if (box.Direction == CssConstants.Rtl) + { + ApplyRightToLeftOnSingleBox(lineBox, box); + } + } + } + } + + /// + /// Applies RTL direction to all the words on the line. + /// + /// the line to apply RTL to + private static void ApplyRightToLeftOnLine(CssLineBox line) + { + float left = line.Words[0].Left; + float right = line.Words[line.Words.Count-1].Right; foreach (CssRect word in line.Words) { @@ -455,6 +482,39 @@ private static void ApplyRightToLeft(CssLineBox line) } } + /// + /// Applies RTL direction to specific box words on the line. + /// + /// + /// + private static void ApplyRightToLeftOnSingleBox(CssLineBox lineBox, CssBox box) + { + int leftWordIdx = -1; + int rightWordIdx = -1; + for (int i = 0; i < lineBox.Words.Count; i++) + { + if (lineBox.Words[i].OwnerBox == box) + { + if (leftWordIdx < 0) + leftWordIdx = i; + rightWordIdx = i; + } + } + + if (leftWordIdx > -1 && rightWordIdx > leftWordIdx) + { + float left = lineBox.Words[leftWordIdx].Left; + float right = lineBox.Words[rightWordIdx].Right; + + for (int i = leftWordIdx; i <= rightWordIdx; i++) + { + float diff = lineBox.Words[i].Left - left; + float wright = right - diff; + lineBox.Words[i].Left = wright - lineBox.Words[i].Width; + } + } + } + /// /// Applies vertical alignment to the linebox /// @@ -551,20 +611,21 @@ private static void ApplyCenterAlignment(IGraphics g, CssLineBox line) CssRect lastWord = line.Words[line.Words.Count - 1]; float right = line.OwnerBox.ActualRight - line.OwnerBox.ActualPaddingRight - line.OwnerBox.ActualBorderRightWidth; - float diff = right - lastWord.Right - lastWord.LeftGlyphPadding - lastWord.OwnerBox.ActualBorderRightWidth - lastWord.OwnerBox.ActualPaddingRight; + float diff = right - lastWord.Right - lastWord.OwnerBox.ActualBorderRightWidth - lastWord.OwnerBox.ActualPaddingRight; diff /= 2; - if (diff <= 0) return; - - foreach (CssRect word in line.Words) + if (diff > 0) { - word.Left += diff; - } + foreach (CssRect word in line.Words) + { + word.Left += diff; + } - foreach (CssBox b in line.Rectangles.Keys) - { - RectangleF r = b.Rectangles[line]; - b.Rectangles[line] = new RectangleF(r.X + diff, r.Y, r.Width, r.Height); + foreach (CssBox b in line.Rectangles.Keys) + { + RectangleF r = b.Rectangles[line]; + b.Rectangles[line] = new RectangleF(r.X + diff, r.Y, r.Width, r.Height); + } } } @@ -580,25 +641,20 @@ private static void ApplyRightAlignment(IGraphics g, CssLineBox line) CssRect lastWord = line.Words[line.Words.Count - 1]; float right = line.OwnerBox.ActualRight - line.OwnerBox.ActualPaddingRight - line.OwnerBox.ActualBorderRightWidth; - float diff = right - lastWord.Right - lastWord.LeftGlyphPadding - lastWord.OwnerBox.ActualBorderRightWidth - lastWord.OwnerBox.ActualPaddingRight; - - - if (diff <= 0) return; - - //if (line.OwnerBox.Direction == CssConstants.Rtl) - //{ - - //} + float diff = right - lastWord.Right - lastWord.OwnerBox.ActualBorderRightWidth - lastWord.OwnerBox.ActualPaddingRight; - foreach (CssRect word in line.Words) + if (diff > 0) { - word.Left += diff; - } + foreach (CssRect word in line.Words) + { + word.Left += diff; + } - foreach (CssBox b in line.Rectangles.Keys) - { - RectangleF r = b.Rectangles[line]; - b.Rectangles[line] = new RectangleF(r.X + diff, r.Y, r.Width, r.Height); + foreach (CssBox b in line.Rectangles.Keys) + { + RectangleF r = b.Rectangles[line]; + b.Rectangles[line] = new RectangleF(r.X + diff, r.Y, r.Width, r.Height); + } } } diff --git a/Source/HtmlRenderer/Dom/CssLayoutEngineTable.cs b/Source/HtmlRenderer/Dom/CssLayoutEngineTable.cs index cb577106b..b5a81eb34 100644 --- a/Source/HtmlRenderer/Dom/CssLayoutEngineTable.cs +++ b/Source/HtmlRenderer/Dom/CssLayoutEngineTable.cs @@ -75,6 +75,46 @@ private CssLayoutEngineTable(CssBox tableBox) _tableBox = tableBox; } + /// + /// Get the table cells spacing for all the cells in the table.
    + /// Used to calculate the spacing the table has in addition to regular padding and borders. + ///
    + /// the table box to calculate the spacing for + /// the calculated spacing + public static float GetTableSpacing(CssBox tableBox) + { + int count = 0; + int columns = 0; + foreach (var box in tableBox.Boxes) + { + if( box.Display == CssConstants.TableColumn ) + { + columns += GetSpan(box); + } + else if (box.Display == CssConstants.TableRowGroup) + { + foreach (CssBox cr in tableBox.Boxes) + { + count++; + if (cr.Display == CssConstants.TableRow) + columns = Math.Max(columns, cr.Boxes.Count); + } + } + else if (box.Display == CssConstants.TableRow) + { + count++; + columns = Math.Max(columns, box.Boxes.Count); + } + + // limit the amount of rows to process for performance + if(count > 30) + break; + } + + // +1 columns because padding is between the cell and table borders + return ( columns + 1 )*GetHorizontalSpacing(tableBox); + } + /// /// /// @@ -116,13 +156,13 @@ private void Layout(IGraphics g) // Determine Row and Column Count, and ColumnWidths var availCellSpace = CalculateCountAndWidth(); - DetermineMissingColumnWidths(g, availCellSpace); + DetermineMissingColumnWidths(availCellSpace); // Check for minimum sizes (increment widths if necessary) EnforceMinimumSize(); - // While table width is larger than it should, and width is reductable - EnforceMaximumSize(g); + // While table width is larger than it should, and width is reducible + EnforceMaximumSize(); // Ensure there's no padding _tableBox.PaddingLeft = _tableBox.PaddingTop = _tableBox.PaddingRight = _tableBox.PaddingBottom = "0"; @@ -330,39 +370,77 @@ private float CalculateCountAndWidth() /// /// /// - /// /// - private void DetermineMissingColumnWidths(IGraphics g, float availCellSpace) + private void DetermineMissingColumnWidths(float availCellSpace) { float occupedSpace = 0f; if (_widthSpecified) //If a width was specified, { //Assign NaNs equally with space left after gathering not-NaNs - int numberOfNans = 0; + int numOfNans = 0; - //Calculate number of NaNs and occuped space + //Calculate number of NaNs and occupied space foreach (float colWidth in _columnWidths) { if (float.IsNaN(colWidth)) - numberOfNans++; + numOfNans++; else occupedSpace += colWidth; } + var orgNumOfNans = numOfNans; - if (numberOfNans > 0) + float[] orgColWidths = null; + if (numOfNans < _columnWidths.Length) { - //Determine width that will be assigned to un asigned widths - float nanWidth = (availCellSpace - occupedSpace) / Convert.ToSingle(numberOfNans); - + orgColWidths = new float[_columnWidths.Length]; for (int i = 0; i < _columnWidths.Length; i++) - if (float.IsNaN(_columnWidths[i])) - _columnWidths[i] = nanWidth; + orgColWidths[i] = _columnWidths[i]; + } + + if (numOfNans > 0) + { + // Determine the max width for each column + float[] minFullWidths, maxFullWidths; + GetColumnsMinMaxWidthByContent(true, out minFullWidths, out maxFullWidths); + + // set the columns that can fulfill by the max width in a loop because it changes the nanWidth + int oldNumOfNans; + do + { + oldNumOfNans = numOfNans; + + for (int i = 0; i < _columnWidths.Length; i++) + { + var nanWidth = (availCellSpace - occupedSpace) / numOfNans; + if (float.IsNaN(_columnWidths[i]) && nanWidth > maxFullWidths[i]) + { + _columnWidths[i] = maxFullWidths[i]; + numOfNans--; + occupedSpace += maxFullWidths[i]; + } + } + } + while (oldNumOfNans != numOfNans); + + if (numOfNans > 0) + { + // Determine width that will be assigned to un assigned widths + float nanWidth = (availCellSpace - occupedSpace) / numOfNans; + + for (int i = 0; i < _columnWidths.Length; i++) + { + if (float.IsNaN(_columnWidths[i])) + _columnWidths[i] = nanWidth; + } + } } - else if (occupedSpace < availCellSpace) + + if (numOfNans == 0 && occupedSpace < availCellSpace) { // spread extra width between all columns - float extWidth = (availCellSpace - occupedSpace) / Convert.ToSingle(_columnWidths.Length); + float extWidth = (availCellSpace - occupedSpace) / orgNumOfNans; for (int i = 0; i < _columnWidths.Length; i++) + if (orgColWidths == null || float.IsNaN(orgColWidths[i])) _columnWidths[i] += extWidth; } } @@ -370,7 +448,7 @@ private void DetermineMissingColumnWidths(IGraphics g, float availCellSpace) { //Get the minimum and maximum full length of NaN boxes float[] minFullWidths, maxFullWidths; - GetColumnsMinMaxWidthByContent(g, true, out minFullWidths, out maxFullWidths); + GetColumnsMinMaxWidthByContent(true, out minFullWidths, out maxFullWidths); for (int i = 0; i < _columnWidths.Length; i++) { @@ -394,8 +472,7 @@ private void DetermineMissingColumnWidths(IGraphics g, float availCellSpace) /// While table width is larger than it should, and width is reductable.
    /// If table max width is limited by we need to lower the columns width even if it will result in clipping
    ///
    - /// used for content measure - private void EnforceMaximumSize(IGraphics g) + private void EnforceMaximumSize() { int curCol = 0; var widthSum = GetWidthSum(); @@ -421,7 +498,7 @@ private void EnforceMaximumSize(IGraphics g) { //Get the minimum and maximum full length of NaN boxes float[] minFullWidths, maxFullWidths; - GetColumnsMinMaxWidthByContent(g, false, out minFullWidths, out maxFullWidths); + GetColumnsMinMaxWidthByContent(false, out minFullWidths, out maxFullWidths); // lower all the columns to the minimum for (int i = 0; i < _columnWidths.Length; i++) @@ -784,11 +861,10 @@ private float GetMaxTableWidth() /// the min width possible without clipping content
    /// the max width the cell content can take without wrapping
    /// - /// used to measure /// if to measure only columns that have no calculated width /// return the min width for each column - the min width possible without clipping content /// return the max width for each column - the max width the cell content can take without wrapping - private void GetColumnsMinMaxWidthByContent(IGraphics g, bool onlyNans, out float[] minFullWidths, out float[] maxFullWidths) + private void GetColumnsMinMaxWidthByContent(bool onlyNans, out float[] minFullWidths, out float[] maxFullWidths) { maxFullWidths = new float[_columnWidths.Length]; minFullWidths = new float[_columnWidths.Length]; @@ -800,12 +876,19 @@ private void GetColumnsMinMaxWidthByContent(IGraphics g, bool onlyNans, out floa int col = GetCellRealColumnIndex(row, row.Boxes[i]); col = _columnWidths.Length > col ? col : _columnWidths.Length - 1; - if ((!onlyNans || float.IsNaN(_columnWidths[col])) && i < row.Boxes.Count && GetColSpan(row.Boxes[i]) == 1) + if ((!onlyNans || float.IsNaN(_columnWidths[col])) && i < row.Boxes.Count) { float minWidth, maxWidth; - row.Boxes[i].GetFullWidth(g, out minWidth, out maxWidth); - minFullWidths[col] = Math.Max(minFullWidths[col], minWidth); - maxFullWidths[col] = Math.Max(maxFullWidths[col], maxWidth); + row.Boxes[i].GetMinMaxWidth(out minWidth, out maxWidth); + + var colSpan = GetColSpan(row.Boxes[i]); + minWidth = minWidth / colSpan; + maxWidth = maxWidth / colSpan; + for (int j = 0; j < colSpan; j++) + { + minFullWidths[col + j] = Math.Max(minFullWidths[col+j], minWidth); + maxFullWidths[col + j] = Math.Max(maxFullWidths[col+j], maxWidth); + } } } } @@ -893,6 +976,14 @@ private float GetHorizontalSpacing() return _tableBox.BorderCollapse == CssConstants.Collapse ? -1f : _tableBox.ActualBorderSpacingHorizontal; } + /// + /// Gets the actual horizontal spacing of the table + /// + private static float GetHorizontalSpacing(CssBox box) + { + return box.BorderCollapse == CssConstants.Collapse ? -1f : box.ActualBorderSpacingHorizontal; + } + /// /// Gets the actual vertical spacing of the table /// diff --git a/Source/HtmlRenderer/Entities/CssConstants.cs b/Source/HtmlRenderer/Entities/CssConstants.cs index 5fedbcf45..68f3e9e5b 100644 --- a/Source/HtmlRenderer/Entities/CssConstants.cs +++ b/Source/HtmlRenderer/Entities/CssConstants.cs @@ -68,7 +68,7 @@ internal static class CssConstants public const string Monospace = "monospace"; public const string None = "none"; public const string Normal = "normal"; - public const string Nowrap = "nowrap"; + public const string NoWrap = "nowrap"; public const string Oblique = "oblique"; public const string Outset = "outset"; public const string Overline = "overline"; diff --git a/Source/HtmlRenderer/Handlers/ContextMenuHandler.cs b/Source/HtmlRenderer/Handlers/ContextMenuHandler.cs index 2459e2412..29b7bc9b4 100644 --- a/Source/HtmlRenderer/Handlers/ContextMenuHandler.cs +++ b/Source/HtmlRenderer/Handlers/ContextMenuHandler.cs @@ -380,7 +380,8 @@ private void OnOpenLinkClick(object sender, EventArgs eventArgs) { try { - _currentLink.HtmlContainer.HandleLinkClicked(_currentLink); + var mp = _parentControl.PointToClient(Control.MousePosition); + _currentLink.HtmlContainer.HandleLinkClicked(_parentControl, new MouseEventArgs(MouseButtons.None, 0, mp.X, mp.Y, 0), _currentLink); DisposeContextMenu(); } catch (Exception ex) diff --git a/Source/HtmlRenderer/HtmlContainer.cs b/Source/HtmlRenderer/HtmlContainer.cs index 393a7bf02..7410fe0a4 100644 --- a/Source/HtmlRenderer/HtmlContainer.cs +++ b/Source/HtmlRenderer/HtmlContainer.cs @@ -483,7 +483,7 @@ public void HandleMouseUp(Control parent, MouseEventArgs e) var link = DomUtils.GetLinkBox(_root, loc); if (link != null) { - HandleLinkClicked(link); + HandleLinkClicked(parent, e, link); } } } @@ -527,7 +527,7 @@ public void HandleMouseMove(Control parent, MouseEventArgs e) try { - var loc = OffsetByScroll(e.Location); + var loc = OffsetByScroll(e.Location); if (_selectionHandler != null && IsMouseInContainer(e.Location)) _selectionHandler.HandleMouseMove(parent, loc); @@ -689,8 +689,10 @@ internal void ReportError(HtmlRenderErrorType type, string message, Exception ex /// /// Handle link clicked going over event and using if not canceled. /// + /// the control hosting the html to invalidate + /// the mouse event args /// the link that was clicked - internal void HandleLinkClicked(CssBox link) + internal void HandleLinkClicked(Control parent, MouseEventArgs e, CssBox link) { if (LinkClicked != null) { @@ -708,10 +710,12 @@ internal void HandleLinkClicked(CssBox link) { if( ScrollChange != null ) { - var linkBox = DomUtils.GetBoxById(_root, link.HrefLink.Substring(1)); - if( linkBox != null ) + var box = DomUtils.GetBoxById(_root, link.HrefLink.Substring(1)); + if (box != null) { - ScrollChange(this, new HtmlScrollEventArgs(Point.Round(CommonUtils.GetFirstValueOrDefault(linkBox.Rectangles).Location))); + var rect = CommonUtils.GetFirstValueOrDefault(box.Rectangles, box.Bounds); + ScrollChange(this, new HtmlScrollEventArgs(Point.Round(rect.Location))); + HandleMouseMove(parent, e); } } } diff --git a/Source/HtmlRenderer/HtmlPanel.cs b/Source/HtmlRenderer/HtmlPanel.cs index 5d8f92144..ab6078048 100644 --- a/Source/HtmlRenderer/HtmlPanel.cs +++ b/Source/HtmlRenderer/HtmlPanel.cs @@ -287,6 +287,10 @@ protected override void OnPaint(PaintEventArgs e) { _htmlContainer.ScrollOffset = AutoScrollPosition; _htmlContainer.PerformPaint(e.Graphics); + + // call mouse move to handle paint after scroll or html change affecting mouse cursor. + var mp = PointToClient(MousePosition); + _htmlContainer.HandleMouseMove(this, new MouseEventArgs(MouseButtons.None, 0, mp.X, mp.Y, 0)); } } @@ -460,6 +464,7 @@ private void OnRefresh(object sender, HtmlRefreshEventArgs e) private void OnScrollChange(object sender, HtmlScrollEventArgs e) { AutoScrollPosition = e.Location; + _htmlContainer.ScrollOffset = AutoScrollPosition; } /// diff --git a/Source/HtmlRenderer/Parse/CssValueParser.cs b/Source/HtmlRenderer/Parse/CssValueParser.cs index a2c3b4506..70e149584 100644 --- a/Source/HtmlRenderer/Parse/CssValueParser.cs +++ b/Source/HtmlRenderer/Parse/CssValueParser.cs @@ -90,7 +90,20 @@ public static float ParseNumber(string number, float hundredPercent) /// the parsed length value with adjustments public static float ParseLength(string length, float hundredPercent, CssBoxProperties box, bool fontAdjust = false) { - return ParseLength(length, hundredPercent, box.GetEmHeight(), fontAdjust, false); + return ParseLength(length, hundredPercent, box.GetEmHeight(), null, fontAdjust, false); + } + + /// + /// Parses a length. Lengths are followed by an unit identifier (e.g. 10px, 3.1em) + /// + /// Specified length + /// Equivalent to 100 percent when length is percentage + /// + /// + /// the parsed length value with adjustments + public static float ParseLength(string length, float hundredPercent, CssBoxProperties box, string defaultUnit) + { + return ParseLength(length, hundredPercent, box.GetEmHeight(), defaultUnit, false, false); } /// @@ -99,10 +112,11 @@ public static float ParseLength(string length, float hundredPercent, CssBoxPrope /// Specified length /// Equivalent to 100 percent when length is percentage /// + /// /// if the length is in pixels and the length is font related it needs to use 72/96 factor /// Allows the return float to be in points. If false, result will be pixels /// the parsed length value with adjustments - public static float ParseLength(string length, float hundredPercent, float emFactor, bool fontAdjust, bool returnPoints) + public static float ParseLength(string length, float hundredPercent, float emFactor, string defaultUnit, bool fontAdjust, bool returnPoints) { //Return zero if no length specified, zero specified if (string.IsNullOrEmpty(length) || length == "0") return 0f; @@ -110,17 +124,15 @@ public static float ParseLength(string length, float hundredPercent, float emFac //If percentage, use ParseNumber if (length.EndsWith("%")) return ParseNumber(length, hundredPercent); - //If no units, return zero - if (length.Length < 3) return 0f; - //Get units of the length - string unit = length.Substring(length.Length - 2, 2); + bool hasUnit; + string unit = GetUnit(length, defaultUnit, out hasUnit); //Factor will depend on the unit float factor; //Number of the length - string number = length.Substring(0, length.Length - 2); + string number = hasUnit ? length.Substring(0, length.Length - 2) : length; //TODO: Units behave different in paper and in screen! switch (unit) @@ -163,6 +175,32 @@ public static float ParseLength(string length, float hundredPercent, float emFac return factor * ParseNumber(number, hundredPercent); } + /// + /// Get the unit to use for the length, use default if no unit found in length string. + /// + private static string GetUnit(string length, string defaultUnit, out bool hasUnit) + { + var unit = length.Length >= 3 ? length.Substring(length.Length - 2, 2) : string.Empty; + switch (unit) + { + case CssConstants.Em: + case CssConstants.Ex: + case CssConstants.Px: + case CssConstants.Mm: + case CssConstants.Cm: + case CssConstants.In: + case CssConstants.Pt: + case CssConstants.Pc: + hasUnit = true; + break; + default: + hasUnit = false; + unit = defaultUnit ?? String.Empty; + break; + } + return unit; + } + /// /// Check if the given color string value is valid. /// diff --git a/Source/HtmlRenderer/Parse/DomParser.cs b/Source/HtmlRenderer/Parse/DomParser.cs index 723a1ee8b..b3517df8d 100644 --- a/Source/HtmlRenderer/Parse/DomParser.cs +++ b/Source/HtmlRenderer/Parse/DomParser.cs @@ -222,7 +222,7 @@ private static bool IsBlockAssignableToBox(CssBox box, CssBlock block) { assignable = IsBlockAssignableToBoxWithSelector(box, block); } - else if (box.HtmlTag.Name.Equals("a") && !box.HtmlTag.HasAttribute("href")) + else if (box.HtmlTag.Name.Equals("a", StringComparison.OrdinalIgnoreCase) && block.Class.Equals("a", StringComparison.OrdinalIgnoreCase) && !box.HtmlTag.HasAttribute("href")) { assignable = false; } @@ -369,9 +369,9 @@ private static void TranslateAttributes(HtmlTag tag, CssBox box) { case HtmlConstants.Align: if (value == HtmlConstants.Left || value == HtmlConstants.Center || value == HtmlConstants.Right || value == HtmlConstants.Justify) - box.TextAlign = value; + box.TextAlign = value.ToLower(); else - box.VerticalAlign = value; + box.VerticalAlign = value.ToLower(); break; case HtmlConstants.Background: box.BackgroundImage = value; @@ -404,10 +404,10 @@ private static void TranslateAttributes(HtmlTag tag, CssBox box) ApplyTablePadding(box, value); break; case HtmlConstants.Color: - box.Color = value; + box.Color = value.ToLower(); break; case HtmlConstants.Dir: - box.Direction = value; + box.Direction = value.ToLower(); break; case HtmlConstants.Face: box.FontFamily = CssParser.ParseFontFamily(value); @@ -419,7 +419,7 @@ private static void TranslateAttributes(HtmlTag tag, CssBox box) box.MarginRight = box.MarginLeft = TranslateLength(value); break; case HtmlConstants.Nowrap: - box.WhiteSpace = CssConstants.Nowrap; + box.WhiteSpace = CssConstants.NoWrap; break; case HtmlConstants.Size: if (tag.Name.Equals(HtmlConstants.Hr,StringComparison.OrdinalIgnoreCase)) @@ -428,7 +428,7 @@ private static void TranslateAttributes(HtmlTag tag, CssBox box) box.FontSize = value; break; case HtmlConstants.Valign: - box.VerticalAlign = value; + box.VerticalAlign = value.ToLower(); break; case HtmlConstants.Vspace: box.MarginTop = box.MarginBottom = TranslateLength(value); diff --git a/Source/HtmlRenderer/Properties/AssemblyInfo.cs b/Source/HtmlRenderer/Properties/AssemblyInfo.cs index 8a906e3d8..ab142d311 100644 --- a/Source/HtmlRenderer/Properties/AssemblyInfo.cs +++ b/Source/HtmlRenderer/Properties/AssemblyInfo.cs @@ -28,4 +28,4 @@ // Build Number // Revision // -[assembly: AssemblyVersion("1.4.6.2")] +[assembly: AssemblyVersion("1.4.7.0")] diff --git a/Source/HtmlRenderer/Utils/RenderUtils.cs b/Source/HtmlRenderer/Utils/RenderUtils.cs index d1a27cd39..49bed0028 100644 --- a/Source/HtmlRenderer/Utils/RenderUtils.cs +++ b/Source/HtmlRenderer/Utils/RenderUtils.cs @@ -103,6 +103,10 @@ public static Pen GetPen(Color color) pen = new Pen(GetSolidBrush(color)); _penCache[color] = pen; } + else + { + pen.Width = 1; + } return pen; }