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
ConverterisSend + Syncand can be shared across threadsConverterPoolmanages 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
| Feature | Description | Dependencies |
|---|---|---|
default | Core + all PDF features | - |
pdf-full | All PDF post-processing | - |
signing | Digital signatures | p12, x509-cert, rsa, ecdsa, cms |
forms | PDF form fields | regex |
pdfa | PDF/A compliance | chrono |
pdfua | PDF/UA accessibility | chrono |
Minimal build:
[dependencies]
printwell = { version = "0.1", default-features = false }