Table of Contents

Rendering

Every render starts from a CobaltEngine instance and ends with one of the three terminal methods. Options set before the terminal call are consumed and reset automatically, so the same CobaltEngine instance is safe to reuse.

Render from a URL

var renderer = new CobaltEngine();
var pdf = await renderer.RenderUrlAsPdfAsync("https://example.com");
pdf.SaveAs("output.pdf");

Chromium navigates to the URL, waits for the page to be ready (configurable via Wait Strategies), and captures the PDF.

Render from an HTML String

string html = """
    <html>
    <head>
        <style>body { font-family: sans-serif; padding: 40px; }</style>
    </head>
    <body>
        <h1>Invoice #1042</h1>
        <p>Total due: <strong>£149.99</strong></p>
    </body>
    </html>
    """;

var pdf = await renderer.RenderHtmlAsPdfAsync(html);

// Access raw bytes directly — no file is written
byte[] bytes = pdf.BinaryData;
// Stream it to a client, upload to S3, attach to an email, etc.
Note

The HTML is loaded into Chromium using page.setContent(). External resources (images, fonts, scripts) referenced by absolute URLs are fetched normally. Relative URLs will not resolve — use absolute URLs or embed resources inline (e.g. Base64 images, inline CSS).

Render from an HTML File

var pdf = await renderer.RenderHtmlFileAsPdfAsync("C:/reports/invoice.html");
pdf.SaveAs("invoice.pdf");

The file is read from disk and rendered as an HTML string. The same note about relative URLs applies.

Saving the Output

PdfDocument exposes three ways to persist or consume the result:

// Classic two-step
var pdf = await renderer.RenderUrlAsPdfAsync("https://example.com");
pdf.SaveAs("output.pdf");

// Chain SaveAs directly off the render call — no intermediate variable needed
await renderer
    .Landscape()
    .RenderUrlAsPdfAsync("https://example.com")
    .SaveAs("output_landscape.pdf");

// Async variant — uses File.WriteAllBytesAsync internally
await renderer
    .RenderUrlAsPdfAsync("https://example.com")
    .SaveAsAsync("output.pdf");

// Get raw bytes (for streaming, email, blob storage, etc.)
var pdf = await renderer.RenderUrlAsPdfAsync("https://example.com");
byte[] bytes = pdf.BinaryData;

// Get a Stream
using var stream = pdf.GetStream();
await stream.CopyToAsync(responseStream);

SaveAs vs SaveAsAsync

Both methods are available both on PdfDocument directly and as extension methods on Task<PdfDocument>, so they can be chained straight off any render call.

Method Where callable Write Returns
SaveAs(path) PdfDocument or Task<PdfDocument> Synchronous PdfDocument
SaveAsAsync(path) PdfDocument or Task<PdfDocument> Async (File.WriteAllBytesAsync) Task<PdfDocument>

Paper Format and Orientation

var pdf = await renderer
    .WithPaperFormat("A3")               // change paper size
    .Landscape()                         // landscape orientation
    .RenderUrlAsPdfAsync("https://example.com");

The paper format defaults to A4. Use WithPaperFormat to change it:

Format string Dimensions
"A4" (default) 210 × 297 mm
"A3" 297 × 420 mm
"Letter" 8.5 × 11 in
"Legal" 8.5 × 14 in
"Tabloid" 11 × 17 in

Margins

Margins default to 1 cm on all sides. Use WithMargins to change them:

using CobaltPdf.Configuration;
using CobaltPdf.Enums;

// No margins at all
var pdf = await renderer
    .WithMargins(MarginOptions.None)
    .RenderUrlAsPdfAsync("https://example.com");

// Uniform margin
var pdf2 = await renderer
    .WithMargins(new MarginOptions(15, MarginUnit.Millimeters))
    .RenderUrlAsPdfAsync("https://example.com");

// Different top/bottom and left/right
var pdf3 = await renderer
    .WithMargins(new MarginOptions(topBottom: 10, leftRight: 20, MarginUnit.Millimeters))
    .RenderUrlAsPdfAsync("https://example.com");

// Full per-side control
var pdf4 = await renderer
    .WithMargins(new MarginOptions(top: 10, right: 15, bottom: 10, left: 15, MarginUnit.Millimeters))
    .RenderUrlAsPdfAsync("https://example.com");
Tip

Headers and footers are rendered inside the margin area. If your header or footer is clipped, increase the top or bottom margin respectively.

To render using the page's @media print CSS rules instead of @media screen:

using CobaltPdf.Enums;

var pdf = await renderer
    .EmulateMediaType(CssMediaType.Print)
    .RenderUrlAsPdfAsync("https://example.com");

Grayscale

var pdf = await renderer
    .UseGrayscale()
    .RenderUrlAsPdfAsync("https://example.com");

CSP Bypass

Some pages use a strict Content Security Policy that would block injected fonts or custom scripts. Enable the bypass flag when needed:

var pdf = await renderer
    .WithCspBypass()
    .ConfigureFonts("C:/fonts")
    .RenderUrlAsPdfAsync("https://strict-csp-site.com");

Cancellation

All three render methods accept an optional CancellationToken. Cancelling aborts the pool lease acquire, or the in-progress render if it has already started.

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

var pdf = await renderer
    .RenderUrlAsPdfAsync("https://example.com", cts.Token);

In ASP.NET Core, pass HttpContext.RequestAborted to automatically cancel if the HTTP client disconnects:

[HttpGet("report")]
public async Task<IActionResult> GetReport(CancellationToken cancellationToken)
{
    var pdf = await _renderer
        .WithPaperFormat("A4")
        .RenderUrlAsPdfAsync("https://example.com/report", cancellationToken);

    return File(pdf.BinaryData, "application/pdf", "report.pdf");
}

Parallel Renders

CobaltEngine is thread-safe. Multiple concurrent renders on the same instance each get their own browser lease from the pool:

var renderer = new CobaltEngine();

var tasks = Enumerable.Range(1, 10).Select(i =>
    renderer.RenderUrlAsPdfAsync("https://example.com"));

var results = await Task.WhenAll(tasks);
Console.WriteLine($"Rendered {results.Length} PDFs");