From 0e83496f2071ad91f5d0c5c360e1e7057831894d Mon Sep 17 00:00:00 2001 From: Henry Roeland Date: Sun, 20 Nov 2022 13:22:08 +0100 Subject: [PATCH 01/22] Introducing net6.0 port of HtmlRenderer - HtmlRenderer.PdfSharp for GDI based rendering: Tested with the Winforms App. - HtmlRenderer.SkiaSharp for Rendering using SkiaSharp: Work in progress! --- .gitignore | 4 +- .../Common/HtmlRenderer.Demo.Common.csproj | 178 --------- Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj | 149 -------- Source/Demo/WPF/Properties/AssemblyInfo.cs | 56 --- Source/Demo/WPF/packages.config | 4 - .../HtmlRenderer.Demo.WinForms.csproj | 198 ---------- .../HtmlRenderer.Demo.Common}/DemoUtils.cs | 0 .../HtmlRenderer.Demo.Common.csproj | 296 +++++++++++++++ .../HtmlRenderer.Demo.Common}/HtmlSample.cs | 0 .../HtmlSyntaxHighlighter.cs | 0 .../PerfSamples/1.Big table.htm | 0 .../PerfSamples/2.Lots blocks in inline.htm | 0 .../Properties/AssemblyInfo.cs | 0 .../Properties/Resources.Designer.cs | 6 +- .../Properties/Resources.resx | 0 .../HtmlRenderer.Demo.Common}/Resources.cs | 2 +- .../Resources/CustomFont.ttf | Bin .../Resources/Event16.png | Bin .../Resources/Tooltip.html | 0 .../Resources/browser.png | Bin .../Resources/chrome.png | Bin .../Resources/code.png | Bin .../Resources/comment16.gif | Bin .../Resources/delete16.gif | Bin .../Resources/delete32.gif | Bin .../Resources/favorites32.png | Bin .../Resources/font32.png | Bin .../Resources/form.png | Bin .../Resources/formula32.png | Bin .../Resources/html32.png | Bin .../Resources/image.png | Bin .../Resources/image32.png | Bin .../Resources/method16.gif | Bin .../Resources/pdf.png | Bin .../Resources/property16.gif | Bin .../Resources/stopwatch.png | Bin .../Resources/web_pallete.gif | Bin .../Samples/00.Intro.htm | 0 .../Samples/01.History.htm | 0 .../Samples/02.Text.htm | 10 +- .../Samples/03.Tables.htm | 0 .../Samples/04.Links.htm | 0 .../Samples/05.Images.htm | 0 .../Samples/06.Embeded video.htm | 0 .../Samples/07.Additional features.htm | 0 .../Samples/08.Tooltip.htm | 0 .../Samples/09.Using the library.htm | 0 .../Samples/10.HtmlPanel.htm | 0 .../Samples/11.HtmlLabel.htm | 0 .../Samples/12.HtmlToolTip.htm | 0 .../Samples/13.HtmlRender.htm | 0 .../Samples/14.HtmlContainer.htm | 0 .../Samples/20.About.htm | 0 .../SamplesLoader.cs | 0 .../TestSamples/01.Header.htm | 0 .../TestSamples/02.Line break.htm | 0 .../TestSamples/03.Paragraphs.htm | 0 .../TestSamples/04.Blockquotes.htm | 0 .../TestSamples/05.Images.htm | 0 .../TestSamples/06.External Image.htm | 0 .../TestSamples/07.Background Image.htm | 0 .../TestSamples/08.White-space.htm | 0 .../TestSamples/09.Inline.htm | 0 .../TestSamples/10.BlockInInline.htm | 0 .../TestSamples/11.LineHeight.htm | 0 .../TestSamples/12.Text.htm | 14 +- .../TestSamples/13.Tables.htm | 0 .../TestSamples/14.Iframes.htm | 0 .../TestSamples/15.MaxWidth.htm | 0 .../TestSamples/16.Borders.htm | 0 .../TestSamples/17.Languages.htm | 0 .../TestSamples/18.Anchors.htm | 0 .../TestSamples/19.Many images.htm | 0 .../TestSamples/20.Fonts decorations.htm | 0 .../TestSamples/21.Bullets.htm | 0 .../TestSamples/22.RTL.htm | 34 +- .../TestSamples/30.Misc.htm | 0 .../TestSamples/31.ACID 1.htm | 0 .../TestSamples/32.Image in css content.htm | 0 .../TestSamples/33.Fixed position.htm | 0 .../34.Breaking pages 1 - Paragraphs.htm | 0 .../35.Breaking pages 2 - Tables.htm | 0 .../HtmlRenderer.Demo.SkiaSharp.csproj | 14 + .../HtmlRenderer.Demo.SkiaSharp/Program.cs | 59 +++ .../HtmlRenderer.Demo.WPF}/App.xaml | 2 +- .../HtmlRenderer.Demo.WPF}/App.xaml.cs | 0 .../HtmlRenderer.Demo.WPF/AssemblyInfo.cs | 10 + .../HtmlRenderer.Demo.WPF}/DemoWindow.xaml | 2 +- .../HtmlRenderer.Demo.WPF}/DemoWindow.xaml.cs | 5 + .../GenerateImageWindow.xaml | 2 +- .../GenerateImageWindow.xaml.cs | 0 .../HtmlRenderer.Demo.WPF.csproj | 29 ++ .../HtmlRenderingHelper.cs | 0 .../HtmlRenderer.Demo.WPF}/MainControl.xaml | 1 + .../MainControl.xaml.cs | 0 .../HtmlRenderer.Demo.WPF/MainWindow.xaml | 12 + .../HtmlRenderer.Demo.WPF/MainWindow.xaml.cs | 28 ++ .../HtmlRenderer.Demo.WPF}/SampleWindow.xaml | 0 .../SampleWindow.xaml.cs | 1 + .../ToolStripImageConverter.cs | 2 +- .../fonts/CustomFont.ttf | Bin .../HtmlRenderer.Demo.WPF}/html.ico | Bin .../DemoForm.Designer.cs | 0 .../HtmlRenderer.Demo.WinForms}/DemoForm.cs | 4 +- .../HtmlRenderer.Demo.WinForms}/DemoForm.resx | 0 .../GenerateImageForm.Designer.cs | 0 .../GenerateImageForm.cs | 0 .../GenerateImageForm.resx | 0 .../HtmlRenderer.Demo.WinForms.csproj | 25 ++ .../HtmlRenderingHelper.cs | 11 +- .../MainControl.Designer.cs | 0 .../MainControl.cs | 0 .../MainControl.resx | 0 .../PerfForm.Designer.cs | 0 .../HtmlRenderer.Demo.WinForms}/PerfForm.cs | 0 .../HtmlRenderer.Demo.WinForms}/PerfForm.resx | 0 .../HtmlRenderer.Demo.WinForms}/Program.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../SampleForm.Designer.cs | 0 .../HtmlRenderer.Demo.WinForms}/SampleForm.cs | 0 .../SampleForm.resx | 0 .../HtmlRenderer.Demo.WinForms/app.config | 3 + .../HtmlRenderer.Demo.WinForms}/html.ico | Bin .../packages.config | 0 .../Adapters/BrushAdapter.cs | 2 +- .../Adapters/FontAdapter.cs | 2 +- .../Adapters/FontFamilyAdapter.cs | 2 +- .../Adapters/GraphicsAdapter.cs | 2 +- .../Adapters/GraphicsPathAdapter.cs | 2 +- .../Adapters/ImageAdapter.cs | 2 +- .../Adapters/PdfSharpAdapter.cs | 6 +- .../Adapters/PenAdapter.cs | 2 +- .../Adapters/XTextureBrush.cs | 2 +- Source/HtmlRenderer.PdfSharp/HtmlContainer.cs | 2 +- .../HtmlRenderer.PdfSharp.csproj | 96 +---- .../PdfGenerateConfig.cs | 4 +- Source/HtmlRenderer.PdfSharp/PdfGenerator.cs | 6 +- .../HtmlRenderer.PdfSharp/Utilities/Utils.cs | 2 +- Source/HtmlRenderer.PdfSharp/packages.config | 4 - .../Adapters/BrushAdapter.cs | 103 +++++ .../Adapters/FontAdapter.cs | 106 ++++++ .../Adapters/FontFamilyAdapter.cs | 49 +++ .../Adapters/GraphicsAdapter.cs | 246 ++++++++++++ .../Adapters/GraphicsPathAdapter.cs | 92 +++++ .../Adapters/ImageAdapter.cs | 60 +++ .../Adapters/PenAdapter.cs | 105 ++++++ .../Adapters/SkiaSharpAdapter.cs | 147 ++++++++ .../Adapters/XTextureBrush.cs | 77 ++++ .../HtmlRenderer.SkiaSharp/HtmlContainer.cs | 356 ++++++++++++++++++ .../HtmlRenderer.SkiaSharp.csproj | 17 + .../PdfGenerateConfig.cs | 234 ++++++++++++ Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs | 268 +++++++++++++ .../HtmlRenderer.SkiaSharp/Utilities/Utils.cs | 100 +++++ Source/HtmlRenderer.WPF/HtmlLabel.cs | 31 +- Source/HtmlRenderer.WPF/HtmlRender.cs | 12 +- .../HtmlRenderer.WPF/HtmlRenderer.WPF.csproj | 92 +---- .../Adapters/ContextMenuAdapter.cs | 1 - Source/HtmlRenderer.WinForms/HtmlLabel.cs | 31 +- Source/HtmlRenderer.WinForms/HtmlRender.cs | 20 +- .../HtmlRenderer.WinForms.csproj | 132 +------ Source/HtmlRenderer.WinForms/HtmlToolTip.cs | 1 + Source/HtmlRenderer.sln | 149 +++----- Source/HtmlRenderer/Adapters/RAdapter.cs | 4 +- Source/HtmlRenderer/Core/Dom/CssBox.cs | 38 +- Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs | 29 +- Source/HtmlRenderer/Core/Dom/CssBoxHr.cs | 4 +- Source/HtmlRenderer/Core/Dom/CssBoxImage.cs | 10 +- .../HtmlRenderer/Core/Dom/CssLayoutEngine.cs | 19 +- .../Core/Dom/CssLayoutEngineTable.cs | 20 +- .../Core/Entities/HtmlImageLoadEventArgs.cs | 6 +- .../Core/Entities/HtmlRenderErrorEventArgs.cs | 10 +- .../Core/Handlers/ImageDownloader.cs | 26 +- .../Core/Handlers/ImageLoadHandler.cs | 18 +- .../Core/Handlers/StylesheetLoadHandler.cs | 31 +- Source/HtmlRenderer/Core/HtmlContainerInt.cs | 14 +- Source/HtmlRenderer/Core/HtmlRendererUtils.cs | 18 +- Source/HtmlRenderer/Core/Parse/DomParser.cs | 14 +- Source/HtmlRenderer/HtmlRenderer.csproj | 140 +------ 178 files changed, 2816 insertions(+), 1311 deletions(-) delete mode 100644 Source/Demo/Common/HtmlRenderer.Demo.Common.csproj delete mode 100644 Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj delete mode 100644 Source/Demo/WPF/Properties/AssemblyInfo.cs delete mode 100644 Source/Demo/WPF/packages.config delete mode 100644 Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/DemoUtils.cs (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/HtmlSample.cs (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/HtmlSyntaxHighlighter.cs (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/PerfSamples/1.Big table.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/PerfSamples/2.Lots blocks in inline.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Properties/AssemblyInfo.cs (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Properties/Resources.Designer.cs (95%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Properties/Resources.resx (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources.cs (97%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/CustomFont.ttf (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/Event16.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/Tooltip.html (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/browser.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/chrome.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/code.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/comment16.gif (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/delete16.gif (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/delete32.gif (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/favorites32.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/font32.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/form.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/formula32.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/html32.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/image.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/image32.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/method16.gif (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/pdf.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/property16.gif (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/stopwatch.png (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Resources/web_pallete.gif (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/00.Intro.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/01.History.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/02.Text.htm (99%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/03.Tables.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/04.Links.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/05.Images.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/06.Embeded video.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/07.Additional features.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/08.Tooltip.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/09.Using the library.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/10.HtmlPanel.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/11.HtmlLabel.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/12.HtmlToolTip.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/13.HtmlRender.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/14.HtmlContainer.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/Samples/20.About.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/SamplesLoader.cs (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/01.Header.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/02.Line break.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/03.Paragraphs.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/04.Blockquotes.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/05.Images.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/06.External Image.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/07.Background Image.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/08.White-space.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/09.Inline.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/10.BlockInInline.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/11.LineHeight.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/12.Text.htm (99%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/13.Tables.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/14.Iframes.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/15.MaxWidth.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/16.Borders.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/17.Languages.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/18.Anchors.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/19.Many images.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/20.Fonts decorations.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/21.Bullets.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/22.RTL.htm (97%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/30.Misc.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/31.ACID 1.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/32.Image in css content.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/33.Fixed position.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/34.Breaking pages 1 - Paragraphs.htm (100%) rename Source/{Demo/Common => Demos/HtmlRenderer.Demo.Common}/TestSamples/35.Breaking pages 2 - Tables.htm (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj create mode 100644 Source/Demos/HtmlRenderer.Demo.SkiaSharp/Program.cs rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/App.xaml (98%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/App.xaml.cs (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.WPF/AssemblyInfo.cs rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/DemoWindow.xaml (99%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/DemoWindow.xaml.cs (97%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/GenerateImageWindow.xaml (98%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/GenerateImageWindow.xaml.cs (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/HtmlRenderingHelper.cs (100%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/MainControl.xaml (99%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/MainControl.xaml.cs (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml create mode 100644 Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml.cs rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/SampleWindow.xaml (100%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/SampleWindow.xaml.cs (96%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/ToolStripImageConverter.cs (90%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/fonts/CustomFont.ttf (100%) rename Source/{Demo/WPF => Demos/HtmlRenderer.Demo.WPF}/html.ico (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/DemoForm.Designer.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/DemoForm.cs (98%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/DemoForm.resx (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/GenerateImageForm.Designer.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/GenerateImageForm.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/GenerateImageForm.resx (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/HtmlRenderingHelper.cs (90%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/MainControl.Designer.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/MainControl.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/MainControl.resx (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/PerfForm.Designer.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/PerfForm.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/PerfForm.resx (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/Program.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/Properties/AssemblyInfo.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/SampleForm.Designer.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/SampleForm.cs (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/SampleForm.resx (100%) create mode 100644 Source/Demos/HtmlRenderer.Demo.WinForms/app.config rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/html.ico (100%) rename Source/{Demo/WinForms => Demos/HtmlRenderer.Demo.WinForms}/packages.config (100%) delete mode 100644 Source/HtmlRenderer.PdfSharp/packages.config create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/FontAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/FontFamilyAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Adapters/XTextureBrush.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/HtmlContainer.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj create mode 100644 Source/HtmlRenderer.SkiaSharp/PdfGenerateConfig.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs create mode 100644 Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs diff --git a/.gitignore b/.gitignore index ce3a150ad..0fe3b09d4 100644 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,6 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML -*.DotSettings \ No newline at end of file +*.DotSettings + +Source/.vs/ diff --git a/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj b/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj deleted file mode 100644 index 7413cc397..000000000 --- a/Source/Demo/Common/HtmlRenderer.Demo.Common.csproj +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Debug - AnyCPU - {2390B71F-9400-47F4-B23A-7F2649C87D35} - Library - Properties - TheArtOfDev.HtmlRenderer.Demo.Common - HtmlRendererDemoCommon - v2.0 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - Resources.resx - True - True - - - - - - - - - - - - - PublicResXFileCodeGenerator - Resources.Designer.cs - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {FE611685-391F-4E3E-B27E-D3150E51E49B} - HtmlRenderer - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj b/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj deleted file mode 100644 index 8c26cdcc4..000000000 --- a/Source/Demo/WPF/HtmlRenderer.Demo.WPF.csproj +++ /dev/null @@ -1,149 +0,0 @@ - - - - - Debug - AnyCPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0} - WinExe - Properties - TheArtOfDev.HtmlRenderer.Demo.WPF - HtmlRendererWpfDemo - v4.0 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - - ..\..\ - true - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - html.ico - - - - - - - - - - - ..\..\packages\Extended.Wpf.Toolkit.2.2.1\lib\net40\Xceed.Wpf.DataGrid.dll - - - ..\..\packages\Extended.Wpf.Toolkit.2.2.1\lib\net40\Xceed.Wpf.Toolkit.dll - - - - - MSBuild:Compile - Designer - - - GenerateImageWindow.xaml - - - SampleWindow.xaml - - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - DemoWindow.xaml - Code - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - - - - MainControl.xaml - - - Code - - - - - - {7e4e8db5-85ad-4388-bdcb-38c6f423b8b0} - HtmlRenderer.WPF - - - {fe611685-391f-4e3e-b27e-d3150e51e49b} - HtmlRenderer - - - {2390B71F-9400-47F4-B23A-7F2649C87D35} - HtmlRenderer.Demo.Common - - - - - - - - - - - - - - - - %(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension) - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/Source/Demo/WPF/Properties/AssemblyInfo.cs b/Source/Demo/WPF/Properties/AssemblyInfo.cs deleted file mode 100644 index d5c8b4eb2..000000000 --- a/Source/Demo/WPF/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using System.Windows; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("HTML Renderer WPF Demo")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("HTML Renderer WPF Demo")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US english -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) - )] - - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Source/Demo/WPF/packages.config b/Source/Demo/WPF/packages.config deleted file mode 100644 index d3eaa3c6b..000000000 --- a/Source/Demo/WPF/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj b/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj deleted file mode 100644 index 95dafc2f1..000000000 --- a/Source/Demo/WinForms/HtmlRenderer.Demo.WinForms.csproj +++ /dev/null @@ -1,198 +0,0 @@ - - - - Debug - AnyCPU - 8.0.50727 - 2.0 - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423} - WinExe - Properties - TheArtOfDev.HtmlRenderer.Demo.WinForms - HtmlRendererWinFormsDemo - v2.0 - - - - - 2.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - ..\..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - x86 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - x86 - false - - - html.ico - - - - ..\..\packages\PDFsharp.1.50.4000-beta3b\lib\net20\PdfSharp.dll - True - - - ..\..\packages\PDFsharp.1.50.4000-beta3b\lib\net20\PdfSharp.Charting.dll - True - - - - - - - - - - Form - - - GenerateImageForm.cs - - - - UserControl - - - MainControl.cs - - - Form - - - PerfForm.cs - - - Form - - - DemoForm.cs - - - - - GenerateImageForm.cs - - - MainControl.cs - - - PerfForm.cs - Designer - - - Designer - DemoForm.cs - - - Form - - - SampleForm.cs - - - - - Designer - SampleForm.cs - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - {ca249f5d-9285-40a6-b217-5889ef79fd7e} - HtmlRenderer.PdfSharp - - - {1b058920-24b4-4140-8ae7-c8c6c38ca52d} - HtmlRenderer.WinForms - - - {fe611685-391f-4e3e-b27e-d3150e51e49b} - HtmlRenderer - - - {2390b71f-9400-47f4-b23a-7f2649c87d35} - HtmlRenderer.Demo.Common - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - %(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension) - - - - - \ No newline at end of file diff --git a/Source/Demo/Common/DemoUtils.cs b/Source/Demos/HtmlRenderer.Demo.Common/DemoUtils.cs similarity index 100% rename from Source/Demo/Common/DemoUtils.cs rename to Source/Demos/HtmlRenderer.Demo.Common/DemoUtils.cs diff --git a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj new file mode 100644 index 000000000..b792bc023 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj @@ -0,0 +1,296 @@ + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + + diff --git a/Source/Demo/Common/HtmlSample.cs b/Source/Demos/HtmlRenderer.Demo.Common/HtmlSample.cs similarity index 100% rename from Source/Demo/Common/HtmlSample.cs rename to Source/Demos/HtmlRenderer.Demo.Common/HtmlSample.cs diff --git a/Source/Demo/Common/HtmlSyntaxHighlighter.cs b/Source/Demos/HtmlRenderer.Demo.Common/HtmlSyntaxHighlighter.cs similarity index 100% rename from Source/Demo/Common/HtmlSyntaxHighlighter.cs rename to Source/Demos/HtmlRenderer.Demo.Common/HtmlSyntaxHighlighter.cs diff --git a/Source/Demo/Common/PerfSamples/1.Big table.htm b/Source/Demos/HtmlRenderer.Demo.Common/PerfSamples/1.Big table.htm similarity index 100% rename from Source/Demo/Common/PerfSamples/1.Big table.htm rename to Source/Demos/HtmlRenderer.Demo.Common/PerfSamples/1.Big table.htm diff --git a/Source/Demo/Common/PerfSamples/2.Lots blocks in inline.htm b/Source/Demos/HtmlRenderer.Demo.Common/PerfSamples/2.Lots blocks in inline.htm similarity index 100% rename from Source/Demo/Common/PerfSamples/2.Lots blocks in inline.htm rename to Source/Demos/HtmlRenderer.Demo.Common/PerfSamples/2.Lots blocks in inline.htm diff --git a/Source/Demo/Common/Properties/AssemblyInfo.cs b/Source/Demos/HtmlRenderer.Demo.Common/Properties/AssemblyInfo.cs similarity index 100% rename from Source/Demo/Common/Properties/AssemblyInfo.cs rename to Source/Demos/HtmlRenderer.Demo.Common/Properties/AssemblyInfo.cs diff --git a/Source/Demo/Common/Properties/Resources.Designer.cs b/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.Designer.cs similarity index 95% rename from Source/Demo/Common/Properties/Resources.Designer.cs rename to Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.Designer.cs index 8b1aa6a38..811f5d1c6 100644 --- a/Source/Demo/Common/Properties/Resources.Designer.cs +++ b/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34014 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace TheArtOfDev.HtmlRenderer.Demo.Common.Properties { // 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")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { @@ -39,7 +39,7 @@ internal Resources() { public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TheArtOfDev.HtmlRenderer.Demo.Common.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HtmlRenderer.Demo.Common.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; diff --git a/Source/Demo/Common/Properties/Resources.resx b/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx similarity index 100% rename from Source/Demo/Common/Properties/Resources.resx rename to Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx diff --git a/Source/Demo/Common/Resources.cs b/Source/Demos/HtmlRenderer.Demo.Common/Resources.cs similarity index 97% rename from Source/Demo/Common/Resources.cs rename to Source/Demos/HtmlRenderer.Demo.Common/Resources.cs index 6370c567c..f4550c0c6 100644 --- a/Source/Demo/Common/Resources.cs +++ b/Source/Demos/HtmlRenderer.Demo.Common/Resources.cs @@ -96,7 +96,7 @@ public static string Tooltip private static Stream GetManifestResourceStream(string name) { - return typeof(Resources).Assembly.GetManifestResourceStream("TheArtOfDev.HtmlRenderer.Demo.Common.Resources." + name); + return typeof(Resources).Assembly.GetManifestResourceStream("HtmlRenderer.Demo.Common.Resources." + name); } } } \ No newline at end of file diff --git a/Source/Demo/Common/Resources/CustomFont.ttf b/Source/Demos/HtmlRenderer.Demo.Common/Resources/CustomFont.ttf similarity index 100% rename from Source/Demo/Common/Resources/CustomFont.ttf rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/CustomFont.ttf diff --git a/Source/Demo/Common/Resources/Event16.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/Event16.png similarity index 100% rename from Source/Demo/Common/Resources/Event16.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/Event16.png diff --git a/Source/Demo/Common/Resources/Tooltip.html b/Source/Demos/HtmlRenderer.Demo.Common/Resources/Tooltip.html similarity index 100% rename from Source/Demo/Common/Resources/Tooltip.html rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/Tooltip.html diff --git a/Source/Demo/Common/Resources/browser.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/browser.png similarity index 100% rename from Source/Demo/Common/Resources/browser.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/browser.png diff --git a/Source/Demo/Common/Resources/chrome.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/chrome.png similarity index 100% rename from Source/Demo/Common/Resources/chrome.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/chrome.png diff --git a/Source/Demo/Common/Resources/code.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/code.png similarity index 100% rename from Source/Demo/Common/Resources/code.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/code.png diff --git a/Source/Demo/Common/Resources/comment16.gif b/Source/Demos/HtmlRenderer.Demo.Common/Resources/comment16.gif similarity index 100% rename from Source/Demo/Common/Resources/comment16.gif rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/comment16.gif diff --git a/Source/Demo/Common/Resources/delete16.gif b/Source/Demos/HtmlRenderer.Demo.Common/Resources/delete16.gif similarity index 100% rename from Source/Demo/Common/Resources/delete16.gif rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/delete16.gif diff --git a/Source/Demo/Common/Resources/delete32.gif b/Source/Demos/HtmlRenderer.Demo.Common/Resources/delete32.gif similarity index 100% rename from Source/Demo/Common/Resources/delete32.gif rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/delete32.gif diff --git a/Source/Demo/Common/Resources/favorites32.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/favorites32.png similarity index 100% rename from Source/Demo/Common/Resources/favorites32.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/favorites32.png diff --git a/Source/Demo/Common/Resources/font32.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/font32.png similarity index 100% rename from Source/Demo/Common/Resources/font32.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/font32.png diff --git a/Source/Demo/Common/Resources/form.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/form.png similarity index 100% rename from Source/Demo/Common/Resources/form.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/form.png diff --git a/Source/Demo/Common/Resources/formula32.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/formula32.png similarity index 100% rename from Source/Demo/Common/Resources/formula32.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/formula32.png diff --git a/Source/Demo/Common/Resources/html32.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/html32.png similarity index 100% rename from Source/Demo/Common/Resources/html32.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/html32.png diff --git a/Source/Demo/Common/Resources/image.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/image.png similarity index 100% rename from Source/Demo/Common/Resources/image.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/image.png diff --git a/Source/Demo/Common/Resources/image32.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/image32.png similarity index 100% rename from Source/Demo/Common/Resources/image32.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/image32.png diff --git a/Source/Demo/Common/Resources/method16.gif b/Source/Demos/HtmlRenderer.Demo.Common/Resources/method16.gif similarity index 100% rename from Source/Demo/Common/Resources/method16.gif rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/method16.gif diff --git a/Source/Demo/Common/Resources/pdf.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/pdf.png similarity index 100% rename from Source/Demo/Common/Resources/pdf.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/pdf.png diff --git a/Source/Demo/Common/Resources/property16.gif b/Source/Demos/HtmlRenderer.Demo.Common/Resources/property16.gif similarity index 100% rename from Source/Demo/Common/Resources/property16.gif rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/property16.gif diff --git a/Source/Demo/Common/Resources/stopwatch.png b/Source/Demos/HtmlRenderer.Demo.Common/Resources/stopwatch.png similarity index 100% rename from Source/Demo/Common/Resources/stopwatch.png rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/stopwatch.png diff --git a/Source/Demo/Common/Resources/web_pallete.gif b/Source/Demos/HtmlRenderer.Demo.Common/Resources/web_pallete.gif similarity index 100% rename from Source/Demo/Common/Resources/web_pallete.gif rename to Source/Demos/HtmlRenderer.Demo.Common/Resources/web_pallete.gif diff --git a/Source/Demo/Common/Samples/00.Intro.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/00.Intro.htm similarity index 100% rename from Source/Demo/Common/Samples/00.Intro.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/00.Intro.htm diff --git a/Source/Demo/Common/Samples/01.History.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/01.History.htm similarity index 100% rename from Source/Demo/Common/Samples/01.History.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/01.History.htm diff --git a/Source/Demo/Common/Samples/02.Text.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/02.Text.htm similarity index 99% rename from Source/Demo/Common/Samples/02.Text.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/02.Text.htm index 727d1fea4..b0ba57426 100644 --- a/Source/Demo/Common/Samples/02.Text.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/Samples/02.Text.htm @@ -2,11 +2,11 @@ Text - diff --git a/Source/Demo/Common/Samples/03.Tables.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/03.Tables.htm similarity index 100% rename from Source/Demo/Common/Samples/03.Tables.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/03.Tables.htm diff --git a/Source/Demo/Common/Samples/04.Links.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/04.Links.htm similarity index 100% rename from Source/Demo/Common/Samples/04.Links.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/04.Links.htm diff --git a/Source/Demo/Common/Samples/05.Images.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/05.Images.htm similarity index 100% rename from Source/Demo/Common/Samples/05.Images.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/05.Images.htm diff --git a/Source/Demo/Common/Samples/06.Embeded video.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/06.Embeded video.htm similarity index 100% rename from Source/Demo/Common/Samples/06.Embeded video.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/06.Embeded video.htm diff --git a/Source/Demo/Common/Samples/07.Additional features.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/07.Additional features.htm similarity index 100% rename from Source/Demo/Common/Samples/07.Additional features.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/07.Additional features.htm diff --git a/Source/Demo/Common/Samples/08.Tooltip.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/08.Tooltip.htm similarity index 100% rename from Source/Demo/Common/Samples/08.Tooltip.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/08.Tooltip.htm diff --git a/Source/Demo/Common/Samples/09.Using the library.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/09.Using the library.htm similarity index 100% rename from Source/Demo/Common/Samples/09.Using the library.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/09.Using the library.htm diff --git a/Source/Demo/Common/Samples/10.HtmlPanel.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/10.HtmlPanel.htm similarity index 100% rename from Source/Demo/Common/Samples/10.HtmlPanel.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/10.HtmlPanel.htm diff --git a/Source/Demo/Common/Samples/11.HtmlLabel.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/11.HtmlLabel.htm similarity index 100% rename from Source/Demo/Common/Samples/11.HtmlLabel.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/11.HtmlLabel.htm diff --git a/Source/Demo/Common/Samples/12.HtmlToolTip.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/12.HtmlToolTip.htm similarity index 100% rename from Source/Demo/Common/Samples/12.HtmlToolTip.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/12.HtmlToolTip.htm diff --git a/Source/Demo/Common/Samples/13.HtmlRender.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/13.HtmlRender.htm similarity index 100% rename from Source/Demo/Common/Samples/13.HtmlRender.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/13.HtmlRender.htm diff --git a/Source/Demo/Common/Samples/14.HtmlContainer.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/14.HtmlContainer.htm similarity index 100% rename from Source/Demo/Common/Samples/14.HtmlContainer.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/14.HtmlContainer.htm diff --git a/Source/Demo/Common/Samples/20.About.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/20.About.htm similarity index 100% rename from Source/Demo/Common/Samples/20.About.htm rename to Source/Demos/HtmlRenderer.Demo.Common/Samples/20.About.htm diff --git a/Source/Demo/Common/SamplesLoader.cs b/Source/Demos/HtmlRenderer.Demo.Common/SamplesLoader.cs similarity index 100% rename from Source/Demo/Common/SamplesLoader.cs rename to Source/Demos/HtmlRenderer.Demo.Common/SamplesLoader.cs diff --git a/Source/Demo/Common/TestSamples/01.Header.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/01.Header.htm similarity index 100% rename from Source/Demo/Common/TestSamples/01.Header.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/01.Header.htm diff --git a/Source/Demo/Common/TestSamples/02.Line break.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/02.Line break.htm similarity index 100% rename from Source/Demo/Common/TestSamples/02.Line break.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/02.Line break.htm diff --git a/Source/Demo/Common/TestSamples/03.Paragraphs.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/03.Paragraphs.htm similarity index 100% rename from Source/Demo/Common/TestSamples/03.Paragraphs.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/03.Paragraphs.htm diff --git a/Source/Demo/Common/TestSamples/04.Blockquotes.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/04.Blockquotes.htm similarity index 100% rename from Source/Demo/Common/TestSamples/04.Blockquotes.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/04.Blockquotes.htm diff --git a/Source/Demo/Common/TestSamples/05.Images.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/05.Images.htm similarity index 100% rename from Source/Demo/Common/TestSamples/05.Images.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/05.Images.htm diff --git a/Source/Demo/Common/TestSamples/06.External Image.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm similarity index 100% rename from Source/Demo/Common/TestSamples/06.External Image.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm diff --git a/Source/Demo/Common/TestSamples/07.Background Image.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/07.Background Image.htm similarity index 100% rename from Source/Demo/Common/TestSamples/07.Background Image.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/07.Background Image.htm diff --git a/Source/Demo/Common/TestSamples/08.White-space.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/08.White-space.htm similarity index 100% rename from Source/Demo/Common/TestSamples/08.White-space.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/08.White-space.htm diff --git a/Source/Demo/Common/TestSamples/09.Inline.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/09.Inline.htm similarity index 100% rename from Source/Demo/Common/TestSamples/09.Inline.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/09.Inline.htm diff --git a/Source/Demo/Common/TestSamples/10.BlockInInline.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/10.BlockInInline.htm similarity index 100% rename from Source/Demo/Common/TestSamples/10.BlockInInline.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/10.BlockInInline.htm diff --git a/Source/Demo/Common/TestSamples/11.LineHeight.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/11.LineHeight.htm similarity index 100% rename from Source/Demo/Common/TestSamples/11.LineHeight.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/11.LineHeight.htm diff --git a/Source/Demo/Common/TestSamples/12.Text.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/12.Text.htm similarity index 99% rename from Source/Demo/Common/TestSamples/12.Text.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/12.Text.htm index c53a43609..833196bae 100644 --- a/Source/Demo/Common/TestSamples/12.Text.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/12.Text.htm @@ -2,13 +2,13 @@ Text - diff --git a/Source/Demo/Common/TestSamples/13.Tables.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/13.Tables.htm similarity index 100% rename from Source/Demo/Common/TestSamples/13.Tables.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/13.Tables.htm diff --git a/Source/Demo/Common/TestSamples/14.Iframes.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/14.Iframes.htm similarity index 100% rename from Source/Demo/Common/TestSamples/14.Iframes.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/14.Iframes.htm diff --git a/Source/Demo/Common/TestSamples/15.MaxWidth.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/15.MaxWidth.htm similarity index 100% rename from Source/Demo/Common/TestSamples/15.MaxWidth.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/15.MaxWidth.htm diff --git a/Source/Demo/Common/TestSamples/16.Borders.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/16.Borders.htm similarity index 100% rename from Source/Demo/Common/TestSamples/16.Borders.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/16.Borders.htm diff --git a/Source/Demo/Common/TestSamples/17.Languages.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/17.Languages.htm similarity index 100% rename from Source/Demo/Common/TestSamples/17.Languages.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/17.Languages.htm diff --git a/Source/Demo/Common/TestSamples/18.Anchors.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/18.Anchors.htm similarity index 100% rename from Source/Demo/Common/TestSamples/18.Anchors.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/18.Anchors.htm diff --git a/Source/Demo/Common/TestSamples/19.Many images.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/19.Many images.htm similarity index 100% rename from Source/Demo/Common/TestSamples/19.Many images.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/19.Many images.htm diff --git a/Source/Demo/Common/TestSamples/20.Fonts decorations.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/20.Fonts decorations.htm similarity index 100% rename from Source/Demo/Common/TestSamples/20.Fonts decorations.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/20.Fonts decorations.htm diff --git a/Source/Demo/Common/TestSamples/21.Bullets.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/21.Bullets.htm similarity index 100% rename from Source/Demo/Common/TestSamples/21.Bullets.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/21.Bullets.htm diff --git a/Source/Demo/Common/TestSamples/22.RTL.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/22.RTL.htm similarity index 97% rename from Source/Demo/Common/TestSamples/22.RTL.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/22.RTL.htm index 35bf9bf6c..a24d4f4e7 100644 --- a/Source/Demo/Common/TestSamples/22.RTL.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/22.RTL.htm @@ -1,18 +1,18 @@ - - -
-
שלום עולם, יש ברבורים בעגם הזה
-
-
שלום עולם, יש ברבורים בעגם הזה
-
שלום עולם, יש ברבורים בעגם הזה
-
-
-
-
שלום עולם,hello world יש ברבורים בעגם הזה
-
-
שלום עולם, יש ברבורים בעגם הזה
-
שלום עולם, יש ברבורים בעגם הזה
-
-
- + + +
+
שלום עולם, יש ברבורים בעגם הזה
+
+
שלום עולם, יש ברבורים בעגם הזה
+
שלום עולם, יש ברבורים בעגם הזה
+
+
+
+
שלום עולם,hello world יש ברבורים בעגם הזה
+
+
שלום עולם, יש ברבורים בעגם הזה
+
שלום עולם, יש ברבורים בעגם הזה
+
+
+ \ No newline at end of file diff --git a/Source/Demo/Common/TestSamples/30.Misc.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/30.Misc.htm similarity index 100% rename from Source/Demo/Common/TestSamples/30.Misc.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/30.Misc.htm diff --git a/Source/Demo/Common/TestSamples/31.ACID 1.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/31.ACID 1.htm similarity index 100% rename from Source/Demo/Common/TestSamples/31.ACID 1.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/31.ACID 1.htm diff --git a/Source/Demo/Common/TestSamples/32.Image in css content.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/32.Image in css content.htm similarity index 100% rename from Source/Demo/Common/TestSamples/32.Image in css content.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/32.Image in css content.htm diff --git a/Source/Demo/Common/TestSamples/33.Fixed position.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/33.Fixed position.htm similarity index 100% rename from Source/Demo/Common/TestSamples/33.Fixed position.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/33.Fixed position.htm diff --git a/Source/Demo/Common/TestSamples/34.Breaking pages 1 - Paragraphs.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/34.Breaking pages 1 - Paragraphs.htm similarity index 100% rename from Source/Demo/Common/TestSamples/34.Breaking pages 1 - Paragraphs.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/34.Breaking pages 1 - Paragraphs.htm diff --git a/Source/Demo/Common/TestSamples/35.Breaking pages 2 - Tables.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/35.Breaking pages 2 - Tables.htm similarity index 100% rename from Source/Demo/Common/TestSamples/35.Breaking pages 2 - Tables.htm rename to Source/Demos/HtmlRenderer.Demo.Common/TestSamples/35.Breaking pages 2 - Tables.htm diff --git a/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj b/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj new file mode 100644 index 000000000..e10e1d767 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/Source/Demos/HtmlRenderer.Demo.SkiaSharp/Program.cs b/Source/Demos/HtmlRenderer.Demo.SkiaSharp/Program.cs new file mode 100644 index 000000000..fa147813d --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.SkiaSharp/Program.cs @@ -0,0 +1,59 @@ +// See https://aka.ms/new-console-template for more information + +using SkiaSharp; +using TheArtOfDev.HtmlRenderer.SkiaSharp; +/* +var info = new SKImageInfo(256, 256); +using (var surface = SKSurface.Create(info)) +{ +SKCanvas canvas = surface.Canvas; + +canvas.Clear(SKColors.White); + +// configure our brush +var redBrush = new SKPaint +{ +Color = new SKColor(0xff, 0, 0), +IsStroke = true +}; +var blueBrush = new SKPaint +{ +Color = new SKColor(0, 0, 0xff), +IsStroke = true +}; + +for (int i = 0; i < 64; i += 8) +{ +var rect = new SKRect(i, i, 256 - i - 1, 256 - i - 1); +canvas.DrawRect(rect, (i % 16 == 0) ? redBrush : blueBrush); +} + +using (var image = surface.Snapshot()) +using (var data = image.Encode(SKEncodedImageFormat.Png, 80)) +using (var stream = File.OpenWrite(Path.Combine(@"c:\temp\SkiaSharpTests", "1.png"))) +{ +// save the data to a stream +data.SaveTo(stream); +} + +Console.ReadLine(); +}*/ + +using (var outputFile = new FileStream(@"c:\temp\SkiaSharpTests\test.pdf", FileMode.Create)) +{ + var pdfDocument = PdfGenerator.GeneratePdfAsync(@" + + + w3resource tutorial + +

we are learning html

+

we are learning html at w3resource.com.

+

This section covers the introduction to html

+

Look here to get a list of the topics covered in + w3resource.com

+ + ", outputFile); + + await outputFile.FlushAsync(); +} diff --git a/Source/Demo/WPF/App.xaml b/Source/Demos/HtmlRenderer.Demo.WPF/App.xaml similarity index 98% rename from Source/Demo/WPF/App.xaml rename to Source/Demos/HtmlRenderer.Demo.WPF/App.xaml index 680903bfb..b48841847 100644 --- a/Source/Demo/WPF/App.xaml +++ b/Source/Demos/HtmlRenderer.Demo.WPF/App.xaml @@ -27,4 +27,4 @@ - + \ No newline at end of file diff --git a/Source/Demo/WPF/App.xaml.cs b/Source/Demos/HtmlRenderer.Demo.WPF/App.xaml.cs similarity index 100% rename from Source/Demo/WPF/App.xaml.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/App.xaml.cs diff --git a/Source/Demos/HtmlRenderer.Demo.WPF/AssemblyInfo.cs b/Source/Demos/HtmlRenderer.Demo.WPF/AssemblyInfo.cs new file mode 100644 index 000000000..8b5504ecf --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.WPF/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Source/Demo/WPF/DemoWindow.xaml b/Source/Demos/HtmlRenderer.Demo.WPF/DemoWindow.xaml similarity index 99% rename from Source/Demo/WPF/DemoWindow.xaml rename to Source/Demos/HtmlRenderer.Demo.WPF/DemoWindow.xaml index 538e685e5..80b1ca646 100644 --- a/Source/Demo/WPF/DemoWindow.xaml +++ b/Source/Demos/HtmlRenderer.Demo.WPF/DemoWindow.xaml @@ -59,4 +59,4 @@ - + \ No newline at end of file diff --git a/Source/Demo/WPF/DemoWindow.xaml.cs b/Source/Demos/HtmlRenderer.Demo.WPF/DemoWindow.xaml.cs similarity index 97% rename from Source/Demo/WPF/DemoWindow.xaml.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/DemoWindow.xaml.cs index 474572865..56510b7db 100644 --- a/Source/Demo/WPF/DemoWindow.xaml.cs +++ b/Source/Demos/HtmlRenderer.Demo.WPF/DemoWindow.xaml.cs @@ -1,3 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; // "Therefore those skilled at the unorthodox // are infinite as heaven and earth, // inexhaustible as the great rivers. diff --git a/Source/Demo/WPF/GenerateImageWindow.xaml b/Source/Demos/HtmlRenderer.Demo.WPF/GenerateImageWindow.xaml similarity index 98% rename from Source/Demo/WPF/GenerateImageWindow.xaml rename to Source/Demos/HtmlRenderer.Demo.WPF/GenerateImageWindow.xaml index 8d8cb6b94..be06c88b6 100644 --- a/Source/Demo/WPF/GenerateImageWindow.xaml +++ b/Source/Demos/HtmlRenderer.Demo.WPF/GenerateImageWindow.xaml @@ -12,4 +12,4 @@ - + \ No newline at end of file diff --git a/Source/Demo/WPF/GenerateImageWindow.xaml.cs b/Source/Demos/HtmlRenderer.Demo.WPF/GenerateImageWindow.xaml.cs similarity index 100% rename from Source/Demo/WPF/GenerateImageWindow.xaml.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/GenerateImageWindow.xaml.cs diff --git a/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj b/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj new file mode 100644 index 000000000..0da32991f --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj @@ -0,0 +1,29 @@ + + + + WinExe + net6.0-windows + enable + true + + + + + + + + + PreserveNewest + + + + + + + + + + + + + diff --git a/Source/Demo/WPF/HtmlRenderingHelper.cs b/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderingHelper.cs similarity index 100% rename from Source/Demo/WPF/HtmlRenderingHelper.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderingHelper.cs diff --git a/Source/Demo/WPF/MainControl.xaml b/Source/Demos/HtmlRenderer.Demo.WPF/MainControl.xaml similarity index 99% rename from Source/Demo/WPF/MainControl.xaml rename to Source/Demos/HtmlRenderer.Demo.WPF/MainControl.xaml index 714c2dbf1..c8156d383 100644 --- a/Source/Demo/WPF/MainControl.xaml +++ b/Source/Demos/HtmlRenderer.Demo.WPF/MainControl.xaml @@ -50,3 +50,4 @@ + diff --git a/Source/Demo/WPF/MainControl.xaml.cs b/Source/Demos/HtmlRenderer.Demo.WPF/MainControl.xaml.cs similarity index 100% rename from Source/Demo/WPF/MainControl.xaml.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/MainControl.xaml.cs diff --git a/Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml b/Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml new file mode 100644 index 000000000..fd573e40d --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml.cs b/Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml.cs new file mode 100644 index 000000000..a3d871eb3 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.WPF/MainWindow.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace HtmlRenderer.Demo.WPF +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow() + { + InitializeComponent(); + } + } +} diff --git a/Source/Demo/WPF/SampleWindow.xaml b/Source/Demos/HtmlRenderer.Demo.WPF/SampleWindow.xaml similarity index 100% rename from Source/Demo/WPF/SampleWindow.xaml rename to Source/Demos/HtmlRenderer.Demo.WPF/SampleWindow.xaml diff --git a/Source/Demo/WPF/SampleWindow.xaml.cs b/Source/Demos/HtmlRenderer.Demo.WPF/SampleWindow.xaml.cs similarity index 96% rename from Source/Demo/WPF/SampleWindow.xaml.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/SampleWindow.xaml.cs index 8b13056de..7be876f63 100644 --- a/Source/Demo/WPF/SampleWindow.xaml.cs +++ b/Source/Demos/HtmlRenderer.Demo.WPF/SampleWindow.xaml.cs @@ -14,6 +14,7 @@ using System.Windows.Input; using TheArtOfDev.HtmlRenderer.Demo.Common; using Xceed.Wpf.Toolkit.PropertyGrid; +//using Xceed.Wpf.Toolkit.PropertyGrid; namespace TheArtOfDev.HtmlRenderer.Demo.WPF { diff --git a/Source/Demo/WPF/ToolStripImageConverter.cs b/Source/Demos/HtmlRenderer.Demo.WPF/ToolStripImageConverter.cs similarity index 90% rename from Source/Demo/WPF/ToolStripImageConverter.cs rename to Source/Demos/HtmlRenderer.Demo.WPF/ToolStripImageConverter.cs index 9e3804993..bf303d177 100644 --- a/Source/Demo/WPF/ToolStripImageConverter.cs +++ b/Source/Demos/HtmlRenderer.Demo.WPF/ToolStripImageConverter.cs @@ -21,7 +21,7 @@ public class ToolStripImageConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var imageStream = typeof(Resources).Assembly.GetManifestResourceStream("TheArtOfDev.HtmlRenderer.Demo.Common.Resources." + parameter + ".png"); + var imageStream = typeof(Resources).Assembly.GetManifestResourceStream("HtmlRenderer.Demo.Common.Resources." + parameter + ".png"); return HtmlRenderingHelper.ImageFromStream(imageStream); } diff --git a/Source/Demo/WPF/fonts/CustomFont.ttf b/Source/Demos/HtmlRenderer.Demo.WPF/fonts/CustomFont.ttf similarity index 100% rename from Source/Demo/WPF/fonts/CustomFont.ttf rename to Source/Demos/HtmlRenderer.Demo.WPF/fonts/CustomFont.ttf diff --git a/Source/Demo/WPF/html.ico b/Source/Demos/HtmlRenderer.Demo.WPF/html.ico similarity index 100% rename from Source/Demo/WPF/html.ico rename to Source/Demos/HtmlRenderer.Demo.WPF/html.ico diff --git a/Source/Demo/WinForms/DemoForm.Designer.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.Designer.cs similarity index 100% rename from Source/Demo/WinForms/DemoForm.Designer.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.Designer.cs diff --git a/Source/Demo/WinForms/DemoForm.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs similarity index 98% rename from Source/Demo/WinForms/DemoForm.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs index 3e7287b70..3eb680ea2 100644 --- a/Source/Demo/WinForms/DemoForm.cs +++ b/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs @@ -19,7 +19,7 @@ using TheArtOfDev.HtmlRenderer.Demo.Common; using TheArtOfDev.HtmlRenderer.PdfSharp; using TheArtOfDev.HtmlRenderer.WinForms; -using PdfSharp; +using PdfSharpCore; namespace TheArtOfDev.HtmlRenderer.Demo.WinForms { @@ -148,7 +148,7 @@ private void OnGeneratePdf_Click(object sender, EventArgs e) var tmpFile = Path.GetTempFileName(); tmpFile = Path.GetFileNameWithoutExtension(tmpFile) + ".pdf"; doc.Save(tmpFile); - Process.Start(tmpFile); + //Process.Start($@"C:\Windows\System32\cmd /C {tmpFile}"); } /// diff --git a/Source/Demo/WinForms/DemoForm.resx b/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.resx similarity index 100% rename from Source/Demo/WinForms/DemoForm.resx rename to Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.resx diff --git a/Source/Demo/WinForms/GenerateImageForm.Designer.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/GenerateImageForm.Designer.cs similarity index 100% rename from Source/Demo/WinForms/GenerateImageForm.Designer.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/GenerateImageForm.Designer.cs diff --git a/Source/Demo/WinForms/GenerateImageForm.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/GenerateImageForm.cs similarity index 100% rename from Source/Demo/WinForms/GenerateImageForm.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/GenerateImageForm.cs diff --git a/Source/Demo/WinForms/GenerateImageForm.resx b/Source/Demos/HtmlRenderer.Demo.WinForms/GenerateImageForm.resx similarity index 100% rename from Source/Demo/WinForms/GenerateImageForm.resx rename to Source/Demos/HtmlRenderer.Demo.WinForms/GenerateImageForm.resx diff --git a/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj b/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj new file mode 100644 index 000000000..5cb8ad907 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj @@ -0,0 +1,25 @@ + + + + WinExe + net6.0-windows + disable + true + enable + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Demo/WinForms/HtmlRenderingHelper.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderingHelper.cs similarity index 90% rename from Source/Demo/WinForms/HtmlRenderingHelper.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderingHelper.cs index 8ca593314..011030906 100644 --- a/Source/Demo/WinForms/HtmlRenderingHelper.cs +++ b/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderingHelper.cs @@ -10,7 +10,7 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; using System; using System.Collections.Generic; using System.Drawing; @@ -88,7 +88,7 @@ public static XImage TryLoadResourceXImage(string src) using (var ms = new MemoryStream()) { img.Save(ms, img.RawFormat); - xImg = img != null ? XImage.FromStream(ms) : null; + xImg = img != null ? XImage.FromStream(() => ms) : null; } return xImg; @@ -123,7 +123,8 @@ public static void ImageLoad(HtmlImageLoadEventArgs e, bool pdfSharp) using (var ms = new MemoryStream()) { img.Save(ms, img.RawFormat); - xImg = img != null ? XImage.FromStream(ms) : null; + ms.Seek(0, SeekOrigin.Begin); + xImg = img != null ? XImage.FromStream(() => ms) : null; } } @@ -144,13 +145,13 @@ public static void ImageLoad(HtmlImageLoadEventArgs e, bool pdfSharp) 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"); + //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"); + //e.Callback("http://sphotos-a.xx.fbcdn.net/hphotos-ash4/c22.0.403.403/p403x403/263440_10152243591765596_773620816_n.jpg"); return; } } diff --git a/Source/Demo/WinForms/MainControl.Designer.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/MainControl.Designer.cs similarity index 100% rename from Source/Demo/WinForms/MainControl.Designer.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/MainControl.Designer.cs diff --git a/Source/Demo/WinForms/MainControl.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/MainControl.cs similarity index 100% rename from Source/Demo/WinForms/MainControl.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/MainControl.cs diff --git a/Source/Demo/WinForms/MainControl.resx b/Source/Demos/HtmlRenderer.Demo.WinForms/MainControl.resx similarity index 100% rename from Source/Demo/WinForms/MainControl.resx rename to Source/Demos/HtmlRenderer.Demo.WinForms/MainControl.resx diff --git a/Source/Demo/WinForms/PerfForm.Designer.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/PerfForm.Designer.cs similarity index 100% rename from Source/Demo/WinForms/PerfForm.Designer.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/PerfForm.Designer.cs diff --git a/Source/Demo/WinForms/PerfForm.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/PerfForm.cs similarity index 100% rename from Source/Demo/WinForms/PerfForm.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/PerfForm.cs diff --git a/Source/Demo/WinForms/PerfForm.resx b/Source/Demos/HtmlRenderer.Demo.WinForms/PerfForm.resx similarity index 100% rename from Source/Demo/WinForms/PerfForm.resx rename to Source/Demos/HtmlRenderer.Demo.WinForms/PerfForm.resx diff --git a/Source/Demo/WinForms/Program.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/Program.cs similarity index 100% rename from Source/Demo/WinForms/Program.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/Program.cs diff --git a/Source/Demo/WinForms/Properties/AssemblyInfo.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/Properties/AssemblyInfo.cs similarity index 100% rename from Source/Demo/WinForms/Properties/AssemblyInfo.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/Properties/AssemblyInfo.cs diff --git a/Source/Demo/WinForms/SampleForm.Designer.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/SampleForm.Designer.cs similarity index 100% rename from Source/Demo/WinForms/SampleForm.Designer.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/SampleForm.Designer.cs diff --git a/Source/Demo/WinForms/SampleForm.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/SampleForm.cs similarity index 100% rename from Source/Demo/WinForms/SampleForm.cs rename to Source/Demos/HtmlRenderer.Demo.WinForms/SampleForm.cs diff --git a/Source/Demo/WinForms/SampleForm.resx b/Source/Demos/HtmlRenderer.Demo.WinForms/SampleForm.resx similarity index 100% rename from Source/Demo/WinForms/SampleForm.resx rename to Source/Demos/HtmlRenderer.Demo.WinForms/SampleForm.resx diff --git a/Source/Demos/HtmlRenderer.Demo.WinForms/app.config b/Source/Demos/HtmlRenderer.Demo.WinForms/app.config new file mode 100644 index 000000000..3e0e37cfc --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.WinForms/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Source/Demo/WinForms/html.ico b/Source/Demos/HtmlRenderer.Demo.WinForms/html.ico similarity index 100% rename from Source/Demo/WinForms/html.ico rename to Source/Demos/HtmlRenderer.Demo.WinForms/html.ico diff --git a/Source/Demo/WinForms/packages.config b/Source/Demos/HtmlRenderer.Demo.WinForms/packages.config similarity index 100% rename from Source/Demo/WinForms/packages.config rename to Source/Demos/HtmlRenderer.Demo.WinForms/packages.config diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/BrushAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/BrushAdapter.cs index a07a5dcaf..4a3bfe242 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/BrushAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/BrushAdapter.cs @@ -12,7 +12,7 @@ using System; using TheArtOfDev.HtmlRenderer.Adapters; -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs index 07e7c40a9..d606567f5 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/FontAdapter.cs @@ -11,7 +11,7 @@ // "The Art of War" using TheArtOfDev.HtmlRenderer.Adapters; -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/FontFamilyAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/FontFamilyAdapter.cs index 5bfd46494..fdb5cb94b 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/FontFamilyAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/FontFamilyAdapter.cs @@ -11,7 +11,7 @@ // "The Art of War" using TheArtOfDev.HtmlRenderer.Adapters; -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs index c6d406c58..f3e7f9d8c 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs @@ -10,7 +10,7 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; using System; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs index 48290543c..6646987bb 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs @@ -13,7 +13,7 @@ using System; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/ImageAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/ImageAdapter.cs index 48b911aca..36f809334 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/ImageAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/ImageAdapter.cs @@ -11,7 +11,7 @@ // "The Art of War" using TheArtOfDev.HtmlRenderer.Adapters; -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs index e5fa309c0..e9aa3c341 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs @@ -10,8 +10,8 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; -using PdfSharp.Pdf; +using PdfSharpCore.Drawing; +using PdfSharpCore.Pdf; using System.Drawing; using System.Drawing.Text; using System.IO; @@ -114,7 +114,7 @@ protected override RImage ConvertImageInt(object image) protected override RImage ImageFromStreamInt(Stream memoryStream) { - return new ImageAdapter(XImage.FromStream(memoryStream)); + return new ImageAdapter(XImage.FromStream(() => memoryStream)); } protected override RFont CreateFontInt(string family, double size, RFontStyle style) diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/PenAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/PenAdapter.cs index bc5cb2099..4391b339c 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/PenAdapter.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/PenAdapter.cs @@ -12,7 +12,7 @@ using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/XTextureBrush.cs b/Source/HtmlRenderer.PdfSharp/Adapters/XTextureBrush.cs index 39f1fac82..31ffe3e84 100644 --- a/Source/HtmlRenderer.PdfSharp/Adapters/XTextureBrush.cs +++ b/Source/HtmlRenderer.PdfSharp/Adapters/XTextureBrush.cs @@ -10,7 +10,7 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters { diff --git a/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs b/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs index 9339fd562..3c12f5fb4 100644 --- a/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs +++ b/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs @@ -10,7 +10,7 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; using System; using System.Collections.Generic; using TheArtOfDev.HtmlRenderer.Adapters.Entities; diff --git a/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj b/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj index ef0fd7acc..bc3a56924 100644 --- a/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj +++ b/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj @@ -1,90 +1,18 @@ - - - + + - Debug - AnyCPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E} - Library - Properties - TheArtOfDev.HtmlRenderer.PdfSharp - HtmlRenderer.PdfSharp - v2.0 - 512 - ..\ - true + net6.0 + enable + disable - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\PDFsharp.1.50.4000-beta3b\lib\net20\PdfSharp.dll - True - - - ..\packages\PDFsharp.1.50.4000-beta3b\lib\net20\PdfSharp.Charting.dll - True - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - + - - {fe611685-391f-4e3e-b27e-d3150e51e49b} - HtmlRenderer - + + + - + - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file + + diff --git a/Source/HtmlRenderer.PdfSharp/PdfGenerateConfig.cs b/Source/HtmlRenderer.PdfSharp/PdfGenerateConfig.cs index b0aa0d64c..810570743 100644 --- a/Source/HtmlRenderer.PdfSharp/PdfGenerateConfig.cs +++ b/Source/HtmlRenderer.PdfSharp/PdfGenerateConfig.cs @@ -10,8 +10,8 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp; -using PdfSharp.Drawing; +using PdfSharpCore; +using PdfSharpCore.Drawing; namespace TheArtOfDev.HtmlRenderer.PdfSharp { diff --git a/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs b/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs index 365be2d95..bb3ce4bc3 100644 --- a/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs +++ b/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs @@ -10,9 +10,9 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp; -using PdfSharp.Drawing; -using PdfSharp.Pdf; +using PdfSharpCore; +using PdfSharpCore.Drawing; +using PdfSharpCore.Pdf; using System; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; diff --git a/Source/HtmlRenderer.PdfSharp/Utilities/Utils.cs b/Source/HtmlRenderer.PdfSharp/Utilities/Utils.cs index 59ac2c4b5..106858645 100644 --- a/Source/HtmlRenderer.PdfSharp/Utilities/Utils.cs +++ b/Source/HtmlRenderer.PdfSharp/Utilities/Utils.cs @@ -10,7 +10,7 @@ // - Sun Tsu, // "The Art of War" -using PdfSharp.Drawing; +using PdfSharpCore.Drawing; using System.Drawing; using TheArtOfDev.HtmlRenderer.Adapters.Entities; diff --git a/Source/HtmlRenderer.PdfSharp/packages.config b/Source/HtmlRenderer.PdfSharp/packages.config deleted file mode 100644 index 2f092fd78..000000000 --- a/Source/HtmlRenderer.PdfSharp/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs new file mode 100644 index 000000000..750e016aa --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs @@ -0,0 +1,103 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System; +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + + + public class SKBrush + { + public enum Types + { + Solid, + LinearGradient + } + + + public static SKBrush Black = new SKBrush(SKColors.Black); + public static SKBrush White = new SKBrush(SKColors.White); + public static SKBrush Transparent = new SKBrush(SKColors.Transparent); + + public Types Type { get; set; } + + public SKColor Color { get; set; } + + public SKPathEffect PathEffect { get; set; } + + public SKShader Shader { get; set; } + + + public SKBrush(SKColor color = default, Types type = Types.Solid) + { + Type = type; + Color = color; + } + + public SKBrush(SKRect rect, SKColor color1, SKColor color2, float angle = 0, Types type = Types.LinearGradient) + { + Type = type; + + var colors = new SKColor[] { + color1, + color2 + }; + + // Leave it Horizontal for now... + var pointA = new SKPoint(rect.Left, rect.MidY); + var pointB = new SKPoint(rect.Right, rect.MidY); + + Shader = SKShader.CreateLinearGradient( + pointA, pointB, + colors, + null, + SKShaderTileMode.Clamp); + } + } + + + /// + /// Adapter for WinForms brushes objects for core. + /// + internal sealed class BrushAdapter : RBrush + { + /// + /// The actual PdfSharp brush instance.
+ /// Should be but there is some fucking issue inheriting from it =/ + ///
+ private readonly Object _brush; + + /// + /// Init. + /// + public BrushAdapter(Object brush) + { + _brush = brush; + } + + /// + /// The actual WinForms brush instance. + /// + public Object Brush + { + get { return _brush; } + } + + public override void Dispose() + { } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/FontAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/FontAdapter.cs new file mode 100644 index 000000000..e1028f5b3 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/FontAdapter.cs @@ -0,0 +1,106 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using TheArtOfDev.HtmlRenderer.Adapters; + + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Adapter for WinForms Font object for core. + /// + internal sealed class FontAdapter : RFont + { + #region Fields and Consts + + /// + /// the underline win-forms font. + /// + private readonly SKFont _font; + + /// + /// the vertical offset of the font underline location from the top of the font. + /// + private double _underlineOffset = -1; + + /// + /// Cached font height. + /// + private double _height = -1; + + /// + /// Cached font whitespace width. + /// + private double _whitespaceWidth = -1; + + #endregion + + + /// + /// Init. + /// + public FontAdapter(SKFont font) + { + _font = font; + } + + /// + /// the underline win-forms font. + /// + public SKFont Font + { + get { return _font; } + } + + public override double Size + { + get { return _font.Size; } + } + + public override double UnderlineOffset + { + get { return _underlineOffset; } + } + + public override double Height + { + get { return _height; } + } + + public override double LeftPadding + { + get { return _height / 6f; } + } + + + public override double GetWhitespaceWidth(RGraphics graphics) + { + if (_whitespaceWidth < 0) + { + _whitespaceWidth = graphics.MeasureString(" ", this).Width; + } + return _whitespaceWidth; + } + + /// + /// Set font metrics to be cached for the font for future use. + /// + /// the full height of the font + /// the vertical offset of the font underline location from the top of the font. + internal void SetMetrics(int height, int underlineOffset) + { + _height = height; + _underlineOffset = underlineOffset; + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/FontFamilyAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/FontFamilyAdapter.cs new file mode 100644 index 000000000..d53c11a59 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/FontFamilyAdapter.cs @@ -0,0 +1,49 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using TheArtOfDev.HtmlRenderer.Adapters; + + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Adapter for WinForms Font object for core. + /// + internal sealed class FontFamilyAdapter : RFontFamily + { + /// + /// the underline win-forms font. + /// + private readonly string _fontFamily; + + /// + /// Init. + /// + public FontFamilyAdapter(string fontFamily) + { + _fontFamily = fontFamily; + } + + /// + /// the underline win-forms font family. + /// + public string FontFamily + { + get { return _fontFamily; } + } + + public override string Name + { + get { return _fontFamily; } + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs new file mode 100644 index 000000000..44a1c4842 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs @@ -0,0 +1,246 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System; +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; +using TheArtOfDev.HtmlRenderer.Core.Utils; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Adapter for WinForms Graphics for core. + /// + internal sealed class GraphicsAdapter : RGraphics + { + #region Fields and Consts + + /// + /// The wrapped SkiaSharp graphics object + /// + private readonly SKCanvas _g; + + /// + /// if to release the graphics object on dispose + /// + private readonly bool _releaseGraphics; + + /// + /// Used to measure and draw strings + /// + private static readonly SKTextAlign _stringFormat; + + #endregion + + + static GraphicsAdapter() + { + _stringFormat = new SKTextAlign(); + //_stringFormat.Alignment = XStringAlignment.Near; + //_stringFormat.LineAlignment = XLineAlignment.Near; + _stringFormat = SKTextAlign.Left; + } + + /// + /// Init. + /// + /// the win forms graphics object to use + /// optional: if to release the graphics object on dispose (default - false) + public GraphicsAdapter(SKCanvas g, bool releaseGraphics = false) + : base(SkiaSharpAdapter.Instance, new RRect(0, 0, double.MaxValue, double.MaxValue)) + { + ArgChecker.AssertArgNotNull(g, "g"); + + _g = g; + _releaseGraphics = releaseGraphics; + } + + public override void PopClip() + { + _clipStack.Pop(); + _g.Restore(); + } + + public override void PushClip(RRect rect) + { + _clipStack.Push(rect); + _g.Save(); + _g.ClipRect(Utils.Convert(rect)); + } + + public override void PushClipExclude(RRect rect) + { } + + public override Object SetAntiAliasSmoothingMode() + { + /* + var prevMode = _; + _g.SmoothingMode = XSmoothingMode.AntiAlias; + + return prevMode; + */ + throw new NotImplementedException(); + } + + public override void ReturnPreviousSmoothingMode(Object prevMode) + { + /* + if (prevMode != null) + { + _g.SmoothingMode = (XSmoothingMode)prevMode; + }*/ + throw new NotImplementedException(); + } + + public override RSize MeasureString(string str, RFont font) + { + var fontAdapter = (FontAdapter)font; + var realFont = fontAdapter.Font; + var p = new SKPaint(realFont); + var width = p.MeasureText(str); + var height = realFont.Metrics.XHeight; + + if (font.Height < 0) + { + var descent = realFont.Metrics.Descent; + fontAdapter.SetMetrics((int)height, (int)Math.Round((height - descent + 1f))); + } + + return new RSize(width, height); + } + + public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) + { + // there is no need for it - used for text selection + throw new NotSupportedException(); + } + + public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl) + { + //var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush; + var p = new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.Medium, + Color = Utils.Convert(color) + }; + _g.DrawText(str, (float)point.X, (float)point.Y, ((FontAdapter)font).Font, p); + } + + public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation) + { + return new BrushAdapter(new XTextureBrush(((ImageAdapter)image).Image, Utils.Convert(dstRect), Utils.Convert(translateTransformLocation))); + } + + public override RGraphicsPath GetGraphicsPath() + { + return new GraphicsPathAdapter(); + } + + public override void Dispose() + { + if (_releaseGraphics) + _g.Dispose(); + } + + + #region Delegate graphics methods + + public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2) + { + var p = new SKPaint + { + IsAntialias = true + }; + + _g.DrawLine((float)x1, (float)y1, (float)x2, (float)y2, p); + } + + public override void DrawRectangle(RPen pen, double x, double y, double width, double height) + { + var p = new SKPaint + { + IsAntialias = true + }; + + _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); + } + + public override void DrawRectangle(RBrush brush, double x, double y, double width, double height) + { + var xBrush = ((BrushAdapter)brush).Brush; + var xTextureBrush = xBrush as XTextureBrush; + if (xTextureBrush != null) + { + xTextureBrush.DrawRectangle(_g, (float)x, (float)y, (float)width, (float)height); + } + else + { + var p = new SKPaint + { + IsAntialias = true + }; + _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); + } + } + + public override void DrawImage(RImage image, RRect destRect, RRect srcRect) + { + _g.DrawImage(((ImageAdapter)image).Image, Utils.Convert(destRect), Utils.Convert(srcRect)); + } + + public override void DrawImage(RImage image, RRect destRect) + { + _g.DrawImage(((ImageAdapter)image).Image, Utils.Convert(destRect)); + } + + public override void DrawPath(RPen pen, RGraphicsPath path) + { + var p = new SKPaint + { + IsAntialias = true + }; + _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, p); + } + + public override void DrawPath(RBrush brush, RGraphicsPath path) + { + //(XBrush)((BrushAdapter)brush).Brush, + var p = new SKPaint + { + IsAntialias = true + }; + + _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, p); + } + + public override void DrawPolygon(RBrush brush, RPoint[] points) + { + var p = new SKPaint + { + IsAntialias = true + }; + + if (points != null && points.Length > 0) + { + //(XBrush)((BrushAdapter)brush).Brush + var path = new SKPath(); + path.AddPoly(Utils.Convert(points)); + _g.DrawPath(path, p); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs new file mode 100644 index 000000000..484c96c14 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs @@ -0,0 +1,92 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System; +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; + + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Adapter for WinForms graphics path object for core. + /// + internal sealed class GraphicsPathAdapter : RGraphicsPath + { + /// + /// The actual PdfSharp graphics path instance. + /// + private readonly SKPath _graphicsPath = new SKPath(); + + /// + /// the last point added to the path to begin next segment from + /// + private RPoint _lastPoint; + + /// + /// The actual PdfSharp graphics path instance. + /// + public SKPath GraphicsPath + { + get { return _graphicsPath; } + } + + public override void Start(double x, double y) + { + _lastPoint = new RPoint(x, y); + } + + public override void LineTo(double x, double y) + { + _graphicsPath.LineTo((float)x, (float)y); + _lastPoint = new RPoint(x, y); + } + + public override void ArcTo(double x, double y, double size, Corner corner) + { + float left = (float)(Math.Min(x, _lastPoint.X) - (corner == Corner.TopRight || corner == Corner.BottomRight ? size : 0)); + float top = (float)(Math.Min(y, _lastPoint.Y) - (corner == Corner.BottomLeft || corner == Corner.BottomRight ? size : 0)); + _graphicsPath.ArcTo(left, top, (float)size * 2, (float)size * 2, GetStartAngle(corner)); + _lastPoint = new RPoint(x, y); + } + + public override void Dispose() + { } + + /// + /// Get arc start angle for the given corner. + /// + private static int GetStartAngle(Corner corner) + { + int startAngle; + switch (corner) + { + case Corner.TopLeft: + startAngle = 180; + break; + case Corner.TopRight: + startAngle = 270; + break; + case Corner.BottomLeft: + startAngle = 90; + break; + case Corner.BottomRight: + startAngle = 0; + break; + default: + throw new ArgumentOutOfRangeException("corner"); + } + return startAngle; + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs new file mode 100644 index 000000000..171c24213 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs @@ -0,0 +1,60 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using TheArtOfDev.HtmlRenderer.Adapters; + + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Adapter for SkiaSharp Image object for core. + /// + internal sealed class ImageAdapter : RImage + { + /// + /// the underline win-forms image. + /// + private readonly SKImage _image; + + /// + /// Initializes a new instance of the class. + /// + public ImageAdapter(SKImage image) + { + _image = image; + } + + /// + /// the underline SkiaSharp image. + /// + public SKImage Image + { + get { return _image; } + } + + public override double Width + { + get { return _image.Width; } + } + + public override double Height + { + get { return _image.Height; } + } + + public override void Dispose() + { + _image.Dispose(); + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs new file mode 100644 index 000000000..93582a7f2 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs @@ -0,0 +1,105 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; +using SkiaSharp; +using System.Security.Cryptography.X509Certificates; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + public class SKPen + { + public float Height {get;set;} + public float Width {get;set;} + + public SKColor Color { get; set; } + + + public SKPen(SKColor color = default) + { + Color = color; + } + + + } + + + /// + /// Adapter for SkiaSharp "pens" objects for core. + /// + internal sealed class PenAdapter : RPen + { + /// + /// The actual WinForms brush instance. + /// + private readonly SKPen _pen; + + /// + /// Init. + /// + public PenAdapter(SKPen pen) + { + _pen = pen; + } + + /// + /// The actual WinForms brush instance. + /// + public SKPen Pen + { + get { return _pen; } + } + + public override double Width + { + get { return _pen.Width; } + set { _pen.Width = (float)value; } + } + + public override RDashStyle DashStyle + { + set + { + /* + SKPathEffect.CreateDash() + switch (value) + { + case RDashStyle.Solid: + _pen.DashStyle = XDashStyle.Solid; + break; + case RDashStyle.Dash: + _pen.DashStyle = XDashStyle.Dash; + if (Width < 2) + _pen.DashPattern = new[] { 4, 4d }; // better looking + break; + case RDashStyle.Dot: + _pen.DashStyle = XDashStyle.Dot; + break; + case RDashStyle.DashDot: + _pen.DashStyle = XDashStyle.DashDot; + break; + case RDashStyle.DashDotDot: + _pen.DashStyle = XDashStyle.DashDotDot; + break; + case RDashStyle.Custom: + _pen.DashStyle = XDashStyle.Custom; + break; + default: + _pen.DashStyle = XDashStyle.Solid; + break; + } + */ + } + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs new file mode 100644 index 000000000..eb913f4a1 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs @@ -0,0 +1,147 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System.Drawing; +using System.Drawing.Text; +using System.IO; +using TheArtOfDev.HtmlRenderer.Adapters; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Adapter for SkiaSharp library platform. + /// + internal sealed class SkiaSharpAdapter : RAdapter + { + #region Fields and Consts + + /// + /// Singleton instance of global adapter. + /// + private static readonly SkiaSharpAdapter _instance = new SkiaSharpAdapter(); + + #endregion + + + /// + /// Init color resolve. + /// + private SkiaSharpAdapter() + { + AddFontFamilyMapping("monospace", "Courier New"); + AddFontFamilyMapping("Helvetica", "Arial"); + + var manager = SKFontManager.CreateDefault(); + var families = manager.GetFontFamilies(); + + foreach (var family in families) + { + AddFontFamily(new FontFamilyAdapter(family)); + } + } + + /// + /// Singleton instance of global adapter. + /// + public static SkiaSharpAdapter Instance + { + get { return _instance; } + } + + protected override RColor GetColorInt(string colorName) + { + try + { + var color = Color.FromKnownColor((KnownColor)System.Enum.Parse(typeof(KnownColor), colorName, true)); + return Utils.Convert(color); + } + catch + { + return RColor.Empty; + } + } + + protected override RPen CreatePen(RColor color) + { + return new PenAdapter(new SKPen(Utils.Convert(color))); + } + + protected override RBrush CreateSolidBrush(RColor color) + { + SKBrush solidBrush; + if (color == RColor.White) + solidBrush = SKBrush.White; + else if (color == RColor.Black) + solidBrush = SKBrush.Black; + else if (color.A < 1) + solidBrush = SKBrush.Transparent; + else + solidBrush = new SKBrush(Utils.Convert(color)); + + return new BrushAdapter(solidBrush); + } + + protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle) + { + //XLinearGradientMode mode; + //if (angle < 45) + // mode = XLinearGradientMode.ForwardDiagonal; + //else if (angle < 90) + // mode = XLinearGradientMode.Vertical; + //else if (angle < 135) + // mode = XLinearGradientMode.BackwardDiagonal; + //else + // mode = XLinearGradientMode.Horizontal; + return new BrushAdapter(new SKBrush(Utils.Convert(rect), Utils.Convert(color1), Utils.Convert(color2), (float)angle)); + } + + protected override RImage ConvertImageInt(object image) + { + return image != null ? new ImageAdapter((SKImage)image) : null; + } + + protected override RImage ImageFromStreamInt(Stream memoryStream) + { + SKBitmap bitmap = SKBitmap.Decode(memoryStream); + return new ImageAdapter(SKImage.FromBitmap(bitmap)); + } + + protected override RFont CreateFontInt(string family, double size, RFontStyle style) + { + SKFontStyle fontStyle; + + switch(style) + { + case RFontStyle.Bold: fontStyle = SKFontStyle.Bold; break; + case RFontStyle.Italic: fontStyle = SKFontStyle.Italic; break; + + case RFontStyle.Regular: + default: + fontStyle = SKFontStyle.Normal; break; + } + + var typeface = SKTypeface.FromFamilyName(family, fontStyle); + + var xFont = new SKFont(typeface, (float)size); + + return new FontAdapter(xFont); + } + + protected override RFont CreateFontInt(RFontFamily family, double size, RFontStyle style) + { + return this.CreateFontInt(((FontFamilyAdapter)family).FontFamily, size, style); + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/XTextureBrush.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/XTextureBrush.cs new file mode 100644 index 000000000..e580e0c39 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/XTextureBrush.cs @@ -0,0 +1,77 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + + +using SkiaSharp; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters +{ + /// + /// Because PdfSharp doesn't support texture brush we need to implement it ourselves. + /// + internal sealed class XTextureBrush + { + #region Fields/Consts + + /// + /// The image to draw in the brush + /// + private readonly SKImage _image; + + /// + /// the + /// + private readonly SKRect _dstRect; + + /// + /// the transform the location of the image to handle center alignment + /// + private readonly SKPoint _translateTransformLocation; + + #endregion + + + /// + /// Init. + /// + public XTextureBrush(SKImage image, SKRect dstRect, SKPoint translateTransformLocation) + { + _image = image; + _dstRect = dstRect; + _translateTransformLocation = translateTransformLocation; + } + + /// + /// Draw the texture image in the given graphics at the given location. + /// + public void DrawRectangle(SKCanvas g, float x, float y, float width, float height) + { + var prevState = g.Save(); + g.ClipRect(new SKRect(x, y, width, height)); + + float rx = _translateTransformLocation.X; + float w = _image.Width, h = _image.Height; + while (rx < x + width) + { + float ry = _translateTransformLocation.Y; + while (ry < y + height) + { + g.DrawImage(_image, new SKRect(rx, ry, w, h)); + ry += h; + } + rx += w; + } + + g.Restore(); + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlContainer.cs b/Source/HtmlRenderer.SkiaSharp/HtmlContainer.cs new file mode 100644 index 000000000..974e78296 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/HtmlContainer.cs @@ -0,0 +1,356 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System; +using System.Collections.Generic; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; +using TheArtOfDev.HtmlRenderer.Core; +using TheArtOfDev.HtmlRenderer.Core.Entities; +using TheArtOfDev.HtmlRenderer.Core.Utils; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp +{ + /// + /// Low level handling of Html Renderer logic, this class is used by . + /// + /// + public sealed class HtmlContainer : IDisposable + { + #region Fields and Consts + + /// + /// The internal core html container + /// + private readonly HtmlContainerInt _htmlContainerInt; + + #endregion + + + /// + /// Init. + /// + public HtmlContainer() + { + _htmlContainerInt = new HtmlContainerInt(SkiaSharpAdapter.Instance); + _htmlContainerInt.AvoidAsyncImagesLoading = true; + _htmlContainerInt.AvoidImagesLateLoading = true; + } + + /// + /// Raised when the set html document has been fully loaded.
+ /// Allows manipulation of the html dom, scroll position, etc. + ///
+ public event EventHandler LoadComplete + { + add { _htmlContainerInt.LoadComplete += value; } + remove { _htmlContainerInt.LoadComplete -= value; } + } + + /// + /// Raised when an error occurred during html rendering.
+ ///
+ /// + /// There is no guarantee that the event will be raised on the main thread, it can be raised on thread-pool thread. + /// + public event EventHandler RenderError + { + add { _htmlContainerInt.RenderError += value; } + 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 + /// + internal HtmlContainerInt HtmlContainerInt + { + get { return _htmlContainerInt; } + } + + /// + /// the parsed stylesheet data used for handling the html + /// + public CssData CssData + { + get { return _htmlContainerInt.CssData; } + } + + /// + /// Gets or sets a value indicating if anti-aliasing should be avoided for geometry like backgrounds and borders (default - false). + /// + public bool AvoidGeometryAntialias + { + get { return _htmlContainerInt.AvoidGeometryAntialias; } + set { _htmlContainerInt.AvoidGeometryAntialias = value; } + } + + /// + /// The scroll offset of the html.
+ /// This will adjust the rendered html by the given offset so the content will be "scrolled".
+ ///
+ /// + /// Element that is rendered at location (50,100) with offset of (0,200) will not be rendered as it + /// will be at -100 therefore outside the client rectangle. + /// + public SKPoint ScrollOffset + { + get { return Utils.Convert(_htmlContainerInt.ScrollOffset); } + set { _htmlContainerInt.ScrollOffset = Utils.Convert(value); } + } + + /// + /// The top-left most location of the rendered html.
+ /// This will offset the top-left corner of the rendered html. + ///
+ public SKPoint Location + { + get { return Utils.Convert(_htmlContainerInt.Location); } + set { _htmlContainerInt.Location = Utils.Convert(value); } + } + + /// + /// The max width and height of the rendered html.
+ /// The max width will effect the html layout wrapping lines, resize images and tables where possible.
+ /// The max height does NOT effect layout, but will not render outside it (clip).
+ /// can be exceed the max size by layout restrictions (unwrappable line, set image size, etc.).
+ /// Set zero for unlimited (width\height separately).
+ ///
+ public SKSize MaxSize + { + get { return Utils.Convert(_htmlContainerInt.MaxSize); } + set { _htmlContainerInt.MaxSize = Utils.Convert(value); } + } + + /// + /// The actual size of the rendered html (after layout) + /// + public SKSize ActualSize + { + get { return Utils.Convert(_htmlContainerInt.ActualSize); } + internal set { _htmlContainerInt.ActualSize = Utils.Convert(value); } + } + + public SKSize PageSize { + get + { + return new SKSize((float)_htmlContainerInt.PageSize.Width, (float)_htmlContainerInt.PageSize.Height); + } + set + { + _htmlContainerInt.PageSize = new RSize(value.Width, value.Height); + } + } + + /// + /// the top margin between the page start and the text + /// + public int MarginTop + { + get { return _htmlContainerInt.MarginTop; } + set + { + if (value > -1) + _htmlContainerInt.MarginTop = value; + } + } + + /// + /// the bottom margin between the page end and the text + /// + public int MarginBottom + { + get { return _htmlContainerInt.MarginBottom; } + set + { + if (value > -1) + _htmlContainerInt.MarginBottom = value; + } + } + + /// + /// the left margin between the page start and the text + /// + public int MarginLeft + { + get { return _htmlContainerInt.MarginLeft; } + set + { + if (value > -1) + _htmlContainerInt.MarginLeft = value; + } + } + + /// + /// the right margin between the page end and the text + /// + public int MarginRight + { + get { return _htmlContainerInt.MarginRight; } + set + { + if (value > -1) + _htmlContainerInt.MarginRight = value; + } + } + + /// + /// Set all 4 margins to the given value. + /// + /// + public void SetMargins(int value) + { + if (value > -1) + _htmlContainerInt.SetMargins(value); + } + + /// + /// Get the currently selected text segment in the html. + /// + public string SelectedText + { + get { return _htmlContainerInt.SelectedText; } + } + + /// + /// Copy the currently selected html segment with style. + /// + public string SelectedHtml + { + get { return _htmlContainerInt.SelectedHtml; } + } + + /// + /// Init with optional document and stylesheet. + /// + /// the html to init with, init empty if not given + /// optional: the stylesheet to init with, init default if not given + public async Task SetHtml(string htmlSource, CssData baseCssData = null) + { + await _htmlContainerInt.SetHtml(htmlSource, baseCssData); + } + + /// + /// Get html from the current DOM tree with style if requested. + /// + /// Optional: controls the way styles are generated when html is generated (default: ) + /// generated html + public string GetHtml(HtmlGenerationStyle styleGen = HtmlGenerationStyle.Inline) + { + return _htmlContainerInt.GetHtml(styleGen); + } + + /// + /// Get attribute value of element at the given x,y location by given key.
+ /// If more than one element exist with the attribute at the location the inner most is returned. + ///
+ /// the location to find the attribute at + /// the attribute key to get value by + /// found attribute value or null if not found + public string GetAttributeAt(SKPoint location, string attribute) + { + return _htmlContainerInt.GetAttributeAt(Utils.Convert(location), attribute); + } + + /// + /// Get all the links in the HTML with the element rectangle and href data. + /// + /// collection of all the links in the HTML + public List> GetLinks() + { + var linkElements = new List>(); + foreach (var link in HtmlContainerInt.GetLinks()) + { + linkElements.Add(new LinkElementData(link.Id, link.Href, Utils.Convert(link.Rectangle))); + } + return linkElements; + } + + /// + /// Get css link href at the given x,y location. + /// + /// the location to find the link at + /// css link href if exists or null + public string GetLinkAt(SKPoint location) + { + return _htmlContainerInt.GetLinkAt(Utils.Convert(location)); + } + + /// + /// Get the rectangle of html element as calculated by html layout.
+ /// Element if found by id (id attribute on the html element).
+ /// Note: to get the screen rectangle you need to adjust by the hosting control.
+ ///
+ /// the id of the element to get its rectangle + /// the rectangle of the element or null if not found + public SKRect? GetElementRectangle(string elementId) + { + var r = _htmlContainerInt.GetElementRectangle(elementId); + return r.HasValue ? Utils.Convert(r.Value) : (SKRect?)null; + } + + /// + /// Measures the bounds of box and children, recursively. + /// + /// Device context to draw + public async Task PerformLayout(SKCanvas g) + { + ArgChecker.AssertArgNotNull(g, "g"); + + using (var ig = new GraphicsAdapter(g)) + { + await _htmlContainerInt.PerformLayout(ig); + } + } + + /// + /// Render the html using the given device. + /// + /// the device to use to render + public async Task PerformPaint(SKCanvas g) + { + ArgChecker.AssertArgNotNull(g, "g"); + + using (var ig = new GraphicsAdapter(g)) + { + await _htmlContainerInt.PerformPaint(ig); + } + } + + public void Dispose() + { + _htmlContainerInt.Dispose(); + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj new file mode 100644 index 000000000..f1aabe47f --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/Source/HtmlRenderer.SkiaSharp/PdfGenerateConfig.cs b/Source/HtmlRenderer.SkiaSharp/PdfGenerateConfig.cs new file mode 100644 index 000000000..aa7ff9586 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/PdfGenerateConfig.cs @@ -0,0 +1,234 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp +{ + + public enum PageSize + { + Undefined = 0, + A0 = 1, + A1 = 2, + A2 = 3, + A3 = 4, + A4 = 5, + A5 = 6, + RA0 = 7, + RA1 = 8, + RA2 = 9, + RA3 = 10, + RA4 = 11, + RA5 = 12, + B0 = 13, + B1 = 14, + B2 = 0xF, + B3 = 0x10, + B4 = 17, + B5 = 18, + A6 = 19, + Quarto = 100, + Foolscap = 101, + Executive = 102, + GovernmentLetter = 103, + Letter = 104, + Legal = 105, + Ledger = 106, + Tabloid = 107, + Post = 108, + Crown = 109, + LargePost = 110, + Demy = 111, + Medium = 112, + Royal = 113, + Elephant = 114, + DoubleDemy = 115, + QuadDemy = 116, + STMT = 117, + Folio = 120, + Statement = 121, + Size10x14 = 122 + } + + public enum PageOrientation + { + Unknown = 0, + Portrait = 1, + Landscape = 2 + } + + public enum PageDirection + { + Downwards, + //[Obsolete("Not implemeted - yagni")] + //Upwards + } + + /// + /// The settings for generating PDF using + /// + public sealed class PdfGenerateConfig + { + #region Fields/Consts + + /// + /// the page size to use for each page in the generated pdf + /// + private PageSize _pageSize; + + /// + /// if the page size is undefined this allow you to set manually the page size + /// + private SKSize _xsize; + + /// + /// the orientation of each page of the generated pdf + /// + private PageOrientation _pageOrientation; + + /// + /// the top margin between the page start and the text + /// + private int _marginTop; + + /// + /// the bottom margin between the page end and the text + /// + private int _marginBottom; + + /// + /// the left margin between the page start and the text + /// + private int _marginLeft; + + /// + /// the right margin between the page end and the text + /// + private int _marginRight; + + #endregion + + /// + /// the page size to use for each page in the generated pdf + /// + public PageSize PageSize + { + get { return _pageSize; } + set { _pageSize = value; } + } + + /// + /// if the page size is undefined this allow you to set manually the page size + /// + public SKSize ManualPageSize { + get { return _xsize; } + set { _xsize = value; } + } + + /// + /// the orientation of each page of the generated pdf + /// + public PageOrientation PageOrientation + { + get { return _pageOrientation; } + set { _pageOrientation = value; } + } + + + /// + /// the top margin between the page start and the text + /// + public int MarginTop + { + get { return _marginTop; } + set + { + if (value > -1) + _marginTop = value; + } + } + + /// + /// the bottom margin between the page end and the text + /// + public int MarginBottom + { + get { return _marginBottom; } + set + { + if (value > -1) + _marginBottom = value; + } + } + + /// + /// the left margin between the page start and the text + /// + public int MarginLeft + { + get { return _marginLeft; } + set + { + if (value > -1) + _marginLeft = value; + } + } + + /// + /// the right margin between the page end and the text + /// + public int MarginRight + { + get { return _marginRight; } + set + { + if (value > -1) + _marginRight = value; + } + } + + /// + /// Set all 4 margins to the given value. + /// + /// + public void SetMargins(int value) + { + if (value > -1) + _marginBottom = _marginLeft = _marginTop = _marginRight = value; + } + + // The international definitions are: + // 1 inch == 25.4 mm + // 1 inch == 72 point + + /// + /// Convert the units passed in milimiters to the units used in SkiaSharp + /// + /// + /// + /// + public static SKSize MilimitersToUnits(float width, float height) { + return new SKSize(width / 25.4f * 72, height / 25.4f * 72); + } + + /// + /// Convert the units passed in inches to the units used in SkiaSharp + /// + /// + /// + /// + public static SKSize InchesToUnits(float width, float height) { + return new SKSize(width * 72, height * 72); + } + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs new file mode 100644 index 000000000..8026a4a8b --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs @@ -0,0 +1,268 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System; +using TheArtOfDev.HtmlRenderer.Core; +using TheArtOfDev.HtmlRenderer.Core.Entities; +using TheArtOfDev.HtmlRenderer.Core.Utils; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp +{ + /// + /// TODO:a add doc + /// + public static class PdfGenerator + { + /// + /// Adds a font mapping from to iff the is not found.
+ /// When the font is used in rendered html and is not found in existing + /// fonts (installed or added) it will be replaced by .
+ ///
+ /// + /// This fonts mapping can be used as a fallback in case the requested font is not installed in the client system. + /// + /// the font family to replace + /// the font family to replace with + public static void AddFontFamilyMapping(string fromFamily, string toFamily) + { + ArgChecker.AssertArgNotNullOrEmpty(fromFamily, "fromFamily"); + ArgChecker.AssertArgNotNullOrEmpty(toFamily, "toFamily"); + + SkiaSharpAdapter.Instance.AddFontFamilyMapping(fromFamily, toFamily); + } + + /// + /// Parse the given stylesheet to object.
+ /// If is true the parsed css blocks are added to the + /// default css data (as defined by W3), merged if class name already exists. If false only the data in the given stylesheet is returned. + ///
+ /// + /// the stylesheet source to parse + /// true - combine the parsed css data with default css data, false - return only the parsed css data + /// the parsed css data + public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault = true) + { + return CssData.Parse(SkiaSharpAdapter.Instance, stylesheet, combineWithDefault); + } + + /// + /// Create PDF document from given HTML.
+ ///
+ /// HTML source to create PDF from + /// the page size to use for each page in the generated pdf + /// the margin to use between the HTML and the edges of each page + /// 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 + /// the generated image of the html + public static async Task GeneratePdfAsync( + string html, + Stream outputStream, + int margin = 20, + CssData cssData = null, + EventHandler stylesheetLoad = null, + EventHandler imageLoad = null) + { + + var config = new PdfGenerateConfig(); + config.PageSize = PageSize.A4; + config.PageOrientation = PageOrientation.Portrait; + + return await GeneratePdfAsync(html, outputStream, config, cssData, stylesheetLoad, imageLoad); + } + + /// + /// Create PDF document from given HTML.
+ ///
+ /// HTML source to create PDF from + /// the configuration to use for the PDF generation (page size/page orientation/margins/etc.) + /// 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 + /// the generated image of the html + public static async Task GeneratePdfAsync( + string html, + Stream outputStream, + PdfGenerateConfig config, + CssData cssData = null, + EventHandler stylesheetLoad = null, + EventHandler imageLoad = null) + { + // create PDF document to render the HTML into + var document = SKDocument.CreatePdf(outputStream, 72f); + + // add rendered PDF pages to document + await AddPdfPagesAsync(document, html, config, cssData, stylesheetLoad, imageLoad); + + return document; + } + + /// + /// Create PDF pages from given HTML and appends them to the provided PDF document.
+ ///
+ /// PDF document to append pages to + /// HTML source to create PDF from + /// the page size to use for each page in the generated pdf + /// the margin to use between the HTML and the edges of each page + /// 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 + /// the generated image of the html + public static async Task AddPdfPages(SKDocument document, string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) + { + var config = new PdfGenerateConfig(); + config.PageSize = pageSize; + config.SetMargins(margin); + await AddPdfPagesAsync(document, html, config, cssData, stylesheetLoad, imageLoad); + } + + /// + /// Create PDF pages from given HTML and appends them to the provided PDF document.
+ ///
+ /// PDF document to append pages to + /// HTML source to create PDF from + /// the configuration to use for the PDF generation (page size/page orientation/margins/etc.) + /// 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 + /// the generated image of the html + public static async Task AddPdfPagesAsync( + SKDocument document, + string html, + PdfGenerateConfig config, + CssData cssData = null, + EventHandler stylesheetLoad = null, + EventHandler imageLoad = null) + { + SKSize orgPageSize; + // get the size of each page to layout the HTML in + if (config.PageSize == PageSize.A4) + //8.3 x 11.7 inches in Portrait + orgPageSize = new SKSize(8.3f * 72, 11.7f * 72); + else + orgPageSize = config.ManualPageSize; + + if (config.PageOrientation == PageOrientation.Landscape) + { + // invert pagesize for landscape + orgPageSize = new SKSize(orgPageSize.Height, orgPageSize.Width); + } + + var pageSize = new SKSize(orgPageSize.Width - config.MarginLeft - config.MarginRight, orgPageSize.Height - config.MarginTop - config.MarginBottom); + + if (!string.IsNullOrEmpty(html)) + { + using (var container = new HtmlContainer()) + { + if (stylesheetLoad != null) + container.StylesheetLoad += stylesheetLoad; + if (imageLoad != null) + container.ImageLoad += imageLoad; + + container.Location = new SKPoint(config.MarginLeft, config.MarginTop); + container.MaxSize = new SKSize(pageSize.Width, 0); + await container.SetHtml(html, cssData); + container.PageSize = pageSize; + container.MarginBottom = config.MarginBottom; + container.MarginLeft = config.MarginLeft; + container.MarginRight = config.MarginRight; + container.MarginTop = config.MarginTop; + + // layout the HTML with the page width restriction to know how many pages are required + var docImageInfo = new SKImageInfo((int)orgPageSize.Width, (int)orgPageSize.Height * 7); + using (var s = SKSurface.Create(docImageInfo)) + using (var g = s.Canvas) + { + await container.PerformLayout(g); + + using (var image = s.Snapshot()) + using (var data = image.Encode(SKEncodedImageFormat.Png, 80)) + using (var stream = File.OpenWrite(Path.Combine(@"c:\temp\SkiaSharpTests", "layout.png"))) + { + // save the data to a stream + data.SaveTo(stream); + } + } + + + // while there is un-rendered HTML, create another PDF page and render with proper offset for the next page + double scrollOffset = 0; + while (scrollOffset > -container.ActualSize.Height) + { + using (var page = document.BeginPage(orgPageSize.Width, orgPageSize.Height)) + { + page.ClipRect(new SKRect(config.MarginLeft, config.MarginTop, pageSize.Width, pageSize.Height)); + + container.ScrollOffset = new SKPoint(0, (float)scrollOffset); + + await container.PerformPaint(page); + + document.EndPage(); + } + scrollOffset -= pageSize.Height; + } + + // add web links and anchors + HandleLinks(document, container, orgPageSize, pageSize); + + document.Close(); + } + } + } + + + + #region Private/Protected methods + + /// + /// Handle HTML links by create PDF Documents link either to external URL or to another page in the document. + /// + private static void HandleLinks(SKDocument document, HtmlContainer container, SKSize orgPageSize, SKSize pageSize) + { + /* + foreach (var link in container.GetLinks()) + { + int i = (int)(link.Rectangle.Top / pageSize.Height); + for (; i < document.Pages.Count && pageSize.Height * i < link.Rectangle.Bottom; i++) + { + var offset = pageSize.Height * i; + + // fucking position is from the bottom of the page + var xRect = new XRect(link.Rectangle.Left, orgPageSize.Height - (link.Rectangle.Height + link.Rectangle.Top - offset), link.Rectangle.Width, link.Rectangle.Height); + + if (link.IsAnchor) + { + // create link to another page in the document + var anchorRect = container.GetElementRectangle(link.AnchorId); + if (anchorRect.HasValue) + { + // document links to the same page as the link is not allowed + int anchorPageIdx = (int)(anchorRect.Value.Top / pageSize.Height); + if (i != anchorPageIdx) + document.Pages[i].AddDocumentLink(new PdfRectangle(xRect), anchorPageIdx); + } + } + else + { + // create link to URL + document.Pages[i].AddWebLink(new PdfRectangle(xRect), link.Href); + } + } + } + */ + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs b/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs new file mode 100644 index 000000000..ea566a0e6 --- /dev/null +++ b/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs @@ -0,0 +1,100 @@ +// "Therefore those skilled at the unorthodox +// are infinite as heaven and earth, +// inexhaustible as the great rivers. +// When they come to an end, +// they begin again, +// like the days and months; +// they die and are reborn, +// like the four seasons." +// +// - Sun Tsu, +// "The Art of War" + +using SkiaSharp; +using System.Drawing; +using TheArtOfDev.HtmlRenderer.Adapters.Entities; + +namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities +{ + /// + /// Utilities for converting SkiaSharp entities to HtmlRenderer core entities. + /// + internal static class Utils + { + /// + /// Convert from SkiaSharp point to core point. + /// + public static RPoint Convert(SKPoint p) + { + return new RPoint(p.X, p.Y); + } + + /// + /// Convert from SkiaSharp point to core point. + /// + public static SKPoint[] Convert(RPoint[] points) + { + SKPoint[] myPoints = new SKPoint[points.Length]; + for (int i = 0; i < points.Length; i++) + myPoints[i] = Convert(points[i]); + return myPoints; + } + + /// + /// Convert from core point to SkiaSharp point. + /// + public static SKPoint Convert(RPoint p) + { + return new SKPoint((float)p.X, (float)p.Y); + } + + /// + /// Convert from SkiaSharp size to core size. + /// + public static RSize Convert(SKSize s) + { + return new RSize(s.Width, s.Height); + } + + /// + /// Convert from core size to SkiaSharp size. + /// + public static SKSize Convert(RSize s) + { + return new SKSize((float)s.Width, (float)s.Height); + } + + /// + /// Convert from SkiaSharp rectangle to core rectangle. + /// + public static RRect Convert(SKRect r) + { + return new RRect(r.Left, r.Bottom, r.Width, r.Height); + } + + /// + /// Convert from core rectangle to SkiaSharp rectangle. + /// + public static SKRect Convert(RRect r) + { + return new SKRect((float)r.Left, (float)r.Bottom, (float)r.Width, (float)r.Height); + } + + /// + /// Convert from core color to SkiaSharp color. + /// + public static SKColor Convert(RColor c) + { + return new SKColor(c.R, c.G, c.B, c.A); + } + + /// + /// Convert from color to SkiaSharp color. + /// + public static RColor Convert(Color c) + { + return RColor.FromArgb(c.A, c.R, c.G, c.B); + } + + } +} \ No newline at end of file diff --git a/Source/HtmlRenderer.WPF/HtmlLabel.cs b/Source/HtmlRenderer.WPF/HtmlLabel.cs index 01429c872..c4d362802 100644 --- a/Source/HtmlRenderer.WPF/HtmlLabel.cs +++ b/Source/HtmlRenderer.WPF/HtmlLabel.cs @@ -79,27 +79,32 @@ public virtual bool AutoSizeHeightOnly ///
protected override Size MeasureOverride(Size constraint) { - if (_htmlContainer != null) + Task.Run(async () => { - using (var ig = new GraphicsAdapter()) + if (_htmlContainer != null) { - var horizontal = Padding.Left + Padding.Right + BorderThickness.Left + BorderThickness.Right; - var vertical = Padding.Top + Padding.Bottom + BorderThickness.Top + BorderThickness.Bottom; + using (var ig = new GraphicsAdapter()) + { + var horizontal = Padding.Left + Padding.Right + BorderThickness.Left + BorderThickness.Right; + var vertical = Padding.Top + Padding.Bottom + BorderThickness.Top + BorderThickness.Bottom; - var size = new RSize(constraint.Width < Double.PositiveInfinity ? constraint.Width - horizontal : 0, constraint.Height < Double.PositiveInfinity ? constraint.Height - vertical : 0); - var minSize = new RSize(MinWidth < Double.PositiveInfinity ? MinWidth - horizontal : 0, MinHeight < Double.PositiveInfinity ? MinHeight - vertical : 0); - var maxSize = new RSize(MaxWidth < Double.PositiveInfinity ? MaxWidth - horizontal : 0, MaxHeight < Double.PositiveInfinity ? MaxHeight - vertical : 0); + var size = new RSize(constraint.Width < Double.PositiveInfinity ? constraint.Width - horizontal : 0, constraint.Height < Double.PositiveInfinity ? constraint.Height - vertical : 0); + var minSize = new RSize(MinWidth < Double.PositiveInfinity ? MinWidth - horizontal : 0, MinHeight < Double.PositiveInfinity ? MinHeight - vertical : 0); + var maxSize = new RSize(MaxWidth < Double.PositiveInfinity ? MaxWidth - horizontal : 0, MaxHeight < Double.PositiveInfinity ? MaxHeight - vertical : 0); - var newSize = HtmlRendererUtils.Layout(ig, _htmlContainer.HtmlContainerInt, size, minSize, maxSize, AutoSize, AutoSizeHeightOnly); + var newSize = await HtmlRendererUtils.LayoutAsync(ig, _htmlContainer.HtmlContainerInt, size, minSize, maxSize, AutoSize, AutoSizeHeightOnly); - constraint = new Size(newSize.Width + horizontal, newSize.Height + vertical); + constraint = new Size(newSize.Width + horizontal, newSize.Height + vertical); + } } - } - if (double.IsPositiveInfinity(constraint.Width) || double.IsPositiveInfinity(constraint.Height)) - constraint = Size.Empty; + if (double.IsPositiveInfinity(constraint.Width) || double.IsPositiveInfinity(constraint.Height)) + constraint = Size.Empty; + + return constraint; + }); - return constraint; + return default; } /// diff --git a/Source/HtmlRenderer.WPF/HtmlRender.cs b/Source/HtmlRenderer.WPF/HtmlRender.cs index 77eb4a046..ca9595f58 100644 --- a/Source/HtmlRenderer.WPF/HtmlRender.cs +++ b/Source/HtmlRenderer.WPF/HtmlRender.cs @@ -255,10 +255,10 @@ public static BitmapFrame RenderToImage(string html, Size size, CssData cssData /// 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 BitmapFrame RenderToImage(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null, + public static async Task RenderToImageAsync(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { - return RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); + return await RenderToImageAsync(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); } /// @@ -281,7 +281,7 @@ public static BitmapFrame RenderToImage(string html, Size size, CssData cssData /// 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 BitmapFrame RenderToImage(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null, + public static async Task RenderToImageAsync(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { RenderTargetBitmap renderTarget; @@ -298,7 +298,7 @@ public static BitmapFrame RenderToImage(string html, Size size, CssData cssData container.ImageLoad += imageLoad; container.SetHtml(html, cssData); - var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); + var finalSize = await MeasureHtmlByRestrictionsAsync(container, minSize, maxSize); container.MaxSize = finalSize; renderTarget = new RenderTargetBitmap((int)finalSize.Width, (int)finalSize.Height, 96, 96, PixelFormats.Pbgra32); @@ -332,12 +332,12 @@ public static BitmapFrame RenderToImage(string html, Size size, CssData cssData /// the minimal size of the rendered html (zero - not limit the width/height) /// the maximum size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height) /// return: the size of the html to be rendered within the min/max limits - private static Size MeasureHtmlByRestrictions(HtmlContainer htmlContainer, Size minSize, Size maxSize) + private static async Task MeasureHtmlByRestrictionsAsync(HtmlContainer htmlContainer, Size minSize, Size maxSize) { // use desktop created graphics to measure the HTML using (var mg = new GraphicsAdapter()) { - var sizeInt = HtmlRendererUtils.MeasureHtmlByRestrictions(mg, htmlContainer.HtmlContainerInt, Utils.Convert(minSize), Utils.Convert(maxSize)); + var sizeInt = await HtmlRendererUtils.MeasureHtmlByRestrictionsAsync(mg, htmlContainer.HtmlContainerInt, Utils.Convert(minSize), Utils.Convert(maxSize)); if (maxSize.Width < 1 && sizeInt.Width > 4096) sizeInt.Width = 4096; return Utils.ConvertRound(sizeInt); diff --git a/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj b/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj index 80c4b97f9..4fb1df302 100644 --- a/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj +++ b/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj @@ -1,85 +1,15 @@ - - - + + - Debug - AnyCPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0} - library - Properties - TheArtOfDev.HtmlRenderer.WPF - HtmlRenderer.WPF - v3.0 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - - + net6.0-windows + enable + disable + true + true - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release.WPF.Net35\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - {fe611685-391f-4e3e-b27e-d3150e51e49b} - HtmlRenderer - - + - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - + - - - \ No newline at end of file + + diff --git a/Source/HtmlRenderer.WinForms/Adapters/ContextMenuAdapter.cs b/Source/HtmlRenderer.WinForms/Adapters/ContextMenuAdapter.cs index ca0c83b7e..7fdc987f5 100644 --- a/Source/HtmlRenderer.WinForms/Adapters/ContextMenuAdapter.cs +++ b/Source/HtmlRenderer.WinForms/Adapters/ContextMenuAdapter.cs @@ -11,7 +11,6 @@ // "The Art of War" using System; -using System.Windows.Forms; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; using TheArtOfDev.HtmlRenderer.Adapters; diff --git a/Source/HtmlRenderer.WinForms/HtmlLabel.cs b/Source/HtmlRenderer.WinForms/HtmlLabel.cs index 8853490d2..da5c05dd5 100644 --- a/Source/HtmlRenderer.WinForms/HtmlLabel.cs +++ b/Source/HtmlRenderer.WinForms/HtmlLabel.cs @@ -487,23 +487,26 @@ protected override void OnLayout(LayoutEventArgs levent) { if (_htmlContainer != null) { - Graphics g = Utils.CreateGraphics(this); - if (g != null) - { - using (g) - using (var ig = new GraphicsAdapter(g, _htmlContainer.UseGdiPlusTextRendering)) + Task.Run(async () => { + Graphics g = Utils.CreateGraphics(this); + if (g != null) { - var newSize = HtmlRendererUtils.Layout(ig, - _htmlContainer.HtmlContainerInt, - new RSize(ClientSize.Width - Padding.Horizontal, ClientSize.Height - Padding.Vertical), - new RSize(MinimumSize.Width - Padding.Horizontal, MinimumSize.Height - Padding.Vertical), - new RSize(MaximumSize.Width - Padding.Horizontal, MaximumSize.Height - Padding.Vertical), - AutoSize, - AutoSizeHeightOnly); - ClientSize = Utils.ConvertRound(new RSize(newSize.Width + Padding.Horizontal, newSize.Height + Padding.Vertical)); + using (g) + using (var ig = new GraphicsAdapter(g, _htmlContainer.UseGdiPlusTextRendering)) + { + var newSize = await HtmlRendererUtils.LayoutAsync(ig, + _htmlContainer.HtmlContainerInt, + new RSize(ClientSize.Width - Padding.Horizontal, ClientSize.Height - Padding.Vertical), + new RSize(MinimumSize.Width - Padding.Horizontal, MinimumSize.Height - Padding.Vertical), + new RSize(MaximumSize.Width - Padding.Horizontal, MaximumSize.Height - Padding.Vertical), + AutoSize, + AutoSizeHeightOnly); + ClientSize = Utils.ConvertRound(new RSize(newSize.Width + Padding.Horizontal, newSize.Height + Padding.Vertical)); + } } - } + }); } + base.OnLayout(levent); } diff --git a/Source/HtmlRenderer.WinForms/HtmlRender.cs b/Source/HtmlRenderer.WinForms/HtmlRender.cs index 3c6ad3ca9..ed8057ca5 100644 --- a/Source/HtmlRenderer.WinForms/HtmlRender.cs +++ b/Source/HtmlRenderer.WinForms/HtmlRender.cs @@ -435,10 +435,10 @@ 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, + public static async Task RenderToImageAsync(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { - return RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); + return await RenderToImageAsync(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); } /// @@ -462,7 +462,7 @@ 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, + public static async Task RenderToImageAsync(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { if (backgroundColor == Color.Transparent) @@ -482,7 +482,7 @@ public static void RenderToImage(Image image, string html, PointF location, Size container.ImageLoad += imageLoad; container.SetHtml(html, cssData); - var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); + var finalSize = await MeasureHtmlByRestrictionsAsync(container, minSize, maxSize); container.MaxSize = finalSize; // create the final image to render into by measured size @@ -559,10 +559,10 @@ 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, + public static async Task RenderToImageGdiPlusAsync(string html, int maxWidth = 0, int maxHeight = 0, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { - return RenderToImageGdiPlus(html, Size.Empty, new Size(maxWidth, maxHeight), textRenderingHint, cssData, stylesheetLoad, imageLoad); + return await RenderToImageGdiPlusAsync(html, Size.Empty, new Size(maxWidth, maxHeight), textRenderingHint, cssData, stylesheetLoad, imageLoad); } /// @@ -584,7 +584,7 @@ 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, + public static async Task RenderToImageGdiPlusAsync(string html, Size minSize, Size maxSize, TextRenderingHint textRenderingHint = TextRenderingHint.AntiAlias, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { if (string.IsNullOrEmpty(html)) @@ -602,7 +602,7 @@ public static Image RenderToImageGdiPlus(string html, Size minSize, Size maxSize container.ImageLoad += imageLoad; container.SetHtml(html, cssData); - var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); + var finalSize = await MeasureHtmlByRestrictionsAsync(container, minSize, maxSize); container.MaxSize = finalSize; // create the final image to render into by measured size @@ -667,13 +667,13 @@ private static SizeF Measure(Graphics g, string html, float maxWidth, CssData cs /// the minimal size of the rendered html (zero - not limit the width/height) /// the maximum size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height) /// return: the size of the html to be rendered within the min/max limits - private static Size MeasureHtmlByRestrictions(HtmlContainer htmlContainer, Size minSize, Size maxSize) + private static async Task MeasureHtmlByRestrictionsAsync(HtmlContainer htmlContainer, Size minSize, Size maxSize) { // use desktop created graphics to measure the HTML using (var g = Graphics.FromHwnd(IntPtr.Zero)) using (var mg = new GraphicsAdapter(g, htmlContainer.UseGdiPlusTextRendering)) { - var sizeInt = HtmlRendererUtils.MeasureHtmlByRestrictions(mg, htmlContainer.HtmlContainerInt, Utils.Convert(minSize), Utils.Convert(maxSize)); + var sizeInt = await HtmlRendererUtils.MeasureHtmlByRestrictionsAsync(mg, htmlContainer.HtmlContainerInt, Utils.Convert(minSize), Utils.Convert(maxSize)); if (maxSize.Width < 1 && sizeInt.Width > 4096) sizeInt.Width = 4096; return Utils.ConvertRound(sizeInt); diff --git a/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj b/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj index ce1a98759..c249138dc 100644 --- a/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj +++ b/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj @@ -1,126 +1,18 @@ - - + + - Debug - AnyCPU - 8.0.50727 - 2.0 - {1B058920-24B4-4140-8AE7-C8C6C38CA52D} - Library - Properties - TheArtOfDev.HtmlRenderer.WinForms - HtmlRenderer.WinForms - - - v2.0 - - - - - 2.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - + net6.0-windows + enable + disable + true - - true - full - false - bin\DebugNet20\ - DEBUG;TRACE - prompt - 4 - false - AllRules.ruleset - - - pdbonly - true - bin\ReleaseNet20\ - TRACE - prompt - 4 - AllRules.ruleset - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - Component - - - Component - - - - Component - - - - - - + - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - + + - - {fe611685-391f-4e3e-b27e-d3150e51e49b} - HtmlRenderer - + - - - \ No newline at end of file + + diff --git a/Source/HtmlRenderer.WinForms/HtmlToolTip.cs b/Source/HtmlRenderer.WinForms/HtmlToolTip.cs index 40d7ab6ca..25a3988a3 100644 --- a/Source/HtmlRenderer.WinForms/HtmlToolTip.cs +++ b/Source/HtmlRenderer.WinForms/HtmlToolTip.cs @@ -18,6 +18,7 @@ using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.WinForms.Utilities; +using Timer = System.Windows.Forms.Timer; namespace TheArtOfDev.HtmlRenderer.WinForms { diff --git a/Source/HtmlRenderer.sln b/Source/HtmlRenderer.sln index cf7aaa634..b8c8d8e78 100644 --- a/Source/HtmlRenderer.sln +++ b/Source/HtmlRenderer.sln @@ -1,118 +1,81 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30501.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33103.201 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demo", "Demo", "{E263EA16-2E6A-4269-A319-AA2F97ADA8E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer", "HtmlRenderer\HtmlRenderer.csproj", "{CC492ED6-C849-4703-BCB8-3680B2B7E014}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.Common", "Demo\Common\HtmlRenderer.Demo.Common.csproj", "{2390B71F-9400-47F4-B23A-7F2649C87D35}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.PdfSharp", "HtmlRenderer.PdfSharp\HtmlRenderer.PdfSharp.csproj", "{C0C1CD37-272F-4659-B7E6-953F86BEEEC7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.WinForms", "Demo\WinForms\HtmlRenderer.Demo.WinForms.csproj", "{8AD34FE8-8382-4A8A-B3AA-A0392ED42423}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.WinForms", "HtmlRenderer.WinForms\HtmlRenderer.WinForms.csproj", "{2847DA9E-DE87-4B4F-8597-9165ECAA6D58}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.WPF", "Demo\WPF\HtmlRenderer.Demo.WPF.csproj", "{F02E0216-4AE3-474F-9381-FCB93411CDB0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.WPF", "HtmlRenderer.WPF\HtmlRenderer.WPF.csproj", "{620AC72F-BC10-44F4-8387-250F3FE765C4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.WinForms", "HtmlRenderer.WinForms\HtmlRenderer.WinForms.csproj", "{1B058920-24B4-4140-8AE7-C8C6C38CA52D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demos", "Demos", "{CF4F888A-71EE-4EDC-99BA-8A02B4B841F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.WPF", "HtmlRenderer.WPF\HtmlRenderer.WPF.csproj", "{7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.Common", "Demos\HtmlRenderer.Demo.Common\HtmlRenderer.Demo.Common.csproj", "{22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer", "HtmlRenderer\HtmlRenderer.csproj", "{FE611685-391F-4E3E-B27E-D3150E51E49B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.WinForms", "Demos\HtmlRenderer.Demo.WinForms\HtmlRenderer.Demo.WinForms.csproj", "{1A66CBC4-0297-4928-AECC-35E3A4205609}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.PdfSharp", "HtmlRenderer.PdfSharp\HtmlRenderer.PdfSharp.csproj", "{CA249F5D-9285-40A6-B217-5889EF79FD7E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.WPF", "Demos\HtmlRenderer.Demo.WPF\HtmlRenderer.Demo.WPF.csproj", "{46175F41-83B5-4E4E-BA90-775BA6AC144A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{AA47D163-2ECF-45FB-8798-2432681396B5}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.SkiaSharp", "HtmlRenderer.SkiaSharp\HtmlRenderer.SkiaSharp.csproj", "{B1054D40-9825-42B5-9BE0-55EDE79BC00B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.SkiaSharp", "Demos\HtmlRenderer.Demo.SkiaSharp\HtmlRenderer.Demo.SkiaSharp.csproj", "{5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Debug|x86.ActiveCfg = Debug|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Release|Any CPU.Build.0 = Release|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {2390B71F-9400-47F4-B23A-7F2649C87D35}.Release|x86.ActiveCfg = Release|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Debug|x86.ActiveCfg = Debug|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Release|Any CPU.Build.0 = Release|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423}.Release|x86.ActiveCfg = Release|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Debug|x86.ActiveCfg = Debug|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Release|Any CPU.Build.0 = Release|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {F02E0216-4AE3-474F-9381-FCB93411CDB0}.Release|x86.ActiveCfg = Release|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Debug|x86.ActiveCfg = Debug|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Release|Any CPU.Build.0 = Release|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {1B058920-24B4-4140-8AE7-C8C6C38CA52D}.Release|x86.ActiveCfg = Release|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Debug|x86.ActiveCfg = Debug|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Release|Any CPU.Build.0 = Release|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {7E4E8DB5-85AD-4388-BDCB-38C6F423B8B0}.Release|x86.ActiveCfg = Release|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Debug|x86.ActiveCfg = Debug|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Release|Any CPU.Build.0 = Release|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {FE611685-391F-4E3E-B27E-D3150E51E49B}.Release|x86.ActiveCfg = Release|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Debug|x86.ActiveCfg = Debug|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Release|Any CPU.Build.0 = Release|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {CA249F5D-9285-40A6-B217-5889EF79FD7E}.Release|x86.ActiveCfg = Release|Any CPU + {CC492ED6-C849-4703-BCB8-3680B2B7E014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC492ED6-C849-4703-BCB8-3680B2B7E014}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC492ED6-C849-4703-BCB8-3680B2B7E014}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC492ED6-C849-4703-BCB8-3680B2B7E014}.Release|Any CPU.Build.0 = Release|Any CPU + {C0C1CD37-272F-4659-B7E6-953F86BEEEC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0C1CD37-272F-4659-B7E6-953F86BEEEC7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0C1CD37-272F-4659-B7E6-953F86BEEEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0C1CD37-272F-4659-B7E6-953F86BEEEC7}.Release|Any CPU.Build.0 = Release|Any CPU + {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Release|Any CPU.Build.0 = Release|Any CPU + {620AC72F-BC10-44F4-8387-250F3FE765C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {620AC72F-BC10-44F4-8387-250F3FE765C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {620AC72F-BC10-44F4-8387-250F3FE765C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {620AC72F-BC10-44F4-8387-250F3FE765C4}.Release|Any CPU.Build.0 = Release|Any CPU + {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Release|Any CPU.Build.0 = Release|Any CPU + {1A66CBC4-0297-4928-AECC-35E3A4205609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A66CBC4-0297-4928-AECC-35E3A4205609}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A66CBC4-0297-4928-AECC-35E3A4205609}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A66CBC4-0297-4928-AECC-35E3A4205609}.Release|Any CPU.Build.0 = Release|Any CPU + {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Release|Any CPU.Build.0 = Release|Any CPU + {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Release|Any CPU.Build.0 = Release|Any CPU + {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {2390B71F-9400-47F4-B23A-7F2649C87D35} = {E263EA16-2E6A-4269-A319-AA2F97ADA8E1} - {8AD34FE8-8382-4A8A-B3AA-A0392ED42423} = {E263EA16-2E6A-4269-A319-AA2F97ADA8E1} - {F02E0216-4AE3-474F-9381-FCB93411CDB0} = {E263EA16-2E6A-4269-A319-AA2F97ADA8E1} + {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} + {1A66CBC4-0297-4928-AECC-35E3A4205609} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} + {46175F41-83B5-4E4E-BA90-775BA6AC144A} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} + {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B4ABBC6A-EFE0-46EB-BEED-7C8017BA004C} EndGlobalSection EndGlobal diff --git a/Source/HtmlRenderer/Adapters/RAdapter.cs b/Source/HtmlRenderer/Adapters/RAdapter.cs index 6b13459c1..00ba36209 100644 --- a/Source/HtmlRenderer/Adapters/RAdapter.cs +++ b/Source/HtmlRenderer/Adapters/RAdapter.cs @@ -213,7 +213,7 @@ public RImage GetLoadingImage() { if (_loadImage == null) { - var stream = typeof(HtmlRendererUtils).Assembly.GetManifestResourceStream("TheArtOfDev.HtmlRenderer.Core.Utils.ImageLoad.png"); + var stream = typeof(HtmlRendererUtils).Assembly.GetManifestResourceStream("HtmlRenderer.Core.Utils.ImageLoad.png"); if (stream != null) _loadImage = ImageFromStream(stream); } @@ -227,7 +227,7 @@ public RImage GetLoadingFailedImage() { if (_errorImage == null) { - var stream = typeof(HtmlRendererUtils).Assembly.GetManifestResourceStream("TheArtOfDev.HtmlRenderer.Core.Utils.ImageError.png"); + var stream = typeof(HtmlRendererUtils).Assembly.GetManifestResourceStream("HtmlRenderer.Core.Utils.ImageError.png"); if (stream != null) _errorImage = ImageFromStream(stream); } diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs index b2cd3d984..324b5e0a7 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBox.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs @@ -441,11 +441,11 @@ public static CssBox CreateBlock(CssBox parent, HtmlTag tag = null, CssBox befor /// Performs layout of the DOM structure creating lines by set bounds restrictions. /// /// Device context to use - public void PerformLayout(RGraphics g) + public async Task PerformLayoutAsync(RGraphics g) { try { - PerformLayoutImp(g); + await PerformLayoutImpAsync(g); } catch (Exception ex) { @@ -457,7 +457,7 @@ public void PerformLayout(RGraphics g) /// Paints the fragment /// /// Device context to use - public void Paint(RGraphics g) + public async Task PaintAsync(RGraphics g) { try { @@ -489,7 +489,7 @@ public void Paint(RGraphics g) } if (visible) - PaintImp(g); + await PaintImpAsync(g); // Restore clips if (this.Position == CssConstants.Fixed) @@ -610,12 +610,12 @@ public virtual void Dispose() /// Performs layout of the DOM structure creating lines by set bounds restrictions.
///
/// Device context to use - protected virtual void PerformLayoutImp(RGraphics g) + protected virtual async Task PerformLayoutImpAsync(RGraphics g) { if (Display != CssConstants.None) { RectanglesReset(); - MeasureWordsSize(g); + await MeasureWordsSizeAsync(g); } if (IsBlock || Display == CssConstants.ListItem || Display == CssConstants.Table || Display == CssConstants.InlineTable || Display == CssConstants.TableCell) @@ -661,7 +661,7 @@ protected virtual void PerformLayoutImp(RGraphics g) //If we're talking about a table here.. if (Display == CssConstants.Table || Display == CssConstants.InlineTable) { - CssLayoutEngineTable.PerformLayout(g, this); + await CssLayoutEngineTable.PerformLayoutAsync(g, this); } else { @@ -669,13 +669,13 @@ protected virtual void PerformLayoutImp(RGraphics g) if (DomUtils.ContainsInlinesOnly(this)) { ActualBottom = Location.Y; - CssLayoutEngine.CreateLineBoxes(g, this); //This will automatically set the bottom of this block + await CssLayoutEngine.CreateLineBoxesAsync(g, this); //This will automatically set the bottom of this block } else if (_boxes.Count > 0) { foreach (var childBox in Boxes) { - childBox.PerformLayout(g); + await childBox.PerformLayoutAsync(g); } ActualRight = CalculateActualRight(); ActualBottom = MarginBottomCollapse(); @@ -694,7 +694,7 @@ protected virtual void PerformLayoutImp(RGraphics g) } ActualBottom = Math.Max(ActualBottom, Location.Y + ActualHeight); - CreateListItemBox(g); + await CreateListItemBoxAsync(g); if (!IsFixed) { @@ -707,14 +707,14 @@ protected virtual void PerformLayoutImp(RGraphics g) /// Assigns words its width and height ///
/// - internal virtual void MeasureWordsSize(RGraphics g) + internal virtual async Task MeasureWordsSizeAsync(RGraphics g) { if (!_wordsSizeMeasured) { if (BackgroundImage != CssConstants.None && _imageLoadHandler == null) { _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnImageLoadComplete); - _imageLoadHandler.LoadImage(BackgroundImage, HtmlTag != null ? HtmlTag.Attributes : null); + await _imageLoadHandler.LoadImageAsync(BackgroundImage, HtmlTag != null ? HtmlTag.Attributes : null); } MeasureWordSpacing(g); @@ -782,7 +782,7 @@ private int GetIndexForList() /// Creates the /// /// - private void CreateListItemBox(RGraphics g) + private async Task CreateListItemBoxAsync(RGraphics g) { if (Display == CssConstants.ListItem && ListStyleType != CssConstants.None) { @@ -820,7 +820,7 @@ private void CreateListItemBox(RGraphics g) _listItemBox.ParseToWords(); - _listItemBox.PerformLayoutImp(g); + await _listItemBox.PerformLayoutImpAsync(g); _listItemBox.Size = new RSize(_listItemBox.Words[0].Width, _listItemBox.Words[0].Height); } _listItemBox.Words[0].Left = Location.X - _listItemBox.Size.Width - 5; @@ -1210,7 +1210,7 @@ internal void OffsetTop(double amount) /// Paints the fragment /// /// the device to draw to - protected virtual void PaintImp(RGraphics g) + protected virtual async Task PaintImpAsync(RGraphics g) { if (Display != CssConstants.None && (Display != CssConstants.TableCell || EmptyCells != CssConstants.Hide || !IsSpaceOrEmpty)) { @@ -1254,17 +1254,17 @@ protected virtual void PaintImp(RGraphics g) foreach (CssBox b in Boxes) { if (b.Position != CssConstants.Absolute && !b.IsFixed) - b.Paint(g); + await b.PaintAsync(g); } foreach (CssBox b in Boxes) { if (b.Position == CssConstants.Absolute) - b.Paint(g); + await b.PaintAsync(g); } foreach (CssBox b in Boxes) { if (b.IsFixed) - b.Paint(g); + await b.PaintAsync(g); } if (clipped) @@ -1272,7 +1272,7 @@ protected virtual void PaintImp(RGraphics g) if (_listItemBox != null) { - _listItemBox.Paint(g); + await _listItemBox.PaintAsync(g); } } } diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs b/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs index 7bb6ed57f..4bb8ddf8a 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs @@ -12,6 +12,7 @@ using System; using System.Net; +using System.Net.Http.Json; using System.Text; using System.Threading; using TheArtOfDev.HtmlRenderer.Adapters; @@ -142,16 +143,17 @@ public override void Dispose() /// private void LoadYoutubeDataAsync(Uri uri) { - ThreadPool.QueueUserWorkItem(state => + ThreadPool.QueueUserWorkItem(async state => { try { var apiUri = new Uri(string.Format("http://gdata.youtube.com/feeds/api/videos/{0}?v=2&alt=json", uri.Segments[2])); - var client = new WebClient(); - client.Encoding = Encoding.UTF8; - client.DownloadStringCompleted += OnDownloadYoutubeApiCompleted; - client.DownloadStringAsync(apiUri); + var client = new HttpClient(); + //client.Encoding = Encoding.UTF8; + var response = await client.GetAsync(apiUri); + //client.DownloadStringCompleted += OnDownloadYoutubeApiCompleted; + //client.DownloadStringAsync(apiUri); } catch (Exception ex) { @@ -278,16 +280,17 @@ private void OnDownloadYoutubeApiCompleted(object sender, DownloadStringComplete /// private void LoadVimeoDataAsync(Uri uri) { - ThreadPool.QueueUserWorkItem(state => + ThreadPool.QueueUserWorkItem(async state => { try { var apiUri = new Uri(string.Format("http://vimeo.com/api/v2/video/{0}.json", uri.Segments[2])); - var client = new WebClient(); - client.Encoding = Encoding.UTF8; - client.DownloadStringCompleted += OnDownloadVimeoApiCompleted; - client.DownloadStringAsync(apiUri); + var client = new HttpClient(); + var response = await client.GetAsync(apiUri); + //client.Encoding = Encoding.UTF8; + //client.DownloadStringCompleted += OnDownloadVimeoApiCompleted; + //client.DownloadStringAsync(apiUri); } catch (Exception ex) { @@ -441,12 +444,12 @@ private void HandlePostApiCall(object sender) /// Paints the fragment /// /// the device to draw to - protected override void PaintImp(RGraphics g) + protected override async Task PaintImpAsync(RGraphics g) { if (_videoImageUrl != null && _imageLoadHandler == null) { _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnLoadImageComplete); - _imageLoadHandler.LoadImage(_videoImageUrl, HtmlTag != null ? HtmlTag.Attributes : null); + await _imageLoadHandler.LoadImageAsync(_videoImageUrl, HtmlTag != null ? HtmlTag.Attributes : null); } var rects = CommonUtils.GetFirstValueOrDefault(Rectangles); @@ -554,7 +557,7 @@ private void DrawPlay(RGraphics g, RRect rect) /// Assigns words its width and height /// /// the device to use - internal override void MeasureWordsSize(RGraphics g) + internal override async Task MeasureWordsSizeAsync(RGraphics g) { if (!_wordsSizeMeasured) { diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxHr.cs b/Source/HtmlRenderer/Core/Dom/CssBoxHr.cs index 8280f47c3..a6eeade17 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBoxHr.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBoxHr.cs @@ -39,7 +39,7 @@ public CssBoxHr(CssBox parent, HtmlTag tag) /// Performs layout of the DOM structure creating lines by set bounds restrictions. /// /// Device context to use - protected override void PerformLayoutImp(RGraphics g) + protected override async Task PerformLayoutImpAsync(RGraphics g) { if (Display == CssConstants.None) return; @@ -93,7 +93,7 @@ protected override void PerformLayoutImp(RGraphics g) /// Paints the fragment /// /// the device to draw to - protected override void PaintImp(RGraphics g) + protected override async Task PaintImpAsync(RGraphics g) { var offset = (HtmlContainer != null && !IsFixed) ? HtmlContainer.ScrollOffset : RPoint.Empty; var rect = new RRect(Bounds.X + offset.X, Bounds.Y + offset.Y, Bounds.Width, Bounds.Height); diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxImage.cs b/Source/HtmlRenderer/Core/Dom/CssBoxImage.cs index a33627c83..d0a547936 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBoxImage.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBoxImage.cs @@ -67,13 +67,13 @@ public RImage Image /// Paints the fragment /// /// the device to draw to - protected override void PaintImp(RGraphics g) + protected override async Task PaintImpAsync(RGraphics g) { // load image if it is in visible rectangle if (_imageLoadHandler == null) { _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnLoadImageComplete); - _imageLoadHandler.LoadImage(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null); + await _imageLoadHandler.LoadImageAsync(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null); } var rect = CommonUtils.GetFirstValueOrDefault(Rectangles); @@ -135,7 +135,7 @@ protected override void PaintImp(RGraphics g) /// Assigns words its width and height /// /// the device to use - internal override void MeasureWordsSize(RGraphics g) + internal override async Task MeasureWordsSizeAsync(RGraphics g) { if (!_wordsSizeMeasured) { @@ -144,9 +144,9 @@ internal override void MeasureWordsSize(RGraphics g) _imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnLoadImageComplete); if (this.Content != null && this.Content != CssConstants.Normal) - _imageLoadHandler.LoadImage(this.Content, HtmlTag != null ? HtmlTag.Attributes : null); + await _imageLoadHandler.LoadImageAsync(this.Content, HtmlTag != null ? HtmlTag.Attributes : null); else - _imageLoadHandler.LoadImage(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null); + await _imageLoadHandler.LoadImageAsync(GetAttribute("src"), HtmlTag != null ? HtmlTag.Attributes : null); } MeasureWordSpacing(g); diff --git a/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs b/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs index a24f9e708..498f987dc 100644 --- a/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs +++ b/Source/HtmlRenderer/Core/Dom/CssLayoutEngine.cs @@ -117,7 +117,7 @@ public static void MeasureImageSize(CssRectImage imageWord) /// /// /// - public static void CreateLineBoxes(RGraphics g, CssBox blockBox) + public static async Task CreateLineBoxesAsync(RGraphics g, CssBox blockBox) { ArgChecker.AssertArgNotNull(g, "g"); ArgChecker.AssertArgNotNull(blockBox, "blockBox"); @@ -140,7 +140,7 @@ public static void CreateLineBoxes(RGraphics g, CssBox blockBox) CssLineBox line = new CssLineBox(blockBox); //Flow words and boxes - FlowBox(g, blockBox, blockBox, limitRight, 0, startx, ref line, ref curx, ref cury, ref maxRight, ref maxBottom); + (line, curx, cury, maxRight, maxBottom) = await FlowBoxAsync(g, blockBox, blockBox, limitRight, 0, startx, line, curx, cury, maxRight, maxBottom); // if width is not restricted we need to lower it to the actual width if (blockBox.ActualRight >= 90999) @@ -239,7 +239,14 @@ public static void ApplyCellVerticalAlignment(RGraphics g, CssBox cell) /// Current y coordinate that will be the top of the next word /// Maximum right reached so far /// Maximum bottom reached so far - private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double limitRight, double linespacing, double startx, ref CssLineBox line, ref double curx, ref double cury, ref double maxRight, ref double maxbottom) + private static async Task<(CssLineBox line, double curx, double cury, double maxRight, double maxbottom)> FlowBoxAsync( + RGraphics g, + CssBox blockbox, + CssBox box, + double limitRight, + double linespacing, + double startx, + CssLineBox line, double curx, double cury, double maxRight, double maxbottom) { var startX = curx; var startY = cury; @@ -254,7 +261,7 @@ private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double lim double rightspacing = (b.Position != CssConstants.Absolute && b.Position != CssConstants.Fixed) ? b.ActualMarginRight + b.ActualBorderRightWidth + b.ActualPaddingRight : 0; b.RectanglesReset(); - b.MeasureWordsSize(g); + await b.MeasureWordsSizeAsync(g); curx += leftspacing; @@ -323,7 +330,7 @@ private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double lim } else { - FlowBox(g, blockbox, b, limitRight, linespacing, startx, ref line, ref curx, ref cury, ref maxRight, ref maxbottom); + (line, curx, cury, maxRight, maxbottom) = await FlowBoxAsync(g, blockbox, b, limitRight, linespacing, startx, line, curx, cury, maxRight, maxbottom); } curx += rightspacing; @@ -359,6 +366,8 @@ private static void FlowBox(RGraphics g, CssBox blockbox, CssBox box, double lim } box.LastHostingLineBox = line; + + return (line, curx, cury, maxRight, maxbottom); } /// diff --git a/Source/HtmlRenderer/Core/Dom/CssLayoutEngineTable.cs b/Source/HtmlRenderer/Core/Dom/CssLayoutEngineTable.cs index 79627161a..ddaf8b0f7 100644 --- a/Source/HtmlRenderer/Core/Dom/CssLayoutEngineTable.cs +++ b/Source/HtmlRenderer/Core/Dom/CssLayoutEngineTable.cs @@ -121,7 +121,7 @@ public static double GetTableSpacing(CssBox tableBox) /// /// /// - public static void PerformLayout(RGraphics g, CssBox tableBox) + public static async Task PerformLayoutAsync(RGraphics g, CssBox tableBox) { ArgChecker.AssertArgNotNull(g, "g"); ArgChecker.AssertArgNotNull(tableBox, "tableBox"); @@ -129,7 +129,7 @@ public static void PerformLayout(RGraphics g, CssBox tableBox) try { var table = new CssLayoutEngineTable(tableBox); - table.Layout(g); + await table.LayoutAsync(g); } catch (Exception ex) { @@ -144,9 +144,9 @@ public static void PerformLayout(RGraphics g, CssBox tableBox) /// Analyzes the Table and assigns values to this CssTable object. /// To be called from the constructor /// - private void Layout(RGraphics g) + private async Task LayoutAsync(RGraphics g) { - MeasureWords(_tableBox, g); + await MeasureWordsAsync(_tableBox, g); // get the table boxes into the proper fields AssignBoxKinds(); @@ -169,7 +169,7 @@ private void Layout(RGraphics g) _tableBox.PaddingLeft = _tableBox.PaddingTop = _tableBox.PaddingRight = _tableBox.PaddingBottom = "0"; //Actually layout cells! - LayoutCells(g); + await LayoutCellsAsync(g); } /// @@ -604,7 +604,7 @@ private void EnforceMinimumSize() /// Layout the cells by the calculated table layout /// /// - private void LayoutCells(RGraphics g) + private async Task LayoutCellsAsync(RGraphics g) { double startx = Math.Max(_tableBox.ClientLeft + GetHorizontalSpacing(), 0); double starty = Math.Max(_tableBox.ClientTop + GetVerticalSpacing(), 0); @@ -642,7 +642,7 @@ private void LayoutCells(RGraphics g) double width = GetCellWidth(columnIndex, cell); cell.Location = new RPoint(curx, cury); cell.Size = new RSize(width, 0f); - cell.PerformLayout(g); //That will automatically set the bottom of the cell + await cell.PerformLayoutAsync(g); //That will automatically set the bottom of the cell //Alter max bottom only if row is cell's row + cell's rowspan - 1 CssSpacingBox sb = cell as CssSpacingBox; @@ -808,14 +808,14 @@ private static int GetRowSpan(CssBox b) /// /// the box to measure /// Device to use - private static void MeasureWords(CssBox box, RGraphics g) + private static async Task MeasureWordsAsync(CssBox box, RGraphics g) { if (box != null) { foreach (var childBox in box.Boxes) { - childBox.MeasureWordsSize(g); - MeasureWords(childBox, g); + await childBox.MeasureWordsSizeAsync(g); + await MeasureWordsAsync(childBox, g); } } } diff --git a/Source/HtmlRenderer/Core/Entities/HtmlImageLoadEventArgs.cs b/Source/HtmlRenderer/Core/Entities/HtmlImageLoadEventArgs.cs index 5d5a4c5af..407da4e04 100644 --- a/Source/HtmlRenderer/Core/Entities/HtmlImageLoadEventArgs.cs +++ b/Source/HtmlRenderer/Core/Entities/HtmlImageLoadEventArgs.cs @@ -26,7 +26,7 @@ namespace TheArtOfDev.HtmlRenderer.Core.Entities /// the path to the image to load (file path or URL) /// the image to use /// optional: limit to specific rectangle in the loaded image - public delegate void HtmlImageLoadCallback(string path, Object image, RRect imageRectangle); + public delegate Task HtmlImageLoadCallbackAsync(string path, Object image, RRect imageRectangle); /// /// Invoked when an image is about to be loaded by file path, URL or inline data in 'img' element or background-image CSS style.
@@ -59,7 +59,7 @@ public sealed class HtmlImageLoadEventArgs : EventArgs /// /// Callback used to allow setting image externally and async. /// - private readonly HtmlImageLoadCallback _callback; + private readonly HtmlImageLoadCallbackAsync _callback; #endregion @@ -70,7 +70,7 @@ public sealed class HtmlImageLoadEventArgs : EventArgs /// the source of the image (file path or Uri) /// collection of all the attributes that are defined on the image element /// Callback used to allow setting image externally and async. - internal HtmlImageLoadEventArgs(string src, Dictionary attributes, HtmlImageLoadCallback callback) + internal HtmlImageLoadEventArgs(string src, Dictionary attributes, HtmlImageLoadCallbackAsync callback) { _src = src; _attributes = attributes; diff --git a/Source/HtmlRenderer/Core/Entities/HtmlRenderErrorEventArgs.cs b/Source/HtmlRenderer/Core/Entities/HtmlRenderErrorEventArgs.cs index 4696ed3b9..ee84efa41 100644 --- a/Source/HtmlRenderer/Core/Entities/HtmlRenderErrorEventArgs.cs +++ b/Source/HtmlRenderer/Core/Entities/HtmlRenderErrorEventArgs.cs @@ -39,8 +39,14 @@ public sealed class HtmlRenderErrorEventArgs : EventArgs ///
/// the type of error to report /// the error message - /// optional: the exception that occurred - public HtmlRenderErrorEventArgs(HtmlRenderErrorType type, string message, Exception exception = null) + public HtmlRenderErrorEventArgs(HtmlRenderErrorType type, string message) + { + _type = type; + _message = message; + _exception = default; + } + + public HtmlRenderErrorEventArgs(HtmlRenderErrorType type, string message, Exception exception) { _type = type; _message = message; diff --git a/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs b/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs index 2f8b0f5e6..d2b547899 100644 --- a/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs +++ b/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs @@ -41,7 +41,7 @@ internal sealed class ImageDownloader : IDisposable /// /// the web client used to download image from URL (to cancel on dispose) /// - private readonly List _clients = new List(); + private readonly List _clients = new List(); /// /// dictionary of image cache path to callbacks of download to handle multiple requests to download the same image @@ -60,7 +60,7 @@ public ImageDownloader() /// the path on disk to download the file to /// is to download the file sync or async (true-async) /// This callback will be called with local file path. If something went wrong in the download it will return null. - public void DownloadImage(Uri imageUri, string filePath, bool async, DownloadFileAsyncCallback cachedFileCallback) + public async Task DownloadImageAsync(Uri imageUri, string filePath, bool async, DownloadFileAsyncCallback cachedFileCallback) { ArgChecker.AssertArgNotNull(imageUri, "imageUri"); ArgChecker.AssertArgNotNull(cachedFileCallback, "cachedFileCallback"); @@ -86,7 +86,7 @@ public void DownloadImage(Uri imageUri, string filePath, bool async, DownloadFil if (async) ThreadPool.QueueUserWorkItem(DownloadImageFromUrlAsync, new DownloadData(imageUri, tempPath, filePath)); else - DownloadImageFromUrl(imageUri, tempPath, filePath); + await DownloadImageFromUrl(imageUri, tempPath, filePath); } } @@ -105,15 +105,16 @@ public void Dispose() /// Download the requested file in the URI to the given file path.
/// Use async sockets API to download from web, . ///
- private void DownloadImageFromUrl(Uri source, string tempPath, string filePath) + private async Task DownloadImageFromUrl(Uri source, string tempPath, string filePath) { try { - using (var client = new WebClient()) + using (var client = new HttpClient()) { _clients.Add(client); - client.DownloadFile(source, tempPath); - OnDownloadImageCompleted(client, source, tempPath, filePath, null, false); + //client.DownloadFile(source, tempPath); + var response = await client.GetStreamAsync(source); + //OnDownloadImageCompleted(client, source, tempPath, filePath, null, false); } } catch (Exception ex) @@ -127,15 +128,16 @@ private void DownloadImageFromUrl(Uri source, string tempPath, string filePath) /// Use async sockets API to download from web, . /// /// key value pair of URL and file info to download the file to - private void DownloadImageFromUrlAsync(object data) + private async void DownloadImageFromUrlAsync(object data) { var downloadData = (DownloadData)data; try { - var client = new WebClient(); + var client = new HttpClient(); _clients.Add(client); - client.DownloadFileCompleted += OnDownloadImageAsyncCompleted; - client.DownloadFileAsync(downloadData._uri, downloadData._tempPath, downloadData); + //client.DownloadFileCompleted += OnDownloadImageAsyncCompleted; + //client.DownloadFileAsync(downloadData._uri, downloadData._tempPath, downloadData); + var response = await client.GetStreamAsync(downloadData._uri); } catch (Exception ex) { @@ -231,7 +233,7 @@ private void ReleaseObjects() try { var client = _clients[0]; - client.CancelAsync(); + client.CancelPendingRequests(); client.Dispose(); _clients.RemoveAt(0); } diff --git a/Source/HtmlRenderer/Core/Handlers/ImageLoadHandler.cs b/Source/HtmlRenderer/Core/Handlers/ImageLoadHandler.cs index eb9ace4fd..5a7901163 100644 --- a/Source/HtmlRenderer/Core/Handlers/ImageLoadHandler.cs +++ b/Source/HtmlRenderer/Core/Handlers/ImageLoadHandler.cs @@ -131,11 +131,11 @@ public RRect Rectangle /// the source of the image to load /// the collection of attributes on the element to use in event /// the image object (null if failed) - public void LoadImage(string src, Dictionary attributes) + public async Task LoadImageAsync(string src, Dictionary attributes) { try { - var args = new HtmlImageLoadEventArgs(src, attributes, OnHtmlImageLoadEventCallback); + var args = new HtmlImageLoadEventArgs(src, attributes, OnHtmlImageLoadEventCallbackAsync); _htmlContainer.RaiseHtmlImageLoadEvent(args); _asyncCallback = !_htmlContainer.AvoidAsyncImagesLoading; @@ -149,7 +149,7 @@ public void LoadImage(string src, Dictionary attributes) } else { - SetImageFromPath(src); + await SetImageFromPathAsync(src); } } else @@ -183,7 +183,7 @@ public void Dispose() /// the path to the image to load (file path or uri) /// the image to load /// optional: limit to specific rectangle of the image and not all of it - private void OnHtmlImageLoadEventCallback(string path, object image, RRect imageRectangle) + private async Task OnHtmlImageLoadEventCallbackAsync(string path, object image, RRect imageRectangle) { if (!_disposed) { @@ -196,7 +196,7 @@ private void OnHtmlImageLoadEventCallback(string path, object image, RRect image } else if (!string.IsNullOrEmpty(path)) { - SetImageFromPath(path); + await SetImageFromPathAsync(path); } else { @@ -251,12 +251,12 @@ private RImage GetImageFromData(string src) /// Load image from path of image file or URL. /// /// the file path or uri to load image from - private void SetImageFromPath(string path) + private async Task SetImageFromPathAsync(string path) { var uri = CommonUtils.TryGetUri(path); if (uri != null && uri.Scheme != "file") { - SetImageFromUrl(uri); + await SetImageFromUrlAsync(uri); } else { @@ -323,7 +323,7 @@ private void LoadImageFromFile(string source) /// Create local file name in temp folder from the URI, if the file already exists use it as it has already been downloaded. /// If not download the file. /// - private void SetImageFromUrl(Uri source) + private async Task SetImageFromUrlAsync(Uri source) { var filePath = CommonUtils.GetLocalfileName(source); if (filePath.Exists && filePath.Length > 0) @@ -332,7 +332,7 @@ private void SetImageFromUrl(Uri source) } else { - _htmlContainer.GetImageDownloader().DownloadImage(source, filePath.FullName, !_htmlContainer.AvoidAsyncImagesLoading, OnDownloadImageCompleted); + await _htmlContainer.GetImageDownloader().DownloadImageAsync(source, filePath.FullName, !_htmlContainer.AvoidAsyncImagesLoading, OnDownloadImageCompleted); } } diff --git a/Source/HtmlRenderer/Core/Handlers/StylesheetLoadHandler.cs b/Source/HtmlRenderer/Core/Handlers/StylesheetLoadHandler.cs index 719f3cebb..fd3c552cb 100644 --- a/Source/HtmlRenderer/Core/Handlers/StylesheetLoadHandler.cs +++ b/Source/HtmlRenderer/Core/Handlers/StylesheetLoadHandler.cs @@ -35,12 +35,12 @@ internal static class StylesheetLoadHandler /// the attributes of the link element /// return the stylesheet string that has been loaded (null if failed or is given) /// return stylesheet data object that was provided by overwrite (null if failed or is given) - public static void LoadStylesheet(HtmlContainerInt htmlContainer, string src, Dictionary attributes, out string stylesheet, out CssData stylesheetData) + public static async Task<(string, CssData)> LoadStylesheetAsync(HtmlContainerInt htmlContainer, string src, Dictionary attributes) { ArgChecker.AssertArgNotNull(htmlContainer, "htmlContainer"); - stylesheet = null; - stylesheetData = null; + string stylesheet = null; + CssData stylesheetData = null; try { var args = new HtmlStylesheetLoadEventArgs(src, attributes); @@ -56,17 +56,19 @@ public static void LoadStylesheet(HtmlContainerInt htmlContainer, string src, Di } else if (args.SetSrc != null) { - stylesheet = LoadStylesheet(htmlContainer, args.SetSrc); + stylesheet = await LoadStylesheetAsync(htmlContainer, args.SetSrc); } else { - stylesheet = LoadStylesheet(htmlContainer, src); + stylesheet = await LoadStylesheetAsync(htmlContainer, src); } } catch (Exception ex) { htmlContainer.ReportError(HtmlRenderErrorType.CssParsing, "Exception in handling stylesheet source", ex); } + + return (stylesheet, stylesheetData); } @@ -78,7 +80,7 @@ public static void LoadStylesheet(HtmlContainerInt htmlContainer, string src, Di /// the container of the html to handle load stylesheet for /// the file path or uri to load the stylesheet from /// the stylesheet string - private static string LoadStylesheet(HtmlContainerInt htmlContainer, string src) + private static async Task LoadStylesheetAsync(HtmlContainerInt htmlContainer, string src) { var uri = CommonUtils.TryGetUri(src); if (uri == null || uri.Scheme == "file") @@ -87,7 +89,7 @@ private static string LoadStylesheet(HtmlContainerInt htmlContainer, string src) } else { - return LoadStylesheetFromUri(htmlContainer, uri); + return await LoadStylesheetFromUriAsync(htmlContainer, uri); } } @@ -127,19 +129,28 @@ private static string LoadStylesheetFromFile(HtmlContainerInt htmlContainer, str /// the container of the html to handle load stylesheet for /// the uri to download from /// the loaded stylesheet string - private static string LoadStylesheetFromUri(HtmlContainerInt htmlContainer, Uri uri) + private static async Task LoadStylesheetFromUriAsync(HtmlContainerInt htmlContainer, Uri uri) { - using (var client = new WebClient()) + using (var client = new HttpClient()) { - var stylesheet = client.DownloadString(uri); + //var stylesheet = client.DownloadString(uri); + string stylesheet = default; + var response = await client.GetAsync(uri); + if (response == null || !response.IsSuccessStatusCode) + { + htmlContainer.ReportError(HtmlRenderErrorType.CssParsing, "Error in retrieving stylesheet"); + } + try { + stylesheet = await response.Content.ReadAsStringAsync(); stylesheet = CorrectRelativeUrls(stylesheet, uri); } catch (Exception ex) { htmlContainer.ReportError(HtmlRenderErrorType.CssParsing, "Error in correcting relative URL in loaded stylesheet", ex); } + return stylesheet; } } diff --git a/Source/HtmlRenderer/Core/HtmlContainerInt.cs b/Source/HtmlRenderer/Core/HtmlContainerInt.cs index 1d71de9fb..780bcaf58 100644 --- a/Source/HtmlRenderer/Core/HtmlContainerInt.cs +++ b/Source/HtmlRenderer/Core/HtmlContainerInt.cs @@ -504,7 +504,7 @@ internal RColor SelectionBackColor /// /// 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 async Task SetHtml(string htmlSource, CssData baseCssData = null) { Clear(); if (!string.IsNullOrEmpty(htmlSource)) @@ -513,7 +513,7 @@ public void SetHtml(string htmlSource, CssData baseCssData = null) _cssData = baseCssData ?? _adapter.DefaultCssData; DomParser parser = new DomParser(_cssParser); - _root = parser.GenerateCssTree(htmlSource, this, ref _cssData); + (_root, _cssData) = await parser.GenerateCssTreeAsync(htmlSource, this, _cssData); if (_root != null) { _selectionHandler = new SelectionHandler(_root); @@ -628,7 +628,7 @@ public string GetLinkAt(RPoint location) /// Measures the bounds of box and children, recursively. /// /// Device context to draw - public void PerformLayout(RGraphics g) + public async Task PerformLayout(RGraphics g) { ArgChecker.AssertArgNotNull(g, "g"); @@ -638,14 +638,14 @@ public void PerformLayout(RGraphics g) // if width is not restricted we set it to large value to get the actual later _root.Size = new RSize(_maxSize.Width > 0 ? _maxSize.Width : 99999, 0); _root.Location = _location; - _root.PerformLayout(g); + await _root.PerformLayoutAsync(g); if (_maxSize.Width <= 0.1) { // in case the width is not restricted we need to double layout, first will find the width so second can layout by it (center alignment) _root.Size = new RSize((int)Math.Ceiling(_actualSize.Width), 0); _actualSize = RSize.Empty; - _root.PerformLayout(g); + await _root.PerformLayoutAsync(g); } if (!_loadComplete) @@ -662,7 +662,7 @@ public void PerformLayout(RGraphics g) /// Render the html using the given device. /// /// the device to use to render - public void PerformPaint(RGraphics g) + public async Task PerformPaint(RGraphics g) { ArgChecker.AssertArgNotNull(g, "g"); @@ -677,7 +677,7 @@ public void PerformPaint(RGraphics g) if (_root != null) { - _root.Paint(g); + await _root.PaintAsync(g); } g.PopClip(); diff --git a/Source/HtmlRenderer/Core/HtmlRendererUtils.cs b/Source/HtmlRenderer/Core/HtmlRendererUtils.cs index 9a414a7fe..caed22fcc 100644 --- a/Source/HtmlRenderer/Core/HtmlRendererUtils.cs +++ b/Source/HtmlRenderer/Core/HtmlRendererUtils.cs @@ -29,16 +29,16 @@ public static class HtmlRendererUtils /// the minimal size of the rendered html (zero - not limit the width/height) /// the maximum size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height) /// return: the size of the html to be rendered within the min/max limits - public static RSize MeasureHtmlByRestrictions(RGraphics g, HtmlContainerInt htmlContainer, RSize minSize, RSize maxSize) + public static async Task MeasureHtmlByRestrictionsAsync(RGraphics g, HtmlContainerInt htmlContainer, RSize minSize, RSize maxSize) { // first layout without size restriction to know html actual size - htmlContainer.PerformLayout(g); + await htmlContainer.PerformLayout(g); if (maxSize.Width > 0 && maxSize.Width < htmlContainer.ActualSize.Width) { // to allow the actual size be smaller than max we need to set max size only if it is really larger htmlContainer.MaxSize = new RSize(maxSize.Width, 0); - htmlContainer.PerformLayout(g); + await htmlContainer.PerformLayout(g); } // restrict the final size by min/max @@ -48,7 +48,7 @@ public static RSize MeasureHtmlByRestrictions(RGraphics g, HtmlContainerInt html if (finalWidth > htmlContainer.ActualSize.Width) { htmlContainer.MaxSize = new RSize(finalWidth, 0); - htmlContainer.PerformLayout(g); + await htmlContainer.PerformLayout(g); } var finalHeight = Math.Max(maxSize.Height > 0 ? Math.Min(maxSize.Height, (int)htmlContainer.ActualSize.Height) : (int)htmlContainer.ActualSize.Height, minSize.Height); @@ -72,7 +72,7 @@ public static RSize MeasureHtmlByRestrictions(RGraphics g, HtmlContainerInt html /// the max size restriction - can be empty for no restriction /// if to modify the size (width and height) by html content layout /// if to modify the height by html content layout - public static RSize Layout(RGraphics g, HtmlContainerInt htmlContainer, RSize size, RSize minSize, RSize maxSize, bool autoSize, bool autoSizeHeightOnly) + public static async Task LayoutAsync(RGraphics g, HtmlContainerInt htmlContainer, RSize size, RSize minSize, RSize maxSize, bool autoSize, bool autoSizeHeightOnly) { if (autoSize) htmlContainer.MaxSize = new RSize(0, 0); @@ -81,7 +81,7 @@ public static RSize Layout(RGraphics g, HtmlContainerInt htmlContainer, RSize si else htmlContainer.MaxSize = size; - htmlContainer.PerformLayout(g); + await htmlContainer.PerformLayout(g); RSize newSize = size; if (autoSize || autoSizeHeightOnly) @@ -92,13 +92,13 @@ public static RSize Layout(RGraphics g, HtmlContainerInt htmlContainer, RSize si { // to allow the actual size be smaller than max we need to set max size only if it is really larger htmlContainer.MaxSize = maxSize; - htmlContainer.PerformLayout(g); + await htmlContainer.PerformLayout(g); } else if (minSize.Width > 0 && minSize.Width > htmlContainer.ActualSize.Width) { // if min size is larger than the actual we need to re-layout so all 100% layouts will be correct htmlContainer.MaxSize = new RSize(minSize.Width, 0); - htmlContainer.PerformLayout(g); + await htmlContainer.PerformLayout(g); } newSize = htmlContainer.ActualSize; } @@ -113,7 +113,7 @@ public static RSize Layout(RGraphics g, HtmlContainerInt htmlContainer, RSize si // handle if changing the height of the label affects the desired width and those require re-layout if (Math.Abs(prevWidth - size.Width) > 0.01) - return Layout(g, htmlContainer, size, minSize, maxSize, false, true); + return await LayoutAsync(g, htmlContainer, size, minSize, maxSize, false, true); } } diff --git a/Source/HtmlRenderer/Core/Parse/DomParser.cs b/Source/HtmlRenderer/Core/Parse/DomParser.cs index d841485f0..4c213c34e 100644 --- a/Source/HtmlRenderer/Core/Parse/DomParser.cs +++ b/Source/HtmlRenderer/Core/Parse/DomParser.cs @@ -52,7 +52,7 @@ public DomParser(CssParser cssParser) /// the html container to use for reference resolve /// the css data to use /// the root of the generated tree - public CssBox GenerateCssTree(string html, HtmlContainerInt htmlContainer, ref CssData cssData) + public async Task<(CssBox, CssData)> GenerateCssTreeAsync(string html, HtmlContainerInt htmlContainer, CssData cssData) { var root = HtmlParser.ParseDocument(html); if (root != null) @@ -60,7 +60,7 @@ public CssBox GenerateCssTree(string html, HtmlContainerInt htmlContainer, ref C root.HtmlContainer = htmlContainer; bool cssDataChanged = false; - CascadeParseStyles(root, htmlContainer, ref cssData, ref cssDataChanged); + (cssData, cssDataChanged) = await CascadeParseStyles(root, htmlContainer, cssData, cssDataChanged); CascadeApplyStyles(root, cssData); @@ -79,7 +79,7 @@ public CssBox GenerateCssTree(string html, HtmlContainerInt htmlContainer, ref C CorrectInlineBoxesParent(root); } - return root; + return (root, cssData); } @@ -94,7 +94,7 @@ public CssBox GenerateCssTree(string html, HtmlContainerInt htmlContainer, ref C /// the html container to use for reference resolve /// the style data to fill with found styles /// check if the css data has been modified by the handled html not to change the base css data - private void CascadeParseStyles(CssBox box, HtmlContainerInt htmlContainer, ref CssData cssData, ref bool cssDataChanged) + private async Task<(CssData, bool)> CascadeParseStyles(CssBox box, HtmlContainerInt htmlContainer, CssData cssData, bool cssDataChanged) { if (box.HtmlTag != null) { @@ -105,7 +105,7 @@ private void CascadeParseStyles(CssBox box, HtmlContainerInt htmlContainer, ref CloneCssData(ref cssData, ref cssDataChanged); string stylesheet; CssData stylesheetData; - StylesheetLoadHandler.LoadStylesheet(htmlContainer, box.GetAttribute("href", string.Empty), box.HtmlTag.Attributes, out stylesheet, out stylesheetData); + (stylesheet, stylesheetData) = await StylesheetLoadHandler.LoadStylesheetAsync(htmlContainer, box.GetAttribute("href", string.Empty), box.HtmlTag.Attributes); if (stylesheet != null) _cssParser.ParseStyleSheet(cssData, stylesheet); else if (stylesheetData != null) @@ -123,8 +123,10 @@ private void CascadeParseStyles(CssBox box, HtmlContainerInt htmlContainer, ref foreach (var childBox in box.Boxes) { - CascadeParseStyles(childBox, htmlContainer, ref cssData, ref cssDataChanged); + (cssData, cssDataChanged) = await CascadeParseStyles(childBox, htmlContainer, cssData, cssDataChanged); } + + return (cssData, cssDataChanged); } diff --git a/Source/HtmlRenderer/HtmlRenderer.csproj b/Source/HtmlRenderer/HtmlRenderer.csproj index 74549b41d..f4d6558a8 100644 --- a/Source/HtmlRenderer/HtmlRenderer.csproj +++ b/Source/HtmlRenderer/HtmlRenderer.csproj @@ -1,129 +1,23 @@ - - - + + - Debug - AnyCPU - {FE611685-391F-4E3E-B27E-D3150E51E49B} - Library - Properties - TheArtOfDev.HtmlRenderer - HtmlRenderer - v2.0 - 512 + net6.0 + enable + disable - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - + - - + + + - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + PreserveNewest + + + PreserveNewest + - - - - \ No newline at end of file + + From 28506df85d6e4aaa0c2943a09cadb8ad5eedf473 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 11 Nov 2023 18:14:14 +1000 Subject: [PATCH 02/22] Adding demo app to compare, Modify PdfSharp to use Async invocation, WIP on Skia. --- .gitignore | 1 + .../HtmlRenderer.Demo.Console.csproj | 17 +++++++ .../PdfSharpCoreConverter.cs | 31 +++++++++++++ .../HtmlRenderer.Demo.Console/Program.cs | 30 +++++++++++++ .../Properties/launchSettings.json | 8 ++++ .../SampleConverterBase.cs | 45 +++++++++++++++++++ .../SkiaConverter.cs | 35 +++++++++++++++ Source/HtmlRenderer.PdfSharp/HtmlContainer.cs | 8 ++-- Source/HtmlRenderer.PdfSharp/PdfGenerator.cs | 18 ++++---- .../Adapters/GraphicsAdapter.cs | 33 ++++++++------ Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs | 14 +++--- .../HtmlRenderer.SkiaSharp/Utilities/Utils.cs | 4 +- Source/HtmlRenderer.sln | 23 ++++++---- 13 files changed, 224 insertions(+), 43 deletions(-) create mode 100644 Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj create mode 100644 Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs create mode 100644 Source/Demos/HtmlRenderer.Demo.Console/Program.cs create mode 100644 Source/Demos/HtmlRenderer.Demo.Console/Properties/launchSettings.json create mode 100644 Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs create mode 100644 Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs diff --git a/.gitignore b/.gitignore index 0fe3b09d4..c6da85118 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,4 @@ UpgradeLog*.XML *.DotSettings Source/.vs/ +/Source/Demos/HtmlRenderer.Demo.Console/Output diff --git a/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj b/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj new file mode 100644 index 000000000..d1a4691e6 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj @@ -0,0 +1,17 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + diff --git a/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs new file mode 100644 index 000000000..3e1c05114 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TheArtOfDev.HtmlRenderer.Demo.Common; +using TheArtOfDev.HtmlRenderer.PdfSharp; + +namespace HtmlRenderer.Demo.Console +{ + public class PdfSharpCoreConverter : SampleConverterBase + { + public PdfSharpCoreConverter(string sampleRunIdentifier, string basePath) : base(sampleRunIdentifier, basePath) + { + } + + public async Task GenerateSampleAsync(HtmlSample sample) + { + var config = new PdfGenerateConfig(); + + config.PageSize = PdfSharpCore.PageSize.A4; + config.MarginLeft = 0; + config.MarginRight = 0; + config.MarginTop = 0; + config.MarginBottom = 0; + + var pdf = await PdfGenerator.GeneratePdfAsync(sample.Html, config); + pdf.Save(GetSamplePath(sample)); + } + } +} diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs new file mode 100644 index 000000000..c88c1a5af --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -0,0 +1,30 @@ +using HtmlRenderer.Demo.Console; +using System.Diagnostics; +using TheArtOfDev.HtmlRenderer.Demo.Common; + +//By default, write to a sub folder 'output' +string basePath= @".\Ouput"; +if (args.Length > 0) +{ + //And if there's an output path given, use that. + basePath = args[0]; +} + +//Probably won't be running a suite of tests more than once a second, so this will do. +string runIdentifier = DateTime.Now.ToString("ddMMyyyy-hhMMss"); + +var skia = new SkiaConverter(runIdentifier, basePath); +var pdfSharp = new PdfSharpCoreConverter(runIdentifier, basePath); + +SamplesLoader.Init("Console", typeof(Program).Assembly.GetName().Version.ToString()); + +var samples = SamplesLoader.TestSamples; + +foreach (var htmlSample in samples) +{ + skia.GenerateSampleAsync(htmlSample); + await pdfSharp.GenerateSampleAsync(htmlSample); +} + + +//At this point.. there should be something!! \ No newline at end of file diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Properties/launchSettings.json b/Source/Demos/HtmlRenderer.Demo.Console/Properties/launchSettings.json new file mode 100644 index 000000000..6deb4f2a6 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Console/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "HtmlRenderer.Demo.Console": { + "commandName": "Project", + "commandLineArgs": "..\\..\\..\\Output" + } + } +} \ No newline at end of file diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs b/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs new file mode 100644 index 000000000..237af910f --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using TheArtOfDev.HtmlRenderer.Core; +using TheArtOfDev.HtmlRenderer.Core.Entities; +using TheArtOfDev.HtmlRenderer.Demo.Common; + +namespace HtmlRenderer.Demo.Console +{ + public class SampleConverterBase + { + private string _sampleRunIdentifier; + private string _thisTypeName; + private string _basePath; + + public SampleConverterBase(string sampleRunIdentifier, string basePath) + { + _sampleRunIdentifier = sampleRunIdentifier; + _basePath = basePath; + _thisTypeName = this.GetType().Name; + } + + public CssData CssData => null; + + protected string GetSamplePath(HtmlSample sample) + { + var path = Path.Combine(_basePath, _sampleRunIdentifier); + Directory.CreateDirectory(path); + return Path.Combine(path, sample.FullName + _thisTypeName + "_" + ".pdf"); + } + + internal void ImageLoad(object? sender, HtmlStylesheetLoadEventArgs e) + { + throw new NotImplementedException(); + } + + internal void StylesheetLoad(object? sender, HtmlStylesheetLoadEventArgs e) + { + throw new NotImplementedException(); + } + } +} diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs new file mode 100644 index 000000000..57e6bd774 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TheArtOfDev.HtmlRenderer.Demo.Common; +using TheArtOfDev.HtmlRenderer.SkiaSharp; + +namespace HtmlRenderer.Demo.Console +{ + public class SkiaConverter : SampleConverterBase + { + public SkiaConverter(string sampleRunIdentifier, string basePath) : base(sampleRunIdentifier, basePath) + { + } + + public async Task GenerateSampleAsync(HtmlSample sample) + { + var config = new PdfGenerateConfig(); + + config.PageSize = PageSize.A4; + + config.MarginLeft = 0; + config.MarginRight = 0; + config.MarginTop = 0; + config.MarginBottom = 0; + + using (var fileStream = File.Open(GetSamplePath(sample), FileMode.CreateNew)) + { + await PdfGenerator.GeneratePdfAsync(sample.Html, fileStream, config); + fileStream.Flush(); + } + } + } +} diff --git a/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs b/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs index 3c12f5fb4..ddc1a4891 100644 --- a/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs +++ b/Source/HtmlRenderer.PdfSharp/HtmlContainer.cs @@ -324,13 +324,13 @@ public string GetLinkAt(XPoint location) /// Measures the bounds of box and children, recursively. /// /// Device context to draw - public void PerformLayout(XGraphics g) + public async Task PerformLayoutAsync(XGraphics g) { ArgChecker.AssertArgNotNull(g, "g"); using (var ig = new GraphicsAdapter(g)) { - _htmlContainerInt.PerformLayout(ig); + await _htmlContainerInt.PerformLayout(ig); } } @@ -338,13 +338,13 @@ public void PerformLayout(XGraphics g) /// Render the html using the given device. /// /// the device to use to render - public void PerformPaint(XGraphics g) + public async Task PerformPaintAsync(XGraphics g) { ArgChecker.AssertArgNotNull(g, "g"); using (var ig = new GraphicsAdapter(g)) { - _htmlContainerInt.PerformPaint(ig); + await _htmlContainerInt.PerformPaint(ig); } } diff --git a/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs b/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs index bb3ce4bc3..c7bda8ce4 100644 --- a/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs +++ b/Source/HtmlRenderer.PdfSharp/PdfGenerator.cs @@ -68,12 +68,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 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 async Task GeneratePdfAsync(string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { var config = new PdfGenerateConfig(); config.PageSize = pageSize; config.SetMargins(margin); - return GeneratePdf(html, config, cssData, stylesheetLoad, imageLoad); + return await GeneratePdfAsync(html, config, cssData, stylesheetLoad, imageLoad); } /// @@ -85,13 +85,13 @@ 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 GeneratePdfAsync(string html, PdfGenerateConfig config, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { // 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); + await AddPdfPagesAsync(document, html, config, cssData, stylesheetLoad, imageLoad); return document; } @@ -107,12 +107,12 @@ 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 async Task AddPdfPagesAsync(PdfDocument document, string html, PageSize pageSize, int margin = 20, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { var config = new PdfGenerateConfig(); config.PageSize = pageSize; config.SetMargins(margin); - AddPdfPages(document, html, config, cssData, stylesheetLoad, imageLoad); + await AddPdfPagesAsync(document, html, config, cssData, stylesheetLoad, imageLoad); } /// @@ -125,7 +125,7 @@ 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 AddPdfPagesAsync(PdfDocument document, string html, PdfGenerateConfig config, CssData cssData = null, EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { XSize orgPageSize; // get the size of each page to layout the HTML in @@ -163,7 +163,7 @@ public static void AddPdfPages(PdfDocument document, string html, PdfGenerateCon // layout the HTML with the page width restriction to know how many pages are required using (var measure = XGraphics.CreateMeasureContext(pageSize, XGraphicsUnit.Point, XPageDirection.Downwards)) { - container.PerformLayout(measure); + await container.PerformLayoutAsync(measure); } // while there is un-rendered HTML, create another PDF page and render with proper offset for the next page @@ -180,7 +180,7 @@ public static void AddPdfPages(PdfDocument document, string html, PdfGenerateCon g.IntersectClip(new XRect(0, 0, page.Width, page.Height)); container.ScrollOffset = new XPoint(0, scrollOffset); - container.PerformPaint(g); + await container.PerformPaintAsync(g); } scrollOffset -= pageSize.Height; } diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs index 44a1c4842..e2c1e097b 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs @@ -82,6 +82,7 @@ public override void PushClip(RRect rect) public override void PushClipExclude(RRect rect) { } + bool _antiAlias = true; public override Object SetAntiAliasSmoothingMode() { /* @@ -90,7 +91,10 @@ public override Object SetAntiAliasSmoothingMode() return prevMode; */ - throw new NotImplementedException(); + + var prevMode = _antiAlias; + _antiAlias = true; + return prevMode; } public override void ReturnPreviousSmoothingMode(Object prevMode) @@ -100,7 +104,10 @@ public override void ReturnPreviousSmoothingMode(Object prevMode) { _g.SmoothingMode = (XSmoothingMode)prevMode; }*/ - throw new NotImplementedException(); + if (prevMode != null) + { + _antiAlias = (bool)prevMode; + } } public override RSize MeasureString(string str, RFont font) @@ -109,12 +116,12 @@ public override RSize MeasureString(string str, RFont font) var realFont = fontAdapter.Font; var p = new SKPaint(realFont); var width = p.MeasureText(str); - var height = realFont.Metrics.XHeight; + var height = -realFont.Metrics.Top + realFont.Metrics.Descent; if (font.Height < 0) { var descent = realFont.Metrics.Descent; - fontAdapter.SetMetrics((int)height, (int)Math.Round((height - descent + 1f))); + fontAdapter.SetMetrics((int)Math.Round(height, MidpointRounding.AwayFromZero), (int)Math.Round((height - descent + 1f), MidpointRounding.AwayFromZero)); } return new RSize(width, height); @@ -128,14 +135,14 @@ public override void MeasureString(string str, RFont font, double maxWidth, out public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl) { - //var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush; + var skiaFont = ((FontAdapter)font).Font; var p = new SKPaint { - IsAntialias = true, + IsAntialias = _antiAlias, FilterQuality = SKFilterQuality.Medium, Color = Utils.Convert(color) }; - _g.DrawText(str, (float)point.X, (float)point.Y, ((FontAdapter)font).Font, p); + _g.DrawText(str, (float)point.X, (float)point.Y - skiaFont.Metrics.Ascent, skiaFont, p); } public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation) @@ -161,7 +168,7 @@ public override void DrawLine(RPen pen, double x1, double y1, double x2, double { var p = new SKPaint { - IsAntialias = true + IsAntialias = _antiAlias }; _g.DrawLine((float)x1, (float)y1, (float)x2, (float)y2, p); @@ -171,7 +178,7 @@ public override void DrawRectangle(RPen pen, double x, double y, double width, d { var p = new SKPaint { - IsAntialias = true + IsAntialias = _antiAlias }; _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); @@ -189,7 +196,7 @@ public override void DrawRectangle(RBrush brush, double x, double y, double widt { var p = new SKPaint { - IsAntialias = true + IsAntialias = _antiAlias }; _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); } @@ -209,7 +216,7 @@ public override void DrawPath(RPen pen, RGraphicsPath path) { var p = new SKPaint { - IsAntialias = true + IsAntialias = _antiAlias }; _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, p); } @@ -219,7 +226,7 @@ public override void DrawPath(RBrush brush, RGraphicsPath path) //(XBrush)((BrushAdapter)brush).Brush, var p = new SKPaint { - IsAntialias = true + IsAntialias = _antiAlias }; _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, p); @@ -229,7 +236,7 @@ public override void DrawPolygon(RBrush brush, RPoint[] points) { var p = new SKPaint { - IsAntialias = true + IsAntialias = _antiAlias }; if (points != null && points.Length > 0) diff --git a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs index 8026a4a8b..77b94f2b6 100644 --- a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs +++ b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs @@ -186,13 +186,13 @@ public static async Task AddPdfPagesAsync( { await container.PerformLayout(g); - using (var image = s.Snapshot()) - using (var data = image.Encode(SKEncodedImageFormat.Png, 80)) - using (var stream = File.OpenWrite(Path.Combine(@"c:\temp\SkiaSharpTests", "layout.png"))) - { - // save the data to a stream - data.SaveTo(stream); - } + //using (var image = s.Snapshot()) + //using (var data = image.Encode(SKEncodedImageFormat.Png, 80)) + //using (var stream = File.OpenWrite(Path.Combine(@"c:\temp\SkiaSharpTests", "layout.png"))) + //{ + // // save the data to a stream + // data.SaveTo(stream); + //} } diff --git a/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs b/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs index ea566a0e6..e3191eae3 100644 --- a/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs +++ b/Source/HtmlRenderer.SkiaSharp/Utilities/Utils.cs @@ -69,7 +69,7 @@ public static SKSize Convert(RSize s) /// public static RRect Convert(SKRect r) { - return new RRect(r.Left, r.Bottom, r.Width, r.Height); + return new RRect(r.Left, r.Top, r.Width, r.Height); } /// @@ -77,7 +77,7 @@ public static RRect Convert(SKRect r) /// public static SKRect Convert(RRect r) { - return new SKRect((float)r.Left, (float)r.Bottom, (float)r.Width, (float)r.Height); + return new SKRect((float)r.X, (float)r.Y, (float)(r.X + r.Width), (float)(r.Y + r.Height)); } /// diff --git a/Source/HtmlRenderer.sln b/Source/HtmlRenderer.sln index b8c8d8e78..62c9988c9 100644 --- a/Source/HtmlRenderer.sln +++ b/Source/HtmlRenderer.sln @@ -5,23 +5,25 @@ VisualStudioVersion = 17.5.33103.201 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer", "HtmlRenderer\HtmlRenderer.csproj", "{CC492ED6-C849-4703-BCB8-3680B2B7E014}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.PdfSharp", "HtmlRenderer.PdfSharp\HtmlRenderer.PdfSharp.csproj", "{C0C1CD37-272F-4659-B7E6-953F86BEEEC7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.PdfSharp", "HtmlRenderer.PdfSharp\HtmlRenderer.PdfSharp.csproj", "{C0C1CD37-272F-4659-B7E6-953F86BEEEC7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.WinForms", "HtmlRenderer.WinForms\HtmlRenderer.WinForms.csproj", "{2847DA9E-DE87-4B4F-8597-9165ECAA6D58}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.WinForms", "HtmlRenderer.WinForms\HtmlRenderer.WinForms.csproj", "{2847DA9E-DE87-4B4F-8597-9165ECAA6D58}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.WPF", "HtmlRenderer.WPF\HtmlRenderer.WPF.csproj", "{620AC72F-BC10-44F4-8387-250F3FE765C4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.WPF", "HtmlRenderer.WPF\HtmlRenderer.WPF.csproj", "{620AC72F-BC10-44F4-8387-250F3FE765C4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demos", "Demos", "{CF4F888A-71EE-4EDC-99BA-8A02B4B841F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.Common", "Demos\HtmlRenderer.Demo.Common\HtmlRenderer.Demo.Common.csproj", "{22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.Common", "Demos\HtmlRenderer.Demo.Common\HtmlRenderer.Demo.Common.csproj", "{22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.WinForms", "Demos\HtmlRenderer.Demo.WinForms\HtmlRenderer.Demo.WinForms.csproj", "{1A66CBC4-0297-4928-AECC-35E3A4205609}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.WinForms", "Demos\HtmlRenderer.Demo.WinForms\HtmlRenderer.Demo.WinForms.csproj", "{1A66CBC4-0297-4928-AECC-35E3A4205609}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.WPF", "Demos\HtmlRenderer.Demo.WPF\HtmlRenderer.Demo.WPF.csproj", "{46175F41-83B5-4E4E-BA90-775BA6AC144A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.WPF", "Demos\HtmlRenderer.Demo.WPF\HtmlRenderer.Demo.WPF.csproj", "{46175F41-83B5-4E4E-BA90-775BA6AC144A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.SkiaSharp", "HtmlRenderer.SkiaSharp\HtmlRenderer.SkiaSharp.csproj", "{B1054D40-9825-42B5-9BE0-55EDE79BC00B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.SkiaSharp", "HtmlRenderer.SkiaSharp\HtmlRenderer.SkiaSharp.csproj", "{B1054D40-9825-42B5-9BE0-55EDE79BC00B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.SkiaSharp", "Demos\HtmlRenderer.Demo.SkiaSharp\HtmlRenderer.Demo.SkiaSharp.csproj", "{5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.SkiaSharp", "Demos\HtmlRenderer.Demo.SkiaSharp\HtmlRenderer.Demo.SkiaSharp.csproj", "{5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.Console", "Demos\HtmlRenderer.Demo.Console\HtmlRenderer.Demo.Console.csproj", "{7E5B30E9-34C6-4123-B8A6-B50AC078DA53}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -65,6 +67,10 @@ Global {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Release|Any CPU.ActiveCfg = Release|Any CPU {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A}.Release|Any CPU.Build.0 = Release|Any CPU + {7E5B30E9-34C6-4123-B8A6-B50AC078DA53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E5B30E9-34C6-4123-B8A6-B50AC078DA53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E5B30E9-34C6-4123-B8A6-B50AC078DA53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E5B30E9-34C6-4123-B8A6-B50AC078DA53}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -74,6 +80,7 @@ Global {1A66CBC4-0297-4928-AECC-35E3A4205609} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} {46175F41-83B5-4E4E-BA90-775BA6AC144A} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} {5E5E2495-4F7A-4C36-B7DF-3EBE71A5108A} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} + {7E5B30E9-34C6-4123-B8A6-B50AC078DA53} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B4ABBC6A-EFE0-46EB-BEED-7C8017BA004C} From df4712ee992d2e2dbcb8950d30ed4050ef467d30 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sun, 12 Nov 2023 14:15:29 +1000 Subject: [PATCH 03/22] Updated to .net 7, lots more work getting Skia font sizing more accurately matching PDFSharp, Implemented lines/rectangles in Skia. --- .../HtmlRenderer.Demo.Common.csproj | 2 +- .../HtmlRenderer.Demo.Console.csproj | 7 ++ .../HtmlRenderer.Demo.Console/Program.cs | 2 +- .../HtmlRenderer.Demo.WPF.csproj | 2 +- .../HtmlRenderer.PdfSharp.csproj | 2 +- .../Adapters/BrushAdapter.cs | 19 +++++ .../Adapters/GraphicsAdapter.cs | 85 ++++++++++--------- .../Adapters/ImageAdapter.cs | 28 ++++++ .../Adapters/PenAdapter.cs | 73 +++++++--------- .../Adapters/SkiaSharpAdapter.cs | 9 +- .../HtmlRenderer.SkiaSharp.csproj | 2 +- Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs | 12 ++- .../HtmlRenderer.WPF/HtmlRenderer.WPF.csproj | 2 +- .../HtmlRenderer.WinForms.csproj | 2 +- Source/HtmlRenderer/Core/Dom/CssUnit.cs | 2 +- .../Core/Dom/CssUnitConversion.cs | 40 +++++++++ Source/HtmlRenderer/HtmlRenderer.csproj | 2 +- 17 files changed, 194 insertions(+), 97 deletions(-) create mode 100644 Source/HtmlRenderer/Core/Dom/CssUnitConversion.cs diff --git a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj index b792bc023..0697803f8 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj +++ b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable disable diff --git a/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj b/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj index d1a4691e6..89139f338 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj +++ b/Source/Demos/HtmlRenderer.Demo.Console/HtmlRenderer.Demo.Console.csproj @@ -7,6 +7,13 @@ enable + + + + + + + diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index c88c1a5af..efdfb0e9f 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -22,7 +22,7 @@ foreach (var htmlSample in samples) { - skia.GenerateSampleAsync(htmlSample); + await skia.GenerateSampleAsync(htmlSample); await pdfSharp.GenerateSampleAsync(htmlSample); } diff --git a/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj b/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj index 0da32991f..f6a48b62a 100644 --- a/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj +++ b/Source/Demos/HtmlRenderer.Demo.WPF/HtmlRenderer.Demo.WPF.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows + net7.0-windows enable true diff --git a/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj b/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj index bc3a56924..5aa475723 100644 --- a/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj +++ b/Source/HtmlRenderer.PdfSharp/HtmlRenderer.PdfSharp.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable disable diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs index 750e016aa..09e727b8d 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/BrushAdapter.cs @@ -33,6 +33,25 @@ public enum Types public static SKBrush White = new SKBrush(SKColors.White); public static SKBrush Transparent = new SKBrush(SKColors.Transparent); + public SKPaint GetPaint() + { + if (this.Type == Types.Solid) + { + return new SKPaint + { + Color = Color + }; + } + else + { + //TODO: linear gradient. + return new SKPaint + { + Color = Color + }; + } + } + public Types Type { get; set; } public SKColor Color { get; set; } diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs index e2c1e097b..2812e6b0a 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs @@ -115,8 +115,21 @@ public override RSize MeasureString(string str, RFont font) var fontAdapter = (FontAdapter)font; var realFont = fontAdapter.Font; var p = new SKPaint(realFont); - var width = p.MeasureText(str); - var height = -realFont.Metrics.Top + realFont.Metrics.Descent; + var boundingRect = new SKRect(); + var measuredWidth = p.MeasureText(str, ref boundingRect); + + float width; + if (str == " ") + { + width = measuredWidth; + } + else + { + //The bounding rect width looks a bit odd. We'll take the midpoint until I can work out why this is. + width = measuredWidth;// measuredWidth;// (boundingRect.Width + measuredWidth) / 2; + } + + var height = realFont.Metrics.XMax + realFont.Metrics.XMin; if (font.Height < 0) { @@ -135,6 +148,7 @@ public override void MeasureString(string str, RFont font, double maxWidth, out public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl) { + //var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush; var skiaFont = ((FontAdapter)font).Font; var p = new SKPaint { @@ -166,78 +180,52 @@ public override void Dispose() public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2) { - var p = new SKPaint - { - IsAntialias = _antiAlias - }; - - _g.DrawLine((float)x1, (float)y1, (float)x2, (float)y2, p); + _g.DrawLine((float)x1, (float)y1, (float)x2, (float)y2, GetPaint(pen)); } public override void DrawRectangle(RPen pen, double x, double y, double width, double height) { - var p = new SKPaint - { - IsAntialias = _antiAlias - }; - - _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); + _g.DrawRect((float)x, (float)y, (float)width, (float)height, GetPaint(pen)); } public override void DrawRectangle(RBrush brush, double x, double y, double width, double height) { - var xBrush = ((BrushAdapter)brush).Brush; - var xTextureBrush = xBrush as XTextureBrush; - if (xTextureBrush != null) + var untypedBrush = ((BrushAdapter)brush).Brush; + if (untypedBrush is XTextureBrush textureBrush) { - xTextureBrush.DrawRectangle(_g, (float)x, (float)y, (float)width, (float)height); + textureBrush.DrawRectangle(_g, (float)x, (float)y, (float)width, (float)height); } - else + else if (untypedBrush is SKBrush skBrush) { - var p = new SKPaint - { - IsAntialias = _antiAlias - }; + var p = skBrush.GetPaint().Clone(); + p.IsAntialias = _antiAlias; _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); } } public override void DrawImage(RImage image, RRect destRect, RRect srcRect) { - _g.DrawImage(((ImageAdapter)image).Image, Utils.Convert(destRect), Utils.Convert(srcRect)); + ((ImageAdapter)image).DrawImage(_g, Utils.Convert(destRect), Utils.Convert(srcRect)); } public override void DrawImage(RImage image, RRect destRect) { - _g.DrawImage(((ImageAdapter)image).Image, Utils.Convert(destRect)); + ((ImageAdapter)image).DrawImage(_g, Utils.Convert(destRect)); } public override void DrawPath(RPen pen, RGraphicsPath path) { - var p = new SKPaint - { - IsAntialias = _antiAlias - }; - _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, p); + _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, GetPaint(pen)); } public override void DrawPath(RBrush brush, RGraphicsPath path) { - //(XBrush)((BrushAdapter)brush).Brush, - var p = new SKPaint - { - IsAntialias = _antiAlias - }; - - _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, p); + _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, GetPaint(brush)); } public override void DrawPolygon(RBrush brush, RPoint[] points) { - var p = new SKPaint - { - IsAntialias = _antiAlias - }; + var p = GetPaint(brush); if (points != null && points.Length > 0) { @@ -248,6 +236,21 @@ public override void DrawPolygon(RBrush brush, RPoint[] points) } } + private SKPaint GetPaint(RBrush brush) + { + SKBrush skBrush = (SKBrush)((BrushAdapter)brush).Brush; + var p = skBrush.GetPaint().Clone(); + p.IsAntialias = _antiAlias; + return p; + } + + private SKPaint GetPaint(RPen pen) + { + var skPaint =((PenAdapter)pen).Pen.Clone(); + skPaint.IsAntialias = _antiAlias; + return skPaint; + } + #endregion } } \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs index 171c24213..d2f8c1959 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs @@ -12,6 +12,8 @@ using SkiaSharp; using TheArtOfDev.HtmlRenderer.Adapters; +using static System.Net.Mime.MediaTypeNames; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters @@ -42,6 +44,11 @@ public SKImage Image get { return _image; } } + /// + /// Picture if this represents a structured image, eg, SVG. Not implemented yet!! + /// + public SKPicture Picture { get; set; } + public override double Width { get { return _image.Width; } @@ -56,5 +63,26 @@ public override void Dispose() { _image.Dispose(); } + + internal void DrawImage(SKCanvas canvas, SKRect dstRect, SKRect? srcRect = null) + { + + if (Picture != null) + { + //TODO: support the overload that passes a source rect. Using Matrix overload perhaps?.. + canvas.DrawPicture(Picture, dstRect.Location); + } + else + { + if (srcRect != null) + { + canvas.DrawImage(Image, dstRect, srcRect.Value); + } + else + { + canvas.DrawImage(Image, dstRect); + } + } + } } } \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs index 93582a7f2..42390d079 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/PenAdapter.cs @@ -17,37 +17,21 @@ namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters { - public class SKPen - { - public float Height {get;set;} - public float Width {get;set;} - - public SKColor Color { get; set; } - - - public SKPen(SKColor color = default) - { - Color = color; - } - - - } - - /// - /// Adapter for SkiaSharp "pens" objects for core. + /// Adapter for a Pen. Skia contains pen information in the SKPaint info, so this also wraps + /// an SKPaint. /// internal sealed class PenAdapter : RPen { /// - /// The actual WinForms brush instance. + /// The skia equiv of a 'pen'. /// - private readonly SKPen _pen; + private readonly SKPaint _pen; /// /// Init. /// - public PenAdapter(SKPen pen) + public PenAdapter(SKPaint pen) { _pen = pen; } @@ -55,50 +39,57 @@ public PenAdapter(SKPen pen) /// /// The actual WinForms brush instance. /// - public SKPen Pen + public SKPaint Pen { get { return _pen; } } public override double Width { - get { return _pen.Width; } - set { _pen.Width = (float)value; } + get { return _pen.StrokeWidth; } + set { _pen.StrokeWidth = (float)value; } } public override RDashStyle DashStyle { set { - /* - SKPathEffect.CreateDash() switch (value) { case RDashStyle.Solid: - _pen.DashStyle = XDashStyle.Solid; + _pen.PathEffect = null; + //_pen.DashStyle = XDashStyle.Solid; break; case RDashStyle.Dash: - _pen.DashStyle = XDashStyle.Dash; + _pen.PathEffect = SKPathEffect.CreateDash(new float[] { 10, 10 }, 0); if (Width < 2) - _pen.DashPattern = new[] { 4, 4d }; // better looking + { + _pen.PathEffect = SKPathEffect.CreateDash(new float[] { 4, 4 }, 0); + } + //_pen.DashStyle = XDashStyle.Dash; + //if (Width < 2) + // _pen.DashPattern = new[] { 4, 4d }; // better looking break; case RDashStyle.Dot: - _pen.DashStyle = XDashStyle.Dot; - break; - case RDashStyle.DashDot: - _pen.DashStyle = XDashStyle.DashDot; - break; - case RDashStyle.DashDotDot: - _pen.DashStyle = XDashStyle.DashDotDot; - break; - case RDashStyle.Custom: - _pen.DashStyle = XDashStyle.Custom; + _pen.PathEffect = SKPathEffect.CreateDash(new float[] { 2, 10 }, 5); + //_pen.DashStyle = XDashStyle.Dot; break; + + //TODO: support these. + //case RDashStyle.DashDot: + // _pen.DashStyle = XDashStyle.DashDot; + // break; + //case RDashStyle.DashDotDot: + // _pen.DashStyle = XDashStyle.DashDotDot; + // break; + //case RDashStyle.Custom: + // _pen.DashStyle = XDashStyle.Custom; + // break; default: - _pen.DashStyle = XDashStyle.Solid; + _pen.PathEffect = null; + //_pen.DashStyle = XDashStyle.Solid break; } - */ } } } diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs index eb913f4a1..dbdbfd1b3 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs @@ -75,7 +75,7 @@ protected override RColor GetColorInt(string colorName) protected override RPen CreatePen(RColor color) { - return new PenAdapter(new SKPen(Utils.Convert(color))); + return new PenAdapter(new SKPaint { Color = Utils.Convert(color) }); } protected override RBrush CreateSolidBrush(RColor color) @@ -134,9 +134,12 @@ protected override RFont CreateFontInt(string family, double size, RFontStyle st var typeface = SKTypeface.FromFamilyName(family, fontStyle); - var xFont = new SKFont(typeface, (float)size); + var skFont = new SKFont(typeface, (float)size); + skFont.LinearMetrics = true; + skFont.Subpixel = true; + - return new FontAdapter(xFont); + return new FontAdapter(skFont); } protected override RFont CreateFontInt(RFontFamily family, double size, RFontStyle style) diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj index f1aabe47f..5c9cbc216 100644 --- a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable enable diff --git a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs index 77b94f2b6..81b7ee9b0 100644 --- a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs +++ b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs @@ -10,12 +10,14 @@ // - Sun Tsu, // "The Art of War" +using HtmlRenderer.Core.Dom; using SkiaSharp; using System; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; using TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters; +using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; namespace TheArtOfDev.HtmlRenderer.SkiaSharp { @@ -24,6 +26,8 @@ namespace TheArtOfDev.HtmlRenderer.SkiaSharp /// public static class PdfGenerator { + public static readonly float DefaultDPI = 72f; + /// /// Adds a font mapping from to iff the is not found.
/// When the font is used in rendered html and is not found in existing @@ -100,7 +104,7 @@ public static async Task GeneratePdfAsync( EventHandler imageLoad = null) { // create PDF document to render the HTML into - var document = SKDocument.CreatePdf(outputStream, 72f); + var document = SKDocument.CreatePdf(outputStream, DefaultDPI); // add rendered PDF pages to document await AddPdfPagesAsync(document, html, config, cssData, stylesheetLoad, imageLoad); @@ -148,8 +152,10 @@ public static async Task AddPdfPagesAsync( SKSize orgPageSize; // get the size of each page to layout the HTML in if (config.PageSize == PageSize.A4) - //8.3 x 11.7 inches in Portrait - orgPageSize = new SKSize(8.3f * 72, 11.7f * 72); + //210 * 297 + orgPageSize = new SKSize( + CssUnitConversion.Convert(210f, Core.Dom.CssUnit.Milimeters, Core.Dom.CssUnit.Inches) * DefaultDPI, + CssUnitConversion.Convert(297f, Core.Dom.CssUnit.Milimeters, Core.Dom.CssUnit.Inches) * DefaultDPI); else orgPageSize = config.ManualPageSize; diff --git a/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj b/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj index 4fb1df302..aa37ed031 100644 --- a/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj +++ b/Source/HtmlRenderer.WPF/HtmlRenderer.WPF.csproj @@ -1,7 +1,7 @@  - net6.0-windows + net7.0-windows enable disable true diff --git a/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj b/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj index c249138dc..24947287d 100644 --- a/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj +++ b/Source/HtmlRenderer.WinForms/HtmlRenderer.WinForms.csproj @@ -1,7 +1,7 @@  - net6.0-windows + net7.0-windows enable disable true diff --git a/Source/HtmlRenderer/Core/Dom/CssUnit.cs b/Source/HtmlRenderer/Core/Dom/CssUnit.cs index 269893e8d..c8baa6347 100644 --- a/Source/HtmlRenderer/Core/Dom/CssUnit.cs +++ b/Source/HtmlRenderer/Core/Dom/CssUnit.cs @@ -18,7 +18,7 @@ namespace TheArtOfDev.HtmlRenderer.Core.Dom /// /// http://www.w3.org/TR/CSS21/syndata.html#length-units /// - internal enum CssUnit + public enum CssUnit { None, Ems, diff --git a/Source/HtmlRenderer/Core/Dom/CssUnitConversion.cs b/Source/HtmlRenderer/Core/Dom/CssUnitConversion.cs new file mode 100644 index 000000000..3a3c4fe54 --- /dev/null +++ b/Source/HtmlRenderer/Core/Dom/CssUnitConversion.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Data.SqlTypes; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using TheArtOfDev.HtmlRenderer.Core.Dom; + +namespace HtmlRenderer.Core.Dom +{ + public class CssUnitConversion + { + /// + /// TODO: fully implement, and use in the core as well. + /// + /// + /// Some extra thought needed here as conversion from size to px needs to know DPI. + /// + public static T Convert(T value, CssUnit from, CssUnit to) + where T : IFloatingPoint + { + var valueInMm = from switch + { + CssUnit.Milimeters => value, + CssUnit.Centimeters => value * (T)T.CreateChecked(10), + CssUnit.Inches => value * T.CreateChecked(2.54) * T.CreateChecked(10), + _ => throw new NotImplementedException(), + }; + var valueInDest = to switch + { + CssUnit.Centimeters => valueInMm * T.CreateChecked(10), + CssUnit.Milimeters => valueInMm, + CssUnit.Inches => valueInMm / T.CreateChecked(10) / T.CreateChecked(2.54), + _ => throw new NotImplementedException(), + }; + return valueInDest; + } + } +} diff --git a/Source/HtmlRenderer/HtmlRenderer.csproj b/Source/HtmlRenderer/HtmlRenderer.csproj index f4d6558a8..d72c801c0 100644 --- a/Source/HtmlRenderer/HtmlRenderer.csproj +++ b/Source/HtmlRenderer/HtmlRenderer.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 enable disable From b8bcc17908feb109bcf94d69c43493773514f52e Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sun, 12 Nov 2023 14:25:00 +1000 Subject: [PATCH 04/22] Updated remaining projects to net7, some tweaks for winforms sample. --- .../HtmlRenderer.Demo.SkiaSharp.csproj | 2 +- Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs | 10 +++++++--- .../HtmlRenderer.Demo.WinForms.csproj | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj b/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj index e10e1d767..d14493737 100644 --- a/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj +++ b/Source/Demos/HtmlRenderer.Demo.SkiaSharp/HtmlRenderer.Demo.SkiaSharp.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net7.0 enable enable diff --git a/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs b/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs index 3eb680ea2..948fb340d 100644 --- a/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs +++ b/Source/Demos/HtmlRenderer.Demo.WinForms/DemoForm.cs @@ -138,17 +138,21 @@ 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 void 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.GeneratePdfAsync(_mainControl.GetHtml(), config, null, DemoUtils.OnStylesheetLoad, HtmlRenderingHelper.OnImageLoadPdfSharp); var tmpFile = Path.GetTempFileName(); tmpFile = Path.GetFileNameWithoutExtension(tmpFile) + ".pdf"; doc.Save(tmpFile); - //Process.Start($@"C:\Windows\System32\cmd /C {tmpFile}"); + + ProcessStartInfo psi = new ProcessStartInfo(); + psi.FileName = tmpFile; + psi.UseShellExecute = true; + Process.Start(psi); } /// diff --git a/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj b/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj index 5cb8ad907..3df2670a5 100644 --- a/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj +++ b/Source/Demos/HtmlRenderer.Demo.WinForms/HtmlRenderer.Demo.WinForms.csproj @@ -2,7 +2,7 @@ WinExe - net6.0-windows + net7.0-windows disable true enable From 7ed753787640f4514e33f0d5c5b80048728f6f5a Mon Sep 17 00:00:00 2001 From: Henry Roeland Date: Wed, 29 Nov 2023 17:09:02 +0100 Subject: [PATCH 05/22] Update README.md Test github --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a21ca388..14983cc46 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ HTML Renderer [![Build status](https://ci.appveyor.com/api/projects/status/cm8xpf8ebt3hyi3e)](https://ci.appveyor.com/project/ArthurHub/html-renderer) ============= -## Help Wanted +## 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. From 1965791d3e0f84ab29dd9c64777cd1a0c854d1b7 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 12:32:07 +1000 Subject: [PATCH 06/22] Update skia dependency --- Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj index 5c9cbc216..66c1a800b 100644 --- a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -7,7 +7,7 @@ - + From f1441cf13c800d3bf83f672bdf71d5829ba78d46 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 13:05:25 +1000 Subject: [PATCH 07/22] Adding build pipeline for the net6 branch to push Core and Skia. --- .github/workflows/build-action.yml | 26 +++++++ .github/workflows/release-action.yml | 68 +++++++++++++++++++ .gitignore | 1 + .../HtmlRenderer.SkiaSharp.csproj | 24 ++++--- Source/HtmlRenderer/HtmlRenderer.csproj | 36 +++++----- Source/SharedAssemblyInfo.cs | 2 +- 6 files changed, 128 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/build-action.yml create mode 100644 .github/workflows/release-action.yml diff --git a/.github/workflows/build-action.yml b/.github/workflows/build-action.yml new file mode 100644 index 000000000..fec8c35e9 --- /dev/null +++ b/.github/workflows/build-action.yml @@ -0,0 +1,26 @@ +#Builds on every commit to main. +name: CI Build + +on: + push: + branches: [ features/net60 ] + pull_request: + branches: [ features/net60 ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x + + - name: Restore dependencies + run: dotnet restore Source/HtmlRenderer.sln + + - name: Debug Build + run: dotnet build Source/HtmlRenderer.sln --no-restore -p:Version=${{ format('0.9.{0}', github.run_number) }} \ No newline at end of file diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml new file mode 100644 index 000000000..4f748413f --- /dev/null +++ b/.github/workflows/release-action.yml @@ -0,0 +1,68 @@ +name: Release + +on: + push: + tags: [ v* ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + #Extract the version from the github tag + - id: version + run: echo "::set-output name=version_str::`echo "${{ github.ref }}" | sed -n 's/.*v\(.*\)/\1/p'`" + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + body: TODO - Document Changes for Tagged Release + draft: true + prerelease: ${{ contains(steps.version.outputs.version_str, '-') }} + + - name: Setup .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x + + - name: Restore dependencies + run: dotnet restore Source/HtmlRenderer.sln + + - name: Release Build + run: dotnet build --no-restore Source/HtmlRenderer.sln -c Release -p:Version=${{ steps.version.outputs.version_str }} + + - name: Pack + run: dotnet pack 'Source/HtmlRenderer.sln' --include-symbols --include-source --no-build -c Release -p:Version=${{ steps.version.outputs.version_str }} -p:SymbolPackageFormat=snupkg -o dist + + - name: Add Nuget Packages as Release Asset + id: upload-release-asset-core + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: dist/ThreeCS.HtmlRenderer.${{ steps.version.outputs.version_str }}.nupkg + asset_name: ThreeCS.HtmlRenderer.${{ steps.version.outputs.version_str }}.nupkg + asset_content_type: application/octet-stream + + - name: Add Skia Nuget Packages as Release Asset + id: upload-release-asset-skia + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: dist/ThreeCS.HtmlRenderer.SkiaSharp.${{ steps.version.outputs.version_str }}.nupkg + asset_name: ThreeCS.HtmlRenderer.SkiaSharp.${{ steps.version.outputs.version_str }}.nupkg + asset_content_type: application/octet-stream + + - name: Push to Nuget + run: dotnet nuget push dist/ThreeCS.HtmlRenderer.*.${{ steps.version.outputs.version_str }}.nupkg --api-key ${{ secrets.NUGET_ORG_MSTANCOMBE_KEY }} --source https://api.nuget.org/v3/index.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index c6da85118..6251d1d7f 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,4 @@ UpgradeLog*.XML Source/.vs/ /Source/Demos/HtmlRenderer.Demo.Console/Output +dist/ \ No newline at end of file diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj index 66c1a800b..e08151a07 100644 --- a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -1,17 +1,19 @@ - - net7.0 - enable - enable - + + net7.0 + enable + enable + ThreeCS.HtmlRenderer.SkiaSharp + Skia Sharp target for HtmlRenderer. + - - - + + + - - - + + + diff --git a/Source/HtmlRenderer/HtmlRenderer.csproj b/Source/HtmlRenderer/HtmlRenderer.csproj index d72c801c0..231423139 100644 --- a/Source/HtmlRenderer/HtmlRenderer.csproj +++ b/Source/HtmlRenderer/HtmlRenderer.csproj @@ -1,23 +1,25 @@ - - net7.0 - enable - disable - + + net7.0 + enable + disable + ThreeCS.HtmlRenderer + 'new' .net port of HtmlRenderer. Would be nice to merge back to arthub one day. + - - - - + + + + - - - PreserveNewest - - - PreserveNewest - - + + + PreserveNewest + + + PreserveNewest + + diff --git a/Source/SharedAssemblyInfo.cs b/Source/SharedAssemblyInfo.cs index d5336037f..ffa168faf 100644 --- a/Source/SharedAssemblyInfo.cs +++ b/Source/SharedAssemblyInfo.cs @@ -8,7 +8,7 @@ [assembly: AssemblyTitle("HTML Renderer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Open source hosted on CodePlex")] +[assembly: AssemblyCompany("Open source hosted on Github")] [assembly: AssemblyProduct("HTML Renderer")] [assembly: AssemblyCopyright("Copyright © 2008")] [assembly: AssemblyTrademark("")] From 25fe939b534d7d4d3b97d3826e46e96b7132709d Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 13:08:31 +1000 Subject: [PATCH 08/22] Add project URL --- Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj | 1 + Source/HtmlRenderer/HtmlRenderer.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj index e08151a07..595de3e7c 100644 --- a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -6,6 +6,7 @@ enable ThreeCS.HtmlRenderer.SkiaSharp Skia Sharp target for HtmlRenderer. + https://github.com/mstancombe/HTML-Renderer/tree/features/net60 diff --git a/Source/HtmlRenderer/HtmlRenderer.csproj b/Source/HtmlRenderer/HtmlRenderer.csproj index 231423139..3c53ff4d1 100644 --- a/Source/HtmlRenderer/HtmlRenderer.csproj +++ b/Source/HtmlRenderer/HtmlRenderer.csproj @@ -6,6 +6,7 @@ disable ThreeCS.HtmlRenderer 'new' .net port of HtmlRenderer. Would be nice to merge back to arthub one day. + https://github.com/mstancombe/HTML-Renderer/tree/features/net60 From bc0e5556986b5b3b4d91435179fd99a60a201704 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 13:13:05 +1000 Subject: [PATCH 09/22] Set build scripts to net 7 --- .github/workflows/build-action.yml | 2 +- .github/workflows/release-action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-action.yml b/.github/workflows/build-action.yml index fec8c35e9..47d345a3a 100644 --- a/.github/workflows/build-action.yml +++ b/.github/workflows/build-action.yml @@ -17,7 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v2 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Restore dependencies run: dotnet restore Source/HtmlRenderer.sln diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml index 4f748413f..d13576556 100644 --- a/.github/workflows/release-action.yml +++ b/.github/workflows/release-action.yml @@ -31,7 +31,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v2 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Restore dependencies run: dotnet restore Source/HtmlRenderer.sln From 0ebaf37066c94ace02a6d85592b7818505fd2283 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 13:19:27 +1000 Subject: [PATCH 10/22] Removed non cross plat rendering targets (Win/WPF) from default builds. These might just be removed permenantly in future. --- Source/HtmlRenderer.sln | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Source/HtmlRenderer.sln b/Source/HtmlRenderer.sln index 62c9988c9..ff45b31b1 100644 --- a/Source/HtmlRenderer.sln +++ b/Source/HtmlRenderer.sln @@ -40,25 +40,17 @@ Global {C0C1CD37-272F-4659-B7E6-953F86BEEEC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {C0C1CD37-272F-4659-B7E6-953F86BEEEC7}.Release|Any CPU.Build.0 = Release|Any CPU {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Debug|Any CPU.Build.0 = Debug|Any CPU {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2847DA9E-DE87-4B4F-8597-9165ECAA6D58}.Release|Any CPU.Build.0 = Release|Any CPU {620AC72F-BC10-44F4-8387-250F3FE765C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {620AC72F-BC10-44F4-8387-250F3FE765C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {620AC72F-BC10-44F4-8387-250F3FE765C4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {620AC72F-BC10-44F4-8387-250F3FE765C4}.Release|Any CPU.Build.0 = Release|Any CPU {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {22D87EB2-87B0-4A83-BB9F-CFAFD7EC26D4}.Release|Any CPU.Build.0 = Release|Any CPU {1A66CBC4-0297-4928-AECC-35E3A4205609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A66CBC4-0297-4928-AECC-35E3A4205609}.Debug|Any CPU.Build.0 = Debug|Any CPU {1A66CBC4-0297-4928-AECC-35E3A4205609}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A66CBC4-0297-4928-AECC-35E3A4205609}.Release|Any CPU.Build.0 = Release|Any CPU {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Debug|Any CPU.Build.0 = Debug|Any CPU {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {46175F41-83B5-4E4E-BA90-775BA6AC144A}.Release|Any CPU.Build.0 = Release|Any CPU {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Debug|Any CPU.Build.0 = Debug|Any CPU {B1054D40-9825-42B5-9BE0-55EDE79BC00B}.Release|Any CPU.ActiveCfg = Release|Any CPU From 8c27700b00c82d167a1f43ded9f7392268eaf8ae Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 13:39:18 +1000 Subject: [PATCH 11/22] Fix case issue --- Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx b/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx index 081d86fd7..1f6a9275d 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx +++ b/Source/Demos/HtmlRenderer.Demo.Common/Properties/Resources.resx @@ -137,6 +137,6 @@ ..\Resources\code.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\resources\browser.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\browser.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file From 688e6c5944f1cf7a7ac936a5ad12d126c03b4ce1 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 14:10:41 +1000 Subject: [PATCH 12/22] Fix issue where core not published --- .github/workflows/release-action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml index d13576556..dd99688b0 100644 --- a/.github/workflows/release-action.yml +++ b/.github/workflows/release-action.yml @@ -65,4 +65,4 @@ jobs: asset_content_type: application/octet-stream - name: Push to Nuget - run: dotnet nuget push dist/ThreeCS.HtmlRenderer.*.${{ steps.version.outputs.version_str }}.nupkg --api-key ${{ secrets.NUGET_ORG_MSTANCOMBE_KEY }} --source https://api.nuget.org/v3/index.json \ No newline at end of file + run: dotnet nuget push dist/ThreeCS.HtmlRenderer*.${{ steps.version.outputs.version_str }}.nupkg --api-key ${{ secrets.NUGET_ORG_MSTANCOMBE_KEY }} --source https://api.nuget.org/v3/index.json \ No newline at end of file From 9e6cf7b4ac4e0284127e472f25645327de3db14f Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 15:38:17 +1000 Subject: [PATCH 13/22] Image Fixes -Implemented image loading using HttpClient -Added demo resources images to console program -Moved sample test png expected location out of root c: folder. Ideally this should be something less platform specific, but haven't crossed that bridge yet. --- .../TestSamples/06.External Image.htm | 6 +- .../TestSamples/15.MaxWidth.htm | 6 +- .../TestSamples/19.Many images.htm | 98 +++++++++---------- .../PdfSharpCoreConverter.cs | 2 +- .../HtmlRenderer.Demo.Console/Program.cs | 3 + .../SampleConverterBase.cs | 18 +++- .../SkiaConverter.cs | 2 +- Source/HtmlRenderer/Adapters/RAdapter.cs | 9 +- .../Core/Handlers/ImageDownloader.cs | 79 +++++---------- Source/HtmlRenderer/Core/Utils/CommonUtils.cs | 15 --- 10 files changed, 110 insertions(+), 128 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm index 0492a426c..c4201e3f4 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm @@ -3,12 +3,12 @@
Local File:
- +
From web:
- +
GIF: @@ -21,6 +21,6 @@

- + \ No newline at end of file diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/15.MaxWidth.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/15.MaxWidth.htm index d8b98e9ba..7ead83afe 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/15.MaxWidth.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/15.MaxWidth.htm @@ -26,10 +26,10 @@

metus. Integer leo dolor, tristique a, dignissim ac, iaculis eget, elit. Donec arcu.

The image should also be limited by size because it has: style="width:90%"
- - + +

- +

diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/19.Many images.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/19.Many images.htm index a7cd2582c..4c11cb2d4 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/19.Many images.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/19.Many images.htm @@ -2,102 +2,102 @@

Contains many images that should not load until in scroll view

Image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- +

Another image

- + \ No newline at end of file diff --git a/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs index 3e1c05114..983c888e8 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs @@ -24,7 +24,7 @@ public async Task GenerateSampleAsync(HtmlSample sample) config.MarginTop = 0; config.MarginBottom = 0; - var pdf = await PdfGenerator.GeneratePdfAsync(sample.Html, config); + var pdf = await PdfGenerator.GeneratePdfAsync(sample.Html, config, imageLoad: OnImageLoaded); pdf.Save(GetSamplePath(sample)); } } diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index efdfb0e9f..65188c80e 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -22,6 +22,9 @@ foreach (var htmlSample in samples) { + ////Just doing one test here. Comment this for all of them. + //if (!htmlSample.FullName.Contains("05")) continue; + await skia.GenerateSampleAsync(htmlSample); await pdfSharp.GenerateSampleAsync(htmlSample); } diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs b/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs index 237af910f..8c1e4b2cb 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Drawing.Imaging; using System.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -21,6 +23,9 @@ public SampleConverterBase(string sampleRunIdentifier, string basePath) _sampleRunIdentifier = sampleRunIdentifier; _basePath = basePath; _thisTypeName = this.GetType().Name; + + this.OnImageLoaded += ImageLoad; + this.OnStyleLoaded += StylesheetLoad; } public CssData CssData => null; @@ -32,9 +37,18 @@ protected string GetSamplePath(HtmlSample sample) return Path.Combine(path, sample.FullName + _thisTypeName + "_" + ".pdf"); } - internal void ImageLoad(object? sender, HtmlStylesheetLoadEventArgs e) + protected EventHandler OnImageLoaded; + protected EventHandler OnStyleLoaded; + + internal void ImageLoad(object? sender, HtmlImageLoadEventArgs e) { - throw new NotImplementedException(); + //The samples use some well known image resources, so do that here. + var imageStream = DemoUtils.GetImageStream(e.Src); + if (imageStream != null) + { + e.Handled = true; + e.Callback(imageStream); + } } internal void StylesheetLoad(object? sender, HtmlStylesheetLoadEventArgs e) diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs index 57e6bd774..93a749b38 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs @@ -27,7 +27,7 @@ public async Task GenerateSampleAsync(HtmlSample sample) using (var fileStream = File.Open(GetSamplePath(sample), FileMode.CreateNew)) { - await PdfGenerator.GeneratePdfAsync(sample.Html, fileStream, config); + await PdfGenerator.GeneratePdfAsync(sample.Html, fileStream, config, imageLoad: OnImageLoaded); fileStream.Flush(); } } diff --git a/Source/HtmlRenderer/Adapters/RAdapter.cs b/Source/HtmlRenderer/Adapters/RAdapter.cs index 00ba36209..0ed190d7e 100644 --- a/Source/HtmlRenderer/Adapters/RAdapter.cs +++ b/Source/HtmlRenderer/Adapters/RAdapter.cs @@ -150,7 +150,14 @@ public RBrush GetLinearGradientBrush(RRect rect, RColor color1, RColor color2, d public RImage ConvertImage(object image) { // TODO:a remove this by creating better API. - return ConvertImageInt(image); + if (image is Stream imageStream) + { + return ImageFromStream(imageStream); + } + else + { + return ConvertImageInt(image); + } } /// diff --git a/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs b/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs index d2b547899..e2285a3ef 100644 --- a/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs +++ b/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs @@ -84,7 +84,10 @@ public async Task DownloadImageAsync(Uri imageUri, string filePath, bool async, { var tempPath = Path.GetTempFileName(); if (async) - ThreadPool.QueueUserWorkItem(DownloadImageFromUrlAsync, new DownloadData(imageUri, tempPath, filePath)); + { + //Don't wait for the image to be downloaded, just start the task. TODO: cancellation tokens. + var task = DownloadImageFromUrl(imageUri, tempPath, filePath); + } else await DownloadImageFromUrl(imageUri, tempPath, filePath); } @@ -107,84 +110,54 @@ public void Dispose() /// private async Task DownloadImageFromUrl(Uri source, string tempPath, string filePath) { + Exception downloadException = null; try { using (var client = new HttpClient()) { _clients.Add(client); - //client.DownloadFile(source, tempPath); - var response = await client.GetStreamAsync(source); - //OnDownloadImageCompleted(client, source, tempPath, filePath, null, false); + var response = await client.GetAsync(source); + response.EnsureSuccessStatusCode(); + OnDownloadImageCompleted(response, source, tempPath, filePath, downloadException, false); } } - catch (Exception ex) + catch (TaskCanceledException) { - OnDownloadImageCompleted(null, source, tempPath, filePath, ex, false); - } - } - - /// - /// Download the requested file in the URI to the given file path.
- /// Use async sockets API to download from web, . - ///
- /// key value pair of URL and file info to download the file to - private async void DownloadImageFromUrlAsync(object data) - { - var downloadData = (DownloadData)data; - try - { - var client = new HttpClient(); - _clients.Add(client); - //client.DownloadFileCompleted += OnDownloadImageAsyncCompleted; - //client.DownloadFileAsync(downloadData._uri, downloadData._tempPath, downloadData); - var response = await client.GetStreamAsync(downloadData._uri); + //If the download was cancelled, don't fire anything + return; } catch (Exception ex) { - OnDownloadImageCompleted(null, downloadData._uri, downloadData._tempPath, downloadData._filePath, ex, false); - } - } - - /// - /// On download image complete to local file.
- /// If the download canceled do nothing, if failed report error. - ///
- private void OnDownloadImageAsyncCompleted(object sender, AsyncCompletedEventArgs e) - { - var downloadData = (DownloadData)e.UserState; - try - { - using (var client = (WebClient)sender) - { - client.DownloadFileCompleted -= OnDownloadImageAsyncCompleted; - OnDownloadImageCompleted(client, downloadData._uri, downloadData._tempPath, downloadData._filePath, e.Error, e.Cancelled); - } - } - catch (Exception ex) - { - OnDownloadImageCompleted(null, downloadData._uri, downloadData._tempPath, downloadData._filePath, ex, false); + downloadException = ex; } + + } /// /// Checks if the file was downloaded and raises the cachedFileCallback from /// - private void OnDownloadImageCompleted(WebClient client, Uri source, string tempPath, string filePath, Exception error, bool cancelled) + private async Task OnDownloadImageCompleted(HttpResponseMessage response, Uri source, string tempPath, string filePath, Exception error, bool cancelled) { if (!cancelled) { if (error == null) { - var contentType = CommonUtils.GetResponseContentType(client); - if (contentType == null || !contentType.StartsWith("image", StringComparison.OrdinalIgnoreCase)) + var mediaType = response.Content.Headers.ContentType?.MediaType; + if (mediaType == null || !mediaType.StartsWith("image", StringComparison.OrdinalIgnoreCase)) { - error = new Exception("Failed to load image, not image content type: " + contentType); + error = new Exception("Failed to load image, not image content type: " + mediaType); } - } + //Save the content to the temp path. + using (var responseStream = response.Content.ReadAsStream()) + { + using (var tempFile = File.OpenWrite(tempPath)) + { + await responseStream.CopyToAsync(tempFile); + } + } - if (error == null) - { if (File.Exists(tempPath)) { try diff --git a/Source/HtmlRenderer/Core/Utils/CommonUtils.cs b/Source/HtmlRenderer/Core/Utils/CommonUtils.cs index 12c03ad32..fa0827706 100644 --- a/Source/HtmlRenderer/Core/Utils/CommonUtils.cs +++ b/Source/HtmlRenderer/Core/Utils/CommonUtils.cs @@ -191,21 +191,6 @@ public static FileInfo TryGetFileInfo(string path) return null; } - /// - /// Get web client response content type. - /// - /// the web client to get the response content type from - /// response content type or null - public static string GetResponseContentType(WebClient client) - { - foreach (string header in client.ResponseHeaders) - { - if (header.Equals("Content-Type", StringComparison.InvariantCultureIgnoreCase)) - return client.ResponseHeaders[header]; - } - return null; - } - /// /// Gets the representation of the online uri on the local disk. /// From 6541e80f03fb3a1d08ab173810d3114ac59ec277 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sat, 9 Dec 2023 16:53:16 +1000 Subject: [PATCH 14/22] Add SVG Support --- .../TestSamples/06.External Image.htm | 5 ++ .../Adapters/ImageAdapter.cs | 46 +++++++++++++------ .../Adapters/SkiaSharpAdapter.cs | 15 +++++- .../HtmlRenderer.SkiaSharp.csproj | 1 + 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm index c4201e3f4..1b8f97f56 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/06.External Image.htm @@ -10,6 +10,11 @@
+
+ From svg: +
+ +
GIF:
diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs index d2f8c1959..12e9348c8 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs @@ -12,8 +12,7 @@ using SkiaSharp; using TheArtOfDev.HtmlRenderer.Adapters; -using static System.Net.Mime.MediaTypeNames; -using TheArtOfDev.HtmlRenderer.SkiaSharp.Utilities; +using SKSvg = SkiaSharp.Extended.Svg.SKSvg; namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters @@ -24,9 +23,11 @@ namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters internal sealed class ImageAdapter : RImage { /// - /// the underline win-forms image. + /// the underlying image. This may be either a bitmap (_image) or an svg (_svg). /// - private readonly SKImage _image; + private SKImage _image; + + private readonly SKSvg? _svg; /// /// Initializes a new instance of the class. @@ -36,51 +37,70 @@ public ImageAdapter(SKImage image) _image = image; } + public ImageAdapter(SKSvg svg) + { + _svg = svg; + } + + /// /// the underline SkiaSharp image. /// public SKImage Image { - get { return _image; } + get + { + if (_image == null && _svg != null) + { + //Render the image from the picture, as this is being used in a texture brush. + _image = SKImage.FromBitmap(new SKBitmap((int)_svg.CanvasSize.Width, (int)_svg.CanvasSize.Height)); + } + return _image; + } } /// - /// Picture if this represents a structured image, eg, SVG. Not implemented yet!! + /// Picture if this represents a structured image, eg, SVG. /// public SKPicture Picture { get; set; } public override double Width { - get { return _image.Width; } + get { return _image?.Width ?? ((int?)_svg?.CanvasSize.Width) ?? 0; } } public override double Height { - get { return _image.Height; } + get { return _image?.Height ?? ((int?)_svg?.CanvasSize.Height) ?? 0; } } public override void Dispose() { - _image.Dispose(); + _image?.Dispose(); + _svg?.Picture?.Dispose(); } internal void DrawImage(SKCanvas canvas, SKRect dstRect, SKRect? srcRect = null) { - if (Picture != null) + if (_svg != null) { //TODO: support the overload that passes a source rect. Using Matrix overload perhaps?.. - canvas.DrawPicture(Picture, dstRect.Location); + var matrix = SKMatrix.CreateTranslation(dstRect.Left, dstRect.Top); + matrix.ScaleX = dstRect.Width / _svg.Picture.CullRect.Width; + matrix.ScaleY = dstRect.Height / _svg.Picture.CullRect.Height; + + canvas.DrawPicture(_svg.Picture, ref matrix); } else { if (srcRect != null) { - canvas.DrawImage(Image, dstRect, srcRect.Value); + canvas.DrawImage(_image, dstRect, srcRect.Value); } else { - canvas.DrawImage(Image, dstRect); + canvas.DrawImage(_image, dstRect); } } } diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs index dbdbfd1b3..a3a112030 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs @@ -114,8 +114,19 @@ protected override RImage ConvertImageInt(object image) protected override RImage ImageFromStreamInt(Stream memoryStream) { - SKBitmap bitmap = SKBitmap.Decode(memoryStream); - return new ImageAdapter(SKImage.FromBitmap(bitmap)); + var image = SKImage.FromEncodedData(memoryStream); + if (image == null) + { + //Maybe an SVG? In future, let's make the html renderer pass media type if it's available. + memoryStream.Seek(0, SeekOrigin.Begin); + var skSvg = new global::SkiaSharp.Extended.Svg.SKSvg(); + skSvg.Load(memoryStream); + return new ImageAdapter(skSvg); + } + else + { + return new ImageAdapter(image); + } } protected override RFont CreateFontInt(string family, double size, RFontStyle style) diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj index 595de3e7c..56244e714 100644 --- a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -11,6 +11,7 @@ + From f4f084285c1a8acc3bb187c03df4a7cdb51341ef Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Sun, 10 Dec 2023 13:23:03 +1000 Subject: [PATCH 15/22] Fix issue where images not waiting for load. --- Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs | 26 +++++++-------- .../Core/Handlers/ImageDownloader.cs | 32 ++++++++++++------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs index 81b7ee9b0..357bbf5c7 100644 --- a/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs +++ b/Source/HtmlRenderer.SkiaSharp/PdfGenerator.cs @@ -149,6 +149,11 @@ public static async Task AddPdfPagesAsync( EventHandler stylesheetLoad = null, EventHandler imageLoad = null) { + //TODO: https://github.com/mono/SkiaSharp/issues/848 + //Perhaps we could subset the fonts so the pdf's generated weren't as large. Success has been reported + //with this aproach: + //https://github.com/mono/SkiaSharp/issues/848#issuecomment-1013134644 + SKSize orgPageSize; // get the size of each page to layout the HTML in if (config.PageSize == PageSize.A4) @@ -186,34 +191,26 @@ public static async Task AddPdfPagesAsync( container.MarginTop = config.MarginTop; // layout the HTML with the page width restriction to know how many pages are required - var docImageInfo = new SKImageInfo((int)orgPageSize.Width, (int)orgPageSize.Height * 7); + var docImageInfo = new SKImageInfo((int)pageSize.Width, (int)pageSize.Height); using (var s = SKSurface.Create(docImageInfo)) using (var g = s.Canvas) { await container.PerformLayout(g); - - //using (var image = s.Snapshot()) - //using (var data = image.Encode(SKEncodedImageFormat.Png, 80)) - //using (var stream = File.OpenWrite(Path.Combine(@"c:\temp\SkiaSharpTests", "layout.png"))) - //{ - // // save the data to a stream - // data.SaveTo(stream); - //} } - // while there is un-rendered HTML, create another PDF page and render with proper offset for the next page double scrollOffset = 0; while (scrollOffset > -container.ActualSize.Height) { - using (var page = document.BeginPage(orgPageSize.Width, orgPageSize.Height)) + using (var pageCanvas = document.BeginPage(orgPageSize.Width, orgPageSize.Height)) { - page.ClipRect(new SKRect(config.MarginLeft, config.MarginTop, pageSize.Width, pageSize.Height)); + pageCanvas.ClipRect(new SKRect(config.MarginLeft, config.MarginTop, config.MarginLeft + pageSize.Width, config.MarginTop + pageSize.Height)); container.ScrollOffset = new SKPoint(0, (float)scrollOffset); - await container.PerformPaint(page); + await container.PerformPaint(pageCanvas); + pageCanvas.Flush(); document.EndPage(); } scrollOffset -= pageSize.Height; @@ -236,6 +233,9 @@ public static async Task AddPdfPagesAsync( /// private static void HandleLinks(SKDocument document, HtmlContainer container, SKSize orgPageSize, SKSize pageSize) { + //TODO: Annotations might work for this.. + //https://learn.microsoft.com/en-us/dotnet/api/skiasharp.skcanvas.drawannotation?view=skiasharp-2.88 + /* foreach (var link in container.GetLinks()) { diff --git a/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs b/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs index e2285a3ef..3cbfb13e8 100644 --- a/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs +++ b/Source/HtmlRenderer/Core/Handlers/ImageDownloader.cs @@ -106,39 +106,47 @@ public void Dispose() /// /// Download the requested file in the URI to the given file path.
- /// Use async sockets API to download from web, . ///
private async Task DownloadImageFromUrl(Uri source, string tempPath, string filePath) { - Exception downloadException = null; try { using (var client = new HttpClient()) { _clients.Add(client); var response = await client.GetAsync(source); - response.EnsureSuccessStatusCode(); - OnDownloadImageCompleted(response, source, tempPath, filePath, downloadException, false); + await OnDownloadImageCompleted(response, source, tempPath, filePath); + _clients.Remove(client); } } catch (TaskCanceledException) { - //If the download was cancelled, don't fire anything + //If the download was cancelled, don't bother raising this. return; } - catch (Exception ex) - { - downloadException = ex; - } - - } /// /// Checks if the file was downloaded and raises the cachedFileCallback from /// - private async Task OnDownloadImageCompleted(HttpResponseMessage response, Uri source, string tempPath, string filePath, Exception error, bool cancelled) + private async Task OnDownloadImageCompleted(HttpResponseMessage response, Uri source, string tempPath, string filePath) { + Exception error = null; + bool cancelled = false; + + try + { + response.EnsureSuccessStatusCode(); + } + catch (TaskCanceledException) + { + cancelled = true; + } + catch(Exception ex) + { + error = ex; + } + if (!cancelled) { if (error == null) From ca1c472cf12142a29882c9ecadb8b59697944641 Mon Sep 17 00:00:00 2001 From: Michael Stancombe Date: Mon, 11 Dec 2023 07:49:22 +1000 Subject: [PATCH 16/22] Add basic page-break 'always' support, remove default page breaking on printed h1 element. --- .../HtmlRenderer.Demo.Common.csproj | 4 ++ .../TestSamples/36.Breaking Pages Divs.htm | 17 +++++++ .../HtmlRenderer.Demo.Console/Program.cs | 6 +-- Source/HtmlRenderer/Core/CssDefaults.cs | 1 - Source/HtmlRenderer/Core/Dom/CssBox.cs | 44 +++++++++++++++---- .../HtmlRenderer/Core/Dom/CssBoxProperties.cs | 20 +++++++++ .../HtmlRenderer/Core/Utils/CssConstants.cs | 1 + Source/HtmlRenderer/Core/Utils/CssUtils.cs | 10 +++++ 8 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 Source/Demos/HtmlRenderer.Demo.Common/TestSamples/36.Breaking Pages Divs.htm diff --git a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj index 0697803f8..12bf0c0e8 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj +++ b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj @@ -78,6 +78,7 @@ +
@@ -279,6 +280,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/36.Breaking Pages Divs.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/36.Breaking Pages Divs.htm new file mode 100644 index 000000000..55522a541 --- /dev/null +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/36.Breaking Pages Divs.htm @@ -0,0 +1,17 @@ + + +
+ This is on page 1. +
+ +
+ This is on page 2. +
+
+ Also on page 2. +
+
+ On page 3, because the previous section pushed it. +
+ + \ No newline at end of file diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index 65188c80e..a615c8d20 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -11,7 +11,7 @@ } //Probably won't be running a suite of tests more than once a second, so this will do. -string runIdentifier = DateTime.Now.ToString("ddMMyyyy-hhMMss"); +string runIdentifier = DateTime.Now.ToString("ddMMyyyy-hhmmss"); var skia = new SkiaConverter(runIdentifier, basePath); var pdfSharp = new PdfSharpCoreConverter(runIdentifier, basePath); @@ -23,10 +23,10 @@ foreach (var htmlSample in samples) { ////Just doing one test here. Comment this for all of them. - //if (!htmlSample.FullName.Contains("05")) continue; + if (!htmlSample.FullName.Contains("02", StringComparison.OrdinalIgnoreCase)) continue; await skia.GenerateSampleAsync(htmlSample); - await pdfSharp.GenerateSampleAsync(htmlSample); + //await pdfSharp.GenerateSampleAsync(htmlSample); } diff --git a/Source/HtmlRenderer/Core/CssDefaults.cs b/Source/HtmlRenderer/Core/CssDefaults.cs index bb313c223..9fb24e34d 100644 --- a/Source/HtmlRenderer/Core/CssDefaults.cs +++ b/Source/HtmlRenderer/Core/CssDefaults.cs @@ -99,7 +99,6 @@ internal static class CssDefaults *[DIR=""rtl""] { direction: rtl; unicode-bidi: embed } @media print { - h1 { page-break-before: always } h1, h2, h3, h4, h5, h6 { page-break-after: avoid } ul, ol, dl { page-break-before: avoid } diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs index 324b5e0a7..ef97fed42 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBox.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Text; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core.Entities; @@ -652,9 +653,25 @@ protected virtual async Task PerformLayoutImpAsync(RGraphics g) else { left = ContainingBlock.Location.X + ContainingBlock.ActualPaddingLeft + ActualMarginLeft + ContainingBlock.ActualBorderLeftWidth; - top = (prevSibling == null && ParentBox != null ? ParentBox.ClientTop : ParentBox == null ? Location.Y : 0) + MarginTopCollapse(prevSibling) + (prevSibling != null ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth : 0); + top = (prevSibling == null && ParentBox != null + ? ParentBox.ClientTop + : ParentBox == null + ? Location.Y + : 0) + + MarginTopCollapse(prevSibling) + + (prevSibling != null + ? prevSibling.ActualBottom + prevSibling.ActualBorderBottomWidth + : 0); + Location = new RPoint(left, top); - ActualBottom = top; + + if (this.PageBreakBefore == CssConstants.Always || prevSibling?.PageBreakAfter == CssConstants.Always) + { + this.BreakPage(true); + } + + //Start with the assumption this is zero height. + ActualBottom = Location.Y; } } @@ -1118,24 +1135,33 @@ protected double MarginTopCollapse(CssBoxProperties prevSibling) return value; } - public bool BreakPage() + public bool BreakPage(bool force = false) { var container = this.HtmlContainer; + bool shouldBreakToNextPage; + if (this.Size.Height >= container.PageSize.Height) - return false; + shouldBreakToNextPage = false; + else + { + var remTop = (this.Location.Y - container.MarginTop) % container.PageSize.Height; + var remBottom = (this.ActualBottom - container.MarginTop) % container.PageSize.Height; - var remTop = (this.Location.Y - container.MarginTop) % container.PageSize.Height; - var remBottom = (this.ActualBottom - container.MarginTop) % container.PageSize.Height; + shouldBreakToNextPage = remTop > remBottom; + } - if (remTop > remBottom) + if (force || shouldBreakToNextPage) { + var remTop = (this.Location.Y - container.MarginTop) % container.PageSize.Height; var diff = container.PageSize.Height - remTop; this.Location = new RPoint(this.Location.X, this.Location.Y + diff + 1); return true; } - - return false; + else + { + return false; + } } /// diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs index 7abfb53ed..8f2487966 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs @@ -84,6 +84,8 @@ internal abstract class CssBoxProperties private string _paddingBottom = "0"; private string _paddingRight = "0"; private string _paddingTop = "0"; + private string _pageBreakBefore = CssConstants.Auto; + private string _pageBreakAfter = CssConstants.Auto; private string _pageBreakInside = CssConstants.Auto; private string _right; private string _textAlign = string.Empty; @@ -400,6 +402,24 @@ public string PaddingTop } } + public string PageBreakBefore + { + get { return _pageBreakBefore; } + set + { + _pageBreakBefore = value; + } + } + + public string PageBreakAfter + { + get { return _pageBreakAfter; } + set + { + _pageBreakAfter = value; + } + } + public string PageBreakInside { get { return _pageBreakInside; } diff --git a/Source/HtmlRenderer/Core/Utils/CssConstants.cs b/Source/HtmlRenderer/Core/Utils/CssConstants.cs index 655849256..46b7f31ef 100644 --- a/Source/HtmlRenderer/Core/Utils/CssConstants.cs +++ b/Source/HtmlRenderer/Core/Utils/CssConstants.cs @@ -18,6 +18,7 @@ namespace TheArtOfDev.HtmlRenderer.Core.Utils internal static class CssConstants { public const string Absolute = "absolute"; + public const string Always = "always"; public const string Auto = "auto"; public const string Avoid = "avoid"; public const string Baseline = "baseline"; diff --git a/Source/HtmlRenderer/Core/Utils/CssUtils.cs b/Source/HtmlRenderer/Core/Utils/CssUtils.cs index 69f949390..5d9056a4a 100644 --- a/Source/HtmlRenderer/Core/Utils/CssUtils.cs +++ b/Source/HtmlRenderer/Core/Utils/CssUtils.cs @@ -122,6 +122,10 @@ public static string GetPropertyValue(CssBox cssBox, string propName) return cssBox.PaddingRight; case "padding-top": return cssBox.PaddingTop; + case "page-break-before": + return cssBox.PageBreakBefore; + case "page-break-after": + return cssBox.PageBreakAfter; case "page-break-inside": return cssBox.PageBreakInside; case "left": @@ -294,6 +298,12 @@ public static void SetPropertyValue(CssBox cssBox, string propName, string value case "padding-top": cssBox.PaddingTop = value; break; + case "page-break-before": + cssBox.PageBreakBefore = value; + break; + case "page-break-after": + cssBox.PageBreakAfter = value; + break; case "page-break-inside": cssBox.PageBreakInside = value; break; From 373fc09df9fe860c898bdcb789f03796a13b5f26 Mon Sep 17 00:00:00 2001 From: Alby Blyth Date: Tue, 19 Dec 2023 11:42:26 +1000 Subject: [PATCH 17/22] Swap skia svg package --- .../Adapters/ImageAdapter.cs | 24 +++++++------------ .../Adapters/SkiaSharpAdapter.cs | 2 +- .../HtmlRenderer.SkiaSharp.csproj | 2 +- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs index 12e9348c8..1c7575ff7 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/ImageAdapter.cs @@ -12,8 +12,7 @@ using SkiaSharp; using TheArtOfDev.HtmlRenderer.Adapters; -using SKSvg = SkiaSharp.Extended.Svg.SKSvg; - +using Svg.Skia; namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters { @@ -25,8 +24,7 @@ internal sealed class ImageAdapter : RImage /// /// the underlying image. This may be either a bitmap (_image) or an svg (_svg). /// - private SKImage _image; - + private SKImage? _image; private readonly SKSvg? _svg; /// @@ -42,36 +40,31 @@ public ImageAdapter(SKSvg svg) _svg = svg; } - /// /// the underline SkiaSharp image. /// public SKImage Image { get - { - if (_image == null && _svg != null) + { + if (_image == null && _svg?.Picture != null) { //Render the image from the picture, as this is being used in a texture brush. - _image = SKImage.FromBitmap(new SKBitmap((int)_svg.CanvasSize.Width, (int)_svg.CanvasSize.Height)); + _image = SKImage.FromPicture(_svg.Picture, _svg.Picture.CullRect.Size.ToSizeI()); } + return _image; } } - /// - /// Picture if this represents a structured image, eg, SVG. - /// - public SKPicture Picture { get; set; } - public override double Width { - get { return _image?.Width ?? ((int?)_svg?.CanvasSize.Width) ?? 0; } + get { return _image?.Width ?? _svg?.Picture?.CullRect.Width ?? 0; } } public override double Height { - get { return _image?.Height ?? ((int?)_svg?.CanvasSize.Height) ?? 0; } + get { return _image?.Height ?? _svg?.Picture?.CullRect.Height ?? 0; } } public override void Dispose() @@ -82,7 +75,6 @@ public override void Dispose() internal void DrawImage(SKCanvas canvas, SKRect dstRect, SKRect? srcRect = null) { - if (_svg != null) { //TODO: support the overload that passes a source rect. Using Matrix overload perhaps?.. diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs index a3a112030..d09188391 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs @@ -119,7 +119,7 @@ protected override RImage ImageFromStreamInt(Stream memoryStream) { //Maybe an SVG? In future, let's make the html renderer pass media type if it's available. memoryStream.Seek(0, SeekOrigin.Begin); - var skSvg = new global::SkiaSharp.Extended.Svg.SKSvg(); + var skSvg = new Svg.Skia.SKSvg(); skSvg.Load(memoryStream); return new ImageAdapter(skSvg); } diff --git a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj index 56244e714..7967eb004 100644 --- a/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj +++ b/Source/HtmlRenderer.SkiaSharp/HtmlRenderer.SkiaSharp.csproj @@ -11,7 +11,7 @@ - + From 2c135203f65e78c719b847cea672da1a3c5f3554 Mon Sep 17 00:00:00 2001 From: Alby Blyth Date: Tue, 19 Dec 2023 17:49:49 +1000 Subject: [PATCH 18/22] Rename corner radius to border radius --- .../HtmlRenderer.Demo.Common/DemoUtils.cs | 4 +- .../Samples/07.Additional features.htm | 30 ++-- .../TestSamples/16.Borders.htm | 12 +- Source/HtmlRenderer/Core/Dom/CssBox.cs | 2 +- .../HtmlRenderer/Core/Dom/CssBoxProperties.cs | 143 +++++++++--------- .../Core/Handlers/BordersDrawHandler.cs | 56 +++---- Source/HtmlRenderer/Core/Utils/CssUtils.cs | 40 ++--- Source/HtmlRenderer/Core/Utils/RenderUtils.cs | 40 ++--- 8 files changed, 166 insertions(+), 161 deletions(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Common/DemoUtils.cs b/Source/Demos/HtmlRenderer.Demo.Common/DemoUtils.cs index f695886bc..61aece7b5 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/DemoUtils.cs +++ b/Source/Demos/HtmlRenderer.Demo.Common/DemoUtils.cs @@ -74,8 +74,8 @@ public static string GetStylesheet(string src) a:link { text-decoration: none; } a:hover { text-decoration: underline; } .gray { color:gray; } - .example { background-color:#efefef; corner-radius:5px; padding:0.5em; } - .whitehole { background-color:white; corner-radius:10px; padding:15px; } + .example { background-color:#efefef; border-radius:5px; padding:0.5em; } + .whitehole { background-color:white; border-radius:10px; padding:15px; } .caption { font-size: 1.1em } .comment { color: green; margin-bottom: 5px; margin-left: 3px; } .comment2 { color: green; }"; diff --git a/Source/Demos/HtmlRenderer.Demo.Common/Samples/07.Additional features.htm b/Source/Demos/HtmlRenderer.Demo.Common/Samples/07.Additional features.htm index 764e9511c..bffd85781 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/Samples/07.Additional features.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/Samples/07.Additional features.htm @@ -27,16 +27,16 @@ vertical-align: middle; } - .c1 { corner-radius: 0px; } + .c1 { border-radius: 0px; } - .c2 { corner-radius: 10px; } + .c2 { border-radius: 10px; } - .c3 { corner-radius: 0px 10px 10px 0px; } + .c3 { border-radius: 0px 10px 10px 0px; } - .c4 { corner-radius: 18px; } + .c4 { border-radius: 18px; } .c5 { - corner-radius: 10px; + border-radius: 10px; border: outset #BBBB00 2px; } @@ -121,15 +121,15 @@

In this renderer, the rounded corners are achieved by adding this CSS properties:

    -
  • corner-ne-radius: (length) Indicates the radius of the north-east corner. +
  • border-top-right-radius: (length) Indicates the radius of the top-right corner. Not ineritted
  • -
  • corner-se-radius: (length) Indicates the radius of the south-east corner. +
  • border-bottom-right-radius: (length) Indicates the radius of the bottom-right corner. Not ineritted
  • -
  • corner-sw-radius: (length) Indicates the radius of the south-west corner. +
  • border-bottom-left-radius: (length) Indicates the radius of the bottom-left corner. Not ineritted
  • -
  • corner-nw-radius: (length) Indicates the radius of the north-west corner. +
  • border-top-left-radius: (length) Indicates the radius of the top-left corner. Not ineritted
  • -
  • corner-radius: (length){1,4} Shorthand for the other corner properties. +
  • border-radius: (length){1,4} Shorthand for the other corner properties. Not ineritted
@@ -160,11 +160,11 @@

.c1, .c2, .c3, .c4, .c5 { background-color:olive; border:0px; color:white; vertical-align:middle; }
-.c1  { corner-radius: 0px }
-.c2  { corner-radius: 10px }
-.c3  { corner-radius: 0px 10px 10px 0px }
-.c4  { corner-radius: 18px }
-.c5  { corner-radius: 10px; border: outset #bb0 2px; }
+.c1 { border-radius: 0px } +.c2 { border-radius: 10px } +.c3 { border-radius: 0px 10px 10px 0px } +.c4 { border-radius: 18px } +.c5 { border-radius: 10px; border: outset #bb0 2px; } \ No newline at end of file diff --git a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/16.Borders.htm b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/16.Borders.htm index 6fae0757c..117dfbcae 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/16.Borders.htm +++ b/Source/Demos/HtmlRenderer.Demo.Common/TestSamples/16.Borders.htm @@ -12,13 +12,13 @@

-

- border 1px with corner-radius 5px +
+ border 1px with border-radius 5px

-

- border 2px with corner-radius 10px +
+ border 2px with border-radius 10px

@@ -37,8 +37,8 @@

-

- dashed border 2px with corner-radius 10px +
+ dashed border 2px with border-radius 10px

diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs index ef97fed42..bdab4f4d6 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBox.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs @@ -1346,7 +1346,7 @@ protected void PaintBackground(RGraphics g, RRect rect, bool isFirst, bool isLas RGraphicsPath roundrect = null; if (IsRounded) { - roundrect = RenderUtils.GetRoundRect(g, rect, ActualCornerNw, ActualCornerNe, ActualCornerSe, ActualCornerSw); + roundrect = RenderUtils.GetRoundRect(g, rect, ActualBorderRadiusTopLeft, ActualBorderRadiusTopRight, ActualBorderRadiusBottomRight, ActualBorderRadiusBottomLeft); } Object prevMode = null; diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs index 8f2487966..215e6c6b7 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs @@ -54,11 +54,11 @@ internal abstract class CssBoxProperties private string _bottom; private string _color = "black"; private string _content = "normal"; - private string _cornerNwRadius = "0"; - private string _cornerNeRadius = "0"; - private string _cornerSeRadius = "0"; - private string _cornerSwRadius = "0"; - private string _cornerRadius = "0"; + private string _borderTopLeftRadius = "0"; + private string _borderTopRightRadius = "0"; + private string _borderBottomRightRadius = "0"; + private string _borderBottomLeftRadius = "0"; + private string _borderRadius = "0"; private string _emptyCells = "show"; private string _direction = "ltr"; private string _display = "inline"; @@ -116,10 +116,10 @@ internal abstract class CssBoxProperties ///

private RSize _size; - private double _actualCornerNw = double.NaN; - private double _actualCornerNe = double.NaN; - private double _actualCornerSw = double.NaN; - private double _actualCornerSe = double.NaN; + private double _actualBorderRadiusTopLeft = double.NaN; + private double _actualBorderRadiusTopRight = double.NaN; + private double _actualBorderRadiusBottomLeft = double.NaN; + private double _actualBorderRadiusBottomRight = double.NaN; private RColor _actualColor = RColor.Empty; private double _actualBackgroundGradientAngle = double.NaN; private double _actualHeight = double.NaN; @@ -276,66 +276,71 @@ public string BorderCollapse set { _borderCollapse = value; } } - public string CornerRadius + public string BorderRadius { - get { return _cornerRadius; } + get { return _borderRadius; } set { MatchCollection r = RegexParserUtils.Match(RegexParserUtils.CssLength, value); switch (r.Count) { - case 1: - CornerNeRadius = r[0].Value; - CornerNwRadius = r[0].Value; - CornerSeRadius = r[0].Value; - CornerSwRadius = r[0].Value; + // Radius is set for all 4 sides + case 1: + BorderTopRightRadius = r[0].Value; + BorderTopLeftRadius = r[0].Value; + BorderBottomRightRadius = r[0].Value; + BorderBottomLeftRadius = r[0].Value; break; - case 2: - CornerNeRadius = r[0].Value; - CornerNwRadius = r[0].Value; - CornerSeRadius = r[1].Value; - CornerSwRadius = r[1].Value; + // top-left-and-bottom-right | top-right-and-bottom-left + case 2: + BorderTopLeftRadius = r[0].Value; + BorderBottomRightRadius = r[0].Value; + BorderTopRightRadius = r[1].Value; + BorderBottomLeftRadius = r[1].Value; break; - case 3: - CornerNeRadius = r[0].Value; - CornerNwRadius = r[1].Value; - CornerSeRadius = r[2].Value; + // top-left | top-right-and-bottom-left | bottom-right + case 3: + BorderTopLeftRadius = r[0].Value; + BorderTopRightRadius = r[1].Value; + BorderBottomLeftRadius = r[1].Value; + BorderBottomRightRadius = r[2].Value; break; - case 4: - CornerNeRadius = r[0].Value; - CornerNwRadius = r[1].Value; - CornerSeRadius = r[2].Value; - CornerSwRadius = r[3].Value; + // top-left | top-right | bottom-right | bottom-left + case 4: + BorderTopLeftRadius = r[0].Value; + BorderTopRightRadius = r[1].Value; + BorderBottomRightRadius = r[2].Value; + BorderBottomLeftRadius = r[3].Value; break; } - _cornerRadius = value; + _borderRadius = value; } } - public string CornerNwRadius + public string BorderTopLeftRadius { - get { return _cornerNwRadius; } - set { _cornerNwRadius = value; } + get { return _borderTopLeftRadius; } + set { _borderTopLeftRadius = value; } } - public string CornerNeRadius + public string BorderTopRightRadius { - get { return _cornerNeRadius; } - set { _cornerNeRadius = value; } + get { return _borderTopRightRadius; } + set { _borderTopRightRadius = value; } } - public string CornerSeRadius + public string BorderBottomRightRadius { - get { return _cornerSeRadius; } - set { _cornerSeRadius = value; } + get { return _borderBottomRightRadius; } + set { _borderBottomRightRadius = value; } } - public string CornerSwRadius + public string BorderBottomLeftRadius { - get { return _cornerSwRadius; } - set { _cornerSwRadius = value; } + get { return _borderBottomLeftRadius; } + set { _borderBottomLeftRadius = value; } } public string MarginBottom @@ -1125,62 +1130,62 @@ public RColor ActualBorderRightColor } /// - /// Gets the actual length of the north west corner + /// Gets the actual length of the top left border /// - public double ActualCornerNw + public double ActualBorderRadiusTopLeft { get { - if (double.IsNaN(_actualCornerNw)) + if (double.IsNaN(_actualBorderRadiusTopLeft)) { - _actualCornerNw = CssValueParser.ParseLength(CornerNwRadius, 0, this); + _actualBorderRadiusTopLeft = CssValueParser.ParseLength(BorderTopLeftRadius, 0, this); } - return _actualCornerNw; + return _actualBorderRadiusTopLeft; } } /// - /// Gets the actual length of the north east corner + /// Gets the actual length of the top right border /// - public double ActualCornerNe + public double ActualBorderRadiusTopRight { get { - if (double.IsNaN(_actualCornerNe)) + if (double.IsNaN(_actualBorderRadiusTopRight)) { - _actualCornerNe = CssValueParser.ParseLength(CornerNeRadius, 0, this); + _actualBorderRadiusTopRight = CssValueParser.ParseLength(BorderTopRightRadius, 0, this); } - return _actualCornerNe; + return _actualBorderRadiusTopRight; } } /// - /// Gets the actual length of the south east corner + /// Gets the actual length of the bottom right border /// - public double ActualCornerSe + public double ActualBorderRadiusBottomRight { get { - if (double.IsNaN(_actualCornerSe)) + if (double.IsNaN(_actualBorderRadiusBottomRight)) { - _actualCornerSe = CssValueParser.ParseLength(CornerSeRadius, 0, this); + _actualBorderRadiusBottomRight = CssValueParser.ParseLength(BorderBottomRightRadius, 0, this); } - return _actualCornerSe; + return _actualBorderRadiusBottomRight; } } /// - /// Gets the actual length of the south west corner + /// Gets the actual length of the bottom left border /// - public double ActualCornerSw + public double ActualBorderRadiusBottomLeft { get { - if (double.IsNaN(_actualCornerSw)) + if (double.IsNaN(_actualBorderRadiusBottomLeft)) { - _actualCornerSw = CssValueParser.ParseLength(CornerSwRadius, 0, this); + _actualBorderRadiusBottomLeft = CssValueParser.ParseLength(BorderBottomLeftRadius, 0, this); } - return _actualCornerSw; + return _actualBorderRadiusBottomLeft; } } @@ -1189,7 +1194,7 @@ public double ActualCornerSw ///
public bool IsRounded { - get { return ActualCornerNe > 0f || ActualCornerNw > 0f || ActualCornerSe > 0f || ActualCornerSw > 0f; } + get { return ActualBorderRadiusTopRight > 0f || ActualBorderRadiusTopLeft > 0f || ActualBorderRadiusBottomRight > 0f || ActualBorderRadiusBottomLeft > 0f; } } /// @@ -1554,11 +1559,11 @@ protected void InheritStyle(CssBox p, bool everything) _borderBottomStyle = p._borderBottomStyle; _borderLeftStyle = p._borderLeftStyle; _bottom = p._bottom; - _cornerNwRadius = p._cornerNwRadius; - _cornerNeRadius = p._cornerNeRadius; - _cornerSeRadius = p._cornerSeRadius; - _cornerSwRadius = p._cornerSwRadius; - _cornerRadius = p._cornerRadius; + _borderTopLeftRadius = p._borderTopLeftRadius; + _borderTopRightRadius = p._borderTopRightRadius; + _borderBottomRightRadius = p._borderBottomRightRadius; + _borderBottomLeftRadius = p._borderBottomLeftRadius; + _borderRadius = p._borderRadius; _display = p._display; _float = p._float; _height = p._height; diff --git a/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs b/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs index b20c331cb..f52bd9426 100644 --- a/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs +++ b/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs @@ -206,69 +206,69 @@ private static RGraphicsPath GetRoundedBorderPath(RGraphics g, Border border, Cs switch (border) { case Border.Top: - if (b.ActualCornerNw > 0 || b.ActualCornerNe > 0) + if (b.ActualBorderRadiusTopLeft > 0 || b.ActualBorderRadiusTopRight > 0) { path = g.GetGraphicsPath(); - path.Start(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNw); + path.Start(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualBorderRadiusTopLeft); - if (b.ActualCornerNw > 0) - path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerNw, r.Top + b.ActualBorderTopWidth / 2, b.ActualCornerNw, RGraphicsPath.Corner.TopLeft); + if (b.ActualBorderRadiusTopLeft > 0) + path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualBorderRadiusTopLeft, r.Top + b.ActualBorderTopWidth / 2, b.ActualBorderRadiusTopLeft, RGraphicsPath.Corner.TopLeft); - path.LineTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerNe, r.Top + b.ActualBorderTopWidth / 2); + path.LineTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualBorderRadiusTopRight, r.Top + b.ActualBorderTopWidth / 2); - if (b.ActualCornerNe > 0) - path.ArcTo(r.Right - b.ActualBorderRightWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNe, b.ActualCornerNe, RGraphicsPath.Corner.TopRight); + if (b.ActualBorderRadiusTopRight > 0) + path.ArcTo(r.Right - b.ActualBorderRightWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualBorderRadiusTopRight, b.ActualBorderRadiusTopRight, RGraphicsPath.Corner.TopRight); } break; case Border.Bottom: - if (b.ActualCornerSw > 0 || b.ActualCornerSe > 0) + if (b.ActualBorderRadiusBottomLeft > 0 || b.ActualBorderRadiusBottomRight > 0) { path = g.GetGraphicsPath(); - path.Start(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSe); + path.Start(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualBorderRadiusBottomRight); - if (b.ActualCornerSe > 0) - path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerSe, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualCornerSe, RGraphicsPath.Corner.BottomRight); + if (b.ActualBorderRadiusBottomRight > 0) + path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualBorderRadiusBottomRight, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualBorderRadiusBottomRight, RGraphicsPath.Corner.BottomRight); - path.LineTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerSw, r.Bottom - b.ActualBorderBottomWidth / 2); + path.LineTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualBorderRadiusBottomLeft, r.Bottom - b.ActualBorderBottomWidth / 2); - if (b.ActualCornerSw > 0) - path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSw, b.ActualCornerSw, RGraphicsPath.Corner.BottomLeft); + if (b.ActualBorderRadiusBottomLeft > 0) + path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualBorderRadiusBottomLeft, b.ActualBorderRadiusBottomLeft, RGraphicsPath.Corner.BottomLeft); } break; case Border.Right: - if (b.ActualCornerNe > 0 || b.ActualCornerSe > 0) + if (b.ActualBorderRadiusTopRight > 0 || b.ActualBorderRadiusBottomRight > 0) { path = g.GetGraphicsPath(); bool noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden; bool noBottom = b.BorderBottomStyle == CssConstants.None || b.BorderBottomStyle == CssConstants.Hidden; - path.Start(r.Right - b.ActualBorderRightWidth / 2 - (noTop ? b.ActualCornerNe : 0), r.Top + b.ActualBorderTopWidth / 2 + (noTop ? 0 : b.ActualCornerNe)); + path.Start(r.Right - b.ActualBorderRightWidth / 2 - (noTop ? b.ActualBorderRadiusTopRight : 0), r.Top + b.ActualBorderTopWidth / 2 + (noTop ? 0 : b.ActualBorderRadiusTopRight)); - if (b.ActualCornerNe > 0 && noTop) - path.ArcTo(r.Right - b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNe, b.ActualCornerNe, RGraphicsPath.Corner.TopRight); + if (b.ActualBorderRadiusTopRight > 0 && noTop) + path.ArcTo(r.Right - b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualBorderRadiusTopRight, b.ActualBorderRadiusTopRight, RGraphicsPath.Corner.TopRight); - path.LineTo(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSe); + path.LineTo(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualBorderRadiusBottomRight); - if (b.ActualCornerSe > 0 && noBottom) - path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerSe, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualCornerSe, RGraphicsPath.Corner.BottomRight); + if (b.ActualBorderRadiusBottomRight > 0 && noBottom) + path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualBorderRadiusBottomRight, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualBorderRadiusBottomRight, RGraphicsPath.Corner.BottomRight); } break; case Border.Left: - if (b.ActualCornerNw > 0 || b.ActualCornerSw > 0) + if (b.ActualBorderRadiusTopLeft > 0 || b.ActualBorderRadiusBottomLeft > 0) { path = g.GetGraphicsPath(); bool noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden; bool noBottom = b.BorderBottomStyle == CssConstants.None || b.BorderBottomStyle == CssConstants.Hidden; - path.Start(r.Left + b.ActualBorderLeftWidth / 2 + (noBottom ? b.ActualCornerSw : 0), r.Bottom - b.ActualBorderBottomWidth / 2 - (noBottom ? 0 : b.ActualCornerSw)); + path.Start(r.Left + b.ActualBorderLeftWidth / 2 + (noBottom ? b.ActualBorderRadiusBottomLeft : 0), r.Bottom - b.ActualBorderBottomWidth / 2 - (noBottom ? 0 : b.ActualBorderRadiusBottomLeft)); - if (b.ActualCornerSw > 0 && noBottom) - path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSw, b.ActualCornerSw, RGraphicsPath.Corner.BottomLeft); + if (b.ActualBorderRadiusBottomLeft > 0 && noBottom) + path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualBorderRadiusBottomLeft, b.ActualBorderRadiusBottomLeft, RGraphicsPath.Corner.BottomLeft); - path.LineTo(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNw); + path.LineTo(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualBorderRadiusTopLeft); - if (b.ActualCornerNw > 0 && noTop) - path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerNw, r.Top + b.ActualBorderTopWidth / 2, b.ActualCornerNw, RGraphicsPath.Corner.TopLeft); + if (b.ActualBorderRadiusTopLeft > 0 && noTop) + path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualBorderRadiusTopLeft, r.Top + b.ActualBorderTopWidth / 2, b.ActualBorderRadiusTopLeft, RGraphicsPath.Corner.TopLeft); } break; } diff --git a/Source/HtmlRenderer/Core/Utils/CssUtils.cs b/Source/HtmlRenderer/Core/Utils/CssUtils.cs index 5d9056a4a..e51f73238 100644 --- a/Source/HtmlRenderer/Core/Utils/CssUtils.cs +++ b/Source/HtmlRenderer/Core/Utils/CssUtils.cs @@ -96,16 +96,16 @@ public static string GetPropertyValue(CssBox cssBox, string propName) return cssBox.BorderSpacing; case "border-collapse": return cssBox.BorderCollapse; - case "corner-radius": - return cssBox.CornerRadius; - case "corner-nw-radius": - return cssBox.CornerNwRadius; - case "corner-ne-radius": - return cssBox.CornerNeRadius; - case "corner-se-radius": - return cssBox.CornerSeRadius; - case "corner-sw-radius": - return cssBox.CornerSwRadius; + case "border-radius": + return cssBox.BorderRadius; + case "border-top-left-radius": + return cssBox.BorderTopLeftRadius; + case "border-top-right-radius": + return cssBox.BorderTopRightRadius; + case "border-bottom-right-radius": + return cssBox.BorderBottomRightRadius; + case "border-bottom-left-radius": + return cssBox.BorderBottomLeftRadius; case "margin-bottom": return cssBox.MarginBottom; case "margin-left": @@ -259,20 +259,20 @@ public static void SetPropertyValue(CssBox cssBox, string propName, string value case "border-collapse": cssBox.BorderCollapse = value; break; - case "corner-radius": - cssBox.CornerRadius = value; + case "border-radius": + cssBox.BorderRadius = value; break; - case "corner-nw-radius": - cssBox.CornerNwRadius = value; + case "border-top-left-radius": + cssBox.BorderTopLeftRadius = value; break; - case "corner-ne-radius": - cssBox.CornerNeRadius = value; + case "border-top-right-radius": + cssBox.BorderTopRightRadius = value; break; - case "corner-se-radius": - cssBox.CornerSeRadius = value; + case "border-bottom-right-radius": + cssBox.BorderBottomRightRadius = value; break; - case "corner-sw-radius": - cssBox.CornerSwRadius = value; + case "border-bottom-left-radius": + cssBox.BorderBottomLeftRadius = value; break; case "margin-bottom": cssBox.MarginBottom = value; diff --git a/Source/HtmlRenderer/Core/Utils/RenderUtils.cs b/Source/HtmlRenderer/Core/Utils/RenderUtils.cs index 2726b5fb2..5a0f5928b 100644 --- a/Source/HtmlRenderer/Core/Utils/RenderUtils.cs +++ b/Source/HtmlRenderer/Core/Utils/RenderUtils.cs @@ -96,43 +96,43 @@ public static void DrawImageErrorIcon(RGraphics g, HtmlContainerInt htmlContaine /// /// Creates a rounded rectangle using the specified corner radius
- /// NW-----NE + /// TL------TR /// | | /// | | - /// SW-----SE + /// BL------BR ///
/// the device to draw into /// Rectangle to round - /// Radius of the north east corner - /// Radius of the north west corner - /// Radius of the south east corner - /// Radius of the south west corner + /// Radius of the top left corner + /// Radius of the top right corner + /// Radius of the bottom right corner + /// Radius of the bottom left corner /// GraphicsPath with the lines of the rounded rectangle ready to be painted - public static RGraphicsPath GetRoundRect(RGraphics g, RRect rect, double nwRadius, double neRadius, double seRadius, double swRadius) + public static RGraphicsPath GetRoundRect(RGraphics g, RRect rect, double topLeftRadius, double topRightRadius, double bottomRightRadius, double bottomLeftRadius) { var path = g.GetGraphicsPath(); - path.Start(rect.Left + nwRadius, rect.Top); + path.Start(rect.Left + topLeftRadius, rect.Top); - path.LineTo(rect.Right - neRadius, rect.Y); + path.LineTo(rect.Right - topRightRadius, rect.Y); - if (neRadius > 0f) - path.ArcTo(rect.Right, rect.Top + neRadius, neRadius, RGraphicsPath.Corner.TopRight); + if (topRightRadius > 0f) + path.ArcTo(rect.Right, rect.Top + topRightRadius, topRightRadius, RGraphicsPath.Corner.TopRight); - path.LineTo(rect.Right, rect.Bottom - seRadius); + path.LineTo(rect.Right, rect.Bottom - bottomRightRadius); - if (seRadius > 0f) - path.ArcTo(rect.Right - seRadius, rect.Bottom, seRadius, RGraphicsPath.Corner.BottomRight); + if (bottomRightRadius > 0f) + path.ArcTo(rect.Right - bottomRightRadius, rect.Bottom, bottomRightRadius, RGraphicsPath.Corner.BottomRight); - path.LineTo(rect.Left + swRadius, rect.Bottom); + path.LineTo(rect.Left + bottomLeftRadius, rect.Bottom); - if (swRadius > 0f) - path.ArcTo(rect.Left, rect.Bottom - swRadius, swRadius, RGraphicsPath.Corner.BottomLeft); + if (bottomLeftRadius > 0f) + path.ArcTo(rect.Left, rect.Bottom - bottomLeftRadius, bottomLeftRadius, RGraphicsPath.Corner.BottomLeft); - path.LineTo(rect.Left, rect.Top + nwRadius); + path.LineTo(rect.Left, rect.Top + topLeftRadius); - if (nwRadius > 0f) - path.ArcTo(rect.Left + nwRadius, rect.Top, nwRadius, RGraphicsPath.Corner.TopLeft); + if (topLeftRadius > 0f) + path.ArcTo(rect.Left + topLeftRadius, rect.Top, topLeftRadius, RGraphicsPath.Corner.TopLeft); return path; } From f89f8389bb3793469e1af5748365bc863abfdc85 Mon Sep 17 00:00:00 2001 From: Alby Blyth Date: Wed, 20 Dec 2023 08:01:09 +1000 Subject: [PATCH 19/22] Update demo output to use date format yyyyMMdd --- Source/Demos/HtmlRenderer.Demo.Console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index a615c8d20..328899812 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs @@ -11,7 +11,7 @@ } //Probably won't be running a suite of tests more than once a second, so this will do. -string runIdentifier = DateTime.Now.ToString("ddMMyyyy-hhmmss"); +string runIdentifier = DateTime.Now.ToString("yyyyMMdd-hhmmss"); var skia = new SkiaConverter(runIdentifier, basePath); var pdfSharp = new PdfSharpCoreConverter(runIdentifier, basePath); From e11379e993bce479e95cea192e3d859912e25c13 Mon Sep 17 00:00:00 2001 From: Alby Blyth Date: Wed, 20 Dec 2023 10:02:10 +1000 Subject: [PATCH 20/22] Implement skia border radius --- .../Adapters/GraphicsAdapter.cs | 2 +- .../Adapters/GraphicsPathAdapter.cs | 17 +++++++++++------ .../Adapters/SkiaSharpAdapter.cs | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs index 2812e6b0a..c6e45ba86 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs @@ -20,7 +20,7 @@ namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters { /// - /// Adapter for WinForms Graphics for core. + /// Adapter for Skia Graphics for core. /// internal sealed class GraphicsAdapter : RGraphics { diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs index 484c96c14..466bd08eb 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsPathAdapter.cs @@ -19,14 +19,14 @@ namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters { /// - /// Adapter for WinForms graphics path object for core. + /// Adapter for Skia graphics path object for core. /// internal sealed class GraphicsPathAdapter : RGraphicsPath { /// - /// The actual PdfSharp graphics path instance. + /// The actual SKPath graphics path instance. /// - private readonly SKPath _graphicsPath = new SKPath(); + private readonly SKPath _graphicsPath = new(); /// /// the last point added to the path to begin next segment from @@ -34,7 +34,7 @@ internal sealed class GraphicsPathAdapter : RGraphicsPath private RPoint _lastPoint; /// - /// The actual PdfSharp graphics path instance. + /// The actual SKPath graphics path instance. /// public SKPath GraphicsPath { @@ -44,6 +44,7 @@ public SKPath GraphicsPath public override void Start(double x, double y) { _lastPoint = new RPoint(x, y); + _graphicsPath.MoveTo((float)x, (float)y); } public override void LineTo(double x, double y) @@ -56,12 +57,16 @@ public override void ArcTo(double x, double y, double size, Corner corner) { float left = (float)(Math.Min(x, _lastPoint.X) - (corner == Corner.TopRight || corner == Corner.BottomRight ? size : 0)); float top = (float)(Math.Min(y, _lastPoint.Y) - (corner == Corner.BottomLeft || corner == Corner.BottomRight ? size : 0)); - _graphicsPath.ArcTo(left, top, (float)size * 2, (float)size * 2, GetStartAngle(corner)); + + var rect = SKRect.Create(left, top, (float)size * 2, (float)size * 2); + _graphicsPath.ArcTo(rect, GetStartAngle(corner), 90f, false); _lastPoint = new RPoint(x, y); } public override void Dispose() - { } + { + _graphicsPath.Dispose(); + } /// /// Get arc start angle for the given corner. diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs index d09188391..85ee85f22 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs @@ -75,7 +75,7 @@ protected override RColor GetColorInt(string colorName) protected override RPen CreatePen(RColor color) { - return new PenAdapter(new SKPaint { Color = Utils.Convert(color) }); + return new PenAdapter(new SKPaint { Color = Utils.Convert(color), IsStroke = true }); } protected override RBrush CreateSolidBrush(RColor color) From 909bb2eb0538a9b8114213242ff5d8537bfb699d Mon Sep 17 00:00:00 2001 From: Alby Blyth Date: Thu, 25 Jan 2024 14:33:33 +1000 Subject: [PATCH 21/22] Update graphics adapter to use correct calculated height --- .../Adapters/GraphicsAdapter.cs | 434 +++++++++--------- 1 file changed, 211 insertions(+), 223 deletions(-) diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs index c6e45ba86..6ec065bc4 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/GraphicsAdapter.cs @@ -19,238 +19,226 @@ namespace TheArtOfDev.HtmlRenderer.SkiaSharp.Adapters { - /// - /// Adapter for Skia Graphics for core. - /// - internal sealed class GraphicsAdapter : RGraphics - { - #region Fields and Consts - - /// - /// The wrapped SkiaSharp graphics object - /// - private readonly SKCanvas _g; - - /// - /// if to release the graphics object on dispose - /// - private readonly bool _releaseGraphics; - - /// - /// Used to measure and draw strings - /// - private static readonly SKTextAlign _stringFormat; - - #endregion - - - static GraphicsAdapter() - { - _stringFormat = new SKTextAlign(); - //_stringFormat.Alignment = XStringAlignment.Near; - //_stringFormat.LineAlignment = XLineAlignment.Near; - _stringFormat = SKTextAlign.Left; - } - - /// - /// Init. - /// - /// the win forms graphics object to use - /// optional: if to release the graphics object on dispose (default - false) - public GraphicsAdapter(SKCanvas g, bool releaseGraphics = false) - : base(SkiaSharpAdapter.Instance, new RRect(0, 0, double.MaxValue, double.MaxValue)) - { - ArgChecker.AssertArgNotNull(g, "g"); - - _g = g; - _releaseGraphics = releaseGraphics; - } - - public override void PopClip() - { - _clipStack.Pop(); - _g.Restore(); - } - - public override void PushClip(RRect rect) - { - _clipStack.Push(rect); - _g.Save(); - _g.ClipRect(Utils.Convert(rect)); - } - - public override void PushClipExclude(RRect rect) - { } - - bool _antiAlias = true; - public override Object SetAntiAliasSmoothingMode() - { - /* + /// + /// Adapter for Skia Graphics for core. + /// + internal sealed class GraphicsAdapter : RGraphics + { + #region Fields and Consts + + /// + /// The wrapped SkiaSharp graphics object + /// + private readonly SKCanvas _g; + + /// + /// if to release the graphics object on dispose + /// + private readonly bool _releaseGraphics; + + /// + /// Used to measure and draw strings + /// + private static readonly SKTextAlign _stringFormat; + + #endregion + + + static GraphicsAdapter() + { + _stringFormat = new SKTextAlign(); + //_stringFormat.Alignment = XStringAlignment.Near; + //_stringFormat.LineAlignment = XLineAlignment.Near; + _stringFormat = SKTextAlign.Left; + } + + /// + /// Init. + /// + /// the win forms graphics object to use + /// optional: if to release the graphics object on dispose (default - false) + public GraphicsAdapter(SKCanvas g, bool releaseGraphics = false) + : base(SkiaSharpAdapter.Instance, new RRect(0, 0, double.MaxValue, double.MaxValue)) + { + ArgChecker.AssertArgNotNull(g, "g"); + + _g = g; + _releaseGraphics = releaseGraphics; + } + + public override void PopClip() + { + _clipStack.Pop(); + _g.Restore(); + } + + public override void PushClip(RRect rect) + { + _clipStack.Push(rect); + _g.Save(); + _g.ClipRect(Utils.Convert(rect)); + } + + public override void PushClipExclude(RRect rect) + { } + + bool _antiAlias = true; + public override Object SetAntiAliasSmoothingMode() + { + /* var prevMode = _; _g.SmoothingMode = XSmoothingMode.AntiAlias; return prevMode; */ - var prevMode = _antiAlias; - _antiAlias = true; - return prevMode; - } + var prevMode = _antiAlias; + _antiAlias = true; + return prevMode; + } - public override void ReturnPreviousSmoothingMode(Object prevMode) - { - /* + public override void ReturnPreviousSmoothingMode(Object prevMode) + { + /* if (prevMode != null) { _g.SmoothingMode = (XSmoothingMode)prevMode; }*/ - if (prevMode != null) - { - _antiAlias = (bool)prevMode; - } - } - - public override RSize MeasureString(string str, RFont font) - { - var fontAdapter = (FontAdapter)font; - var realFont = fontAdapter.Font; - var p = new SKPaint(realFont); - var boundingRect = new SKRect(); - var measuredWidth = p.MeasureText(str, ref boundingRect); - - float width; - if (str == " ") - { - width = measuredWidth; - } - else - { - //The bounding rect width looks a bit odd. We'll take the midpoint until I can work out why this is. - width = measuredWidth;// measuredWidth;// (boundingRect.Width + measuredWidth) / 2; - } - - var height = realFont.Metrics.XMax + realFont.Metrics.XMin; - - if (font.Height < 0) - { - var descent = realFont.Metrics.Descent; - fontAdapter.SetMetrics((int)Math.Round(height, MidpointRounding.AwayFromZero), (int)Math.Round((height - descent + 1f), MidpointRounding.AwayFromZero)); - } - - return new RSize(width, height); - } - - public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) - { - // there is no need for it - used for text selection - throw new NotSupportedException(); - } - - public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl) - { - //var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush; - var skiaFont = ((FontAdapter)font).Font; - var p = new SKPaint - { - IsAntialias = _antiAlias, - FilterQuality = SKFilterQuality.Medium, - Color = Utils.Convert(color) - }; - _g.DrawText(str, (float)point.X, (float)point.Y - skiaFont.Metrics.Ascent, skiaFont, p); - } - - public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation) - { - return new BrushAdapter(new XTextureBrush(((ImageAdapter)image).Image, Utils.Convert(dstRect), Utils.Convert(translateTransformLocation))); - } - - public override RGraphicsPath GetGraphicsPath() - { - return new GraphicsPathAdapter(); - } - - public override void Dispose() - { - if (_releaseGraphics) - _g.Dispose(); - } - - - #region Delegate graphics methods - - public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2) - { - _g.DrawLine((float)x1, (float)y1, (float)x2, (float)y2, GetPaint(pen)); - } - - public override void DrawRectangle(RPen pen, double x, double y, double width, double height) - { - _g.DrawRect((float)x, (float)y, (float)width, (float)height, GetPaint(pen)); - } - - public override void DrawRectangle(RBrush brush, double x, double y, double width, double height) - { - var untypedBrush = ((BrushAdapter)brush).Brush; - if (untypedBrush is XTextureBrush textureBrush) - { - textureBrush.DrawRectangle(_g, (float)x, (float)y, (float)width, (float)height); - } - else if (untypedBrush is SKBrush skBrush) - { - var p = skBrush.GetPaint().Clone(); - p.IsAntialias = _antiAlias; - _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); - } - } - - public override void DrawImage(RImage image, RRect destRect, RRect srcRect) - { - ((ImageAdapter)image).DrawImage(_g, Utils.Convert(destRect), Utils.Convert(srcRect)); - } - - public override void DrawImage(RImage image, RRect destRect) - { - ((ImageAdapter)image).DrawImage(_g, Utils.Convert(destRect)); - } - - public override void DrawPath(RPen pen, RGraphicsPath path) - { - _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, GetPaint(pen)); - } - - public override void DrawPath(RBrush brush, RGraphicsPath path) - { - _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, GetPaint(brush)); - } - - public override void DrawPolygon(RBrush brush, RPoint[] points) - { - var p = GetPaint(brush); - - if (points != null && points.Length > 0) - { - //(XBrush)((BrushAdapter)brush).Brush - var path = new SKPath(); - path.AddPoly(Utils.Convert(points)); - _g.DrawPath(path, p); - } - } - - private SKPaint GetPaint(RBrush brush) - { - SKBrush skBrush = (SKBrush)((BrushAdapter)brush).Brush; - var p = skBrush.GetPaint().Clone(); - p.IsAntialias = _antiAlias; - return p; - } - - private SKPaint GetPaint(RPen pen) - { - var skPaint =((PenAdapter)pen).Pen.Clone(); - skPaint.IsAntialias = _antiAlias; - return skPaint; - } - - #endregion - } + if (prevMode != null) + { + _antiAlias = (bool)prevMode; + } + } + + public override RSize MeasureString(string str, RFont font) + { + var fontAdapter = (FontAdapter)font; + var realFont = fontAdapter.Font; + var p = new SKPaint(realFont); + var boundingRect = new SKRect(); + var measuredWidth = p.MeasureText(str, ref boundingRect); + + if (font.Height < 0) + { + var height = realFont.Metrics.XMax + realFont.Metrics.XMin; + var descent = realFont.Metrics.Descent; + fontAdapter.SetMetrics((int)Math.Round(height, MidpointRounding.AwayFromZero), (int)Math.Round((height - descent + 1f), MidpointRounding.AwayFromZero)); + } + + return new RSize(measuredWidth, boundingRect.Height); + } + + public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) + { + // there is no need for it - used for text selection + throw new NotSupportedException(); + } + + public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl) + { + //var xBrush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush; + var skiaFont = ((FontAdapter)font).Font; + var p = new SKPaint + { + IsAntialias = _antiAlias, + FilterQuality = SKFilterQuality.Medium, + Color = Utils.Convert(color) + }; + _g.DrawText(str, (float)point.X, (float)point.Y - skiaFont.Metrics.Ascent, skiaFont, p); + } + + public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation) + { + return new BrushAdapter(new XTextureBrush(((ImageAdapter)image).Image, Utils.Convert(dstRect), Utils.Convert(translateTransformLocation))); + } + + public override RGraphicsPath GetGraphicsPath() + { + return new GraphicsPathAdapter(); + } + + public override void Dispose() + { + if (_releaseGraphics) + _g.Dispose(); + } + + + #region Delegate graphics methods + + public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2) + { + _g.DrawLine((float)x1, (float)y1, (float)x2, (float)y2, GetPaint(pen)); + } + + public override void DrawRectangle(RPen pen, double x, double y, double width, double height) + { + _g.DrawRect((float)x, (float)y, (float)width, (float)height, GetPaint(pen)); + } + + public override void DrawRectangle(RBrush brush, double x, double y, double width, double height) + { + var untypedBrush = ((BrushAdapter)brush).Brush; + if (untypedBrush is XTextureBrush textureBrush) + { + textureBrush.DrawRectangle(_g, (float)x, (float)y, (float)width, (float)height); + } + else if (untypedBrush is SKBrush skBrush) + { + var p = skBrush.GetPaint().Clone(); + p.IsAntialias = _antiAlias; + _g.DrawRect((float)x, (float)y, (float)width, (float)height, p); + } + } + + public override void DrawImage(RImage image, RRect destRect, RRect srcRect) + { + ((ImageAdapter)image).DrawImage(_g, Utils.Convert(destRect), Utils.Convert(srcRect)); + } + + public override void DrawImage(RImage image, RRect destRect) + { + ((ImageAdapter)image).DrawImage(_g, Utils.Convert(destRect)); + } + + public override void DrawPath(RPen pen, RGraphicsPath path) + { + _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, GetPaint(pen)); + } + + public override void DrawPath(RBrush brush, RGraphicsPath path) + { + _g.DrawPath(((GraphicsPathAdapter)path).GraphicsPath, GetPaint(brush)); + } + + public override void DrawPolygon(RBrush brush, RPoint[] points) + { + var p = GetPaint(brush); + + if (points != null && points.Length > 0) + { + //(XBrush)((BrushAdapter)brush).Brush + var path = new SKPath(); + path.AddPoly(Utils.Convert(points)); + _g.DrawPath(path, p); + } + } + + private SKPaint GetPaint(RBrush brush) + { + SKBrush skBrush = (SKBrush)((BrushAdapter)brush).Brush; + var p = skBrush.GetPaint().Clone(); + p.IsAntialias = _antiAlias; + return p; + } + + private SKPaint GetPaint(RPen pen) + { + var skPaint = ((PenAdapter)pen).Pen.Clone(); + skPaint.IsAntialias = _antiAlias; + return skPaint; + } + + #endregion + } } \ No newline at end of file From d99d3cfd54207453e7990d39837809c4d52550a1 Mon Sep 17 00:00:00 2001 From: Alby Blyth Date: Thu, 25 Jan 2024 14:33:49 +1000 Subject: [PATCH 22/22] Update skia sharp adapter to use font manager default singleton --- Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs index 85ee85f22..4b6951f03 100644 --- a/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs +++ b/Source/HtmlRenderer.SkiaSharp/Adapters/SkiaSharpAdapter.cs @@ -43,7 +43,7 @@ private SkiaSharpAdapter() AddFontFamilyMapping("monospace", "Courier New"); AddFontFamilyMapping("Helvetica", "Arial"); - var manager = SKFontManager.CreateDefault(); + var manager = SKFontManager.Default; var families = manager.GetFontFamilies(); foreach (var family in families)