@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.

NameDimensions
A3297 × 420 mm
A4210 × 297 mm
A5148 × 210 mm
Letter8.5 × 11 in
Legal8.5 × 14 in
Tabloid11 × 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:

Program.cs
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);
ℹ️
Without 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 this IResourceResolver. The built-in HttpResourceResolver refuses 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. CompositeResolver combines 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 and file: URLs are refused before they reach any resolver, each with a RESOURCE_BLOCKED warning.
Program.cs
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,
});
ℹ️
Without a resolver nothing changes from earlier versions: local files are read and http(s) stylesheets and fonts are fetched from any address. 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).