diff --git a/.gitignore b/.gitignore index ce3a150ad..4f8445b46 100644 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,5 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML -*.DotSettings \ No newline at end of file +*.DotSettings + diff --git a/CUSTOM.md b/CUSTOM.md new file mode 100644 index 000000000..778edfc6c --- /dev/null +++ b/CUSTOM.md @@ -0,0 +1,11 @@ +# Modified + +* Use .Net4.6 for Task +* Added a HttpSession manager + +# ToDo + +* Download task scheduler(max 4 concurrency ?) +* Cancel requests when jump to other page +* Fix OutOfMemoryException of GDI + diff --git a/README.md b/README.md index 5a21ca388..0ba6ad78d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ HTML Renderer [![Build status](https://ci.appveyor.com/api/projects/status/cm8xpf8ebt3hyi3e)](https://ci.appveyor.com/project/ArthurHub/html-renderer) ============= +## This repository is modified version. +* [CUSTOM](CUSTOM.md) + ## Help Wanted * Looking for a contributor(s) to take this project forward as I'm unable to continue supporting it. * Contribute directly to the repository and update nuget packages. diff --git a/Source/.gitignore b/Source/.gitignore new file mode 100644 index 000000000..6cae44f1d --- /dev/null +++ b/Source/.gitignore @@ -0,0 +1 @@ +/.vs/ diff --git a/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj b/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj index 7413cc397..20d826cf2 100644 --- a/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj +++ b/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj @@ -9,7 +9,7 @@ Properties TheArtOfDev.HtmlRenderer.Demo.Common HtmlRendererDemoCommon - v2.0 + v4.5 512 @@ -21,6 +21,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -29,6 +30,7 @@ TRACE prompt 4 + false diff --git a/Source/Demo/Common/Properties/Resources.Designer.cs b/Source/Demo/Common/Properties/Resources.Designer.cs index 8b1aa6a38..a808bf193 100644 --- a/Source/Demo/Common/Properties/Resources.Designer.cs +++ b/Source/Demo/Common/Properties/Resources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.34014 +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 +// コードが再生成されるときに損失したりします。 // //------------------------------------------------------------------------------ @@ -13,13 +13,13 @@ namespace TheArtOfDev.HtmlRenderer.Demo.Common.Properties { /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + // このクラスは StronglyTypedResourceBuilder クラスが ResGen + // または Visual Studio のようなツールを使用して自動生成されました。 + // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に + // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { @@ -33,7 +33,7 @@ internal Resources() { } /// - /// Returns the cached ResourceManager instance used by this class. + /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ internal Resources() { } /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. + /// 厳密に型指定されたこのリソース クラスを使用して、すべての検索リソースに対し、 + /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap browser { get { @@ -71,7 +71,7 @@ public static System.Drawing.Bitmap browser { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap chrome { get { @@ -81,7 +81,7 @@ public static System.Drawing.Bitmap chrome { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap code { get { @@ -91,7 +91,7 @@ public static System.Drawing.Bitmap code { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap form { get { @@ -101,7 +101,7 @@ public static System.Drawing.Bitmap form { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap image { get { @@ -111,7 +111,7 @@ public static System.Drawing.Bitmap image { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap pdf { get { @@ -121,7 +121,7 @@ public static System.Drawing.Bitmap pdf { } /// - /// Looks up a localized resource of type System.Drawing.Bitmap. + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// public static System.Drawing.Bitmap stopwatch { get { diff --git a/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj b/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj index 8c26cdcc4..253cfbfd9 100644 --- a/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj +++ b/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj @@ -9,7 +9,7 @@ Properties TheArtOfDev.HtmlRenderer.Demo.WPF HtmlRendererWpfDemo - v4.0 + v4.5 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 @@ -26,6 +26,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -35,6 +36,7 @@ TRACE prompt 4 + false html.ico @@ -122,6 +124,7 @@ + diff --git a/Source/Demo/WPF/app.config b/Source/Demo/WPF/app.config new file mode 100644 index 000000000..51278a456 --- /dev/null +++ b/Source/Demo/WPF/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Source/Demo/WinForms/DemoForm.Designer.cs b/Source/Demo/WinForms/DemoForm.Designer.cs index 2b220627f..c7a4ebb34 100644 --- a/Source/Demo/WinForms/DemoForm.Designer.cs +++ b/Source/Demo/WinForms/DemoForm.Designer.cs @@ -132,7 +132,7 @@ private void InitializeComponent() this._generatePdfTSB.Size = new System.Drawing.Size(82, 22); this._generatePdfTSB.Text = "Generate PDF"; this._generatePdfTSB.ToolTipText = "Generate PDF from the current HTML using PdfSharp library."; - this._generatePdfTSB.Click += new System.EventHandler(this.OnGeneratePdf_Click); + this._generatePdfTSB.Click += new System.EventHandler((o, e) => this.OnGeneratePdf_Click(o, e)); // // _runPerformanceTSB // diff --git a/Source/Demo/WinForms/DemoForm.cs b/Source/Demo/WinForms/DemoForm.cs index 3e7287b70..28bd06e2a 100644 --- a/Source/Demo/WinForms/DemoForm.cs +++ b/Source/Demo/WinForms/DemoForm.cs @@ -20,6 +20,7 @@ using TheArtOfDev.HtmlRenderer.PdfSharp; using TheArtOfDev.HtmlRenderer.WinForms; using PdfSharp; +using System.Threading.Tasks; namespace TheArtOfDev.HtmlRenderer.Demo.WinForms { @@ -34,6 +35,7 @@ public partial class DemoForm : Form #endregion + WinFormsDemoResourceServer _resourceServer = new WinFormsDemoResourceServer(); /// /// Init. @@ -138,13 +140,13 @@ private void OnGenerateImage_Click(object sender, EventArgs e) /// /// Create PDF using PdfSharp project, save to file and open that file. /// - private void OnGeneratePdf_Click(object sender, EventArgs e) + private async Task OnGeneratePdf_Click(object sender, EventArgs e) { PdfGenerateConfig config = new PdfGenerateConfig(); config.PageSize = PageSize.A4; config.SetMargins(20); - var doc = PdfGenerator.GeneratePdf(_mainControl.GetHtml(), config, null, DemoUtils.OnStylesheetLoad, HtmlRenderingHelper.OnImageLoadPdfSharp); + var doc = await PdfGenerator.GeneratePdf(_resourceServer, config); var tmpFile = Path.GetTempFileName(); tmpFile = Path.GetFileNameWithoutExtension(tmpFile) + ".pdf"; doc.Save(tmpFile); diff --git a/Source/Demo/WinForms/GenerateImageForm.cs b/Source/Demo/WinForms/GenerateImageForm.cs index 5f966a8f2..509a14343 100644 --- a/Source/Demo/WinForms/GenerateImageForm.cs +++ b/Source/Demo/WinForms/GenerateImageForm.cs @@ -16,6 +16,7 @@ using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Reflection; +using System.Threading.Tasks; using System.Windows.Forms; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; @@ -26,12 +27,12 @@ namespace TheArtOfDev.HtmlRenderer.Demo.WinForms { public partial class GenerateImageForm : Form { - private readonly string _html; + private readonly WinFormsDemoResourceServer _resourceServer = new WinFormsDemoResourceServer(); private readonly Bitmap _background; public GenerateImageForm(string html) { - _html = html; + _resourceServer.SetHtml(html); InitializeComponent(); Icon = DemoForm.GetIcon(); @@ -95,7 +96,7 @@ private void OnGenerateImage_Click(object sender, EventArgs e) GenerateImage(); } - private void GenerateImage() + private async Task GenerateImage() { if (_backgroundColorTSB.SelectedItem != null && _textRenderingHintTSCB.SelectedItem != null) { @@ -105,14 +106,12 @@ private void GenerateImage() Image img; if (_useGdiPlusTSB.Checked || HtmlRenderingHelper.IsRunningOnMono()) { - img = HtmlRender.RenderToImageGdiPlus(_html, _pictureBox.ClientSize, textRenderingHint, null, DemoUtils.OnStylesheetLoad, HtmlRenderingHelper.OnImageLoad); + img = await HtmlRender.RenderToImageGdiPlus(_resourceServer, _pictureBox.ClientSize, textRenderingHint); } else { EventHandler stylesheetLoad = DemoUtils.OnStylesheetLoad; - EventHandler imageLoad = HtmlRenderingHelper.OnImageLoad; - var objects = new object[] { _html, _pictureBox.ClientSize, backgroundColor, null, stylesheetLoad, imageLoad }; - + var objects = new object[] { _resourceServer, _pictureBox.ClientSize, backgroundColor, stylesheetLoad }; var types = new[] { typeof(String), typeof(Size), typeof(Color), typeof(CssData), typeof(EventHandler), typeof(EventHandler) }; var m = typeof(HtmlRender).GetMethod("RenderToImage", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, null, types, null); img = (Image)m.Invoke(null, objects); diff --git a/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj b/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj index 95dafc2f1..3f1f5771c 100644 --- a/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj +++ b/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj @@ -10,7 +10,7 @@ Properties TheArtOfDev.HtmlRenderer.Demo.WinForms HtmlRendererWinFormsDemo - v2.0 + v4.5 @@ -105,6 +105,7 @@ + GenerateImageForm.cs @@ -171,6 +172,9 @@ + + Designer + diff --git a/Source/Demo/WinForms/HtmlRenderingHelper.cs b/Source/Demo/WinForms/HtmlRenderingHelper.cs index 8ca593314..7a0f87ff5 100644 --- a/Source/Demo/WinForms/HtmlRenderingHelper.cs +++ b/Source/Demo/WinForms/HtmlRenderingHelper.cs @@ -10,29 +10,14 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; using System; -using System.Collections.Generic; using System.Drawing; -using System.IO; -using System.Threading; -using TheArtOfDev.HtmlRenderer.Core.Entities; -using TheArtOfDev.HtmlRenderer.Demo.Common; + namespace TheArtOfDev.HtmlRenderer.Demo.WinForms { internal static class HtmlRenderingHelper { - #region Fields/Consts - - /// - /// Cache for resource images - /// - private static readonly Dictionary _imageCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - - #endregion - - /// /// Check if currently running in mono. /// @@ -55,116 +40,5 @@ public static Bitmap CreateImageForTransparentBackground() } return image; } - - /// - /// Get image by resource key. - /// - public static Image TryLoadResourceImage(string src) - { - Image image; - if (!_imageCache.TryGetValue(src, out image)) - { - var imageStream = DemoUtils.GetImageStream(src); - if (imageStream != null) - { - image = Image.FromStream(imageStream); - _imageCache[src] = image; - } - } - return image; - } - - /// - /// Get image by resource key. - /// - public static XImage TryLoadResourceXImage(string src) - { - var img = TryLoadResourceImage(src); - XImage xImg; - - if (img == null) - return null; - - using (var ms = new MemoryStream()) - { - img.Save(ms, img.RawFormat); - xImg = img != null ? XImage.FromStream(ms) : null; - } - - return xImg; - } - - /// - /// On image load in renderer set the image by event async. - /// - public static void OnImageLoad(object sender, HtmlImageLoadEventArgs e) - { - ImageLoad(e, false); - } - - /// - /// On image load in renderer set the image by event async. - /// - public static void OnImageLoadPdfSharp(object sender, HtmlImageLoadEventArgs e) - { - ImageLoad(e, true); - } - - /// - /// On image load in renderer set the image by event async. - /// - public static void ImageLoad(HtmlImageLoadEventArgs e, bool pdfSharp) - { - var img = TryLoadResourceImage(e.Src); - XImage xImg = null; - - if (img != null) - { - using (var ms = new MemoryStream()) - { - img.Save(ms, img.RawFormat); - xImg = img != null ? XImage.FromStream(ms) : null; - } - } - - object imgObj; - if (pdfSharp) - imgObj = xImg; - else - imgObj = img; - - if (!e.Handled && e.Attributes != null) - { - if (e.Attributes.ContainsKey("byevent")) - { - int delay; - if (Int32.TryParse(e.Attributes["byevent"], out delay)) - { - e.Handled = true; - ThreadPool.QueueUserWorkItem(state => - { - Thread.Sleep(delay); - e.Callback("https://fbcdn-sphotos-a-a.akamaihd.net/hphotos-ak-snc7/c0.44.403.403/p403x403/318890_10151195988833836_1081776452_n.jpg"); - }); - return; - } - else - { - e.Callback("http://sphotos-a.xx.fbcdn.net/hphotos-ash4/c22.0.403.403/p403x403/263440_10152243591765596_773620816_n.jpg"); - return; - } - } - else if (e.Attributes.ContainsKey("byrect")) - { - var split = e.Attributes["byrect"].Split(','); - var rect = new Rectangle(Int32.Parse(split[0]), Int32.Parse(split[1]), Int32.Parse(split[2]), Int32.Parse(split[3])); - e.Callback(imgObj ?? TryLoadResourceImage("htmlicon"), rect.X, rect.Y, rect.Width, rect.Height); - return; - } - } - - if (img != null) - e.Callback(imgObj); - } } -} \ No newline at end of file +} diff --git a/Source/Demo/WinForms/MainControl.cs b/Source/Demo/WinForms/MainControl.cs index 696fc032c..993b22a7c 100644 --- a/Source/Demo/WinForms/MainControl.cs +++ b/Source/Demo/WinForms/MainControl.cs @@ -16,6 +16,7 @@ using System.IO; using System.Text.RegularExpressions; using System.Windows.Forms; +using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Demo.Common; using Timer = System.Threading.Timer; @@ -51,13 +52,12 @@ public partial class MainControl : UserControl public MainControl() { + ResourceServerFactory.Iniialize(() => new WinFormsDemoResourceServer()); + InitializeComponent(); _htmlPanel.RenderError += OnRenderError; _htmlPanel.LinkClicked += OnLinkClicked; - _htmlPanel.StylesheetLoad += DemoUtils.OnStylesheetLoad; - _htmlPanel.ImageLoad += HtmlRenderingHelper.OnImageLoad; - _htmlToolTip.ImageLoad += HtmlRenderingHelper.OnImageLoad; _htmlPanel.LoadComplete += (sender, args) => _htmlPanel.ScrollToElement("C4"); _htmlToolTip.SetToolTip(_htmlPanel, Resources.Tooltip); @@ -195,7 +195,7 @@ private void OnSamplesTreeViewAfterSelect(object sender, TreeViewEventArgs e) try { - _htmlPanel.AvoidImagesLateLoading = !sample.FullName.Contains("Many images"); + //_htmlPanel.AvoidImagesLateLoading = !sample.FullName.Contains("Many images"); _htmlPanel.Text = sample.Html; } @@ -256,6 +256,9 @@ private string GetFixedHtml() { var html = _htmlEditor.Text; + throw new NotImplementedException(); + +#if flase html = Regex.Replace(html, @"src=\""(\w.*?)\""", match => { var img = HtmlRenderingHelper.TryLoadResourceImage(match.Groups[1].Value); @@ -279,6 +282,7 @@ private string GetFixedHtml() } return match.Value; }, RegexOptions.IgnoreCase); +#endif return html; } @@ -330,6 +334,6 @@ private void SetColoredText(string text) _htmlEditor.SelectionStart = selectionStart; } - #endregion +#endregion } } \ No newline at end of file diff --git a/Source/Demo/WinForms/PerfForm.Designer.cs b/Source/Demo/WinForms/PerfForm.Designer.cs index 8af6045a2..980b767c8 100644 --- a/Source/Demo/WinForms/PerfForm.Designer.cs +++ b/Source/Demo/WinForms/PerfForm.Designer.cs @@ -131,7 +131,6 @@ private void InitializeComponent() // this._htmlPanel.AutoScroll = true; this._htmlPanel.AvoidGeometryAntialias = false; - this._htmlPanel.AvoidImagesLateLoading = false; this._htmlPanel.BackColor = System.Drawing.SystemColors.Window; this._htmlPanel.BaseStylesheet = null; this._htmlPanel.Dock = System.Windows.Forms.DockStyle.Fill; diff --git a/Source/Demo/WinForms/PerfForm.cs b/Source/Demo/WinForms/PerfForm.cs index 472235c73..738d0c7b9 100644 --- a/Source/Demo/WinForms/PerfForm.cs +++ b/Source/Demo/WinForms/PerfForm.cs @@ -19,6 +19,7 @@ using System.Text; using System.Threading; using System.Windows.Forms; +using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.WinForms; namespace TheArtOfDev.HtmlRenderer.Demo.WinForms @@ -81,7 +82,7 @@ public static void Run(bool layout, bool paint) { foreach (var html in _perfTestSamples) { - htmlContainer.SetHtml(html); + htmlContainer.SetResourceServerAsync(new WinFormsDemoResourceServer(html)); if (layout) htmlContainer.PerformLayout(g); diff --git a/Source/Demo/WinForms/WinFormsDemoResourceServer.cs b/Source/Demo/WinForms/WinFormsDemoResourceServer.cs new file mode 100644 index 000000000..fe44ddf7b --- /dev/null +++ b/Source/Demo/WinForms/WinFormsDemoResourceServer.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Core; +using TheArtOfDev.HtmlRenderer.Demo.Common; +using TheArtOfDev.HtmlRenderer.WinForms.Adapters; + + +namespace TheArtOfDev.HtmlRenderer.Demo.WinForms +{ + public class WinFormsDemoResourceServer : IResourceServer + { + /// + /// Cache for resource images + /// + private readonly Dictionary _imageCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + string m_html; + public void SetHtml(string html) + { + m_html=html; + } + + public async Task GetHtmlAsync() + { + return m_html; + } + + CssData m_cssData; + public void SetCssData(CssData cssData) + { + m_cssData=cssData; + } + + public async Task GetCssDataAsync() + { + return m_cssData; + } + + public WinFormsDemoResourceServer(string html="", CssData cssData=null) + { + m_html = html; + m_cssData = cssData; + } + + public void Dispose() + { + } + + public async Task GetCssDataAsync(string src, Dictionary attributes) + { + return CssData.Parse(WinFormsAdapter.Instance, DemoUtils.GetStylesheet(src)); + } + + /// + /// Get image by resource key. + /// + public Image TryLoadResourceImage(string src) + { + Image image; + if (!_imageCache.TryGetValue(src, out image)) + { + var imageStream = DemoUtils.GetImageStream(src); + if (imageStream != null) + { + image = Image.FromStream(imageStream); + _imageCache[src] = image; + } + } + return image; + } + + public virtual async Task GetImageAsync(string location, Dictionary attributes) + { + var imgObj = TryLoadResourceImage(location); + if (attributes != null) + { + if (attributes.ContainsKey("byevent")) + { + int delay; + if (Int32.TryParse(attributes["byevent"], out delay)) + { + await Task.Run(() => + { + Thread.Sleep(delay); + }); + + //e.Callback("https://fbcdn-sphotos-a-a.akamaihd.net/hphotos-ak-snc7/c0.44.403.403/p403x403/318890_10151195988833836_1081776452_n.jpg"); + //return; + throw new NotImplementedException(); + } + else + { + //e.Callback("http://sphotos-a.xx.fbcdn.net/hphotos-ash4/c22.0.403.403/p403x403/263440_10152243591765596_773620816_n.jpg"); + throw new NotImplementedException(); + } + } + else if (attributes.ContainsKey("byrect")) + { + var split = attributes["byrect"].Split(','); + var rect = new Rectangle(Int32.Parse(split[0]), Int32.Parse(split[1]), Int32.Parse(split[2]), Int32.Parse(split[3])); + + if (imgObj != null) + { + return WinFormsAdapter.Instance.ConvertImage(imgObj); + } + else + { + throw new NotImplementedException(); + //return TryLoadResourceImage("htmlicon"), rect.X, rect.Y, rect.Width, rect.Height); + } + } + } + + return WinFormsAdapter.Instance.ConvertImage(imgObj); + } + } + + /* + class WinFormsPdfResourceServer : WinfFormsResourceServer + { + /// + /// Get image by resource key. + /// + public XImage TryLoadResourceXImage(string src) + { + var img = TryLoadResourceImage(src); + XImage xImg; + + if (img == null) + return null; + + using (var ms = new MemoryStream()) + { + img.Save(ms, img.RawFormat); + xImg = img != null ? XImage.FromStream(ms) : null; + } + + return xImg; + } + + public override async Task GetImageAsync(string location, Dictionary attributes) + { + var img = TryLoadResourceImage(location); + XImage xImg = null; + + if (img != null) + { + using (var ms = new MemoryStream()) + { + img.Save(ms, img.RawFormat); + xImg = img != null ? XImage.FromStream(ms) : null; + } + } + + if (!e.Handled && e.Attributes != null) + { + if (e.Attributes.ContainsKey("byevent")) + { + int delay; + if (Int32.TryParse(e.Attributes["byevent"], out delay)) + { + e.Handled = true; + ThreadPool.QueueUserWorkItem(state => + { + Thread.Sleep(delay); + e.Callback("https://fbcdn-sphotos-a-a.akamaihd.net/hphotos-ak-snc7/c0.44.403.403/p403x403/318890_10151195988833836_1081776452_n.jpg"); + }); + return; + } + else + { + e.Callback("http://sphotos-a.xx.fbcdn.net/hphotos-ash4/c22.0.403.403/p403x403/263440_10152243591765596_773620816_n.jpg"); + return; + } + } + else if (e.Attributes.ContainsKey("byrect")) + { + var split = e.Attributes["byrect"].Split(','); + var rect = new Rectangle(Int32.Parse(split[0]), Int32.Parse(split[1]), Int32.Parse(split[2]), Int32.Parse(split[3])); + e.Callback(imgObj ?? TryLoadResourceImage("htmlicon"), rect.X, rect.Y, rect.Width, rect.Height); + return; + } + } + + return xImg; + } + } + */ +} diff --git a/Source/Demo/WinForms/app.config b/Source/Demo/WinForms/app.config new file mode 100644 index 000000000..51278a456 --- /dev/null +++ b/Source/Demo/WinForms/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs b/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs index 9339fd562..39806f52b 100644 --- a/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs +++ b/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs @@ -13,6 +13,7 @@ using PdfSharp.Drawing; using System; using System.Collections.Generic; +using System.Threading.Tasks; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; @@ -44,8 +45,8 @@ public sealed class HtmlContainer : IDisposable public HtmlContainer() { _htmlContainerInt = new HtmlContainerInt(PdfSharpAdapter.Instance); - _htmlContainerInt.AvoidAsyncImagesLoading = true; - _htmlContainerInt.AvoidImagesLateLoading = true; + //_htmlContainerInt.AvoidAsyncImagesLoading = true; + //_htmlContainerInt.AvoidImagesLateLoading = true; } /// @@ -70,27 +71,6 @@ public event EventHandler RenderError remove { _htmlContainerInt.RenderError -= value; } } - /// - /// Raised when a stylesheet is about to be loaded by file path or URI by link element.
- /// This event allows to provide the stylesheet manually or provide new source (file or Uri) to load from.
- /// If no alternative data is provided the original source will be used.
- ///
- public event EventHandler StylesheetLoad - { - add { _htmlContainerInt.StylesheetLoad += value; } - remove { _htmlContainerInt.StylesheetLoad -= value; } - } - - /// - /// Raised when an image is about to be loaded by file path or URI.
- /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI. - ///
- public event EventHandler ImageLoad - { - add { _htmlContainerInt.ImageLoad += value; } - remove { _htmlContainerInt.ImageLoad -= value; } - } - /// /// The internal core html container /// @@ -256,9 +236,9 @@ public string SelectedHtml ///
/// the html to init with, init empty if not given /// optional: the stylesheet to init with, init default if not given - public void SetHtml(string htmlSource, CssData baseCssData = null) + public Task SetResourceServerAsync(IResourceServer resourceServer) { - _htmlContainerInt.SetHtml(htmlSource, baseCssData); + return _htmlContainerInt.SetResoureServer(resourceServer); } /// diff --git a/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj b/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj index ef0fd7acc..d775d14fe 100644 --- a/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj +++ b/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj @@ -9,10 +9,11 @@ Properties TheArtOfDev.HtmlRenderer.PdfSharp HtmlRenderer.PdfSharp - v2.0 + v4.5 512 ..\ true + true @@ -22,6 +23,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -30,6 +32,7 @@ TRACE prompt 4 + false diff --git a/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs b/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs index 365be2d95..40dd3e88c 100644 --- a/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs +++ b/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs @@ -14,6 +14,7 @@ using PdfSharp.Drawing; using PdfSharp.Pdf; using System; +using System.Threading.Tasks; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; @@ -68,12 +69,14 @@ public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static PdfDocument GeneratePdf(string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Task GeneratePdf(IResourceServer resourceServer, + PageSize pageSize, int margin = 20 + ) { var config = new PdfGenerateConfig(); config.PageSize = pageSize; config.SetMargins(margin); - return GeneratePdf(html, config, cssData, stylesheetLoad, imageLoad); + return GeneratePdf(resourceServer, config); } /// @@ -85,13 +88,14 @@ public static PdfDocument GeneratePdf(string html, PageSize pageSize, int margin /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static PdfDocument GeneratePdf(string html, PdfGenerateConfig config, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static async Task GeneratePdf(IResourceServer resourceServer, PdfGenerateConfig config + ) { // create PDF document to render the HTML into var document = new PdfDocument(); // add rendered PDF pages to document - AddPdfPages(document, html, config, cssData, stylesheetLoad, imageLoad); + AddPdfPages(document, resourceServer, config); return document; } @@ -107,12 +111,14 @@ public static PdfDocument GeneratePdf(string html, PdfGenerateConfig config, Css /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static void AddPdfPages(PdfDocument document, string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Task AddPdfPages(PdfDocument document, IResourceServer resourceServer, + PageSize pageSize, int margin = 20 + ) { var config = new PdfGenerateConfig(); config.PageSize = pageSize; config.SetMargins(margin); - AddPdfPages(document, html, config, cssData, stylesheetLoad, imageLoad); + return AddPdfPages(document, resourceServer, config); } /// @@ -125,7 +131,10 @@ public static void AddPdfPages(PdfDocument document, string html, PageSize pageS /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static void AddPdfPages(PdfDocument document, string html, PdfGenerateConfig config, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static async Task AddPdfPages(PdfDocument document, + IResourceServer resourceServer, + PdfGenerateConfig config + ) { XSize orgPageSize; // get the size of each page to layout the HTML in @@ -142,18 +151,14 @@ public static void AddPdfPages(PdfDocument document, string html, PdfGenerateCon var pageSize = new XSize(orgPageSize.Width - config.MarginLeft - config.MarginRight, orgPageSize.Height - config.MarginTop - config.MarginBottom); + var html = await resourceServer.GetHtmlAsync(); if (!string.IsNullOrEmpty(html)) { using (var container = new HtmlContainer()) { - if (stylesheetLoad != null) - container.StylesheetLoad += stylesheetLoad; - if (imageLoad != null) - container.ImageLoad += imageLoad; - container.Location = new XPoint(config.MarginLeft, config.MarginTop); container.MaxSize = new XSize(pageSize.Width, 0); - container.SetHtml(html, cssData); + await container.SetResourceServerAsync(resourceServer); container.PageSize = pageSize; container.MarginBottom = config.MarginBottom; container.MarginLeft = config.MarginLeft; diff --git a/Source/HtmlRenderer.SimpleBrowser/App.config b/Source/HtmlRenderer.SimpleBrowser/App.config new file mode 100644 index 000000000..731f6de6c --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Source/HtmlRenderer.SimpleBrowser/Form1.Designer.cs b/Source/HtmlRenderer.SimpleBrowser/Form1.Designer.cs new file mode 100644 index 000000000..c719bdf3f --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Form1.Designer.cs @@ -0,0 +1,93 @@ +namespace HtmlRenderer.SimpleBrowser +{ + partial class Form1 + { + /// + /// 必要なデザイナー変数です。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 使用中のリソースをすべてクリーンアップします。 + /// + /// マネージ リソースを破棄する場合は true を指定し、その他の場合は false を指定します。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows フォーム デザイナーで生成されたコード + + /// + /// デザイナー サポートに必要なメソッドです。このメソッドの内容を + /// コード エディターで変更しないでください。 + /// + private void InitializeComponent() + { + this.url = new System.Windows.Forms.TextBox(); + this.go_btn = new System.Windows.Forms.Button(); + this.htmlPanel1 = new TheArtOfDev.HtmlRenderer.WinForms.HtmlPanel(); + this.SuspendLayout(); + // + // url + // + this.url.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.url.Location = new System.Drawing.Point(12, 12); + this.url.Name = "url"; + this.url.Size = new System.Drawing.Size(695, 19); + this.url.TabIndex = 0; + // + // go_btn + // + this.go_btn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.go_btn.Location = new System.Drawing.Point(713, 12); + this.go_btn.Name = "go_btn"; + this.go_btn.Size = new System.Drawing.Size(75, 23); + this.go_btn.TabIndex = 1; + this.go_btn.Text = "go"; + this.go_btn.UseVisualStyleBackColor = true; + // + // htmlPanel1 + // + this.htmlPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.htmlPanel1.AutoScroll = true; + this.htmlPanel1.AutoScrollMinSize = new System.Drawing.Size(776, 20); + this.htmlPanel1.BackColor = System.Drawing.SystemColors.Window; + this.htmlPanel1.BaseStylesheet = null; + this.htmlPanel1.Cursor = System.Windows.Forms.Cursors.IBeam; + this.htmlPanel1.Location = new System.Drawing.Point(12, 37); + this.htmlPanel1.Name = "htmlPanel1"; + this.htmlPanel1.Size = new System.Drawing.Size(776, 401); + this.htmlPanel1.TabIndex = 2; + this.htmlPanel1.Text = "htmlPanel1"; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.htmlPanel1); + this.Controls.Add(this.go_btn); + this.Controls.Add(this.url); + this.Name = "Form1"; + this.Text = "Form1"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox url; + private System.Windows.Forms.Button go_btn; + private TheArtOfDev.HtmlRenderer.WinForms.HtmlPanel htmlPanel1; + } +} + diff --git a/Source/HtmlRenderer.SimpleBrowser/Form1.cs b/Source/HtmlRenderer.SimpleBrowser/Form1.cs new file mode 100644 index 000000000..9c8d52093 --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Form1.cs @@ -0,0 +1,34 @@ +using System; +using System.Windows.Forms; +using TheArtOfDev.HtmlRenderer.Core.Entities; + +namespace HtmlRenderer.SimpleBrowser +{ + public partial class Form1 : Form + { + HttpResourceServer m_server; + + public Form1() + { + InitializeComponent(); + + m_server = new HttpResourceServer(); + htmlPanel1.ResourceServer = m_server; + htmlPanel1.LinkClicked += HtmlPanel1_LinkClicked; + go_btn.Click += Go_btn_Click; + } + + private async void HtmlPanel1_LinkClicked(object sender, HtmlLinkClickedEventArgs e) + { + await m_server.Go(e.Link); + //htmlPanel1.Text = m_server.Html; + } + + private async void Go_btn_Click(object sender, EventArgs e) + { + Console.WriteLine($"go {url.Text}"); + await m_server.Go(url.Text); + htmlPanel1.Text = m_server.Html; + } + } +} diff --git a/Source/HtmlRenderer.SimpleBrowser/Form1.resx b/Source/HtmlRenderer.SimpleBrowser/Form1.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/HtmlRenderer.SimpleBrowser/HtmlRenderer.SimpleBrowser.csproj b/Source/HtmlRenderer.SimpleBrowser/HtmlRenderer.SimpleBrowser.csproj new file mode 100644 index 000000000..93e760edb --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/HtmlRenderer.SimpleBrowser.csproj @@ -0,0 +1,94 @@ + + + + + Debug + AnyCPU + {EF7F91E6-B633-4C15-B0F3-93D062C0ECC3} + WinExe + HtmlRenderer.SimpleBrowser + HtmlRenderer.SimpleBrowser + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + + + + Form1.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {1b058920-24b4-4140-8ae7-c8c6c38ca52d} + HtmlRenderer.WinForms + + + {fe611685-391f-4e3e-b27e-d3150e51e49b} + HtmlRenderer + + + + \ No newline at end of file diff --git a/Source/HtmlRenderer.SimpleBrowser/HttpResourceServer.cs b/Source/HtmlRenderer.SimpleBrowser/HttpResourceServer.cs new file mode 100644 index 000000000..277cb499f --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/HttpResourceServer.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Core; +using TheArtOfDev.HtmlRenderer.WinForms.Adapters; + + +namespace HtmlRenderer.SimpleBrowser +{ + class HttpResourceServer : IResourceServer + { + string m_url; + string Url + { + get { return m_url; } + set + { + if (m_url == value) return; + m_url = value; + // url updated + if (m_url.EndsWith("/")) + { + m_baseUrl = m_url; + } + else + { + var splited = m_url.Split('/'); + m_baseUrl = string.Join("/", splited.Take(splited.Length-1)) + "/"; + } + + var uri = new Uri(m_url); + var port = ""; + if (uri.Scheme == "http" && uri.Port != 80) + { + port = ":" + uri.Port; + } + else if (uri.Scheme == "https" && uri.Port != 443) + { + port = ":" + uri.Port; + } + m_baseUrlWithoutPath = $"{uri.Scheme}://{uri.Host}{port}"; + + m_schema = "http:"; + if (uri.Scheme != "http") + { + m_schema = uri.Scheme + ":"; + } + } + } + string m_baseUrl; + string m_baseUrlWithoutPath; + string m_schema; + + public async Task Go(string href) + { + var url = GetUrl(href); + var result = await m_session.GetAsync(url); + var bytes = result.GetBodyBytes(); + Url = url; + Html = Encoding.UTF8.GetString(bytes.ToArray()); + } + + string GetUrl(string href) + { + if (href.StartsWith("http:") + || href.StartsWith("https:")) + { + // external + return href; + } + else if (href.StartsWith("//")) + { + // absolute path + return m_schema + href; + } + else if (href.StartsWith("/")) + { + // absolute path + return m_baseUrlWithoutPath + href; + } + else + { + // relative path + return m_baseUrl + href; + } + } + + HttpSession m_session; + + public HttpResourceServer() + { + m_session = new HttpSession(); + } + + public void Dispose() + { + } + + public event EventHandler Updated; + void RaiseUpdated() + { + var handler = Updated; + if (handler != null) + { + handler(this, new ResourceServerUpdatedEventArgs()); + } + } + + string m_html; + public string Html + { + get { return m_html; } + set + { + if (m_html == value) return; + m_html = value; + RaiseUpdated(); + } + } + + CssData m_cssData; + public CssData CssData + { + get { return m_cssData; } + set + { + if (m_cssData == value) return; + m_cssData = value; + RaiseUpdated(); + } + } + + class HttpTask + { + public string Url; + public Task Task; + public Byte[] Bytes; + public Exception Error; + + HttpTask() { } + + public static HttpTask Create(string url, Task task, Action callback) + { + Console.WriteLine($"[HttpTask.Create]{url}"); + var httpTask = new HttpTask + { + Url = url + }; + httpTask.Task = task.ContinueWith(x => + { + try + { + if (x.IsCompleted) + { + httpTask.Bytes = x.Result.GetBodyBytes(); + callback(); + return x.Result; + } + } + catch(Exception ex) + { + httpTask.Error = ex; + Console.WriteLine($"{httpTask.Url} {ex}"); + } + return default(HttpResult); + }); + return httpTask; + } + + CssData m_cssData; + public CssData GetCssData(RAdapter adapter) + { + if (Bytes == null) return null; + + if (m_cssData == null) + { + var css = Encoding.UTF8.GetString(Bytes); + m_cssData = CssData.Parse(adapter, css); + } + return m_cssData; + } + + RImage m_image; + public RImage GetImage(RAdapter adapter) + { + if (Bytes == null) return null; + + if (m_image == null) + { + using (var ms = new MemoryStream(Bytes)) + { + m_image = adapter.ImageFromStream(ms); + } + } + return m_image; + } + } + + Dictionary m_resultMap=new Dictionary(); + void PushTask(string href, HttpTask body) + { + lock (((ICollection)m_resultMap).SyncRoot){ + m_resultMap[href] = body; + } + } + HttpTask GetTask(string href) + { + lock (((ICollection)m_resultMap).SyncRoot) { + HttpTask httpTask; + if (!m_resultMap.TryGetValue(href, out httpTask)) + { + return null; + } + return httpTask; + } + } + + public CssData GetCssData(/*RAdapter adapter,*/ string href, Dictionary attributes) + { + HttpTask task; + if (m_resultMap.TryGetValue(href, out task)) + { + // found + var adapter = WinFormsAdapter.Instance; + return task.GetCssData(adapter); + } + + // not found. request + var url = GetUrl(href); + var newTask = HttpTask.Create(url, m_session.GetAsync(url), RaiseUpdated); + PushTask(href, newTask); + return null; + } + + public RImage GetImage(/*RAdapter adapter,*/ string href, Dictionary attributes) + { + HttpTask task; + if (m_resultMap.TryGetValue(href, out task)) + { + // found + var adapter = WinFormsAdapter.Instance; + return task.GetImage(adapter); + } + + // not found. request + var url = GetUrl(href); + var newTask = HttpTask.Create(url, m_session.GetAsync(url), RaiseUpdated); + PushTask(href, newTask); + return null; + } + } +} diff --git a/Source/HtmlRenderer.SimpleBrowser/HttpSession.cs b/Source/HtmlRenderer.SimpleBrowser/HttpSession.cs new file mode 100644 index 000000000..0a7df2501 --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/HttpSession.cs @@ -0,0 +1,351 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +#if NETFX_CORE +using Windows.Security.Cryptography.Certificates; +using Windows.Web.Http; +using Windows.Web.Http.Filters; +using System.Runtime.InteropServices.WindowsRuntime; +#else +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +#endif + + +namespace HtmlRenderer.SimpleBrowser +{ + static class HttpConst + { + public const string UserAgent = "HtmlRenderer"; + } + +#if NETFX_CORE + public class HttpResult + { + #region Response + public int StatusCode { get; private set; } + + List> m_headers = new List>(); + public List> Headers { get { return m_headers; } } + public string Cookie + { + get + { + foreach (var kv in Headers) + { + if (kv.Key.ToLower() == "set-cookie") + { + return kv.Value; + } + } + throw new KeyNotFoundException("set-cookie"); + } + } + + List m_body = new List(); + public List Body { get { return m_body; } } + public string GetBodyString() + { + return Encoding.UTF8.GetString(Body.ToArray()); + } + #endregion + + public HttpResult(int statusCode) + { + StatusCode = statusCode; + } + + public void AddHeader(string key, string value) + { + m_headers.Add(new KeyValuePair(key, value)); + } + + public void AddBody(IEnumerable bytes) + { + m_body.AddRange(bytes); + } + + public IEnumerable GetBodyBytes(Encoding encoding = null) + { + return m_body; + } + } + + public static class Http + { + public static Task PostForm(HttpSession session, string url, + Dictionary form) + { + var content = new HttpFormUrlEncodedContent(form); + return Post(session, url, content); + } + + public async static Task Post(HttpSession session, string url, + IHttpContent content) + { + var uri = new Uri(url); + + using (var response = await session.Client.PostAsync(uri, content)) + { + var result = new HttpResult((int)response.StatusCode); + foreach (var h in response.Headers) + { + result.AddHeader(h.Key, h.Value); + } + + // get body + var buffer = await response.Content.ReadAsBufferAsync(); + result.AddBody(buffer.ToArray()); + + return result; + } + } + + public static async Task Get(HttpSession session, string url) + { + var uri = new Uri(url); + using (var response = await session.Client.GetAsync(uri)) + { + var result = new HttpResult((int)response.StatusCode); + foreach (var h in response.Headers) + { + result.AddHeader(h.Key, h.Value); + } + + // get body + var buffer = await response.Content.ReadAsBufferAsync(); + result.AddBody(buffer.ToArray()); + + return result; + } + } + } + + public class HttpSession + { + HttpBaseProtocolFilter m_filter; + public HttpBaseProtocolFilter Filter + { + get { return m_filter; } + } + + // Windows.Web.Httpの方。 + // System.Net.Httpではない。 + HttpClient m_client; + public HttpClient Client + { + get { return m_client; } + } + + public HttpSession() + { + m_filter = new HttpBaseProtocolFilter(); + m_filter.AllowAutoRedirect = false; + m_filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired); + m_filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); + m_filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); + + m_filter.CacheControl.ReadBehavior = HttpCacheReadBehavior.MostRecent; + + m_client = new HttpClient(m_filter); + } + + public void AddCookie(string key, string value, string domain) + { + var cookieManager = Filter.CookieManager; + var cookie = new HttpCookie(key, domain, "/") + { + Value = value + }; + cookieManager.SetCookie(cookie); + } + + public Task Get(string url) + { + return Http.Get(this, url); + } + } + +#else + public static class Cert + { + static bool OnRemoteCertificateValidationCallback( + System.Object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + return true; + } + + static bool s_initialized; + + public static void Initialize() + { + if (s_initialized) return; + s_initialized = true; + + ServicePointManager.ServerCertificateValidationCallback = + new RemoteCertificateValidationCallback(OnRemoteCertificateValidationCallback); + } + } + + public class HttpResult + { + public HttpWebRequest Request; + public HttpWebResponse Response; + + public string Cookie + { + get + { + return Response.Headers["Set-Cookie"]; + } + } + + public Byte[] GetBodyBytes() + { + if (Response.StatusCode!=HttpStatusCode.OK) + { + return null; + } + + using (var s = Response.GetResponseStream()) + { + var ms = new MemoryStream(); + s.CopyTo(ms); + return ms.ToArray(); + } + /* + { + while (true) + { + var readSize = data.Read(buffer, 0, buffer.Length); + if (readSize == 0) + { + break; + } + for (int i = 0; i < readSize; ++i) + { + yield return buffer[i]; + } + } + } + */ + } + + public HttpResult(HttpWebRequest request, HttpWebResponse response) + { + Request = request; + Response = response; + } + } + + public static class Http + { + public static Task Get(HttpSession session, string url) + { + var tcs = new TaskCompletionSource(); + + ThreadPool.QueueUserWorkItem(_ => + { + HttpWebRequest request = null; + try + { + request = (HttpWebRequest)System.Net.WebRequest.Create(url); + request.CookieContainer = session.CookieContainer; + request.UserAgent = HttpConst.UserAgent; + request.KeepAlive = false; + //((HttpWebRequest)request).UserAgent = ".NET Framework Example Client"; + var response = (HttpWebResponse)request.GetResponse(); + tcs.SetResult(new HttpResult(request, response)); + } + catch (WebException ex) + { + var response = (HttpWebResponse)ex.Response; + tcs.SetResult(new HttpResult(request, response)); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + + return tcs.Task; + } + + public static Task PostForm(HttpSession session, string url, + Dictionary form) + { + var postData = string.Join("&", form.Select(kv => kv.Key + "=" + Uri.EscapeDataString(kv.Value)).ToArray()); + var postDataBytes = Encoding.ASCII.GetBytes(postData); + return Post(session, url, "application/x-www-form-urlencoded", postDataBytes); + } + + public static Task Post(HttpSession session, string url, + string contentType, Byte[] bytes) + { + var tcs = new TaskCompletionSource(); + + ThreadPool.QueueUserWorkItem(_ => + { + try + { + var request = (HttpWebRequest)System.Net.WebRequest.Create(url); + request.Method = "POST"; + request.ContentType = contentType; + request.ContentLength = bytes.Length; + request.AllowAutoRedirect = false; + request.CookieContainer = session.CookieContainer; + request.UserAgent = HttpConst.UserAgent; + + using (var reqStream = request.GetRequestStream()) + { + reqStream.Write(bytes, 0, bytes.Length); + reqStream.Close(); + } + + var response = (HttpWebResponse)request.GetResponse(); + tcs.SetResult(new HttpResult(request, response)); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }); + + return tcs.Task; + } + } + + public class HttpSession + { + CookieContainer m_cc = new CookieContainer(); + public CookieContainer CookieContainer + { + get { return m_cc; } + } + + public HttpSession() + { + Cert.Initialize(); + } + + public void AddCookie(string key, string value, string domain) + { + var cookie = new Cookie(key, value) { Domain = domain }; + m_cc.Add(cookie); + } + + public Task GetAsync(string url) + { + return Http.Get(this, url); + } + } +#endif +} diff --git a/Source/HtmlRenderer.SimpleBrowser/Program.cs b/Source/HtmlRenderer.SimpleBrowser/Program.cs new file mode 100644 index 000000000..476339ff2 --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace HtmlRenderer.SimpleBrowser +{ + static class Program + { + /// + /// アプリケーションのメイン エントリ ポイントです。 + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/Source/HtmlRenderer.SimpleBrowser/Properties/AssemblyInfo.cs b/Source/HtmlRenderer.SimpleBrowser/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..672d6b835 --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("HtmlRenderer.SimpleBrowser")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("HtmlRenderer.SimpleBrowser")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから +// 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(false)] + +// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります +[assembly: Guid("ef7f91e6-b633-4c15-b0f3-93d062c0ecc3")] + +// アセンブリのバージョン情報は次の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// Revision +// +// すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/HtmlRenderer.SimpleBrowser/Properties/Resources.Designer.cs b/Source/HtmlRenderer.SimpleBrowser/Properties/Resources.Designer.cs new file mode 100644 index 000000000..db0ca651b --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.42000 +// +// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 +// コードが再生成されるときに損失したりします +// +//------------------------------------------------------------------------------ + +namespace HtmlRenderer.SimpleBrowser.Properties +{ + + + /// + /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 + /// + // このクラスは StronglyTypedResourceBuilder クラスが ResGen + // または Visual Studio のようなツールを使用して自動生成されました。 + // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に + // ResGen を実行し直すか、または VS プロジェクトをリビルドします。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// このクラスで使用されるキャッシュされた ResourceManager インスタンスを返します。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HtmlRenderer.SimpleBrowser.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 厳密に型指定されたこのリソース クラスを使用して、すべての検索リソースに対し、 + /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Source/HtmlRenderer.SimpleBrowser/Properties/Resources.resx b/Source/HtmlRenderer.SimpleBrowser/Properties/Resources.resx new file mode 100644 index 000000000..af7dbebba --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/HtmlRenderer.SimpleBrowser/Properties/Settings.Designer.cs b/Source/HtmlRenderer.SimpleBrowser/Properties/Settings.Designer.cs new file mode 100644 index 000000000..b084f336b --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HtmlRenderer.SimpleBrowser.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Source/HtmlRenderer.SimpleBrowser/Properties/Settings.settings b/Source/HtmlRenderer.SimpleBrowser/Properties/Settings.settings new file mode 100644 index 000000000..39645652a --- /dev/null +++ b/Source/HtmlRenderer.SimpleBrowser/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj b/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj index 80c4b97f9..72c757431 100644 --- a/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj +++ b/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj @@ -9,7 +9,7 @@ Properties TheArtOfDev.HtmlRenderer.WPF HtmlRenderer.WPF - v3.0 + v4.5 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 @@ -24,6 +24,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -32,6 +33,7 @@ TRACE prompt 4 + false diff --git a/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs b/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs index 16bc83db8..f436770b2 100644 --- a/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs +++ b/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs @@ -23,7 +23,7 @@ namespace TheArtOfDev.HtmlRenderer.WinForms.Adapters /// /// Adapter for WinForms platforms. /// - internal sealed class WinFormsAdapter : RAdapter + public sealed class WinFormsAdapter : RAdapter { #region Fields and Consts diff --git a/Source/HtmlRenderer.WinForms/HtmlContainer.cs b/Source/HtmlRenderer.WinForms/HtmlContainer.cs index 5bc7c5cd0..6c5832b72 100644 --- a/Source/HtmlRenderer.WinForms/HtmlContainer.cs +++ b/Source/HtmlRenderer.WinForms/HtmlContainer.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Text; +using System.Threading.Tasks; using System.Windows.Forms; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core; @@ -111,27 +112,6 @@ public event EventHandler RenderError remove { _htmlContainerInt.RenderError -= value; } } - /// - /// Raised when a stylesheet is about to be loaded by file path or URI by link element.
- /// This event allows to provide the stylesheet manually or provide new source (file or Uri) to load from.
- /// If no alternative data is provided the original source will be used.
- ///
- public event EventHandler StylesheetLoad - { - add { _htmlContainerInt.StylesheetLoad += value; } - remove { _htmlContainerInt.StylesheetLoad -= value; } - } - - /// - /// Raised when an image is about to be loaded by file path or URI.
- /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI. - ///
- public event EventHandler ImageLoad - { - add { _htmlContainerInt.ImageLoad += value; } - remove { _htmlContainerInt.ImageLoad -= value; } - } - /// /// The internal core html container /// @@ -183,41 +163,6 @@ public bool AvoidGeometryAntialias set { _htmlContainerInt.AvoidGeometryAntialias = value; } } - /// - /// Gets or sets a value indicating if image asynchronous loading should be avoided (default - false).
- /// True - images are loaded synchronously during html parsing.
- /// False - images are loaded asynchronously to html parsing when downloaded from URL or loaded from disk.
- ///
- /// - /// Asynchronously image loading allows to unblock html rendering while image is downloaded or loaded from disk using IO - /// ports to achieve better performance.
- /// Asynchronously image loading should be avoided when the full html content must be available during render, like render to image. - ///
- public bool AvoidAsyncImagesLoading - { - get { return _htmlContainerInt.AvoidAsyncImagesLoading; } - set { _htmlContainerInt.AvoidAsyncImagesLoading = value; } - } - - /// - /// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).
- /// True - images are loaded as soon as the html is parsed.
- /// False - images that are not visible because of scroll location are not loaded until they are scrolled to. - ///
- /// - /// 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).
- /// 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.
- /// 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. - ///
- public bool AvoidImagesLateLoading - { - get { return _htmlContainerInt.AvoidImagesLateLoading; } - set { _htmlContainerInt.AvoidImagesLateLoading = value; } - } - /// /// Is content selection is enabled for the rendered html (default - true).
/// If set to 'false' the rendered html will be static only with ability to click on links. @@ -312,9 +257,9 @@ public void ClearSelection() ///
/// the html to init with, init empty if not given /// optional: the stylesheet to init with, init default if not given - public void SetHtml(string htmlSource, CssData baseCssData = null) + public void SetResourceServer(IResourceServer resourceServer) { - _htmlContainerInt.SetHtml(htmlSource, baseCssData); + _htmlContainerInt.SetResoureServer(resourceServer); } /// diff --git a/Source/HtmlRenderer.WinForms/HtmlLabel.cs b/Source/HtmlRenderer.WinForms/HtmlLabel.cs index 8853490d2..c66d8ee1c 100644 --- a/Source/HtmlRenderer.WinForms/HtmlLabel.cs +++ b/Source/HtmlRenderer.WinForms/HtmlLabel.cs @@ -111,7 +111,6 @@ public class HtmlLabel : Control /// The text rendering hint to be used for text rendering. /// protected TextRenderingHint _textRenderingHint = TextRenderingHint.SystemDefault; - #endregion @@ -120,6 +119,7 @@ public class HtmlLabel : Control ///
public HtmlLabel() { + SuspendLayout(); AutoSize = true; @@ -130,18 +130,47 @@ public HtmlLabel() SetStyle(ControlStyles.Opaque, false); _htmlContainer = new HtmlContainer(); - _htmlContainer.AvoidImagesLateLoading = true; _htmlContainer.MaxSize = MaximumSize; _htmlContainer.LoadComplete += OnLoadComplete; _htmlContainer.LinkClicked += OnLinkClicked; _htmlContainer.RenderError += OnRenderError; _htmlContainer.Refresh += OnRefresh; - _htmlContainer.StylesheetLoad += OnStylesheetLoad; - _htmlContainer.ImageLoad += OnImageLoad; ResumeLayout(false); } + private void ResourceServer_Updated(object sender, ResourceServerUpdatedEventArgs e) + { + if (InvokeRequired) + { + Action callback = () => ResourceServer_Updated(sender, e); + Invoke(callback); + return; + } + + _htmlContainer.SetResourceServer(ResourceServer); + PerformLayout(); + Invalidate(); + } + + IResourceServer _resourceServer; + public IResourceServer ResourceServer + { + get + { + if (_resourceServer == null) + { + _resourceServer = ResourceServerFactory.Create(); + } + return _resourceServer; + } + set + { + if (_resourceServer == value) return; + _resourceServer = value; + _resourceServer.Updated += ResourceServer_Updated; + } + } /// /// Raised when the BorderStyle property value changes. /// @@ -165,13 +194,6 @@ public HtmlLabel() ///
public event EventHandler RenderError; - /// - /// Raised when aa stylesheet is about to be loaded by file path or URI by link element.
- /// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.
- /// If no alternative data is provided the original source will be used.
- ///
- public event EventHandler StylesheetLoad; - /// /// Raised when an image is about to be loaded by file path or URI.
/// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI. @@ -303,7 +325,7 @@ public virtual string BaseStylesheet { _baseRawCssData = value; _baseCssData = HtmlRender.ParseStyleSheet(value); - _htmlContainer.SetHtml(_text, _baseCssData); + ResourceServer.CssData=_baseCssData; } } @@ -396,9 +418,7 @@ public override string Text base.Text = value; if (!IsDisposed) { - _htmlContainer.SetHtml(_text, _baseCssData); - PerformLayout(); - Invalidate(); + ResourceServer.Html=_text; } } } @@ -615,16 +635,6 @@ protected virtual void OnRenderError(HtmlRenderErrorEventArgs e) handler(this, e); } - /// - /// Propagate the stylesheet load event from root container. - /// - protected virtual void OnStylesheetLoad(HtmlStylesheetLoadEventArgs e) - { - var handler = StylesheetLoad; - if (handler != null) - handler(this, e); - } - /// /// Propagate the image load event from root container. /// @@ -682,8 +692,6 @@ protected override void Dispose(bool disposing) _htmlContainer.LinkClicked -= OnLinkClicked; _htmlContainer.RenderError -= OnRenderError; _htmlContainer.Refresh -= OnRefresh; - _htmlContainer.StylesheetLoad -= OnStylesheetLoad; - _htmlContainer.ImageLoad -= OnImageLoad; _htmlContainer.Dispose(); _htmlContainer = null; } @@ -711,11 +719,6 @@ private void OnRenderError(object sender, HtmlRenderErrorEventArgs e) OnRenderError(e); } - private void OnStylesheetLoad(object sender, HtmlStylesheetLoadEventArgs e) - { - OnStylesheetLoad(e); - } - private void OnImageLoad(object sender, HtmlImageLoadEventArgs e) { OnImageLoad(e); diff --git a/Source/HtmlRenderer.WinForms/HtmlPanel.cs b/Source/HtmlRenderer.WinForms/HtmlPanel.cs index ce94b0ea9..53d63f5a2 100644 --- a/Source/HtmlRenderer.WinForms/HtmlPanel.cs +++ b/Source/HtmlRenderer.WinForms/HtmlPanel.cs @@ -104,6 +104,24 @@ public class HtmlPanel : ScrollableControl ///
protected Point _lastScrollOffset; + IResourceServer _resourceServer; + public IResourceServer ResourceServer + { + get + { + if (_resourceServer == null) + { + _resourceServer = ResourceServerFactory.Create(); + } + return _resourceServer; + } + set + { + if (_resourceServer == value) return; + _resourceServer = value; + _resourceServer.Updated += ResourceServer_Updated; + } + } #endregion @@ -124,8 +142,21 @@ public HtmlPanel() _htmlContainer.RenderError += OnRenderError; _htmlContainer.Refresh += OnRefresh; _htmlContainer.ScrollChange += OnScrollChange; - _htmlContainer.StylesheetLoad += OnStylesheetLoad; - _htmlContainer.ImageLoad += OnImageLoad; + } + + private void ResourceServer_Updated(object sender, ResourceServerUpdatedEventArgs e) + { + if (InvokeRequired) + { + Action callback = () => ResourceServer_Updated(sender, e); + Invoke(callback); + return; + } + + _htmlContainer.SetResourceServer(ResourceServer); + PerformLayout(); + Invalidate(); + InvokeMouseMove(); } /// @@ -151,19 +182,6 @@ public HtmlPanel() /// public event EventHandler RenderError; - /// - /// Raised when a stylesheet is about to be loaded by file path or URI by link element.
- /// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.
- /// If no alternative data is provided the original source will be used.
- ///
- public event EventHandler StylesheetLoad; - - /// - /// Raised when an image is about to be loaded by file path or URI.
- /// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI. - ///
- public event EventHandler ImageLoad; - /// /// Gets or sets a value indicating if anti-aliasing should be avoided for geometry like backgrounds and borders (default - false). /// @@ -176,28 +194,6 @@ public virtual bool AvoidGeometryAntialias set { _htmlContainer.AvoidGeometryAntialias = value; } } - /// - /// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).
- /// True - images are loaded as soon as the html is parsed.
- /// False - images that are not visible because of scroll location are not loaded until they are scrolled to. - ///
- /// - /// 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).
- /// 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.
- /// 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. - ///
- [Category("Behavior")] - [DefaultValue(false)] - [Description("If image loading only when visible should be avoided")] - public virtual bool AvoidImagesLateLoading - { - get { return _htmlContainer.AvoidImagesLateLoading; } - set { _htmlContainer.AvoidImagesLateLoading = value; } - } - /// /// Use GDI+ text rendering to measure/draw text.
///
@@ -311,7 +307,7 @@ public virtual string BaseStylesheet { _baseRawCssData = value; _baseCssData = HtmlRender.ParseStyleSheet(value); - _htmlContainer.SetHtml(_text, _baseCssData); + ResourceServer.CssData = _baseCssData; } } @@ -336,15 +332,13 @@ public override string Text get { return _text; } set { + if (_text == value) return; _text = value; - base.Text = value; + if (!IsDisposed) { VerticalScroll.Value = VerticalScroll.Minimum; - _htmlContainer.SetHtml(_text, _baseCssData); - PerformLayout(); - Invalidate(); - InvokeMouseMove(); + ResourceServer.Html=_text; } } } @@ -653,26 +647,6 @@ protected virtual void OnRenderError(HtmlRenderErrorEventArgs e) handler(this, e); } - /// - /// Propagate the stylesheet load event from root container. - /// - protected virtual void OnStylesheetLoad(HtmlStylesheetLoadEventArgs e) - { - var handler = StylesheetLoad; - if (handler != null) - handler(this, e); - } - - /// - /// Propagate the image load event from root container. - /// - protected virtual void OnImageLoad(HtmlImageLoadEventArgs e) - { - var handler = ImageLoad; - if (handler != null) - handler(this, e); - } - /// /// Handle html renderer invalidate and re-layout as requested. /// @@ -779,8 +753,6 @@ protected override void Dispose(bool disposing) _htmlContainer.RenderError -= OnRenderError; _htmlContainer.Refresh -= OnRefresh; _htmlContainer.ScrollChange -= OnScrollChange; - _htmlContainer.StylesheetLoad -= OnStylesheetLoad; - _htmlContainer.ImageLoad -= OnImageLoad; _htmlContainer.Dispose(); _htmlContainer = null; } @@ -808,16 +780,6 @@ private void OnRenderError(object sender, HtmlRenderErrorEventArgs e) OnRenderError(e); } - private void OnStylesheetLoad(object sender, HtmlStylesheetLoadEventArgs e) - { - OnStylesheetLoad(e); - } - - private void OnImageLoad(object sender, HtmlImageLoadEventArgs e) - { - OnImageLoad(e); - } - private void OnRefresh(object sender, HtmlRefreshEventArgs e) { if (InvokeRequired) diff --git a/Source/HtmlRenderer.WinForms/HtmlRender.cs b/Source/HtmlRenderer.WinForms/HtmlRender.cs index 3c6ad3ca9..45e536a61 100644 --- a/Source/HtmlRenderer.WinForms/HtmlRender.cs +++ b/Source/HtmlRenderer.WinForms/HtmlRender.cs @@ -15,6 +15,7 @@ using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Drawing.Text; +using System.Threading.Tasks; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; @@ -148,11 +149,12 @@ public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the size required for the html - public static SizeF Measure(Graphics g, string html, float maxWidth = 0, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static SizeF Measure(Graphics g, IResourceServer resourceServer, + float maxWidth = 0 + ) { ArgChecker.AssertArgNotNull(g, "g"); - return Measure(g, html, maxWidth, cssData, false, stylesheetLoad, imageLoad); + return Measure(g, resourceServer, maxWidth, false); } #endif @@ -169,11 +171,12 @@ public static SizeF Measure(Graphics g, string html, float maxWidth = 0, CssData /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the size required for the html - public static SizeF MeasureGdiPlus(Graphics g, string html, float maxWidth = 0, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static SizeF MeasureGdiPlus(Graphics g, IResourceServer resourceServer, + float maxWidth = 0 + ) { ArgChecker.AssertArgNotNull(g, "g"); - return Measure(g, html, maxWidth, cssData, true, stylesheetLoad, imageLoad); + return Measure(g, resourceServer, maxWidth, true); } #if !MONO @@ -193,11 +196,11 @@ public static SizeF MeasureGdiPlus(Graphics g, string html, float maxWidth = 0, /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html - public static SizeF Render(Graphics g, string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static SizeF Render(Graphics g, IResourceServer resourceServer, + float left = 0, float top = 0, float maxWidth = 0) { ArgChecker.AssertArgNotNull(g, "g"); - return RenderClip(g, html, new PointF(left, top), new SizeF(maxWidth, 0), cssData, false, stylesheetLoad, imageLoad); + return RenderClip(g, resourceServer, new PointF(left, top), new SizeF(maxWidth, 0), false); } /// @@ -217,11 +220,11 @@ public static SizeF Render(Graphics g, string html, float left = 0, float top = /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html - public static SizeF Render(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static SizeF Render(Graphics g, IResourceServer resourceServer, + PointF location, SizeF maxSize) { ArgChecker.AssertArgNotNull(g, "g"); - return RenderClip(g, html, location, maxSize, cssData, false, stylesheetLoad, imageLoad); + return RenderClip(g, resourceServer, location, maxSize, false); } #endif @@ -241,11 +244,12 @@ public static SizeF Render(Graphics g, string html, PointF location, SizeF maxSi /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html - public static SizeF RenderGdiPlus(Graphics g, string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static SizeF RenderGdiPlus(Graphics g, IResourceServer resouerceServer, + float left = 0, float top = 0, float maxWidth = 0 + ) { ArgChecker.AssertArgNotNull(g, "g"); - return RenderClip(g, html, new PointF(left, top), new SizeF(maxWidth, 0), cssData, true, stylesheetLoad, imageLoad); + return RenderClip(g, resouerceServer, new PointF(left, top), new SizeF(maxWidth, 0), true); } /// @@ -265,17 +269,19 @@ public static SizeF RenderGdiPlus(Graphics g, string html, float left = 0, float /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html - public static SizeF RenderGdiPlus(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static SizeF RenderGdiPlus(Graphics g, IResourceServer resourceServer, + PointF location, SizeF maxSize + ) { ArgChecker.AssertArgNotNull(g, "g"); - return RenderClip(g, html, location, maxSize, cssData, true, stylesheetLoad, imageLoad); + return RenderClip(g, resourceServer, location, maxSize, true); } #if !MONO - public static Metafile RenderToMetafile(string html, float left = 0, float top = 0, float maxWidth = 0, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static async Task RenderToMetafile(IResourceServer resourceServer, + float left = 0, float top = 0, float maxWidth = 0 + ) { Metafile image; IntPtr dib; @@ -286,7 +292,7 @@ public static Metafile RenderToMetafile(string html, float left = 0, float top = using (var g = Graphics.FromImage(image)) { - Render(g, html, left, top, maxWidth, cssData, stylesheetLoad, imageLoad); + Render(g, resourceServer, left, top, maxWidth); } } finally @@ -309,12 +315,13 @@ public static Metafile RenderToMetafile(string html, float left = 0, float top = /// optional: the style to use for html rendering (default - use W3 default style) /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic - public static void RenderToImage(Image image, string html, PointF location = new PointF(), CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static void RenderToImage(Image image, IResourceServer resourceServer, + PointF location = new PointF() + ) { ArgChecker.AssertArgNotNull(image, "image"); var maxSize = new SizeF(image.Size.Width - location.X, image.Size.Height - location.Y); - RenderToImage(image, html, location, maxSize, cssData, stylesheetLoad, imageLoad); + RenderToImage(image, resourceServer, location, maxSize); } /// @@ -330,12 +337,13 @@ public static Metafile RenderToMetafile(string html, float left = 0, float top = /// optional: the style to use for html rendering (default - use W3 default style) /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic - public static void RenderToImage(Image image, string html, PointF location, SizeF maxSize, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static void RenderToImage(Image image, IResourceServer resourceServer, + PointF location, SizeF maxSize + ) { ArgChecker.AssertArgNotNull(image, "image"); - if (!string.IsNullOrEmpty(html)) + if (!string.IsNullOrEmpty(resourceServer.Html)) { // create memory buffer from desktop handle that supports alpha channel IntPtr dib; @@ -349,7 +357,7 @@ public static void RenderToImage(Image image, string html, PointF location, Size memoryGraphics.DrawImageUnscaled(image, 0, 0); // render HTML into the memory buffer - RenderHtml(memoryGraphics, html, location, maxSize, cssData, false, stylesheetLoad, imageLoad); + RenderHtml(memoryGraphics, resourceServer, location, maxSize, false); } // copy from memory buffer to image @@ -378,8 +386,9 @@ public static void RenderToImage(Image image, string html, PointF location, Size /// optional: can be used to overwrite image resolution logic /// the generated image of the html /// if is . - public static Image RenderToImage(string html, Size size, Color backgroundColor = new Color(), CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Image RenderToImage(IResourceServer resourceServer, + Size size, Color backgroundColor = new Color() + ) { if (backgroundColor == Color.Transparent) throw new ArgumentOutOfRangeException("backgroundColor", "Transparent background in not supported"); @@ -387,7 +396,7 @@ public static void RenderToImage(Image image, string html, PointF location, Size // create the final image to render into var image = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb); - if (!string.IsNullOrEmpty(html)) + if (!string.IsNullOrEmpty(resourceServer.Html)) { // create memory buffer from desktop handle that supports alpha channel IntPtr dib; @@ -400,7 +409,7 @@ public static void RenderToImage(Image image, string html, PointF location, Size memoryGraphics.Clear(backgroundColor != Color.Empty ? backgroundColor : Color.White); // render HTML into the memory buffer - RenderHtml(memoryGraphics, html, PointF.Empty, size, cssData, true, stylesheetLoad, imageLoad); + RenderHtml(memoryGraphics, resourceServer, PointF.Empty, size, true); } // copy from memory buffer to image @@ -435,10 +444,11 @@ public static void RenderToImage(Image image, string html, PointF location, Size /// optional: can be used to overwrite image resolution logic /// the generated image of the html /// if is . - public static Image RenderToImage(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Image RenderToImage(IResourceServer resourceServer, + int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color() + ) { - return RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); + return RenderToImage(resourceServer, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor); } /// @@ -462,25 +472,19 @@ public static void RenderToImage(Image image, string html, PointF location, Size /// optional: can be used to overwrite image resolution logic /// the generated image of the html /// if is . - public static Image RenderToImage(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Image RenderToImage(IResourceServer resourceServer, + Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null + ) { if (backgroundColor == Color.Transparent) throw new ArgumentOutOfRangeException("backgroundColor", "Transparent background in not supported"); - if (string.IsNullOrEmpty(html)) + if (string.IsNullOrEmpty(resourceServer.Html)) return new Bitmap(0, 0, PixelFormat.Format32bppArgb); using (var container = new HtmlContainer()) { - container.AvoidAsyncImagesLoading = true; - container.AvoidImagesLateLoading = true; - - if (stylesheetLoad != null) - container.StylesheetLoad += stylesheetLoad; - if (imageLoad != null) - container.ImageLoad += imageLoad; - container.SetHtml(html, cssData); + container.SetResourceServer(resourceServer); var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); container.MaxSize = finalSize; @@ -527,15 +531,15 @@ public static void RenderToImage(Image image, string html, PointF location, Size /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static Image RenderToImageGdiPlus(string html, Size size, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Image RenderToImageGdiPlus(IResourceServer resourceServer, + Size size, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias) { var image = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb); using (var g = Graphics.FromImage(image)) { g.TextRenderingHint = textRenderingHint; - RenderHtml(g, html, PointF.Empty, size, cssData, true, stylesheetLoad, imageLoad); + RenderHtml(g, resourceServer, PointF.Empty, size, true); } return image; @@ -559,10 +563,11 @@ public static Image RenderToImageGdiPlus(string html, Size size, TextRenderingHi /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static Image RenderToImageGdiPlus(string html, int maxWidth = 0, int maxHeight = 0, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Image RenderToImageGdiPlus(IResourceServer resourceServer, + int maxWidth = 0, int maxHeight = 0, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null + ) { - return RenderToImageGdiPlus(html, Size.Empty, new Size(maxWidth, maxHeight), textRenderingHint, cssData, stylesheetLoad, imageLoad); + return RenderToImageGdiPlus(resourceServer, Size.Empty, new Size(maxWidth, maxHeight), textRenderingHint, cssData); } /// @@ -584,23 +589,17 @@ public static Image RenderToImageGdiPlus(string html, int maxWidth = 0, int maxH /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the generated image of the html - public static Image RenderToImageGdiPlus(string html, Size minSize, Size maxSize, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, - EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + public static Image RenderToImageGdiPlus(IResourceServer resourceServer, Size minSize, Size maxSize, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null + ) { - if (string.IsNullOrEmpty(html)) + if (string.IsNullOrEmpty(resourceServer.Html)) return new Bitmap(0, 0, PixelFormat.Format32bppArgb); using (var container = new HtmlContainer()) { - container.AvoidAsyncImagesLoading = true; - container.AvoidImagesLateLoading = true; container.UseGdiPlusTextRendering = true; - if (stylesheetLoad != null) - container.StylesheetLoad += stylesheetLoad; - if (imageLoad != null) - container.ImageLoad += imageLoad; - container.SetHtml(html, cssData); + container.SetResourceServer(resourceServer); var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); container.MaxSize = finalSize; @@ -633,25 +632,19 @@ public static Image RenderToImageGdiPlus(string html, Size minSize, Size maxSize /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the size required for the html - private static SizeF Measure(Graphics g, string html, float maxWidth, CssData cssData, bool useGdiPlusTextRendering, - EventHandler stylesheetLoad, EventHandler imageLoad) + private static SizeF Measure(Graphics g, IResourceServer resourceServer, + float maxWidth, bool useGdiPlusTextRendering + ) { SizeF actualSize = SizeF.Empty; - if (!string.IsNullOrEmpty(html)) + if (!string.IsNullOrEmpty(resourceServer.Html)) { using (var container = new HtmlContainer()) { container.MaxSize = new SizeF(maxWidth, 0); - container.AvoidAsyncImagesLoading = true; - container.AvoidImagesLateLoading = true; container.UseGdiPlusTextRendering = useGdiPlusTextRendering; - if (stylesheetLoad != null) - container.StylesheetLoad += stylesheetLoad; - if (imageLoad != null) - container.ImageLoad += imageLoad; - - container.SetHtml(html, cssData); + container.SetResourceServer(resourceServer); container.PerformLayout(g); actualSize = container.ActualSize; @@ -698,7 +691,9 @@ private static Size MeasureHtmlByRestrictions(HtmlContainer htmlContainer, Size /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html - private static SizeF RenderClip(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData, bool useGdiPlusTextRendering, EventHandler stylesheetLoad, EventHandler imageLoad) + private static SizeF RenderClip(Graphics g, IResourceServer resourceServer, + PointF location, SizeF maxSize, bool useGdiPlusTextRendering + ) { Region prevClip = null; if (maxSize.Height > 0) @@ -707,7 +702,7 @@ private static SizeF RenderClip(Graphics g, string html, PointF location, SizeF g.SetClip(new RectangleF(location, maxSize)); } - var actualSize = RenderHtml(g, html, location, maxSize, cssData, useGdiPlusTextRendering, stylesheetLoad, imageLoad); + var actualSize = RenderHtml(g, resourceServer, location, maxSize, useGdiPlusTextRendering); if (prevClip != null) { @@ -734,26 +729,19 @@ private static SizeF RenderClip(Graphics g, string html, PointF location, SizeF /// optional: can be used to overwrite stylesheet resolution logic /// optional: can be used to overwrite image resolution logic /// the actual size of the rendered html - private static SizeF RenderHtml(Graphics g, string html, PointF location, SizeF maxSize, CssData cssData, bool useGdiPlusTextRendering, EventHandler stylesheetLoad, EventHandler imageLoad) + private static SizeF RenderHtml(Graphics g, IResourceServer resourceServer, PointF location, SizeF maxSize, bool useGdiPlusTextRendering + ) { SizeF actualSize = SizeF.Empty; - - if (!string.IsNullOrEmpty(html)) + if (!string.IsNullOrEmpty(resourceServer.Html)) { using (var container = new HtmlContainer()) { container.Location = location; container.MaxSize = maxSize; - container.AvoidAsyncImagesLoading = true; - container.AvoidImagesLateLoading = true; container.UseGdiPlusTextRendering = useGdiPlusTextRendering; - if (stylesheetLoad != null) - container.StylesheetLoad += stylesheetLoad; - if (imageLoad != null) - container.ImageLoad += imageLoad; - - container.SetHtml(html, cssData); + container.SetResourceServer(resourceServer); container.PerformLayout(g); container.PerformPaint(g); diff --git a/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj b/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj index ce1a98759..8730b6504 100644 --- a/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj +++ b/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj @@ -12,7 +12,7 @@ HtmlRenderer.WinForms - v2.0 + v4.5 @@ -46,6 +46,7 @@ 4 false AllRules.ruleset + false pdbonly @@ -55,6 +56,7 @@ prompt 4 AllRules.ruleset + false @@ -115,6 +117,9 @@ HtmlRenderer + + +