diff --git a/.github/workflows/build-action.yml b/.github/workflows/build-action.yml index 47d345a3a..3767c37c2 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: 7.0.x + dotnet-version: 8.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 dd99688b0..e396f0d4d 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: 7.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore Source/HtmlRenderer.sln diff --git a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj index 12bf0c0e8..6b0f65ab3 100644 --- a/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj +++ b/Source/Demos/HtmlRenderer.Demo.Common/HtmlRenderer.Demo.Common.csproj @@ -1,300 +1,35 @@ + + net7.0 + enable + disable + - - net7.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 - - - PreserveNewest - - + + + + - - - - - - - - + + + diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs b/Source/Demos/HtmlRenderer.Demo.Common/SampleConverterBase.cs similarity index 87% rename from Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs rename to Source/Demos/HtmlRenderer.Demo.Common/SampleConverterBase.cs index 8c1e4b2cb..ed5370c0d 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/SampleConverterBase.cs +++ b/Source/Demos/HtmlRenderer.Demo.Common/SampleConverterBase.cs @@ -10,33 +10,18 @@ using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Demo.Common; -namespace HtmlRenderer.Demo.Console +namespace HtmlRenderer.Demo.Common { public class SampleConverterBase { - private string _sampleRunIdentifier; - private string _thisTypeName; - private string _basePath; - - public SampleConverterBase(string sampleRunIdentifier, string basePath) + public SampleConverterBase() { - _sampleRunIdentifier = sampleRunIdentifier; - _basePath = basePath; - _thisTypeName = this.GetType().Name; - this.OnImageLoaded += ImageLoad; this.OnStyleLoaded += StylesheetLoad; } 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"); - } - protected EventHandler OnImageLoaded; protected EventHandler OnStyleLoaded; @@ -56,4 +41,25 @@ internal void StylesheetLoad(object? sender, HtmlStylesheetLoadEventArgs e) throw new NotImplementedException(); } } + + public class SampleConverterFileBase : SampleConverterBase + { + private string _sampleRunIdentifier; + private string _thisTypeName; + private string _basePath; + + public SampleConverterFileBase(string sampleRunIdentifier, string basePath) : base() + { + _sampleRunIdentifier = sampleRunIdentifier; + _basePath = basePath; + _thisTypeName = this.GetType().Name; + } + + protected string GetSamplePath(HtmlSample sample) + { + var path = Path.Combine(_basePath, _sampleRunIdentifier); + Directory.CreateDirectory(path); + return Path.Combine(path, sample.FullName + _thisTypeName + "_" + ".pdf"); + } + } } diff --git a/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs index 983c888e8..b18d81cab 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/PdfSharpCoreConverter.cs @@ -1,4 +1,5 @@ -using System; +using HtmlRenderer.Demo.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,7 +9,7 @@ namespace HtmlRenderer.Demo.Console { - public class PdfSharpCoreConverter : SampleConverterBase + public class PdfSharpCoreConverter : SampleConverterFileBase { public PdfSharpCoreConverter(string sampleRunIdentifier, string basePath) : base(sampleRunIdentifier, basePath) { diff --git a/Source/Demos/HtmlRenderer.Demo.Console/Program.cs b/Source/Demos/HtmlRenderer.Demo.Console/Program.cs index 328899812..186bc09a2 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("yyyyMMdd-hhmmss"); +var runIdentifier = DateTime.Now.ToString("yyyyMMdd-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("02", StringComparison.OrdinalIgnoreCase)) continue; + //if (!htmlSample.FullName.Contains("16", StringComparison.OrdinalIgnoreCase)) continue; await skia.GenerateSampleAsync(htmlSample); - //await pdfSharp.GenerateSampleAsync(htmlSample); + await pdfSharp.GenerateSampleAsync(htmlSample); } diff --git a/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs b/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs index 93a749b38..5cfa068a4 100644 --- a/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs +++ b/Source/Demos/HtmlRenderer.Demo.Console/SkiaConverter.cs @@ -1,4 +1,5 @@ -using System; +using HtmlRenderer.Demo.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,7 +9,7 @@ namespace HtmlRenderer.Demo.Console { - public class SkiaConverter : SampleConverterBase + public class SkiaConverter : SampleConverterFileBase { public SkiaConverter(string sampleRunIdentifier, string basePath) : base(sampleRunIdentifier, basePath) { diff --git a/Source/HtmlRenderer.Demo.Web.Api/Controllers/PrinterController.cs b/Source/HtmlRenderer.Demo.Web.Api/Controllers/PrinterController.cs new file mode 100644 index 000000000..20128c5d5 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/Controllers/PrinterController.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Mvc; +using TheArtOfDev.HtmlRenderer.SkiaSharp; +using TheArtOfDev.HtmlRenderer.Demo.Common; + +namespace HtmlRenderer.Demo.Web.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class PrinterController(ILogger logger) : ControllerBase + { + private readonly ILogger _logger = logger; + + + [HttpGet("SampleNames")] + public IEnumerable GetSampleNames() + { + return SamplesLoader + .TestSamples + .Select(s => s.Name); + } + + [HttpGet("Print")] + public async Task PrintSkia(string name) + { + var sample = GetSample(name); + if (sample == null) + { + return NotFound(); + } + + var converter = new SkiaConverter(); + return await PrintPdf(converter, sample); + } + + + [HttpGet("PrintLegacy")] + public async Task PrintLegacy(string name) + { + var sample = GetSample(name); + if (sample == null) + { + return NotFound(); + } + + var converter = new PdfSharpCoreConverter(); + return await PrintPdf(converter, sample); + } + + private HtmlSample? GetSample(string name) + { + var sample = SamplesLoader.TestSamples + .Where(s => s.FullName.Contains(name, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(); + + return sample; + } + + private async Task PrintPdf(IStreamPdfGenerator converter, HtmlSample sample) + { + var stream = await converter.GenerateSampleAsync(sample); + var filename = $"{sample.Name}-{DateTime.Now:yyyyMMdd-hhmmss}"; + + return File(stream, "application/pdf", filename); + } + } +} diff --git a/Source/HtmlRenderer.Demo.Web.Api/HtmlRenderer.Demo.Web.Api.csproj b/Source/HtmlRenderer.Demo.Web.Api/HtmlRenderer.Demo.Web.Api.csproj new file mode 100644 index 000000000..5ce26f230 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/HtmlRenderer.Demo.Web.Api.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + diff --git a/Source/HtmlRenderer.Demo.Web.Api/HtmlRenderer.Demo.Web.Api.http b/Source/HtmlRenderer.Demo.Web.Api/HtmlRenderer.Demo.Web.Api.http new file mode 100644 index 000000000..a90271bb6 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/HtmlRenderer.Demo.Web.Api.http @@ -0,0 +1,16 @@ +@HtmlRenderer.Demo.Web.Api_HostAddress = http://localhost:5181 + +GET {{HtmlRenderer.Demo.Web.Api_HostAddress}}/Printer/Samplenames +Accept: application/json + +### + +GET {{HtmlRenderer.Demo.Web.Api_HostAddress}}/Printer/Print?name=Header +Accept: application/pdf + +### + +GET {{HtmlRenderer.Demo.Web.Api_HostAddress}}/Printer/PrintLegacy?name=Header +Accept: application/pdf + +### \ No newline at end of file diff --git a/Source/HtmlRenderer.Demo.Web.Api/IStreamPdfGenerator.cs b/Source/HtmlRenderer.Demo.Web.Api/IStreamPdfGenerator.cs new file mode 100644 index 000000000..d5821c67f --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/IStreamPdfGenerator.cs @@ -0,0 +1,9 @@ +using TheArtOfDev.HtmlRenderer.Demo.Common; + +namespace HtmlRenderer.Demo.Web.Api +{ + public interface IStreamPdfGenerator + { + Task GenerateSampleAsync(HtmlSample sample); + } +} diff --git a/Source/HtmlRenderer.Demo.Web.Api/PdfConverter.cs b/Source/HtmlRenderer.Demo.Web.Api/PdfConverter.cs new file mode 100644 index 000000000..424f8cd64 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/PdfConverter.cs @@ -0,0 +1,31 @@ +using HtmlRenderer.Demo.Common; +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.Web.Api +{ + public class PdfSharpCoreConverter : SampleConverterBase, IStreamPdfGenerator + { + 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, imageLoad: OnImageLoaded); + var stream = new MemoryStream(); + pdf.Save(stream); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + } +} diff --git a/Source/HtmlRenderer.Demo.Web.Api/Program.cs b/Source/HtmlRenderer.Demo.Web.Api/Program.cs new file mode 100644 index 000000000..d84c78ffc --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/Program.cs @@ -0,0 +1,26 @@ +using TheArtOfDev.HtmlRenderer.Demo.Common; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +app.UseSwagger(); +app.UseSwaggerUI(); + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +SamplesLoader.Init("Console", typeof(Program).Assembly.GetName().Version!.ToString()); + +app.Run(); diff --git a/Source/HtmlRenderer.Demo.Web.Api/Properties/launchSettings.json b/Source/HtmlRenderer.Demo.Web.Api/Properties/launchSettings.json new file mode 100644 index 000000000..2890a2afd --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:12837", + "sslPort": 44340 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5181", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7289;http://localhost:5181", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Source/HtmlRenderer.Demo.Web.Api/SkiaConverter.cs b/Source/HtmlRenderer.Demo.Web.Api/SkiaConverter.cs new file mode 100644 index 000000000..54443d952 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/SkiaConverter.cs @@ -0,0 +1,32 @@ +using HtmlRenderer.Demo.Common; +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.Web.Api +{ + public class SkiaConverter : SampleConverterBase, IStreamPdfGenerator + { + 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; + + var stream = new MemoryStream(); + await PdfGenerator.GeneratePdfAsync(sample.Html, stream, config, imageLoad: OnImageLoaded); + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } + } +} diff --git a/Source/HtmlRenderer.Demo.Web.Api/appsettings.Development.json b/Source/HtmlRenderer.Demo.Web.Api/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Source/HtmlRenderer.Demo.Web.Api/appsettings.json b/Source/HtmlRenderer.Demo.Web.Api/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/Source/HtmlRenderer.Demo.Web.Api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Source/HtmlRenderer.sln b/Source/HtmlRenderer.sln index ff45b31b1..440428e60 100644 --- a/Source/HtmlRenderer.sln +++ b/Source/HtmlRenderer.sln @@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.SkiaSharp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlRenderer.Demo.Console", "Demos\HtmlRenderer.Demo.Console\HtmlRenderer.Demo.Console.csproj", "{7E5B30E9-34C6-4123-B8A6-B50AC078DA53}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Demo.Web.Api", "HtmlRenderer.Demo.Web.Api\HtmlRenderer.Demo.Web.Api.csproj", "{59590969-0677-4DC7-95B1-6D1442B71586}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -63,6 +65,10 @@ Global {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 + {59590969-0677-4DC7-95B1-6D1442B71586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59590969-0677-4DC7-95B1-6D1442B71586}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59590969-0677-4DC7-95B1-6D1442B71586}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59590969-0677-4DC7-95B1-6D1442B71586}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -73,6 +79,7 @@ Global {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} + {59590969-0677-4DC7-95B1-6D1442B71586} = {CF4F888A-71EE-4EDC-99BA-8A02B4B841F0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B4ABBC6A-EFE0-46EB-BEED-7C8017BA004C}