diff --git a/BootstrapBlazor.Extensions.sln b/BootstrapBlazor.Extensions.sln index 65a3f31a..f215cb11 100644 --- a/BootstrapBlazor.Extensions.sln +++ b/BootstrapBlazor.Extensions.sln @@ -184,6 +184,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Authenticat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Graph", "src\components\BootstrapBlazor.Graph\BootstrapBlazor.Graph.csproj", "{CED55D86-57CF-CB0D-E880-370C44C0DB1F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Html2Pdf.Playwright", "src\components\BootstrapBlazor.Html2Pdf.Playwright\BootstrapBlazor.Html2Pdf.Playwright.csproj", "{F3043A78-1942-4524-BDC4-7E88F56DF3D5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -498,6 +500,10 @@ Global {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {CED55D86-57CF-CB0D-E880-370C44C0DB1F}.Release|Any CPU.Build.0 = Release|Any CPU + {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3043A78-1942-4524-BDC4-7E88F56DF3D5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -584,6 +590,7 @@ Global {2F37FBF4-5C1C-4493-B614-0E8361432621} = {FF1089BE-C704-4374-B629-C57C08E1798F} {1FDDF0AD-7AB6-4706-A183-26C680817BB4} = {FF1089BE-C704-4374-B629-C57C08E1798F} {CED55D86-57CF-CB0D-E880-370C44C0DB1F} = {FF1089BE-C704-4374-B629-C57C08E1798F} + {F3043A78-1942-4524-BDC4-7E88F56DF3D5} = {FF1089BE-C704-4374-B629-C57C08E1798F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF} diff --git a/src/components/BootstrapBlazor.Html2Pdf.Playwright/BootstrapBlazor.Html2Pdf.Playwright.csproj b/src/components/BootstrapBlazor.Html2Pdf.Playwright/BootstrapBlazor.Html2Pdf.Playwright.csproj new file mode 100644 index 00000000..2376acf3 --- /dev/null +++ b/src/components/BootstrapBlazor.Html2Pdf.Playwright/BootstrapBlazor.Html2Pdf.Playwright.csproj @@ -0,0 +1,17 @@ + + + + 9.0.0 + + + + Bootstrap Blazor WebAssembly wasm UI Components Pdf + Bootstrap UI components extensions of Html2Pdf use Playwright lib + + + + + + + + diff --git a/src/components/BootstrapBlazor.Html2Pdf.Playwright/Extensions/ServiceCollectionExtensions.cs b/src/components/BootstrapBlazor.Html2Pdf.Playwright/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..fc9e11d8 --- /dev/null +++ b/src/components/BootstrapBlazor.Html2Pdf.Playwright/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using BootstrapBlazor.Components; + +namespace Microsoft.Extensions.DependencyInjection; + +/// +/// BootstrapBlazor 服务扩展类 +/// +public static class BootstrapBlazorHtml2PdfServiceExtensions +{ + /// + /// 添加 AzureOpenAIService 服务 + /// + /// + public static IServiceCollection AddBootstrapBlazorHtml2PdfUsePlaywrightService(this IServiceCollection services) + { + services.AddSingleton(); +#if NET8_0_OR_GREATER + services.AddKeyedSingleton("BootstrapBlazor.Html2Pdf.Playwright"); +#endif + return services; + } +} diff --git a/src/components/BootstrapBlazor.Html2Pdf.Playwright/Services/DefaultPdfService.cs b/src/components/BootstrapBlazor.Html2Pdf.Playwright/Services/DefaultPdfService.cs new file mode 100644 index 00000000..6f9540db --- /dev/null +++ b/src/components/BootstrapBlazor.Html2Pdf.Playwright/Services/DefaultPdfService.cs @@ -0,0 +1,131 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using Microsoft.Playwright; + +namespace BootstrapBlazor.Components; + +/// +/// 默认 Html to Pdf 实现 +/// +class DefaultPdfService : IHtml2Pdf +{ + /// + /// + /// + public Task PdfDataAsync(string url) + { + return GeneratePdfFromUrlAsync(url); + } + + /// + /// + /// + public async Task PdfStreamAsync(string url) + { + var data = await GeneratePdfFromUrlAsync(url); + return new MemoryStream(data); + } + + /// + /// Export method + /// + /// html raw string + /// + /// + public Task PdfDataFromHtmlAsync(string html, IEnumerable? links = null, IEnumerable? scripts = null) + { + return GeneratePdfFromHtmlAsync(html, links, scripts); + } + + /// + /// Export method + /// + /// html raw string + /// + /// + public async Task PdfStreamFromHtmlAsync(string html, IEnumerable? links = null, IEnumerable? scripts = null) + { + var data = await PdfDataFromHtmlAsync(html, links, scripts); + return new MemoryStream(data); + } + + private static async Task GeneratePdfFromUrlAsync(string url) + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Chromium.LaunchAsync(CreateOptions()); + + await using var context = await browser.NewContextAsync(); + var page = await context.NewPageAsync(); + + await page.GotoAsync(url, new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle }); + return await page.PdfAsync(new PagePdfOptions + { + Format = "A4", + Landscape = false, + }); + } + + private static async Task GeneratePdfFromHtmlAsync(string html, IEnumerable? links = null, IEnumerable? scripts = null) + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Chromium.LaunchAsync(CreateOptions()); + + await using var context = await browser.NewContextAsync(); + var page = await context.NewPageAsync(); + + await page.SetContentAsync(html); + await AddStyleTagAsync(page, links); + await AddScriptTagAsync(page, scripts); + + return await page.PdfAsync(new PagePdfOptions + { + Format = "A4", + Landscape = false, + }); + } + + private static BrowserTypeLaunchOptions CreateOptions() => new() + { + Headless = true, + Args = ["--no-sandbox", "--disable-setuid-sandbox", "--disable-web-security"] + }; + + private static async Task AddStyleTagAsync(IPage page, IEnumerable? links = null) + { + var styles = new List(); + + if (links != null) + { + styles.AddRange(links); + } + + foreach (var link in styles) + { + await page.AddStyleTagAsync(new PageAddStyleTagOptions() + { + Url = link, + }); + } + } + + private static async Task AddScriptTagAsync(IPage page, IEnumerable? scripts = null) + { + var tags = new List(); + + if (scripts != null) + { + tags.AddRange(scripts); + } + + foreach (var script in tags) + { + await page.AddScriptTagAsync(new PageAddScriptTagOptions() + { + Url = script, + Type = "script" + }); + } + } +} diff --git a/src/components/BootstrapBlazor.Html2Pdf.Playwright/_Imports.razor b/src/components/BootstrapBlazor.Html2Pdf.Playwright/_Imports.razor new file mode 100644 index 00000000..77285129 --- /dev/null +++ b/src/components/BootstrapBlazor.Html2Pdf.Playwright/_Imports.razor @@ -0,0 +1 @@ +@using Microsoft.AspNetCore.Components.Web