carbonpdf

Crates.iocarbonpdf
lib.rscarbonpdf
version0.2.0
created_at2026-01-02 21:21:30.5616+00
updated_at2026-01-08 17:17:15.003431+00
descriptionProduction-ready HTML to PDF conversion using Headless Chrome
homepage
repositoryhttps://github.com/RockyCott/carbonpdf
max_upload_size
id2019219
size152,728
Daniel Felipe JB Sevani (RockyCott)

documentation

https://docs.rs/carbonpdf

README

CarbonPDF

Production-ready HTML to PDF conversion in Rust using Headless Chrome.

Features

  • High-fidelity rendering - Uses Headless Chrome for accurate HTML/CSS rendering
  • Production-ready - Battle-tested architecture with comprehensive error handling
  • Fully configurable - Control page size, margins, orientation, scale, and more
  • Easy to use - Ergonomic builder API and sensible defaults
  • Type-safe - Leverages Rust's type system for compile-time correctness
  • Async by default - Built on Tokio for high-performance concurrent operations
  • Modern web standards - Full support for CSS3, flexbox, grid, web fonts
  • Extensible - Trait-based architecture for pluggable rendering backends

Requirements

Chrome or Chromium must be installed and accessible. CarbonPDF will automatically detect Chrome in standard locations, or you can specify a custom path.

Installation

On Ubuntu/Debian:

sudo apt-get install chromium-browser

On Arch Linux:

sudo pacman -S chromium

On macOS:

brew install --cask google-chrome

On Windows: Download from google.com/chrome

Quick Start

Add to your Cargo.toml:

[dependencies]
carbonpdf = "0.1"
tokio = { version = "1", features = ["full"] }

Basic Usage

use carbonpdf::{PdfBuilder, PageSize, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Convert HTML string to PDF
    let pdf = PdfBuilder::new()
        .html("<h1>Hello, CarbonPDF!</h1><p>Easy PDF generation.</p>")
        .page_size(PageSize::A4)
        .margin_all(1.0)
        .build()
        .await?;

    std::fs::write("output.pdf", pdf)?;
    Ok(())
}

From File

let pdf = PdfBuilder::new()
    .file("invoice.html")
    .page_size(PageSize::Letter)
    .build()
    .await?;

From URL

let pdf = PdfBuilder::new()
    .url("https://example.com")
    .build()
    .await?;

Template Features (Optional)

CarbonPDF includes optional Handlebars templating support for custom templates with variables.

Enable Templates

Add the templates feature to your Cargo.toml:

[dependencies]
carbonpdf = { version = "0.2", features = ["templates"] }

Custom Templates

Use your own Handlebars templates:

use carbonpdf::{PdfBuilder, Result};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<()> {
    let template = r#"
    <h1>{{title}}</h1>
    <p>Hello {{name}}, your order #{{order_id}} is confirmed!</p>
    <ul>
    {{#each items}}
        <li>{{this}}</li>
    {{/each}}
    </ul>
"#;

    let data = json!({
        "title": "Order Confirmation",
        "name": "Sevani",
        "order_id": "12345",
        "items": ["Product A", "Product B", "Product C"]
    });

    let pdf = PdfBuilder::new()
        .template(template, data)?
        .build()
        .await?;

    std::fs::write("output.pdf", pdf)?;
    Ok(())
}

Template Helpers

CarbonPDF includes custom Handlebars helpers:

  • {{format_currency value symbol}} - Format currency values
  • {{format_date date_string}} - Format dates

Example:

<p>Total: {{format_currency 1234.56 "$"}}</p>
<!-- Outputs: Total: $1234.56 -->

Advanced Configuration

use carbonpdf::{PdfBuilder, PageSize, Orientation};

let pdf = PdfBuilder::new()
    .html(html_content)
    .page_size(PageSize::A4)
    .orientation(Orientation::Landscape)
    .margins(0.5, 0.75, 0.5, 0.75)  // top, right, bottom, left (inches)
    .scale(0.9)
    .print_background(true)
    .header("<div style='font-size: 10px;'>Page <span class='pageNumber'></span></div>")
    .footer("<div style='text-align: center;'>© 2024 Your Company</div>")
    .timeout(60)  // seconds
    .build()
    .await?;

Custom Page Size

let pdf = PdfBuilder::new()
    .html(content)
    .custom_page_size(8.5, 14.0)  // width, height in inches
    .build()
    .await?;

Docker/CI Configuration

let pdf = PdfBuilder::new()
    .html(content)
    .no_sandbox()  // Required in Docker
    .chrome_args(["--disable-dev-shm-usage"])
    .build()
    .await?;

CLI Tool

Install the CLI:

cargo install carbonpdf --features cli

Usage:

# Convert HTML file
carbonpdf input.html -o output.pdf

# Convert from URL
carbonpdf https://example.com -o example.pdf

# With options
carbonpdf input.html -o output.pdf \
    --page-size A4 \
    --landscape \
    --margin 1.0 \
    --scale 0.9

Architecture

CarbonPDF uses a layered architecture:

  1. Public API Layer - Builder pattern and high-level types
  2. Renderer Abstraction - PdfRenderer trait for backend implementations
  3. Chrome Backend - Chrome DevTools Protocol integration
  4. Process Management - Automatic Chrome lifecycle handling

See ARCHITECTURE.md for detailed design documentation.

Error Handling

CarbonPDF provides detailed error types:

use carbonpdf::Error;

match pdf_result {
    Ok(pdf) => println!("Success!"),
    Err(Error::ChromeProcess(msg)) => eprintln!("Chrome error: {}", msg),
    Err(Error::Timeout(secs)) => eprintln!("Timed out after {}s", secs),
    Err(Error::InvalidConfig(msg)) => eprintln!("Config error: {}", msg),
    Err(e) => eprintln!("Error: {}", e),
}

Best Practices

In Backend Services

// Reuse renderer instance across requests
lazy_static! {
    static ref RENDERER: ChromeRenderer = {
        ChromeRenderer::new(ChromeConfig::default())
            .await
            .expect("Failed to initialize Chrome")
    };
}

async fn generate_invoice(html: String) -> Result<Vec<u8>> {
    RENDERER.render(
        InputSource::html(html),
        PdfConfig::default()
    ).await
}

In CI/CD

# Dockerfile
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y \
    chromium \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/myapp /usr/local/bin/
CMD ["myapp"]
// In your code
let pdf = PdfBuilder::new()
    .html(content)
    .no_sandbox()  // Required in Docker
    .chrome_args(["--disable-dev-shm-usage"])
    .build()
    .await?;

Performance Tips

  • Reuse renderer instances - Browser initialization is expensive
  • Set appropriate timeouts - Default is 30s, adjust based on content complexity
  • Optimize HTML - Minimize external resources, inline critical CSS
  • Use connection pooling - For high-volume scenarios (coming in v0.2)

Roadmap

  • Automatic Chrome download (via feature flag)
  • Connection pooling for high-volume scenarios
  • Playwright backend support
  • PDF/A compliance mode
  • Watermarking support
  • Parallel batch processing utilities
  • Add templates to cli
  • Add stdin to cli
  • Add --css to cli
  • migrar a subcommands (carbonpdf render)

Contributing

Contributions welcome! Please see CONTRIBUTING.md.

License

Licensed under either of:

at your option.

Acknowledgments

Built on the shoulders of giants:

  • chromiumoxide - Chrome DevTools Protocol
  • tokio - Async runtime
  • The Chromium team for an amazing rendering engine
Commit count: 12

cargo fmt