HubtelPdfGenerator.Playwright
1.0.6
dotnet add package HubtelPdfGenerator.Playwright --version 1.0.6
NuGet\Install-Package HubtelPdfGenerator.Playwright -Version 1.0.6
<PackageReference Include="HubtelPdfGenerator.Playwright" Version="1.0.6" />
<PackageVersion Include="HubtelPdfGenerator.Playwright" Version="1.0.6" />
<PackageReference Include="HubtelPdfGenerator.Playwright" />
paket add HubtelPdfGenerator.Playwright --version 1.0.6
#r "nuget: HubtelPdfGenerator.Playwright, 1.0.6"
#:package HubtelPdfGenerator.Playwright@1.0.6
#addin nuget:?package=HubtelPdfGenerator.Playwright&version=1.0.6
#tool nuget:?package=HubtelPdfGenerator.Playwright&version=1.0.6
Hubtel HTML to PDF SDK
HubtelPdfGenerator is a modular .NET SDK for converting HTML to PDF using Playwright + Chromium, with support for advanced headers/footers, page visibility rules, watermarks, and robust print pagination behavior.
This guide is written for production use across Windows, macOS, Linux, and containers.
What You Get
- HTML string to PDF (
byte[]orStream) - Custom header and footer templates
- Page-number tokens (
pageNumber,totalPages,date,title,url) - Per-page header/footer visibility rules (first/last/exclude, etc.)
- Dedicated last-page footer template support
- Text and image watermarks
- Modern page-break support (
break-*) with compatibility helper CSS - Cross-platform runtime support: Windows, macOS, Linux, Docker
Packages
Choose the package set based on your usage style:
HubtelPdfGenerator
Facade package with DI registration (AddHubtelPdfGenerator()).HubtelPdfGenerator.Playwright
Concrete converter implementation (PlaywrightHtmlToPdfConverter).HubtelPdfGenerator.Abstractions
Contracts/options (IHtmlToPdfConverter,PdfOptions, etc.).
Target Framework Guidance
The SDK package targets netstandard2.0, so it can be consumed by .NET Core 3.1+ and later .NET versions.
Install from NuGet Feed
Assuming your organization already published the packages to a NuGet feed:
1) Add your feed source
dotnet nuget add source "https://your-feed-url/index.json" --name hubtel-feed
If credentials are required, use your standard feed auth process (PAT, Azure Artifacts credential provider, GitHub Packages token, etc.).
2) Install package(s)
With DI facade:
dotnet add package HubtelPdfGenerator
Or direct implementation:
dotnet add package HubtelPdfGenerator.Playwright
dotnet add package HubtelPdfGenerator.Abstractions
Quick Start
DI-based usage (recommended)
using HubtelPdfGenerator;
using HubtelPdfGenerator.Abstractions;
// Program.cs / Startup.cs
services.AddHubtelPdfGenerator();
Then inject and use:
using HubtelPdfGenerator.Abstractions;
public sealed class ReportService
{
private readonly IHtmlToPdfConverter _converter;
public ReportService(IHtmlToPdfConverter converter)
{
_converter = converter;
}
public async Task<byte[]> RenderAsync(string html, CancellationToken ct)
{
var options = new PdfOptions
{
Format = "A4",
PrintBackground = true,
HeaderFooter = new HeaderFooterOptions
{
Enabled = true,
FooterTemplateHtml =
"<div style='font-size:10px;width:100%;text-align:right;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
}
};
return await _converter.ConvertHtmlAsync(html, options, ct);
}
}
Direct usage (no DI)
using HubtelPdfGenerator.Abstractions;
using HubtelPdfGenerator.Playwright;
var converter = new PlaywrightHtmlToPdfConverter();
var options = new PdfOptions
{
HeaderFooter = new HeaderFooterOptions
{
Enabled = true,
FooterTemplateHtml =
"<div style='font-size:10px;width:100%;text-align:right;'>Page <span class='pageNumber'></span> / <span class='totalPages'></span></div>"
},
Watermark = new WatermarkOptions
{
Kind = WatermarkKind.Text,
Text = "CONFIDENTIAL",
Opacity = 0.12m,
RotationDegrees = -30
}
};
byte[] pdf = await converter.ConvertHtmlAsync("<html><body><h1>Hello PDF</h1></body></html>", options);
API Surface
IHtmlToPdfConverter exposes:
ConvertHtmlAsync(string html, PdfOptions options, CancellationToken ct = default)ConvertHtmlAsync(string html, Stream output, PdfOptions options, CancellationToken ct = default)ConvertHtmlAsync(string html, IReadOnlyCollection<HtmlResource> resources, PdfOptions options, CancellationToken ct = default)
Configuration Reference
PdfOptions
BaseUrl- base URL for relative asset resolutionFormat- page format (defaultA4)Landscape- landscape orientationPrintBackground- include CSS backgroundsScale- print scaleMarginTopInches,MarginRightInches,MarginBottomInches,MarginLeftInchesTimeoutMs- render timeoutHeaderFooter- header/footer optionsWatermark- optional watermark configurationAddCompatPageBreakStyles- include compatibility page-break styles (defaulttrue)BrowserExecutablePath- custom Chromium path when neededDisableSandbox- Chromium sandbox toggle (use carefully; usually container-specific)
HeaderFooterOptions
EnabledHeaderTemplateHtmlFooterTemplateHtmlLastPageFooterTemplateHtmlHeaderVisibility(AllPages,FirstPageOnly,ExcludeFirstPage,LastPageOnly,ExcludeLastPage,None)FooterVisibility(same enum values)ReplaceFooterWithLastPageFooter(defaulttrue)UseChromiumDefaultHeaderFooterWhenTemplateMissing
Setfalseto suppress Chromium default date/title/url when templates are missing.
Last-page footer behavior:
ReplaceFooterWithLastPageFooter = true→ last page shows onlyLastPageFooterTemplateHtml(regular footer is replaced).ReplaceFooterWithLastPageFooter = false→ last page can show both the regular footer andLastPageFooterTemplateHtml.- Combine with
FooterVisibilityto choose regular footer visibility (for exampleAllPagesvsExcludeLastPage).
WatermarkOptions
Kind(TextorImage)TextImageUrlOpacityRotationDegreesFontFamilyFontSizePxColorHexZIndex
Headers, Footers, and Last-Page Footer Example
var options = new PdfOptions
{
HeaderFooter = new HeaderFooterOptions
{
Enabled = true,
HeaderTemplateHtml = "<div style='font-size:9px;width:100%;padding:0 16px;'>My Header</div>",
FooterTemplateHtml = "<div style='font-size:9px;width:100%;padding:0 16px;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
LastPageFooterTemplateHtml = "<div style='font-size:9px;width:100%;padding:0 16px;'>Disclaimer text for final page</div>",
HeaderVisibility = HeaderFooterPageVisibility.FirstPageOnly,
FooterVisibility = HeaderFooterPageVisibility.ExcludeLastPage,
ReplaceFooterWithLastPageFooter = true,
UseChromiumDefaultHeaderFooterWhenTemplateMissing = false
}
};
Resource Loading (CSS/Images/Fonts)
If HTML contains relative paths, use BaseUrl and/or in-memory resources:
var resources = new[]
{
new HtmlResource("/styles/report.css", "text/css", File.ReadAllBytes("report.css")),
new HtmlResource("/images/logo.png", "image/png", File.ReadAllBytes("logo.png"))
};
var options = new PdfOptions { BaseUrl = "https://assets.example.com/" };
byte[] pdf = await converter.ConvertHtmlAsync(html, resources, options);
For maximum reliability in generated headers/footers/watermarks, prefer embedded or absolute assets (for example base64 data URIs).
Page Break Guidance (Important for Large Reports)
Use modern print CSS:
.group { break-inside: avoid; }
.allow-flow { break-inside: auto; }
.new-page { break-before: page; }
thead { display: table-header-group; }
tr { break-inside: avoid; }
PdfOptions.AddCompatPageBreakStyles is enabled by default to improve behavior on older/quirky print engines.
Playwright and Chromium installation (required)
The NuGet packages do not bundle the browser. At runtime, Playwright must find a Chromium build compatible with your installed Microsoft.Playwright version. PDF generation uses headless Chromium (installed via Playwright), not a separate Google Chrome install.
Why two steps?
- Browser binaries — downloaded by Playwright into a per-user cache (see below).
- Linux system libraries — on many distros, Chromium needs extra packages; Playwright can install them when you use
--with-deps.
Prerequisites
- .NET project that references
HubtelPdfGenerator/Microsoft.Playwright, thendotnet buildso the Playwright install script is copied to your output folder. - PowerShell (
pwsh) on macOS and Linux if you use the generatedplaywright.ps1(recommended). macOS and many Linux images do not includepwshby default — see Ifpwshis not found below. - Alternatively, use the global Playwright CLI (no
pwshrequired for that path).
If pwsh is not found (macOS / Linux)
The install script playwright.ps1 is PowerShell. If the shell reports command not found: pwsh, pick one of these:
Option A — Install PowerShell (then use the project script)
macOS (Homebrew):
brew install powershellThen confirm:
pwsh -VersionOther install methods: Install PowerShell on macOS.
Linux: see Install PowerShell on Linux for your distro (packages or direct download).
Option B — Skip pwsh: use the global Playwright CLI
You do not need PowerShell for this path:
dotnet tool install --global Microsoft.Playwright.CLI
playwright install chromium
On Linux, prefer:
playwright install --with-deps chromium
Ensure ~/.dotnet/tools is on your PATH (the installer usually tells you to export it). Then run playwright from the same machine user that will run your app.
Recommended: install from your app’s build output (version-aligned)
From your solution or project directory (the folder that contains your .csproj):
dotnet build
Then run the script under the configuration and target framework you use (examples below — replace net8.0 / Release with yours, e.g. net6.0, net9.0):
macOS — install PowerShell first (once per machine), then run the script:
brew install powershell
pwsh ./bin/Debug/net8.0/playwright.ps1 install chromium
Linux
pwsh ./bin/Debug/net8.0/playwright.ps1 install chromium
For Linux, prefer installing OS dependencies in the same step:
pwsh ./bin/Debug/net8.0/playwright.ps1 install --with-deps chromium
Windows (PowerShell)
pwsh .\bin\Debug\net8.0\playwright.ps1 install chromium
Release output example:
pwsh ./bin/Release/net8.0/playwright.ps1 install --with-deps chromium
This ties the browser build to the same Playwright version your app restored from NuGet.
Alternative: global Playwright CLI
Useful for CI images or when you do not want to invoke playwright.ps1 from output:
dotnet tool install --global Microsoft.Playwright.CLI
playwright install chromium
On Linux, install system dependencies as well:
playwright install --with-deps chromium
The global CLI should be kept roughly in sync with the Microsoft.Playwright package version your app uses.
macOS
Install PowerShell (required for the project script path):
brew install powershellOr see If
pwshis not found for alternatives (global CLI, nopwsh).Build your app (
dotnet build).Install Chromium (pick one):
Project script (recommended; requires
pwsh)pwsh ./bin/Debug/net8.0/playwright.ps1 install chromiumGlobal CLI (no
pwsh)dotnet tool install --global Microsoft.Playwright.CLI playwright install chromiumTypical cache location for browser files:
~/Library/Caches/ms-playwright/(exact path may vary by Playwright version).
Linux
Build your app (
dotnet build).Install Chromium and distro dependencies when possible:
Project script (recommended)
pwsh ./bin/Debug/net8.0/playwright.ps1 install --with-deps chromiumGlobal CLI
dotnet tool install --global Microsoft.Playwright.CLI playwright install --with-deps chromium--with-depsuses Playwright’s dependency installer where supported (e.g. Debian/Ubuntu). On other distros, if installs fail, see Playwright system dependencies and your distro’s package list.Typical cache location:
~/.cache/ms-playwright/.Headless Chromium often needs a writable temp directory and font packages for correct rendering; install fonts your PDFs require (e.g.
fonts-liberation, or language-specific font packages).
Windows
Project script (recommended)
pwsh .\bin\Debug\net8.0\playwright.ps1 install chromium
If playwright is on PATH (e.g. after global CLI install):
playwright install chromium
Browser cache is usually under %USERPROFILE%\AppData\Local\ms-playwright\.
CI and Docker
- Run browser install during image build or a one-time setup step, not on every HTTP request.
- On Linux containers, prefer
install --with-deps chromium(see Docker Usage below).
Docker Usage
In containers, install Chromium during image build (not at request time).
Example Debian/Ubuntu-based Dockerfile steps:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
# Install Playwright browser dependencies + Chromium
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
fonts-liberation \
libasound2 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libc6 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libexpat1 \
libfontconfig1 \
libgbm1 \
libgcc1 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libstdc++6 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxrandr2 \
libxrender1 \
libxshmfence1 \
libxss1 \
libxtst6 \
wget \
xdg-utils \
&& rm -rf /var/lib/apt/lists/*
Then (during build or startup script) run:
playwright install --with-deps chromium
If you only copy published app binaries into the image, run the same install from the app directory where playwright.ps1 exists, or install the global CLI in the image and use playwright install --with-deps chromium.
Container notes:
- Prefer running as non-root with sandbox enabled when possible.
- If your environment requires it, set
PdfOptions.DisableSandbox = trueonly as a controlled fallback. - Install fonts required by your documents (for example CJK, Arabic, corporate fonts).
Startup Validation (Recommended)
Fail fast at startup instead of first PDF request:
using HubtelPdfGenerator.Playwright;
await PlaywrightBrowser.ValidateInstalledAsync();
Performance and Reliability Tips
- Reuse DI singleton converter (
AddHubtelPdfGenerator()already does this). - Avoid downloading external assets during render when possible.
- Use absolute URLs, base64 images, or
HtmlResourcefor deterministic outputs. - Tune
TimeoutMsfor large reports. - Keep print CSS explicit (table layout, break rules, fixed wrappers where needed).
Security Considerations
- Treat HTML as untrusted input unless sanitized upstream.
- Restrict remote asset origins if generating documents in sensitive environments.
- Be careful with
DisableSandbox; only use when your runtime constraints require it.
Troubleshooting
Microsoft.Playwright.PlaywrightException: Executable doesn't exist ...
Chromium is not installed for the current user/machine, or the browser revision does not match your Playwright package version.
Fix: follow Playwright and Chromium installation (required). Quick examples:
macOS / Linux (project output, after dotnet build)
pwsh ./bin/Debug/net8.0/playwright.ps1 install chromium
Linux (with OS deps)
pwsh ./bin/Debug/net8.0/playwright.ps1 install --with-deps chromium
Windows
pwsh .\bin\Debug\net8.0\playwright.ps1 install chromium
Replace net8.0 with your app’s target framework folder name.
command not found: pwsh (macOS / Linux)
Install PowerShell (e.g. on macOS: brew install powershell) or use the global Playwright CLI instead — see If pwsh is not found.
Default date/url/title appears in header/footer
Set:
HeaderFooter = new HeaderFooterOptions
{
Enabled = true,
UseChromiumDefaultHeaderFooterWhenTemplateMissing = false
}
Images render as broken placeholders
- Use absolute URLs or base64 data URIs.
- Ensure
BaseUrlis set when using relative paths. - For in-memory resources, pass
HtmlResourceentries with correct content types.
Layout jumps or clipping around page boundaries
- Review
break-insiderules on wrappers, tables, and rows. - Avoid fixed-height wrappers for pageable tables.
- Keep rows unbreakable and let table/container flow.
Minimal End-to-End Example
var html = """
<!doctype html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.section { break-inside: avoid; }
</style>
</head>
<body>
<h1>Monthly Report</h1>
<div class="section">...</div>
</body>
</html>
""";
var options = new PdfOptions
{
Format = "A4",
PrintBackground = true,
MarginTopInches = 0.6m,
MarginBottomInches = 0.6m,
HeaderFooter = new HeaderFooterOptions
{
Enabled = true,
FooterTemplateHtml = "<div style='font-size:9px;width:100%;text-align:center;'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>"
}
};
byte[] pdfBytes = await converter.ConvertHtmlAsync(html, options);
await File.WriteAllBytesAsync("report.pdf", pdfBytes);
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- HubtelPdfGenerator.Abstractions (>= 1.0.6)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)
- Microsoft.Playwright (>= 1.54.0)
- PdfSharpCore (>= 1.3.67)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on HubtelPdfGenerator.Playwright:
| Package | Downloads |
|---|---|
|
HubtelPdfGenerator
Facade package and DI registration for Hubtel HTML to PDF SDK. |
GitHub repositories
This package is not used by any popular GitHub repositories.