Net4x.HtmlToPdfLibrary
1.0.0.26249
dotnet add package Net4x.HtmlToPdfLibrary --version 1.0.0.26249
NuGet\Install-Package Net4x.HtmlToPdfLibrary -Version 1.0.0.26249
<PackageReference Include="Net4x.HtmlToPdfLibrary" Version="1.0.0.26249" />
<PackageVersion Include="Net4x.HtmlToPdfLibrary" Version="1.0.0.26249" />
<PackageReference Include="Net4x.HtmlToPdfLibrary" />
paket add Net4x.HtmlToPdfLibrary --version 1.0.0.26249
#r "nuget: Net4x.HtmlToPdfLibrary, 1.0.0.26249"
#:package Net4x.HtmlToPdfLibrary@1.0.0.26249
#addin nuget:?package=Net4x.HtmlToPdfLibrary&version=1.0.0.26249
#tool nuget:?package=Net4x.HtmlToPdfLibrary&version=1.0.0.26249
Net4x.HtmlToPdfLibrary
Turns an HTML document into a PDF, or into an image, by driving the
wkhtmltopdf command line tools. It ships the tools, copies them next to
your assembly at build time, and finds them at run time without anything being configured. There is
also an IHttpHandler that serves the PDF as a file download.
Target frameworks: net35, net40, net6.0-windows, net8.0-windows, net10.0-windows.
On the modern targets the System.Web types come from
AspNetCore.Web.Base, so the same handler code
runs unchanged.
Platform: Windows. The library starts wkhtmltopdf.exe / wkhtmltoimage.exe and can optionally
run them as another Windows account.
Install
dotnet add package Net4x.HtmlToPdfLibrary
That is all the setup there is. The package carries wkhtmltopdf 0.11.0 rc2 under tools/, and its
build/Net4x.HtmlToPdfLibrary.props copies the two converters and their four dependent DLLs into
your output directory on every build, only when they are newer:
bin\Debug\net8.0-windows\
YourApp.dll
wkhtmltopdf.exe
wkhtmltoimage.exe
libeay32.dll ssleay32.dll libgcc_s_dw2-1.dll mingwm10.dll
| Property | Default | Effect |
|---|---|---|
CopyWkHtmlConvertersToOutputDirectory |
true |
Set to false to stop the copy entirely. |
WkHtmlConvertersPath |
the package's tools/wkhtmltopdf/ |
Point it at your own build of the tools, trailing separator included. |
<PropertyGroup>
<CopyWkHtmlConvertersToOutputDirectory>false</CopyWkHtmlConvertersToOutputDirectory>
</PropertyGroup>
Converting to PDF
using System.IO;
using HtmlToPdfLibrary;
using (var pdf = File.Create(@"C:\reports\invoice.pdf"))
{
// The converter is found next to your assembly; nothing to configure.
new HtmlToPdfGenerator().ConvertToPdf("https://example.com/invoices/42", pdf);
}
Rendering to an image
var generator = new HtmlToImageGenerator(); // PNG by default
generator.ImageFormat = HtmlToImageGenerator.JpegFormat; // or "bmp", "svg", …
using (var image = File.Create(@"C:\reports\invoice.jpg"))
{
generator.ConvertToImage("https://example.com/invoices/42", image);
}
wkhtmltoimageleaves its standard output in text mode on Windows, which turns every0x0Ain the image into0x0D 0x0Aand corrupts the file. The library renders to a temporary file and copies that out, so what you get back is byte for byte what the tool would have written itself.
Where the converters are looked for
Both generators take the converter directory as an optional argument. When you do not supply one, these directories are searched in order, and the first that holds the executable wins:
- the folder holding
HtmlToPdfLibrary.dll—bin\in a web application, the output root elsewhere; - the base directory of the application — the site root for a web application;
- a
wkhtmltopdfsub folder of 1; - a
wkhtmltopdfsub folder of 2 — this is the classic~/wkhtmltopdfweb layout.
If none of them holds it, the name is left to the PATH variable. WkHtmlConverter.GetProbeDirectories()
and WkHtmlConverter.FindConverterDirectory("wkhtmltopdf.exe") expose this, which is handy when a
deployment cannot find its tools.
To override it, pass the directory explicitly — that always wins over probing:
new HtmlToPdfGenerator().ConvertToPdf(url, pdf, @"C:\tools\wkhtmltopdf");
new HtmlToPdfGenerator().ConvertToPdf(url, pdf, @"C:\tools\wkhtmltopdf", "wkhtmltopdf.exe");
Running as another account
Both generators take a Windows user name and password. The name may be qualified, DOMAIN\user:
new HtmlToPdfGenerator().ConvertToPdf(url, pdf, converterDirectory, null, @"CORP\reporting", secret);
Leaving the user name empty runs the converter as the identity that owns the current process, which is what a normal application pool wants.
Timeouts
TimeoutMilliseconds (one minute by default) bounds the whole conversion, including a converter
stuck waiting on a server that never answers. When the deadline passes the converter is killed and a
TimeoutException is thrown. Set it to Timeout.Infinite to wait indefinitely.
var generator = new HtmlToPdfGenerator { TimeoutMilliseconds = 15000 };
What can go wrong
| Situation | Result |
|---|---|
url is null or blank, or the stream is not writable |
ArgumentNullException / ArgumentException |
| The converter is not in the directory you named | FileNotFoundException |
| The converter never finishes | TimeoutException, converter killed |
| The converter produced nothing at all | InvalidOperationException carrying its diagnostics |
| Writing to your stream failed | IOException wrapping the original failure |
A converter that reports a recoverable problem — a missing image, say — but still emits a document is not treated as a failure: you get the document it produced.
Serving PDFs over HTTP
HtmlToPdfHandler answers ?url=…&filename=… with the converted document as an attachment.
<system.webServer>
<handlers>
<add name="HtmlToPdf" verb="GET" path="htmltopdf.axd"
type="HtmlToPdfLibrary.HtmlToPdfHandler, HtmlToPdfLibrary" />
</handlers>
</system.webServer>
GET /htmltopdf.axd?url=https://example.com/invoices/42&filename=Invoice 42
→ 200, application/pdf, Content-Disposition: attachment; filename="invoice-42.pdf"
GET /htmltopdf.axd
→ 400, text/plain, "Please specify a url"
filename is a title, not a path: it is lower-cased invariantly, spaces become single hyphens,
anything outside a-z, 0-9 and - is dropped, and the result is capped at 50 characters. A title
that leaves nothing usable falls back to converted.
Note: the handler converts whatever url the caller asks for. If it is reachable anonymously, restrict which urls it will accept — override
GetDescriptor— before exposing it.
Configuration
All four settings are optional appSettings entries. With none of them present the converter is
probed for, which already covers both bin\ and ~/wkhtmltopdf:
| Key | Default | Meaning |
|---|---|---|
HtmlToPdf:ConverterDirectory |
(probe) | Folder holding the converter. Application relative values such as ~/wkhtmltopdf are mapped against the current request; anything else is an absolute path. |
HtmlToPdf:ConverterExecutable |
wkhtmltopdf.exe |
Converter file name. |
HtmlToPdf:UserName |
(none) | Windows account to run the converter as. Absent means the application pool identity. |
HtmlToPdf:Password |
(none) | Password for the account above. |
<appSettings>
<add key="HtmlToPdf:ConverterDirectory" value="~/wkhtmltopdf" />
<add key="HtmlToPdf:UserName" value="CORP\reporting" />
</appSettings>
Extending the handler
Everything worth changing is protected virtual, so a derived handler can take over one piece
without reimplementing the rest:
| Member | Override it to |
|---|---|
GetPdfGenerator |
supply a configured or derived generator |
GetDescriptor |
read the request differently, or restrict which urls are allowed |
ConverterDirectory, ConverterExecutable, UserName, Password |
take the settings from somewhere other than appSettings |
ResolveConverterDirectory |
change how the converter folder is located |
WriteToResponse, WriteMissingUrlResponse |
change the shape of the response |
public class ReportPdfHandler : HtmlToPdfHandler
{
protected override HtmlToPdfGenerator GetPdfGenerator()
{
return new HtmlToPdfGenerator { TimeoutMilliseconds = 15000 };
}
protected override HtmlDescriptor GetDescriptor(HttpContext context)
{
HtmlDescriptor descriptor = base.GetDescriptor(context);
if (descriptor != null && !descriptor.Url.StartsWith("https://reports.example.com/"))
return null;
return descriptor;
}
}
HtmlToPdfHandler.CreateSafeFileName is public and static, so the same sanitising is available to
callers that build their own responses.
Extending the converters
HtmlToPdfGenerator and HtmlToImageGenerator both derive from WkHtmlConverter, which owns the
process plumbing. Override BuildArguments to add command line switches, CreateProcess or
ApplyCredentials to change how the tool is started, GetExecutablePath to change how it is
located, or CreateTemporaryFilePath to render somewhere other than the temp folder.
public class LandscapePdfGenerator : HtmlToPdfGenerator
{
protected override string BuildArguments(string url, string outputTarget)
{
return "--orientation Landscape " + base.BuildArguments(url, outputTarget);
}
}
License
Copyright © 2013. A License.txt at the repository root is packed with the library when present.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0-windows7.0 is compatible. net7.0-windows was computed. net8.0-windows was computed. net8.0-windows7.0 is compatible. net9.0-windows was computed. net10.0-windows was computed. net10.0-windows7.0 is compatible. |
| .NET Framework | net35 is compatible. net40 is compatible. net403 was computed. net45 was computed. net451 was computed. net452 was computed. net46 was computed. net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 3.5
- No dependencies.
-
.NETFramework 4.0
- No dependencies.
-
net10.0-windows7.0
- AspNetCore.Web.Base (>= 1.0.0.26249)
-
net6.0-windows7.0
- AspNetCore.Web.Base (>= 1.0.0.26249)
-
net8.0-windows7.0
- AspNetCore.Web.Base (>= 1.0.0.26249)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0.26249 | 102 | 9/6/2026 |