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

Rust Guide

This guide covers using printwell as a Rust library.

Installation

Add to your Cargo.toml:

[dependencies]
printwell = "0.1"

With specific features:

[dependencies]
printwell = { version = "0.1", features = ["signing", "forms"] }

Basic Usage

Creating a Converter

The Converter is the main entry point. Create one instance and reuse it:

use printwell::Converter;

fn main() -> printwell::Result<()> {
    // Create a converter (initializes the rendering engine)
    let converter = Converter::new()?;

    // Reuse for multiple conversions
    let pdf1 = converter.html_to_pdf("<h1>Document 1</h1>", None, None)?;
    let pdf2 = converter.html_to_pdf("<h1>Document 2</h1>", None, None)?;

    Ok(())
}

Converting HTML to PDF

#![allow(unused)]
fn main() {
use printwell::{Converter, PdfOptions, RenderOptions};

let converter = Converter::new()?;

// Simple conversion
let result = converter.html_to_pdf(
    "<h1>Hello</h1>",
    None,  // RenderOptions
    None,  // PdfOptions
)?;

// Get PDF as bytes
let pdf_bytes = result.data();

// Write to file
result.write_to_file("output.pdf")?;

// Get page count
println!("Pages: {}", result.page_count);
}

Converting URLs

#![allow(unused)]
fn main() {
let result = converter.url_to_pdf(
    "https://example.com",
    None,
    None,
)?;
}

Configuration Options

PDF Options

#![allow(unused)]
fn main() {
use printwell::{PdfOptions, PageSize, Orientation, Margins, PdfMetadata};

let options = PdfOptions::builder()
    // Page size
    .page_size(PageSize::A4)
    // Or custom size in mm
    .page_width_mm(210.0)
    .page_height_mm(297.0)

    // Orientation
    .orientation(Orientation::Portrait)

    // Margins in mm
    .margins(Margins::new(20.0, 15.0, 20.0, 15.0))
    // Or uniform margins
    .margins(Margins::uniform(10.0))

    // Scale (0.1 to 2.0)
    .scale(1.0)

    // Print backgrounds
    .print_background(true)

    // Page ranges
    .page_ranges("1-5,8,10-12")

    // Headers and footers
    .header_template("<div style='font-size:10px'>Header</div>")
    .footer_template("<div style='font-size:10px'>Page <span class='pageNumber'></span></div>")

    // Metadata
    .metadata(PdfMetadata {
        title: Some("My Document".into()),
        author: Some("Author Name".into()),
        subject: Some("Subject".into()),
        keywords: Some("pdf, rust".into()),
        creator: Some("My App".into()),
        producer: None,
    })

    .build();
}

Render Options

#![allow(unused)]
fn main() {
use printwell::{RenderOptions, ResourceOptions, Viewport};

let options = RenderOptions::builder()
    // Base URL for relative resources
    .base_url("https://example.com/")

    // Viewport size
    .viewport(Viewport {
        width: 1920,
        height: 1080,
        device_scale_factor: 2.0,
    })

    // Resource loading
    .resources(ResourceOptions {
        allow_remote: true,
        timeout_ms: 30000,
        blocked_domains: vec!["ads.example.com".into()],
        ..Default::default()
    })

    // Custom stylesheets
    .user_stylesheets(vec![
        "body { font-family: 'Custom Font'; }".into()
    ])

    .build();
}

Batch Processing

For high-throughput scenarios, use ConverterPool:

#![allow(unused)]
fn main() {
use printwell::ConverterPool;

// Create a pool with 4 concurrent converters
let pool = ConverterPool::new(4)?;

// Convert multiple documents
let html_docs = vec![
    "<h1>Doc 1</h1>".to_string(),
    "<h1>Doc 2</h1>".to_string(),
    "<h1>Doc 3</h1>".to_string(),
];

let results = pool.convert_batch(&html_docs)?;

for (i, result) in results.iter().enumerate() {
    result.write_to_file(format!("output_{}.pdf", i))?;
}
}

PDF Post-Processing

Watermarks

