Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HTML to PDF Conversion

The core feature of printwell is high-fidelity HTML to PDF conversion using Chromium’s Blink rendering engine.

How It Works

printwell embeds Chromium’s rendering engine (Blink + Skia) as a native library. When you convert HTML to PDF:

  1. HTML is parsed and a DOM tree is built
  2. CSS is applied and the layout is computed
  3. The page is rendered using Skia graphics
  4. PDF is generated using PDFium

This gives you the exact same rendering quality as Chrome, without needing a browser installation.

Supported Features

HTML5

  • Full HTML5 support
  • Semantic elements (<article>, <section>, <nav>, etc.)
  • Forms (for visual rendering; see Form Fields for interactive forms)
  • Tables with complex layouts
  • SVG graphics (inline and external)
  • Canvas elements (static rendering)

CSS3

  • Flexbox and Grid layouts
  • CSS Variables (custom properties)
  • Media queries (print media is used)
  • CSS animations (final frame is captured)
  • Web fonts (@font-face)
  • CSS transforms and filters
  • Multi-column layouts
  • CSS Paged Media (@page rules)
@page {
    size: A4 portrait;
    margin: 2cm;
}

@page :first {
    margin-top: 5cm;
}

.page-break {
    page-break-after: always;
}

@media print {
    .no-print { display: none; }
    a::after { content: " (" attr(href) ")"; }
}

Page Configuration

Page Sizes

SizeDimensions (mm)
A3297 × 420
A4210 × 297
A5148 × 210
Letter216 × 279
Legal216 × 356
Tabloid279 × 432

Or specify custom dimensions:

#![allow(unused)]
fn main() {
let options = PdfOptions::builder()
    .page_width_mm(200.0)
    .page_height_mm(300.0)
    .build();
}

Margins

Margins are specified in millimeters:

#![allow(unused)]
fn main() {
// Individual margins
let margins = Margins::new(20.0, 15.0, 20.0, 15.0); // top, right, bottom, left

// Uniform margins
let margins = Margins::uniform(10.0);

// Symmetric margins
let margins = Margins::symmetric(20.0, 15.0); // vertical, horizontal

// No margins
let margins = Margins::none();
}

Headers and Footers

Headers and footers use HTML templates with special CSS classes:

<div style="font-size: 10px; width: 100%; text-align: center;">
    <span class="title"></span> |
    Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</div>

Available classes:

  • title - Document title
  • pageNumber - Current page number
  • totalPages - Total page count
  • url - Document URL
  • date - Current date

Resource Loading

Remote Resources

By default, remote resources (images, fonts, stylesheets) are loaded:

#![allow(unused)]
fn main() {
let options = RenderOptions::builder()
    .resources(ResourceOptions {
        allow_remote: true,
        timeout_ms: 30000,
        max_concurrent: 6,
        ..Default::default()
    })
    .build();
}

Blocking Domains

Block specific domains (e.g., ads, analytics):

#![allow(unused)]
fn main() {
let options = RenderOptions::builder()
    .resources(ResourceOptions {
        blocked_domains: vec![
            "ads.example.com".into(),
            "analytics.example.com".into(),
        ],
        ..Default::default()
    })
    .build();
}

Base URL

Set a base URL for relative resources:

#![allow(unused)]
fn main() {
let options = RenderOptions::builder()
    .base_url("https://example.com/assets/")
    .build();
}

Web Fonts

Web fonts are fully supported:

<style>
@font-face {
    font-family: 'CustomFont';
    src: url('https://example.com/fonts/custom.woff2') format('woff2');
}

body {
    font-family: 'CustomFont', sans-serif;
}
</style>

Configure font defaults:

#![allow(unused)]
fn main() {
let options = RenderOptions::builder()
    .fonts(FontOptions {
        default_sans_serif: "Arial".into(),
        default_serif: "Times New Roman".into(),
        default_monospace: "Courier New".into(),
        use_system_fonts: true,
        enable_web_fonts: true,
        ..Default::default()
    })
    .build();
}

Viewport Configuration

Control the viewport for rendering:

#![allow(unused)]
fn main() {
let options = RenderOptions::builder()
    .viewport(Viewport {
        width: 1920,
        height: 1080,
        device_scale_factor: 2.0, // Retina/HiDPI
    })
    .build();
}

Scale Factor

Adjust the overall scale:

#![allow(unused)]
fn main() {
let options = PdfOptions::builder()
    .scale(0.8)  // 80% scale
    .build();
}

Valid range: 0.1 to 2.0

Background Graphics

Control background printing:

#![allow(unused)]
fn main() {
// Include backgrounds (default)
let options = PdfOptions::builder()
    .print_background(true)
    .build();

// Exclude backgrounds
let options = PdfOptions::builder()
    .print_background(false)
    .build();
}

Page Ranges

Select specific pages:

#![allow(unused)]
fn main() {
let options = PdfOptions::builder()
    .page_ranges("1-5,8,10-12")
    .build();
}

Metadata

Set PDF metadata:

#![allow(unused)]
fn main() {
let options = PdfOptions::builder()
    .metadata(PdfMetadata {
        title: Some("My Document".into()),
        author: Some("Author Name".into()),
        subject: Some("Document Subject".into()),
        keywords: Some("pdf, rust, printwell".into()),
        creator: Some("My Application".into()),
        producer: Some("printwell".into()),
    })
    .build();
}

Performance Tips

  1. Reuse Converter - Create one Converter instance and reuse it
  2. Use ConverterPool - For batch processing, use a pool
  3. Minimize remote resources - Embed resources when possible
  4. Set appropriate timeouts - Avoid waiting for slow resources
  5. Use CSS print media - Optimize CSS for print output