diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3729ff0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/PdfApi.Client/Class1.cs b/PdfApi.Client/Class1.cs new file mode 100644 index 0000000..65c360c --- /dev/null +++ b/PdfApi.Client/Class1.cs @@ -0,0 +1,52 @@ +using System.Net.Http.Json; +using PdfApi.Shared; + +namespace PdfApi.Client; +public static class Program +{ + public static async Task Main(string[] args) + { + while (true) + { + try + { + HttpClient client = new HttpClient(); + client.BaseAddress = new Uri("http://104.210.129.44:5000"); + var wk = await client.PostAsJsonAsync("/wk", new WkPdfRequest() + { + Url = + "https://painel.teorico.com.br/Classes/presenceData/2e337e88-7587-4a70-8adb-0a7b7988be80?key=pdfExportInternalOnlyChangeThisLater&offset=0", + FooterRight = "[page]/[toPage]", + FooterLeft = "[datetime]", + Replacements = new Dictionary + { + {"datetime", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss")} + }, + PageMargins = new Margins + { + Right = 4, + Left = 4 + }, + FooterSpacing = 3, + IsLowQuality = true, + NoOutline = true, + ImageDpi = 300, + DisableExternalLinks = true, + DisableInternalLinks = true, + ImageQuality = 80, + PrintMediaType = true, + Dpi = 70, + FooterLine = true + }); + + wk.EnsureSuccessStatusCode(); + var content = await wk.Content.ReadAsByteArrayAsync(); + File.WriteAllBytes("hello.pdf", content); + } + catch (Exception ex) + { + Console.WriteLine("Error"); + } + } + } +} diff --git a/PdfApi.Client/PdfApi.Client.csproj b/PdfApi.Client/PdfApi.Client.csproj new file mode 100644 index 0000000..651f09d --- /dev/null +++ b/PdfApi.Client/PdfApi.Client.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + Exe + + + + + + + diff --git a/PdfApi.Client/Program.cs b/PdfApi.Client/Program.cs new file mode 100644 index 0000000..e69de29 diff --git a/PdfApi.Shared/ContentDisposition.cs b/PdfApi.Shared/ContentDisposition.cs new file mode 100644 index 0000000..9c57d6d --- /dev/null +++ b/PdfApi.Shared/ContentDisposition.cs @@ -0,0 +1,7 @@ +namespace PdfApi.Shared; + +public enum ContentDisposition +{ + Attachment = 0, // this is the default + Inline +} \ No newline at end of file diff --git a/PdfApi.Shared/ImageFormat.cs b/PdfApi.Shared/ImageFormat.cs new file mode 100644 index 0000000..18ec93c --- /dev/null +++ b/PdfApi.Shared/ImageFormat.cs @@ -0,0 +1,10 @@ +namespace PdfApi.Shared; + +/// +/// Image output format +/// +public enum ImageFormat +{ + jpeg, + png +} \ No newline at end of file diff --git a/PdfApi.Shared/Margins.cs b/PdfApi.Shared/Margins.cs new file mode 100644 index 0000000..90d6954 --- /dev/null +++ b/PdfApi.Shared/Margins.cs @@ -0,0 +1,24 @@ +namespace PdfApi.Shared; + +public class Margins +{ + /// + /// Page bottom margin in mm. + /// + public int? Bottom { get; set; } + + /// + /// Page left margin in mm. + /// + public int? Left { get; set; } + + /// + /// Page right margin in mm. + /// + public int? Right { get; set; } + + /// + /// Page top margin in mm. + /// + public int? Top { get; set; } +} \ No newline at end of file diff --git a/PdfApi.Shared/Orientation.cs b/PdfApi.Shared/Orientation.cs new file mode 100644 index 0000000..c472967 --- /dev/null +++ b/PdfApi.Shared/Orientation.cs @@ -0,0 +1,10 @@ +namespace PdfApi.Shared; + +/// +/// Page orientation. +/// +public enum Orientation +{ + Landscape, + Portrait +} \ No newline at end of file diff --git a/PdfApi.Shared/PdfApi.Shared.csproj b/PdfApi.Shared/PdfApi.Shared.csproj new file mode 100644 index 0000000..132c02c --- /dev/null +++ b/PdfApi.Shared/PdfApi.Shared.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/PdfApi.Shared/Size.cs b/PdfApi.Shared/Size.cs new file mode 100644 index 0000000..a8a231d --- /dev/null +++ b/PdfApi.Shared/Size.cs @@ -0,0 +1,157 @@ +namespace PdfApi.Shared; + +/// +/// Page size. +/// +public enum Size +{ + /// + /// 841 x 1189 mm + /// + A0, + + /// + /// 594 x 841 mm + /// + A1, + + /// + /// 420 x 594 mm + /// + A2, + + /// + /// 297 x 420 mm + /// + A3, + + /// + /// 210 x 297 mm + /// + A4, + + /// + /// 148 x 210 mm + /// + A5, + + /// + /// 105 x 148 mm + /// + A6, + + /// + /// 74 x 105 mm + /// + A7, + + /// + /// 52 x 74 mm + /// + A8, + + /// + /// 37 x 52 mm + /// + A9, + + /// + /// 1000 x 1414 mm + /// + B0, + + /// + /// 707 x 1000 mm + /// + B1, + + /// + /// 500 x 707 mm + /// + B2, + + /// + /// 353 x 500 mm + /// + B3, + + /// + /// 250 x 353 mm + /// + B4, + + /// + /// 176 x 250 mm + /// + B5, + + /// + /// 125 x 176 mm + /// + B6, + + /// + /// 88 x 125 mm + /// + B7, + + /// + /// 62 x 88 mm + /// + B8, + + /// + /// 33 x 62 mm + /// + B9, + + /// + /// 31 x 44 mm + /// + B10, + + /// + /// 163 x 229 mm + /// + C5E, + + /// + /// 105 x 241 mm - U.S. Common 10 Envelope + /// + Comm10E, + + /// + /// 110 x 220 mm + /// + Dle, + + /// + /// 190.5 x 254 mm + /// + Executive, + + /// + /// 210 x 330 mm + /// + Folio, + + /// + /// 431.8 x 279.4 mm + /// + Ledger, + + /// + /// 215.9 x 355.6 mm + /// + Legal, + + /// + /// 215.9 x 279.4 mm + /// + Letter, + + /// + /// 279.4 x 431.8 mm + /// + Tabloid +} \ No newline at end of file diff --git a/PdfApi.Shared/WkPdfRequest.cs b/PdfApi.Shared/WkPdfRequest.cs new file mode 100644 index 0000000..9795338 --- /dev/null +++ b/PdfApi.Shared/WkPdfRequest.cs @@ -0,0 +1,155 @@ + +using System.ComponentModel.DataAnnotations; + +namespace PdfApi.Shared; +public class WkPdfRequest +{ + public WkPdfRequest() + { + PageMargins = new Margins(); + } + + [Required] + public string Url { get; set; } + + /// + /// Sets the page size. + /// + public Size? PageSize { get; set; } + + /// + /// Sets the page width in mm. + /// + /// Has priority over but has to be also specified. + public double? PageWidth { get; set; } + + /// + /// Sets the page height in mm. + /// + /// Has priority over but has to be also specified. + public double? PageHeight { get; set; } + + /// + /// Sets the page orientation. + /// + public Orientation? PageOrientation { get; set; } + + /// + /// Sets the page margins. + /// + public Margins PageMargins { get; set; } + + /// + /// Indicates whether the PDF should be generated with forms. + /// + public bool EnableForms { get; set; } + + /// + /// Indicates whether the PDF should be generated in lower quality. + /// + public bool IsLowQuality { get; set; } + + /// + /// Number of copies to print into the PDF file. + /// + public int? Copies { get; set; } + + /// + /// When embedding images scale them down to this dpi (default 600) + /// + public uint? ImageDpi { get; set; } + + + /// + /// When jpeg compressing images use this quality (default 94) + /// + public uint? ImageQuality { get; set; } + + + /// + /// When jpeg compressing images use this quality (default 94) + /// + public uint? Dpi { get; set; } + + /// + /// Indicates whether the PDF should be generated in grayscale. + /// + public bool IsGrayScale { get; set; } + + /// + /// Indicates whether the PDF should be generated in grayscale. + /// + public bool PrintMediaType { get; set; } + + /// + /// Disable the intelligent shrinking strategy used by WebKit that makes the pixel/dpi ratio non-constant. + /// + public bool DisableSmartShrinking { get; set; } + + /// + /// Do not put an outline into the pdf + /// + public bool NoOutline { get; set; } + + /// + /// Do not make links to remote web pages + /// + public bool DisableExternalLinks { get; set; } + + /// + /// Do not make local links + /// + public bool DisableInternalLinks { get; set; } + + /// + /// Do not print background + /// + public bool NoBackground { get; set; } + + /// + /// Display line above the footer + /// + public bool FooterLine { get; set; } + + /// + /// Path to header HTML file. + /// + public string? HeaderHtml { get; set; } + + /// + /// Sets the header spacing. + /// + public int? HeaderSpacing { get; set; } + + /// + /// Path to footer HTML file. + /// + public string? FooterHtml { get; set; } + + /// + /// Footer right content. + /// + public string? FooterRight { get; set; } + + /// + /// Footer center content. + /// + public string? FooterCenter { get; set; } + + /// + /// Footer left content. + /// + public string? FooterLeft { get; set; } + + /// + /// Sets the footer spacing. + /// + public int? FooterSpacing { get; set; } + + /// + /// Sets the variables to replace in the header and footer html + /// + /// Replaces [name] with value in header and footer (repeatable). + public Dictionary Replacements { get; set; } + +} \ No newline at end of file diff --git a/PdfApi/Binaries/file.pdf b/PdfApi/Binaries/file.pdf new file mode 100644 index 0000000..203e2e6 Binary files /dev/null and b/PdfApi/Binaries/file.pdf differ diff --git a/PdfApi/Binaries/libwkhtmltox.so b/PdfApi/Binaries/libwkhtmltox.so new file mode 100644 index 0000000..eecc883 Binary files /dev/null and b/PdfApi/Binaries/libwkhtmltox.so differ diff --git a/PdfApi/Binaries/wkhtmltopdf b/PdfApi/Binaries/wkhtmltopdf new file mode 100644 index 0000000..08c32ee Binary files /dev/null and b/PdfApi/Binaries/wkhtmltopdf differ diff --git a/PdfApi/Binaries/wkhtmltopdf.exe b/PdfApi/Binaries/wkhtmltopdf.exe new file mode 100644 index 0000000..af5283b Binary files /dev/null and b/PdfApi/Binaries/wkhtmltopdf.exe differ diff --git a/PdfApi/Binaries/wkhtmltox.dll b/PdfApi/Binaries/wkhtmltox.dll new file mode 100644 index 0000000..98a007b Binary files /dev/null and b/PdfApi/Binaries/wkhtmltox.dll differ diff --git a/PdfApi/Controllers/WkController.cs b/PdfApi/Controllers/WkController.cs new file mode 100644 index 0000000..32e99dc --- /dev/null +++ b/PdfApi/Controllers/WkController.cs @@ -0,0 +1,117 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Razor.Language.Extensions; +using PdfApi.Shared; +using Wkhtmltopdf.NetCore; + +namespace PdfApi.Controllers; +[ApiController] +[Route("[controller]")] +public class WkController : ControllerBase +{ + private readonly ILogger _logger; + private readonly IGeneratePdf _generator; + + public WkController(ILogger logger, IGeneratePdf generator) + { + _logger = logger; + _generator = generator; + } + + [HttpPost] + public async Task Post([FromBody] WkPdfRequest request) + { + try + { + ConvertOptions convertOptions = new() + { + PageSize = (Wkhtmltopdf.NetCore.Options.Size?)request.PageSize, + PageWidth = request.PageWidth, + PageHeight = request.PageHeight, + PageOrientation = (Wkhtmltopdf.NetCore.Options.Orientation?)request.PageOrientation, + PageMargins = new() + { + Top = request.PageMargins.Top, + Bottom = request.PageMargins.Bottom, + Left = request.PageMargins.Left, + Right = request.PageMargins.Right, + }, + EnableForms = request.EnableForms, + IsLowQuality = request.IsLowQuality, + Copies = request.Copies, + ImageDpi = request.ImageDpi, + ImageQuality = request.ImageQuality, + Dpi = request.Dpi, + IsGrayScale = request.IsGrayScale, + PrintMediaType = request.PrintMediaType, + DisableSmartShrinking = request.DisableSmartShrinking, + NoOutline = request.NoOutline, + DisableExternalLinks = request.DisableExternalLinks, + DisableInternalLinks = request.DisableInternalLinks, + NoBackground = request.NoBackground, + FooterLine = request.FooterLine, + HeaderHtml = request.HeaderHtml, + HeaderSpacing = request.HeaderSpacing, + FooterHtml = request.FooterHtml, + FooterCenter = request.FooterCenter, + FooterLeft = request.FooterLeft, + FooterSpacing = request.FooterSpacing, + Replacements = request.Replacements + }; + + _generator.SetConvertOptions(convertOptions); + var pdf = await _generator.GetPdf(new Uri(request.Url)); + if (pdf == null) + return BadRequest("Não foi possível gerar o PDF."); + + return pdf; + } + catch (Exception ex) + { + _logger.LogCritical(ex.Message, ex); + return base.BadRequest("Erro crítico: não foi possível gerar o PDF."); + } + } + [HttpGet] + public async Task Get() + { + try + { + ConvertOptions convertOptions = new() + { + FooterRight = "[page]/[toPage]", + FooterLeft = "[datetime]", + Replacements = new Dictionary + { + {"datetime", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss")} + }, + PageMargins = new () + { + Right = 10, + Left = 4 + }, + FooterSpacing = 3, + IsLowQuality = true, + NoOutline = true, + ImageDpi = 300, + DisableExternalLinks = true, + DisableInternalLinks = true, + ImageQuality = 80, + PrintMediaType = true, + Dpi = 70, + FooterLine = true + }; + + _generator.SetConvertOptions(convertOptions); + var pdf = await _generator.GetPdf(new Uri("https://painel.teorico.com.br/Classes/presenceData/2e337e88-7587-4a70-8adb-0a7b7988be80?key=pdfExportInternalOnlyChangeThisLater&offset=0")); + if (pdf == null) + return BadRequest("Não foi possível gerar o PDF."); + + return pdf; + } + catch (Exception ex) + { + _logger.LogCritical(ex.Message, ex); + return base.BadRequest("Erro crítico: não foi possível gerar o PDF."); + } + } +} diff --git a/PdfApi/Dockerfile b/PdfApi/Dockerfile new file mode 100644 index 0000000..9f2171c --- /dev/null +++ b/PdfApi/Dockerfile @@ -0,0 +1,23 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base +RUN apt update -y && apt install wkhtmltopdf -y +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /src +COPY ["PdfApi/PdfApi.csproj", "PdfApi/"] +RUN dotnet restore "PdfApi/PdfApi.csproj" +COPY . . +WORKDIR "/src/PdfApi" +RUN dotnet build "PdfApi.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "PdfApi.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "PdfApi.dll"] \ No newline at end of file diff --git a/PdfApi/PdfApi.csproj b/PdfApi/PdfApi.csproj new file mode 100644 index 0000000..76c250a --- /dev/null +++ b/PdfApi/PdfApi.csproj @@ -0,0 +1,39 @@ + + + + net6.0 + enable + enable + a34498da-1699-4c2e-9f4e-4100bc5f4c42 + Linux + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/PdfApi/PdfApi.csproj.DotSettings b/PdfApi/PdfApi.csproj.DotSettings new file mode 100644 index 0000000..e31db2f --- /dev/null +++ b/PdfApi/PdfApi.csproj.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/PdfApi/Program.cs b/PdfApi/Program.cs new file mode 100644 index 0000000..b018fe5 --- /dev/null +++ b/PdfApi/Program.cs @@ -0,0 +1,29 @@ +using Microsoft.CodeAnalysis; +using Wkhtmltopdf.NetCore; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.WebHost.UseKestrel(kestrel => +{ + kestrel.ListenAnyIP(80); +}); +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddWkhtmltopdf("./Binaries"); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/PdfApi/appsettings.Development.json b/PdfApi/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/PdfApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/PdfApi/appsettings.json b/PdfApi/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/PdfApi/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Wkhtmltopdf.NetCore.sln b/Wkhtmltopdf.NetCore.sln index 2240f5e..119e61d 100644 --- a/Wkhtmltopdf.NetCore.sln +++ b/Wkhtmltopdf.NetCore.sln @@ -1,10 +1,16 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2041 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31919.166 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wkhtmltopdf.NetCore", "Wkhtmltopdf.NetCore\Wkhtmltopdf.NetCore.csproj", "{AD64E16D-A86E-47AD-8DEF-0B1346A5E30D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfApi", "PdfApi\PdfApi.csproj", "{56BC61DF-7849-4BCB-9D83-827650041F30}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfApi.Client", "PdfApi.Client\PdfApi.Client.csproj", "{29E472AB-02F4-470E-B78F-6C5868C3A1DE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfApi.Shared", "PdfApi.Shared\PdfApi.Shared.csproj", "{9BE46FD9-609A-4BC3-B8A3-820D4EB61B21}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +21,18 @@ Global {AD64E16D-A86E-47AD-8DEF-0B1346A5E30D}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD64E16D-A86E-47AD-8DEF-0B1346A5E30D}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD64E16D-A86E-47AD-8DEF-0B1346A5E30D}.Release|Any CPU.Build.0 = Release|Any CPU + {56BC61DF-7849-4BCB-9D83-827650041F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56BC61DF-7849-4BCB-9D83-827650041F30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56BC61DF-7849-4BCB-9D83-827650041F30}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56BC61DF-7849-4BCB-9D83-827650041F30}.Release|Any CPU.Build.0 = Release|Any CPU + {29E472AB-02F4-470E-B78F-6C5868C3A1DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29E472AB-02F4-470E-B78F-6C5868C3A1DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29E472AB-02F4-470E-B78F-6C5868C3A1DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29E472AB-02F4-470E-B78F-6C5868C3A1DE}.Release|Any CPU.Build.0 = Release|Any CPU + {9BE46FD9-609A-4BC3-B8A3-820D4EB61B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BE46FD9-609A-4BC3-B8A3-820D4EB61B21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BE46FD9-609A-4BC3-B8A3-820D4EB61B21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BE46FD9-609A-4BC3-B8A3-820D4EB61B21}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Wkhtmltopdf.NetCore/Implementation/ConvertOptions.cs b/Wkhtmltopdf.NetCore/Implementation/ConvertOptions.cs index de6f948..a79b641 100644 --- a/Wkhtmltopdf.NetCore/Implementation/ConvertOptions.cs +++ b/Wkhtmltopdf.NetCore/Implementation/ConvertOptions.cs @@ -1,6 +1,7 @@ using Wkhtmltopdf.NetCore.Options; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Text; using Wkhtmltopdf.NetCore.Interfaces; @@ -62,18 +63,81 @@ protected string GetContentType() [OptionFlag("-l")] public bool IsLowQuality { get; set; } + /// /// Number of copies to print into the PDF file. /// [OptionFlag("--copies")] public int? Copies { get; set; } + /// + /// When embedding images scale them down to this dpi (default 600) + /// + [OptionFlag("--image-dpi")] + public uint? ImageDpi { get; set; } + + + /// + /// When jpeg compressing images use this quality (default 94) + /// + [OptionFlag("--image-quality")] + public uint? ImageQuality { get; set; } + + + /// + /// When jpeg compressing images use this quality (default 94) + /// + [OptionFlag("--dpi")] + public uint? Dpi { get; set; } + /// /// Indicates whether the PDF should be generated in grayscale. /// [OptionFlag("-g")] public bool IsGrayScale { get; set; } + /// + /// Indicates whether the PDF should be generated in grayscale. + /// + [OptionFlag("--print-media-type")] + public bool PrintMediaType { get; set; } + + /// + /// Disable the intelligent shrinking strategy used by WebKit that makes the pixel/dpi ratio non-constant. + /// + [OptionFlag("--disable-smart-shrinking")] + public bool DisableSmartShrinking { get; set; } + + /// + /// Do not put an outline into the pdf + /// + [OptionFlag("--no-outline")] + public bool NoOutline { get; set; } + + /// + /// Do not make links to remote web pages + /// + [OptionFlag("--disable-external-links")] + public bool DisableExternalLinks { get; set; } + + /// + /// Do not make local links + /// + [OptionFlag("--disable-internal-links")] + public bool DisableInternalLinks { get; set; } + + /// + /// Do not print background + /// + [OptionFlag("--no-background")] + public bool NoBackground { get; set; } + + /// + /// Display line above the footer + /// + [OptionFlag("--footer-line")] + public bool FooterLine { get; set; } + /// /// Path to header HTML file. /// @@ -92,6 +156,24 @@ protected string GetContentType() [OptionFlag("--footer-html")] public string FooterHtml { get; set; } + /// + /// Footer right content. + /// + [OptionFlag("--footer-right")] + public string FooterRight { get; set; } + + /// + /// Footer center content. + /// + [OptionFlag("--footer-center")] + public string FooterCenter { get; set; } + + /// + /// Footer left content. + /// + [OptionFlag("--footer-left")] + public string FooterLeft { get; set; } + /// /// Sets the footer spacing. /// @@ -135,7 +217,7 @@ protected string GetConvertBaseOptions() if (fi.PropertyType == typeof(Dictionary)) { - var dictionary = (Dictionary)value; + var dictionary = (Dictionary) value; foreach (var d in dictionary) { result.AppendFormat(" {0} \"{1}\" \"{2}\"", of.Name, d.Key, d.Value); @@ -143,7 +225,7 @@ protected string GetConvertBaseOptions() } else if (fi.PropertyType == typeof(bool)) { - if ((bool)value) + if ((bool) value) result.AppendFormat(CultureInfo.InvariantCulture, " {0}", of.Name); } else diff --git a/Wkhtmltopdf.NetCore/Implementation/GeneratePdf.cs b/Wkhtmltopdf.NetCore/Implementation/GeneratePdf.cs index da619b3..751dd7d 100644 --- a/Wkhtmltopdf.NetCore/Implementation/GeneratePdf.cs +++ b/Wkhtmltopdf.NetCore/Implementation/GeneratePdf.cs @@ -22,9 +22,16 @@ public void SetConvertOptions(IConvertOptions convertOptions) _convertOptions = convertOptions; } - public byte[] GetPDF(string html) + public Task GetPDF(string html) { - return WkhtmlDriver.Convert(WkhtmltopdfConfiguration.RotativaPath, _convertOptions.GetConvertOptions(), html); + return WkhtmlDriver.Convert(WkhtmltopdfConfiguration.RotativaPath, _convertOptions.GetConvertOptions(), + html); + } + + public Task GetPDF(Uri url) + { + return WkhtmlDriver.Convert(WkhtmltopdfConfiguration.RotativaPath, _convertOptions.GetConvertOptions(), + url); } public async Task GetByteArray(string View, T model) @@ -32,7 +39,7 @@ public async Task GetByteArray(string View, T model) try { var html = await _engine.RenderViewToStringAsync(View, model); - return GetPDF(html); + return await GetPDF(html); } catch (Exception ex) { @@ -43,9 +50,18 @@ public async Task GetByteArray(string View, T model) public async Task GetPdf(string View, T model) { var html = await _engine.RenderViewToStringAsync(View, model); - var byteArray = GetPDF(html); + var byteArray = await GetPDF(html); MemoryStream pdfStream = new MemoryStream(); - pdfStream.Write(byteArray, 0, byteArray.Length); + await pdfStream.WriteAsync(byteArray, 0, byteArray.Length); + pdfStream.Position = 0; + return new FileStreamResult(pdfStream, "application/pdf"); + } + + public async Task GetPdf(Uri url) + { + var byteArray = await GetPDF(url); + MemoryStream pdfStream = new MemoryStream(); + await pdfStream.WriteAsync(byteArray, 0, byteArray.Length); pdfStream.Position = 0; return new FileStreamResult(pdfStream, "application/pdf"); } @@ -53,7 +69,7 @@ public async Task GetPdf(string View, T model) public async Task GetPdfViewInHtml(string ViewInHtml, T model) { var html = await _engine.RenderHtmlToStringAsync(ViewInHtml, model); - var byteArray = GetPDF(html); + var byteArray = await GetPDF(html); MemoryStream pdfStream = new MemoryStream(); pdfStream.Write(byteArray, 0, byteArray.Length); pdfStream.Position = 0; @@ -65,7 +81,7 @@ public async Task GetByteArrayViewInHtml(string ViewInHtml, T model) try { var view = await _engine.RenderHtmlToStringAsync(ViewInHtml, model); - return GetPDF(view); + return await GetPDF(view); } catch (Exception ex) { @@ -82,7 +98,7 @@ public async Task GetByteArrayViewInHtml(string ViewInHtml, T model) public async Task GetPdfViewInHtml(string ViewInHtml) { var html = await _engine.RenderHtmlToStringAsync(ViewInHtml); - var byteArray = GetPDF(html); + var byteArray = await GetPDF(html); MemoryStream pdfStream = new MemoryStream(); pdfStream.Write(byteArray, 0, byteArray.Length); pdfStream.Position = 0; @@ -94,7 +110,7 @@ public async Task GetByteArrayViewInHtml(string ViewInHtml) try { var view = await _engine.RenderHtmlToStringAsync(ViewInHtml); - return GetPDF(view); + return await GetPDF(view); } catch (Exception ex) { @@ -105,19 +121,28 @@ public async Task GetByteArrayViewInHtml(string ViewInHtml) public async Task GetPdf(string View) { var html = await _engine.RenderViewToStringAsync(View); - var byteArray = GetPDF(html); + var byteArray = await GetPDF(html); MemoryStream pdfStream = new MemoryStream(); pdfStream.Write(byteArray, 0, byteArray.Length); pdfStream.Position = 0; return new FileStreamResult(pdfStream, "application/pdf"); } + public async Task GetPdf(Uri url) + { + var byteArray = await GetPDF(url); + MemoryStream pdfStream = new MemoryStream(); + await pdfStream.WriteAsync(byteArray, 0, byteArray.Length); + pdfStream.Position = 0; + return new FileStreamResult(pdfStream, "application/pdf"); + } + public async Task GetByteArray(string View) { try { var html = await _engine.RenderViewToStringAsync(View); - return GetPDF(html); + return await GetPDF(html); } catch (Exception ex) { diff --git a/Wkhtmltopdf.NetCore/Implementation/Interfaces/IGeneratePdf.cs b/Wkhtmltopdf.NetCore/Implementation/Interfaces/IGeneratePdf.cs index d411eef..1468886 100644 --- a/Wkhtmltopdf.NetCore/Implementation/Interfaces/IGeneratePdf.cs +++ b/Wkhtmltopdf.NetCore/Implementation/Interfaces/IGeneratePdf.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Wkhtmltopdf.NetCore.Interfaces; @@ -9,13 +10,16 @@ public interface IGeneratePdf Task GetPdfViewInHtml(string ViewInHtml, T model); Task GetByteArrayViewInHtml(string ViewInHtml, T model); Task GetPdf(string View, T model); + Task GetPdf(Uri url); Task GetByteArray(string View, T model); Task GetPdfViewInHtml(string ViewInHtml); Task GetByteArrayViewInHtml(string ViewInHtml); Task GetPdf(string View); + Task GetPdf(Uri url); Task GetByteArray(string View); void SetConvertOptions(IConvertOptions options); - byte[] GetPDF(string html); + Task GetPDF(string html); + Task GetPDF(Uri url); void UpdateView(string path, string viewHTML); bool ExistsView(string path); void AddView(string path, string viewHTML); diff --git a/Wkhtmltopdf.NetCore/WkhtmlDriver.cs b/Wkhtmltopdf.NetCore/WkhtmlDriver.cs index 18132aa..acfe76a 100644 --- a/Wkhtmltopdf.NetCore/WkhtmlDriver.cs +++ b/Wkhtmltopdf.NetCore/WkhtmlDriver.cs @@ -3,11 +3,77 @@ using System.IO; using System.Runtime.InteropServices; using System.Text; +using System.Threading.Tasks; namespace Wkhtmltopdf.NetCore { public abstract class WkhtmlDriver { + /// + /// Converts given URL or HTML string to PDF. + /// + /// Path to wkthmltopdf\wkthmltoimage. + /// Switches that will be passed to wkhtmltopdf binary. + /// Path to the url that should be converted to PDF. + /// PDF as byte array. + public static async Task Convert(string wkhtmlPath, string switches, Uri url) + { + // switches: + // "-q" - silent output, only errors - no progress messages + // " -" - switch output to stdout + // "- -" - switch input to stdin and output to stdout + + var file = Path.GetTempFileName(); + switches = $"-q {switches} \"{url}\" \"{file}\""; + + string rotativaLocation; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + rotativaLocation = Path.Combine(wkhtmlPath, "Windows", "wkhtmltopdf.exe"); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + rotativaLocation = Path.Combine(wkhtmlPath, "Mac", "wkhtmltopdf"); + } + else + { + rotativaLocation = Path.Combine(wkhtmlPath, "wkhtmltopdf"); + } + + if (!File.Exists(rotativaLocation)) + { + throw new Exception("wkhtmltopdf not found, searched for " + rotativaLocation); + } + + using (var proc = new Process()) + { + try + { + proc.StartInfo = new ProcessStartInfo + { + FileName = rotativaLocation, + Arguments = switches, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + CreateNoWindow = true + }; + + proc.Start(); + } + catch (Exception ex) + { + throw ex; + } + + await proc.WaitForExitAsync(); + + return await File.ReadAllBytesAsync(file); + } + } + /// /// Converts given URL or HTML string to PDF. /// @@ -15,7 +81,7 @@ public abstract class WkhtmlDriver /// Switches that will be passed to wkhtmltopdf binary. /// String containing HTML code that should be converted to PDF. /// PDF as byte array. - public static byte[] Convert(string wkhtmlPath, string switches, string html) + public static async Task Convert(string wkhtmlPath, string switches, string html) { string rotativaLocation; @@ -29,7 +95,7 @@ public static byte[] Convert(string wkhtmlPath, string switches, string html) } else { - rotativaLocation = Path.Combine(wkhtmlPath, "Linux", "wkhtmltopdf"); + rotativaLocation = Path.Combine(wkhtmlPath, "wkhtmltopdf"); } if (!File.Exists(rotativaLocation)) @@ -169,7 +235,7 @@ public static byte[] Convert(string wkhtmlPath, string switches, string html) private static string SpecialCharsEncode(string text) { var chars = text.ToCharArray(); - var result = new StringBuilder(text.Length + (int)(text.Length * 0.1)); + var result = new StringBuilder(text.Length + (int) (text.Length * 0.1)); foreach (var c in chars) { @@ -183,4 +249,4 @@ private static string SpecialCharsEncode(string text) return result.ToString(); } } -} +} \ No newline at end of file diff --git a/Wkhtmltopdf.NetCore/Wkhtmltopdf.NetCore.csproj b/Wkhtmltopdf.NetCore/Wkhtmltopdf.NetCore.csproj index 9b5dad3..efc7457 100644 --- a/Wkhtmltopdf.NetCore/Wkhtmltopdf.NetCore.csproj +++ b/Wkhtmltopdf.NetCore/Wkhtmltopdf.NetCore.csproj @@ -13,8 +13,8 @@ For more information about how to use it, go to https://github.com/fpanaccia/Wkh - - + + diff --git a/Wkhtmltopdf.NetCore/WkhtmltopdfConfiguration.cs b/Wkhtmltopdf.NetCore/WkhtmltopdfConfiguration.cs index f4f0053..02e430d 100644 --- a/Wkhtmltopdf.NetCore/WkhtmltopdfConfiguration.cs +++ b/Wkhtmltopdf.NetCore/WkhtmltopdfConfiguration.cs @@ -19,15 +19,17 @@ public static class WkhtmltopdfConfiguration /// /// The IServiceCollection object /// Optional. Relative path to the directory containing wkhtmltopdf. Default is "Rotativa". Download at https://wkhtmltopdf.org/downloads.html - public static IServiceCollection AddWkhtmltopdf(this IServiceCollection services, string wkhtmltopdfRelativePath = "Rotativa") + public static IServiceCollection AddWkhtmltopdf(this IServiceCollection services, + string wkhtmltopdfRelativePath = "Rotativa") { - RotativaPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, wkhtmltopdfRelativePath); + RotativaPath = wkhtmltopdfRelativePath; if (!Directory.Exists(RotativaPath)) { throw new Exception("Folder containing wkhtmltopdf not found, searched for " + RotativaPath); } + var fileProvider = new UpdateableFileProvider(); services.TryAddTransient(); services.TryAddSingleton(fileProvider); @@ -40,4 +42,4 @@ public static IServiceCollection AddWkhtmltopdf(this IServiceCollection services return services; } } -} +} \ No newline at end of file