Getting Started

Set your licence key

Activate CobaltPDF by calling SetLicense once at application startup. Without a valid licence the library runs in trial mode and watermarks output.

Program.cs
using CobaltPdf;

// Call once at startup — before any rendering
CobaltEngine.SetLicense("COBALTPDF-AcmeCorp-XXXXX-Annual-2026-06-15");

var renderer = new CobaltEngine();
var pdf = await renderer.RenderUrlAsPdfAsync("https://example.com");
Program.cs — ASP.NET Core
// Load the key from configuration (recommended)
CobaltEngine.SetLicense(builder.Configuration["CobaltPdf:LicenseKey"]!);

builder.Services.AddCobaltPdf();
Tip: Store your licence key in appsettings.json, environment variables, or user secrets — never hard-code it in source control. The key is validated on first use and throws InvalidOperationException if invalid or expired.
Getting Started

Render a URL to PDF

The simplest use case — point CobaltPDF at any URL and get a pixel-perfect PDF back. The engine handles navigation, rendering, and cleanup automatically.

Program.cs
using CobaltPdf;

var renderer = new CobaltEngine();

// Render any URL to a PDF
var pdf = await renderer
    .RenderUrlAsPdfAsync("https://example.com");

// Save to disk
pdf.SaveAs("output.pdf");

// Or get the raw bytes
byte[] bytes = pdf.BinaryData;
Tip: The CobaltEngine is thread-safe and designed to be used as a singleton. Create one instance and reuse it across your application.
Rendering

Render an HTML string to PDF

Pass raw HTML directly — ideal for generating invoices, reports, or documents from templates rendered server-side with Razor, Handlebars, or any templating engine.