#![allow(unused)]
fn main() {
use printwell::watermark::{Watermark, WatermarkPosition, add_watermark};

let pdf_data = std::fs::read("input.pdf")?;

// Text watermark
let watermark = Watermark::text("CONFIDENTIAL")
    .position(WatermarkPosition::Center)
    .rotation(45.0)
    .opacity(0.3)
    .font_size(72.0)
    .color(0x80, 0x80, 0x80);

let result = add_watermark(&pdf_data, &watermark)?;
std::fs::write("watermarked.pdf", result)?;
}

Bookmarks

#![allow(unused)]
fn main() {
use printwell::bookmarks::{Bookmark, add_bookmarks, extract_bookmarks};

let pdf_data = std::fs::read("input.pdf")?;

let bookmarks = vec![
    Bookmark::new("Chapter 1", 1),
    Bookmark::new("Chapter 2", 5).with_children(vec![
        Bookmark::new("Section 2.1", 5),
        Bookmark::new("Section 2.2", 8),
    ]),
    Bookmark::new("Chapter 3", 12),
];

let result = add_bookmarks(&pdf_data, &bookmarks)?;

// Extract existing bookmarks
let existing = extract_bookmarks(&pdf_data)?;
}

Encryption

#![allow(unused)]
fn main() {
use printwell::encrypt::{encrypt_pdf, decrypt_pdf, EncryptionOptions, Permissions};

let pdf_data = std::fs::read("input.pdf")?;

let options = EncryptionOptions::builder()
    .owner_password("admin")
    .user_password("user")
    .permissions(Permissions {
        print: true,
        copy: false,
        modify: false,
        annotate: true,
        ..Default::default()
    })
    .build();

let encrypted = encrypt_pdf(&pdf_data, &options)?;

// Decrypt
let decrypted = decrypt_pdf(&encrypted, "admin")?;
}

Digital Signatures

#![allow(unused)]
fn main() {
use printwell::signing::{sign_pdf, SigningOptions, SignatureAppearance};

let pdf_data = std::fs::read("input.pdf")?;
let cert_data = std::fs::read("certificate.p12")?;

// Invisible signature
let signed = sign_pdf(
    &pdf_data,
    &cert_data,
    "certificate_password",
    SigningOptions {
        reason: Some("Approved".into()),
        location: Some("New York".into()),
        ..Default::default()
    },
)?;

// Visible signature
let signed = sign_pdf_visible(
    &pdf_data,
    &cert_data,
    "password",
    SigningOptions::default(),
    SignatureAppearance {
        page: 1,
        x: 400.0,
        y: 50.0,
        width: 200.0,
        height: 75.0,
        ..Default::default()
    },
)?;
}

Error Handling

printwell uses a custom Result type:

#![allow(unused)]
fn main() {
use printwell::{Result, Error};

fn convert_document() -> Result<()> {
    let converter = Converter::new()?;

    match converter.html_to_pdf("<h1>Test</h1>", None, None) {
        Ok(result) => {
            result.write_to_file("output.pdf")?;
            Ok(())
        }
        Err(Error::Conversion(msg)) => {
            eprintln!("Conversion failed: {}", msg);
            Err(Error::Conversion(msg))
        }
        Err(e) => Err(e),
    }
}
}

Thread Safety

  • Converter is Send + Sync and can be shared across threads
  • ConverterPool manages thread-safe access to multiple converters
  • All PDF manipulation functions are thread-safe
#![allow(unused)]
fn main() {
use std::sync::Arc;
use std::thread;

let converter = Arc::new(Converter::new()?);

let handles: Vec<_> = (0..4).map(|i| {
    let conv = Arc::clone(&converter);
    thread::spawn(move || {
        let html = format!("<h1>Document {}</h1>", i);
        conv.html_to_pdf(&html, None, None)
    })
}).collect();

for handle in handles {
    let result = handle.join().unwrap()?;
    // Process result
}
}

Feature Flags

FeatureDescriptionDependencies
defaultCore + all PDF features-
pdf-fullAll PDF post-processing-
signingDigital signaturesp12, x509-cert, rsa, ecdsa, cms
formsPDF form fieldsregex
pdfaPDF/A compliancechrono
pdfuaPDF/UA accessibilitychrono

Minimal build:

[dependencies]
printwell = { version = "0.1", default-features = false }