How to Convert HTML to PDF with HtmlRenderer
Convert HTML to PDF in .NET with HtmlRenderer.PdfSharp. Learn installation, page sizes, margins, images, troubleshooting, and production tips.
Use the HtmlRenderer.PdfSharp NuGet package and call PdfGenerator.GeneratePdf. The simplest conversion is:
using PdfSharp;
using PdfSharp.Pdf;
using TheArtOfDev.HtmlRenderer.PdfSharp;
PdfDocument pdf = PdfGenerator.GeneratePdf(
"<p><h1>Hello World</h1>This is html rendered text</p>",
PageSize.A4);
pdf.Save("output.pdf");
The method returns a PDFsharp PdfDocument. Use the overload that accepts PdfGenerateConfig when you need custom margins, orientation, or other page settings. HtmlRenderer.PdfSharp supplies the HTML-to-PDF layer; PDFsharp by itself does not convert HTML (NuGet package, API source, PDFsharp FAQ).
1. Install HtmlRenderer.PdfSharp
Add the package to your .NET project. NuGet displayed version 1.6.1 during the research for this guide; check NuGet for the current version before pinning it.
dotnet add package HtmlRenderer.PdfSharp --version 1.6.1
Or add a package reference:
<PackageReference Include="HtmlRenderer.PdfSharp" Version="1.6.1" />
The repository project file declares netstandard2.0 and net8.0 targets. Treat those as the targets declared by that project, and validate your own runtime, operating system, fonts, and deployment model.
2. Convert a complete HTML document
Pass an HTML string to GeneratePdf, select a PDFsharp page size, and save the returned document.
using PdfSharp;
using PdfSharp.Pdf;
using TheArtOfDev.HtmlRenderer.PdfSharp;
string html = @"
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<style>
body { font-family: Arial, sans-serif; color: #222; }
h1 { color: #174ea6; }
.total { font-size: 20px; font-weight: bold; }
</style>
</head>
<body>
<h1>Invoice 1007</h1>
<p>Prepared for Example Ltd.</p>
<p class='total'>Total: $125.00</p>
</body>
</html>";
PdfDocument pdf = PdfGenerator.GeneratePdf(html, PageSize.A4);
pdf.Save("invoice.pdf");
Keep the HTML deterministic when generating invoices, reports, or archived documents. Embed the styles needed for the document and validate the result with the exact fonts and images used in production.
3. Set page size, orientation, and margins
For a single page size and an optional integer margin, use the simple overload. For separate edge margins or landscape output, create a PdfGenerateConfig.
using PdfSharp;
using PdfSharp.Pdf;
using TheArtOfDev.HtmlRenderer.PdfSharp;
string html = "<h1>Quarterly report</h1><p>Report content</p>";
var config = new PdfGenerateConfig
{
PageSize = PageSize.Letter,
Landscape = true,
MarginTop = 36,
MarginBottom = 36,
MarginLeft = 48,
MarginRight = 48
};
PdfDocument pdf = PdfGenerator.GeneratePdf(html, config);
pdf.Save("report-letter-landscape.pdf");
PdfGenerateConfig also provides SetMargins and helpers for converting millimeters or inches to PDFsharp units. Use those helpers when your design specifications are expressed in print units.
var config = new PdfGenerateConfig
{
PageSize = PageSize.A4,
Landscape = false
};
config.SetMargins(25); // same margin on all edges
PdfDocument pdf = PdfGenerator.GeneratePdf(html, config);
Common page choices
| Need | Configuration |
|---|---|
| Standard international paper | PageSize.A4 |
| US office paper | PageSize.Letter |
| Wide report or table | Set Landscape = true |
| Different header and footer spacing | Set each margin property independently |
| Physical measurements | Use the configuration conversion helpers for millimeters or inches |
4. Reuse a PDF document with AddPdfPages
When several HTML fragments belong in one file, create a PDFsharp document and append rendered pages with the AddPdfPages overloads exposed by the package.
using PdfSharp;
using PdfSharp.Pdf;
using TheArtOfDev.HtmlRenderer.PdfSharp;
var document = new PdfDocument();
var config = new PdfGenerateConfig { PageSize = PageSize.A4 };
PdfGenerator.AddPdfPages(document, "<h1>Cover</h1>", config);
PdfGenerator.AddPdfPages(document, "<h1>Details</h1><p>More content</p>", config);
document.Save("combined.pdf");
Use this pattern when each section should start as a separately rendered page. Validate page breaks with your real content because HTML flow, whitespace, and long unbreakable elements affect pagination.
5. Stylesheets, images, and resource loading
The API exposes stylesheet and image loading hooks. If your markup references external CSS or images, confirm that resource loading is configured for your package version and deployment environment. A server may not have the same working directory, network access, certificates, or fonts as a development machine.
- Prefer stable, accessible resource paths and make required assets available to the conversion process.
- Check image formats, dimensions, and file permissions.
- Use a controlled font set and install or deploy the fonts required by the document.
- Keep external dependencies to a minimum for reproducible archives.
The project describes support for HTML 4.01 and CSS level 2 and says it handles malformed real-world HTML. Those are project claims, not a guarantee that every browser feature will render. Test modern layout features, JavaScript-dependent pages, web fonts, SVG, and complex tables with representative documents (project metadata).
6. A production-ready conversion method
using System;
using System.IO;
using PdfSharp;
using PdfSharp.Pdf;
using TheArtOfDev.HtmlRenderer.PdfSharp;
public static class HtmlPdf
{
public static void Write(string html, string outputPath)
{
if (string.IsNullOrWhiteSpace(html))
throw new ArgumentException("HTML cannot be empty.", nameof(html));
var config = new PdfGenerateConfig
{
PageSize = PageSize.A4,
Landscape = false,
MarginTop = 36,
MarginBottom = 36,
MarginLeft = 36,
MarginRight = 36
};
PdfDocument document = PdfGenerator.GeneratePdf(html, config);
string? directory = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(directory))
Directory.CreateDirectory(directory);
document.Save(outputPath);
}
}
For background jobs, write to a unique temporary path, validate that the operation completed, then move the file into its final location. Dispose or otherwise close documents according to the PDFsharp version used by your project.
7. Limitations and edge cases
- Browser-only CSS: The documented scope is HTML 4.01 and CSS level 2, so do not assume full Chromium or browser-layout compatibility.
- JavaScript applications: Client-side rendering is not documented here as supported. Supply the final HTML rather than relying on a page that must execute application JavaScript.
- Very wide content: Tables and long unbroken strings can overflow the printable area. Set landscape mode, reduce widths, or add break opportunities.
- Long documents: Check headings, table rows, and manual page breaks across multiple pages; pagination depends on the actual markup and styles.
- External resources: Network failures, relative paths, missing fonts, and blocked files can produce incomplete output. Make dependencies explicit.
- Malformed markup: The project states that malformed real-world HTML is handled, but validate your own templates and inspect generated PDFs.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Type or namespace not found | Package or namespace is missing | Install HtmlRenderer.PdfSharp, restore packages, and verify the namespaces against the installed version. |
| PDF is blank | Empty HTML, unsupported markup, or an asset-loading problem | Start with a plain heading and paragraph, then add styles and resources incrementally. |
| Images do not appear | Invalid path, inaccessible URL, unsupported format, or blocked resource | Use a resolvable path, verify permissions and format, and review the image loading hook. |
| Text uses the wrong font | Font is unavailable in the runtime environment | Install or deploy the font and use a fallback family in CSS. |
| Margins are unexpected | Default or mixed configuration values | Set all four margin properties explicitly or call SetMargins. |
| Modern CSS layout differs from a browser | The renderer is not documented as a full browser engine | Reduce the template to supported HTML/CSS or choose a browser-based converter when pixel parity is required. |
| Output differs between machines | Different fonts, resource paths, package versions, or runtime settings | Pin the package, control fonts and assets, and render in a consistent environment. |
9. Performance, reliability, and cost considerations
HtmlRenderer.PdfSharp runs as a library inside your .NET process, so there is no browser process to manage. Conversion time and memory depend on document size, images, fonts, and layout complexity. Measure with representative documents before selecting concurrency limits.
- Reuse templates and avoid unnecessarily large images.
- Bound concurrent conversions to protect memory.
- Log package version, template version, page count, and output size.
- Retry only transient resource failures; deterministic markup errors will not improve with retries.
- Store generated PDFs atomically and retain the HTML or template version used to create them.
The package itself is distributed through NuGet. The research sources provide no independent benchmarks, conversion-quality statistics, or adoption figures, so choose based on your tested documents and required HTML/CSS scope.
10. Or skip the browser setup
If your source is already available at a URL and you want a hosted capture service, ScreenshotNeo can return a PDF from one GET request. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o page.pdf
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server includes take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
11. FAQ
Does PDFsharp convert HTML by itself?
No. PDFsharp is the PDF library; HtmlRenderer.PdfSharp provides the HTML rendering integration.
Which method should I use for custom margins?
Use the overload that accepts PdfGenerateConfig, then set individual edge margins or call SetMargins.
Can I use this for pixel-perfect browser screenshots?
Do not assume that. The documented scope is HTML 4.01 and CSS level 2, so validate your templates or choose a browser-based service for browser-engine fidelity.
Can I append multiple HTML sections to one PDF?
Yes. Use the AddPdfPages overloads with an existing PdfDocument.
Where should I check package-version changes?
Check the current NuGet package page and review the API for the version you install.