Program.cs
var html = $@"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {{ font-family: 'Segoe UI', sans-serif; padding: 40px; }}
        h1   {{ color: #1e293b; }}
        table {{ width: 100%; border-collapse: collapse; }}
        th, td {{ padding: 12px; border-bottom: 1px solid #e2e8f0; }}
    </style>
</head>
<body>
    <h1>Invoice #{invoiceNumber}</h1>
    <p>Date: {DateTime.Now:dd MMM yyyy}</p>
    <table>
        <tr><th>Item</th><th>Qty</th><th>Price</th></tr>
        <tr><td>CobaltPDF Licence</td><td>1</td><td>£149</td></tr>
    </table>
</body>
</html>";

var pdf = await renderer
    .RenderHtmlAsPdfAsync(html);

pdf.SaveAs("invoice.pdf");
Layout

Custom paper size & margins

Configure paper format, orientation, and per-side margins. Use standard sizes like A4 and Letter, or specify custom dimensions in any CSS unit.

Program.cs
// A3 landscape with custom margins
var pdf = await renderer
    .WithPaperFormat("A3")
    .WithLandscape()
    .WithMargins(top: "20mm", right: "15mm", bottom: "20mm", left: "15mm")
    .WithPrintBackground()
    .RenderUrlAsPdfAsync("https://app.example.com/dashboard");

// Custom paper size (width × height)
var receipt = await renderer
    .WithPageSize(width: "80mm", height: "200mm")
    .WithMargins("5mm")
    .RenderHtmlAsPdfAsync(receiptHtml);
Supported formats: A3, A4, A5, Legal, Letter, Tabloid — or any custom width/height using px, mm, cm, or in.
Layout

HTML headers & footers

Add dynamic headers and footers using HTML templates. CobaltPDF injects built-in tokens for page numbers, total pages, date, title, and URL.

Program.cs
var headerHtml = @"
<div style='font-size:10px; color:#666; padding:0 20px;
             display:flex; justify-content:space-between;'>
    <span>Acme Corp — Confidential</span>
    <span class='date'></span>
</div>";

var footerHtml = @"
<div style='font-size:9px; color:#999; text-align:center;
             padding:0 20px;'>
    Page <span class='pageNumber'></span>
    of <span class='totalPages'></span>
</div>";

var pdf = await renderer
    .WithHeader(headerHtml)
    .WithFooter(footerHtml)
    .WithMargins(top: "30mm", bottom: "25mm")
    .RenderUrlAsPdfAsync(reportUrl);
Built-in tokens: Use CSS classes pageNumber, totalPages, date, title, and url inside your header/footer HTML — Chromium replaces them automatically.
Authentication

Cookies & authenticated pages

Inject cookies, localStorage, and sessionStorage before the page loads. Perfect for rendering pages that sit behind authentication.

Program.cs
// Inject auth cookies so the page loads as the logged-in user
var pdf = await renderer
    .AddCookie("session", userSessionId)
    .AddCookie("csrf_token", csrfToken)
    .RenderUrlAsPdfAsync("https://app.example.com/dashboard");

// You can also inject localStorage and sessionStorage
var pdf2 = await renderer
    .AddCookie(".AspNetCore.Identity", identityCookie)
    .AddLocalStorage("theme", "dark")
    .AddLocalStorage("locale", "en-GB")
    .AddSessionStorage("activeTab", "analytics")
    .RenderUrlAsPdfAsync(dashboardUrl);
SPA frameworks: If your page uses Angular, React, or another SPA framework that reads storage values only during initialisation, chain .WithReloadAfterStorage() to force a page reload after localStorage and sessionStorage values have been injected. This ensures the framework picks up the injected values during its startup lifecycle.
Authentication

Custom HTTP headers

Set Authorization, User-Agent, Accept-Language, and any custom request headers. Headers are sent on the initial navigation and all sub-resource requests.

Program.cs
// Bearer token auth for an API-rendered page
var pdf = await renderer
    .WithHttpHeader("Authorization", $"Bearer {accessToken}")
    .WithHttpHeader("Accept-Language", "fr-FR")
    .WithHttpHeader("X-Tenant-Id", tenantId)
    .WithUserAgent("CobaltPDF/1.0 ReportBot")
    .RenderUrlAsPdfAsync(reportUrl);
Note: Each render gets its own isolated browser context. Headers set on one render never leak to concurrent renders — the engine is fully thread-safe.
Advanced

Wait strategies

Control exactly when CobaltPDF captures the page. Choose from network idle, DOM selector, JavaScript expression, custom signal, or a fixed delay.

WaitExamples.cs
// Wait for network to be idle (no requests for 500ms)
var pdf1 = await renderer
    .WithWaitStrategy(WaitOptions.DefaultNetworkIdle)
    .RenderUrlAsPdfAsync(url);

// Wait for a specific element to appear in the DOM
var pdf2 = await renderer
    .WithWaitStrategy(WaitOptions.ForSelector("#chart-container svg"))
    .RenderUrlAsPdfAsync(url);

// Wait for a JavaScript expression to return true
var pdf3 = await renderer
    .WithWaitStrategy(WaitOptions.ForJavaScript("window.dataLoaded === true"))
    .RenderUrlAsPdfAsync(url);

// Fixed delay (useful as a last resort)
var pdf4 = await renderer
    .WithWaitStrategy(WaitOptions.ForDelay(TimeSpan.FromSeconds(2)))
    .RenderUrlAsPdfAsync(url);
Advanced

Custom JavaScript execution

Execute JavaScript before capture to manipulate the page — hide elements, inject data, or trigger SPA rendering. Use cobaltNotifyRender() to signal when your app is ready.

Program.cs
// Run JS before capture — e.g. hide nav, expand sections
var pdf = await renderer
    .WithCustomJS(@"
        document.querySelector('nav').style.display = 'none';
        document.querySelector('.sidebar').style.display = 'none';
        document.querySelectorAll('details').forEach(d => d.open = true);
    ")
    .RenderUrlAsPdfAsync(url);

// For SPAs: wait for the app to signal readiness
var spaPdf = await renderer
    .WithWaitStrategy(WaitOptions.ForSignal())
    .RenderUrlAsPdfAsync(spaUrl);

// In your SPA (React, Vue, Angular, etc.):
// window.cobaltNotifyRender()  ← call this when ready
SPA tip: Call window.cobaltNotifyRender() from your front-end code after all data has loaded and the DOM is stable. CobaltPDF will wait indefinitely (up to the timeout) for this signal before capturing.
Advanced

Lazy-loaded content

Modern sites defer images and content until they scroll into view. CobaltPDF can auto-scroll the page to trigger lazy loading before capture, ensuring every image and component is fully rendered in the final PDF.

Program.cs
// Scroll 10 viewport-heights to trigger lazy-loaded images
var pdf = await renderer
    .WithLazyLoadPages(10)
    .RenderUrlAsPdfAsync("https://example.com/gallery");

// Fine-tune scroll speed and timeout
var pdf2 = await renderer
    .WithLazyLoadPages(
        pageCount: 20,
        delay: TimeSpan.FromMilliseconds(300),
        timeout: TimeSpan.FromSeconds(60))
    .RenderUrlAsPdfAsync("https://example.com/infinite-feed");

// Combine with custom JS for stubborn lazy attributes
var pdf3 = await renderer
    .WithLazyLoadPages(10)
    .WithCustomJS(@"
        document.querySelectorAll('img[loading=lazy]')
            .forEach(img => img.removeAttribute('loading'));
    ")
    .RenderUrlAsPdfAsync("https://example.com/blog");
How it works: WithLazyLoadPages(n) scrolls the viewport n viewport-heights down the page, pausing between each step to let IntersectionObserver-based content load. The default delay is 200ms per step with a 30s timeout.
Output

Watermarks

Overlay text or HTML watermarks on every page of the PDF. Configure opacity, rotation, position, and font size.

WatermarkExample.cs
// Pre-styled text watermark
var pdf = await renderer
    .WithWatermark(
        WatermarkOptions.WithText("DRAFT", WatermarkStyle.RedDraft))
    .RenderUrlAsPdfAsync(url);

// Customise position, rotation and opacity via fluent methods
var pdf2 = await renderer
    .WithWatermark(
        WatermarkOptions.WithText("CONFIDENTIAL", WatermarkStyle.SoftGray)
            .WithRotation(-45)
            .WithOpacity(0.15)
            .WithPosition(WatermarkPosition.Center))
    .RenderUrlAsPdfAsync(url);

// Full control with custom HTML
var pdf3 = await renderer
    .WithWatermark(new WatermarkOptions
    {
        Html     = @"<div style='font-size:60px; font-weight:bold;
                        color:red;'>DO NOT DISTRIBUTE</div>",
        Rotation = -30,
        Opacity  = 0.06
    })
    .RenderUrlAsPdfAsync(url);
Security

Password protection & encryption

Protect PDFs with a password and control what recipients can do with the document. Set separate owner and user passwords, and restrict printing, copying, or editing.

Program.cs
// Require a password to open the PDF
var pdf = await renderer
    .WithEncryption(new PdfEncryptionOptions
    {
        UserPassword = "read-only-pass"
    })
    .RenderUrlAsPdfAsync(url);

// Owner + user password with restricted permissions
var secured = await renderer
    .WithEncryption(new PdfEncryptionOptions
    {
        UserPassword     = "viewer-pass",
        OwnerPassword    = "admin-secret",
        AllowPrinting    = true,
        AllowCopying     = false,
        AllowModification = false
    })
    .RenderUrlAsPdfAsync(url);
Note: The user password is required to open the document. The owner password grants full access and allows changing permissions. By default, AllowPrinting is true, while AllowCopying and AllowModification are false.
Output

PDF metadata

Embed document properties like title, author, subject, and keywords into the PDF. Metadata is visible in PDF readers and improves searchability and organisation.

Program.cs
// Set standard PDF document properties
var pdf = await renderer
    .WithMetadata(meta =>
    {
        meta.Title    = "Q4 Financial Report";
        meta.Author   = "Acme Corp";
        meta.Subject  = "Quarterly earnings summary";
        meta.Keywords = "finance, Q4, earnings, 2026";
        meta.Creator  = "Report Generator v2";
    })
    .RenderUrlAsPdfAsync(reportUrl);

// Combine with headers for a polished document
var polished = await renderer
    .WithMetadata(m => {
        m.Title        = "Invoice #1042";
        m.Author       = "Billing Dept";
        m.CreationDate = DateTime.UtcNow;
    })
    .WithFooter(footerHtml)
    .WithPrintBackground()
    .RenderHtmlAsPdfAsync(invoiceHtml);
Tip: The Title property appears in the PDF reader's title bar and browser tab. Keywords help with document search and indexing in enterprise document management systems.
Optional

Pool & browser options

CobaltPDF works out of the box with sensible defaults — no configuration required. Optionally tune the browser pool size, recycling policy, and Chromium flags for production workloads or constrained environments.

Program.cs — ASP.NET Core
// Register with custom pool options
builder.Services.AddCobaltPdf(options =>
{
    options.MinSize = 2;
    options.MaxSize = Environment.ProcessorCount;
    options.MaxUsesPerBrowser = 200;
    options.BrowserLeaseTimeout = TimeSpan.FromSeconds(60);
});

// Or use a cloud preset for Docker, Azure, AWS, etc.
builder.Services.AddCobaltPdf(CloudEnvironment.ConfigureForDocker);
Program.cs — Console app
// Static configuration for non-DI scenarios
CobaltEngine.Configure(options =>
{
    options.MaxSize = 4;
    options.ExtraArgs.Add("--no-sandbox");
});

// Shorthand: just set max pool size
CobaltEngine.Configure(maxSize: 4);
Cloud presets: ConfigureForDocker, ConfigureForAzure, ConfigureForAwsEcs, ConfigureForLinux, and ConfigureForLowMemory each set sensible pool sizes and Chromium flags for the target environment.
Integration

ASP.NET Core dependency injection

Register CobaltPDF as a singleton service in your ASP.NET Core app. The engine is injected into controllers, Razor Pages, or minimal API handlers and shared across all requests.

Program.cs
// Program.cs — register CobaltPDF
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCobaltPdf(options =>
{
    options.MaxSize = 4;
    CloudEnvironment.ConfigureForDocker(options);
});

var app = builder.Build();

// Pre-warm the browser pool at startup
await app.Services
    .GetRequiredService<CobaltEngine>()
    .PreWarmAsync();
ReportController.cs
// Inject into a controller or Razor Page
public class ReportController : ControllerBase
{
    private readonly CobaltEngine _renderer;

    public ReportController(CobaltEngine renderer)
        => _renderer = renderer;

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

        return File(pdf.BinaryData, "application/pdf", "report.pdf");
    }
}
Why pre-warm? Calling PreWarmAsync() at startup launches the browser pool eagerly. Without it, the first render request pays a 1–2 second cold-start cost while Chromium spins up.
Integration

CobaltPDF.Requests — microservice mode

Deploy CobaltPDF as a standalone PDF rendering service and call it from any application. Install the lightweight CobaltPDF.Requests package (~50 KB) in your client — no Chromium or Playwright dependency required.

Client — install
# Client app (web API, mobile backend, other service)
dotnet add package CobaltPDF.Requests

# Rendering service (CobaltPDF.Requests is included automatically)
dotnet add package CobaltPDF
Client — send a PDF request
using CobaltPdf.Requests;
using System.Net.Http.Json;

var request = new PdfRequest
{
    Url         = "https://myapp.com/invoice/42",
    PaperFormat = "A4",
    Header      = "<div style='font-size:10px;text-align:center;'>My Company</div>",
    Footer      = "<div style='font-size:10px;text-align:center;'>" +
                  "Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
    Metadata    = new() { Title = "Invoice #42", Author = "Billing System" },
    Cookies     = [new() { Name = "session", Value = "abc123", Domain = "myapp.com" }]
};

var httpResponse = await httpClient
    .PostAsJsonAsync("https://pdf-service/api/pdf", request);
httpResponse.EnsureSuccessStatusCode();

byte[] pdfBytes = await httpResponse.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("invoice.pdf", pdfBytes);
Server — minimal API endpoint
using CobaltPdf;
using CobaltPdf.Requests;

builder.Services.AddCobaltPdf(o =>
    CloudEnvironment.ConfigureForDocker(o));

var app = builder.Build();
await CobaltEngine.PreWarmAsync();

// ExecuteAsync maps every PdfRequest property to the fluent API
app.MapPost("/api/pdf", async (
    PdfRequest request,
    CobaltEngine renderer,
    CancellationToken ct) =>
{
    var pdf = await request.ExecuteAsync(renderer, ct);
    return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
});
Full guide: See the CobaltPDF.Requests documentation for Azure Functions, AWS Lambda, ECS/Fargate deployment examples, the full JSON schema, and security considerations.
Optional

Logging

CobaltPDF can capture browser console messages and internal warnings. Route them to the console, a log file with daily rotation, or a custom callback for integration with your existing logging pipeline.

Program.cs
// Write browser console output to the application console
CobaltEngine.LoggingMode = BrowserLoggingMode.Console;

// Write to a log file with daily rotation
CobaltEngine.LoggingMode    = BrowserLoggingMode.File;
CobaltEngine.BrowserLogPath = "logs/browser.log";
CobaltEngine.RetainedLogCount = 7;  // keep 7 days of rotated files

// Or both console and file at once
CobaltEngine.LoggingMode = BrowserLoggingMode.Both;
Program.cs
// Custom callback — integrate with ILogger or any logging framework
CobaltEngine.OnBrowserLog = message =>
    logger.LogDebug("[Browser] {Message}", message);
Note: BrowserLoggingMode is a [Flags] enum. File logging uses daily rotation — files are named browser_2025-03-01.log — and old files beyond RetainedLogCount are automatically deleted. The OnBrowserLog callback fires before console or file output, so you can use it alongside the built-in modes.