Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions BootstrapBlazor.Extensions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.0.0</Version>
</PropertyGroup>

<PropertyGroup>
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Pdf</PackageTags>
<Description>Bootstrap UI components extensions of Html2Pdf use Playwright lib</Description>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BootstrapBlazor" Version="$(BBVersion)" />
<PackageReference Include="Microsoft.Playwright" Version="1.52.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// BootstrapBlazor 服务扩展类
/// </summary>
public static class BootstrapBlazorHtml2PdfServiceExtensions
{
/// <summary>
/// 添加 AzureOpenAIService 服务
/// </summary>
/// <param name="services"></param>
public static IServiceCollection AddBootstrapBlazorHtml2PdfUsePlaywrightService(this IServiceCollection services)
{
services.AddSingleton<IHtml2Pdf, DefaultPdfService>();
#if NET8_0_OR_GREATER
services.AddKeyedSingleton<IHtml2Pdf, DefaultPdfService>("BootstrapBlazor.Html2Pdf.Playwright");
#endif
return services;
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// 默认 Html to Pdf 实现
/// </summary>
class DefaultPdfService : IHtml2Pdf
{
/// <summary>
/// <inheritdoc/>
/// </summary>
public Task<byte[]> PdfDataAsync(string url)
{
return GeneratePdfFromUrlAsync(url);
}

/// <summary>
/// <inheritdoc/>
/// </summary>
public async Task<Stream> PdfStreamAsync(string url)
{
var data = await GeneratePdfFromUrlAsync(url);
return new MemoryStream(data);
}

/// <summary>
/// Export method
/// </summary>
/// <param name="html">html raw string</param>
/// <param name="links"></param>
/// <param name="scripts"></param>
public Task<byte[]> PdfDataFromHtmlAsync(string html, IEnumerable<string>? links = null, IEnumerable<string>? scripts = null)
{
return GeneratePdfFromHtmlAsync(html, links, scripts);
}

/// <summary>
/// Export method
/// </summary>
/// <param name="html">html raw string</param>
/// <param name="links"></param>
/// <param name="scripts"></param>
public async Task<Stream> PdfStreamFromHtmlAsync(string html, IEnumerable<string>? links = null, IEnumerable<string>? scripts = null)
{
var data = await PdfDataFromHtmlAsync(html, links, scripts);
return new MemoryStream(data);
}

private static async Task<byte[]> 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<byte[]> GeneratePdfFromHtmlAsync(string html, IEnumerable<string>? links = null, IEnumerable<string>? 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<string>? links = null)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider iterating directly over the input enumerable in AddStyleTagAsync and AddScriptTagAsync to avoid creating intermediate lists.

Consider simplifying the tag helpers to avoid creating intermediate lists. For example, you can iterate directly over the supplied enumerable. This reduces unnecessary nesting and improves readability without changing functionality. Here's a suggestion:

private static async Task AddStyleTagAsync(IPage page, IEnumerable<string>? links = null)
{
    if (links == null) return;

    foreach (var link in links)
    {
        await page.AddStyleTagAsync(new PageAddStyleTagOptions { Url = link });
    }
}

private static async Task AddScriptTagAsync(IPage page, IEnumerable<string>? scripts = null)
{
    if (scripts == null) return;

    foreach (var script in scripts)
    {
        await page.AddScriptTagAsync(new PageAddScriptTagOptions { Url = script, Type = "script" });
    }
}

These concise changes remove unnecessary list instantiation while keeping functionality intact.

{
var styles = new List<string>();

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<string>? scripts = null)
{
var tags = new List<string>();

if (scripts != null)
{
tags.AddRange(scripts);
}

foreach (var script in tags)
{
await page.AddScriptTagAsync(new PageAddScriptTagOptions()
{
Url = script,
Type = "script"
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@using Microsoft.AspNetCore.Components.Web