@page rules
EggPdf reads CSS @page rules embedded in your HTML to configure the output page.
Place the rule inside a <style> block in the document <head>:
@page {
size: A4; /* page size */
margin: 2cm; /* all margins */
}
Any CSS you put inside @page { } applies to every page of the document.
EggPdf uses this as the single source of truth for page geometry internally — a
PdfRenderOptions object (below) is translated into an injected
@page rule, not a separate layout path.
PdfRenderOptions (C# convenience wrapper)
If you'd rather not write CSS by hand, pass a PdfRenderOptions to
HtmlToPdf.Render/RenderAsync. It's translated into the
equivalent @page rule (and a <title>/
<meta name="author"> tag for the two metadata fields) and appended to
the document's <head>, so it wins the cascade over any conflicting
@page rule already in the HTML:
var pdf = await HtmlToPdf.RenderAsync(html, new PdfRenderOptions
{
PageSize = "Letter",
Orientation = "landscape",
MarginTop = 40, // CSS pixels
MarginBottom = 40,
Title = "Q4 Report",
});
Title/Author aren't options-only — the same PDF metadata is set
automatically from a plain <title>/<meta name="author">
tag already in your HTML, with no options object needed.
Page sizes
The following named sizes are supported. The default when no @page rule is present is A4.
| Name | Dimensions |
|---|---|
A3 | 297 × 420 mm |
A4 | 210 × 297 mm |
A5 | 148 × 210 mm |
Letter | 8.5 × 11 in |
Legal | 8.5 × 14 in |
Tabloid | 11 × 17 in |
Landscape orientation
Add the landscape keyword after the size name to rotate the page:
@page {
size: A4 landscape;
margin: 1.5cm;
}
Use portrait to make the orientation explicit, though it is the default when no keyword is given.
Custom dimensions
Any CSS length unit works for size. Provide width then height:
@page {
size: 200mm 120mm; /* width height */
margin: 10mm;
}Supported units: px, mm, cm, in, pt.
Margins
All standard CSS margin shorthands are accepted inside @page:
/* Uniform — 2cm on every side */
@page { margin: 2cm; }
/* Vertical | Horizontal */
@page { margin: 2cm 1.5cm; }
/* Individual sides */
@page {
margin-top: 25mm;
margin-bottom: 25mm;
margin-left: 20mm;
margin-right: 20mm;
}Loading external resources
When rendering from an HTML file that references external stylesheets or images via relative
paths, pass the directory containing the file as basePath. EggPdf will resolve
<link rel="stylesheet"> and <img src="..."> relative to that
directory:
using EggPdf;
string html = File.ReadAllText("invoice.html");
string baseDir = Path.GetDirectoryName(Path.GetFullPath("invoice.html"))!;
byte[] pdf = HtmlToPdf.Render(html, basePath: baseDir);
File.WriteAllBytes("invoice.pdf", pdf);basePath, relative paths resolve against the process's current directory.
Data URIs
always work. By default http(s) <link> stylesheets, @import and
@font-face sources are downloaded too (e.g. Google Fonts); http(s) images load only through a
resource resolver (below).
Supported image formats: PNG (including Adam7-interlaced), JPEG, GIF, BMP, WebP and SVG (an <img> pointing at an .svg file or a data:image/svg+xml URI is painted as vectors, and so is SVG used as a CSS background-image). Images can also be embedded directly as data URIs without needing a basePath. An <img> without both a width and a height takes its natural size and aspect ratio (within max-width/max-height), and a JPEG's EXIF orientation is applied unless image-orientation: none. An image that cannot be decoded is left out with an IMAGE_LOAD_FAILED warning.
Untrusted HTML: resource resolver and local file access
If the HTML comes from users or customers, its src, href, @import and
@font-face URLs could point at files on your server or at internal addresses such as the cloud
metadata endpoint (SSRF). Two options control this:
PdfRenderOptions.ResourceResolver— every external resource (stylesheets,@import,@font-face,<img>, CSS background images) is loaded only through thisIResourceResolver. The built-inHttpResourceResolverrefuses loopback, private, link-local (169.254.169.254) and other non-public addresses — on .NET 6+ it checks the address when it connects, so DNS can't swap it; on .NET Standard builds (e.g. .NET Framework) it resolves the hostname just before each request, which a DNS-rebinding attacker can race — re-checks every redirect (at most 5), stops at 50 MB and 10 seconds by default, and also loads http(s) images.CompositeResolvercombines it with data: URIs and files confined to one directory. Write your own to serve resources from a database or blob store.PdfRenderOptions.AllowLocalFileAccess = false— file paths andfile:URLs are refused before they reach any resolver, each with aRESOURCE_BLOCKEDwarning.
using EggPdf;
using EggPdf.Core.Resources;
// One resolver can serve every render (it is thread-safe)
var safeHttp = new HttpResourceResolver(new HttpResourceOptions { MaxResponseSizeBytes = 10 * 1024 * 1024 });
byte[] pdf = HtmlToPdf.Render(untrustedHtml, new PdfRenderOptions
{
ResourceResolver = safeHttp,
AllowLocalFileAccess = false,
});AllowLocalFileAccess = false alone does not stop that
network access, so set both for untrusted HTML. A resolver's results are reused within one render and never
shared with other renders. The same settings exist on the fluent builder
(.ResourceResolver(...), .AllowLocalFileAccess(false)), the CLI
(--no-local-files, --no-private-network), and the REST service, which is locked
down by default (see REST API).