Generating PDF Reports in Rust: The printpdf Library

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Generating PDF Reports in Rust with the printpdf Library



In the modern programming world, Rust is increasingly used for creating high-performance server applications and command-line tools. One of the in-demand tasks is generating PDF reports — whether for analytical dashboards, invoices, or documentation. The printpdf library provides a lightweight and reliable way to create PDF documents in pure Rust without dependencies on external systems (e.g., LaTeX or wkhtmltopdf).



What is printpdf?



printpdf is a pure-Rust library for generating PDF documents from scratch. It supports:

  • Text with various fonts (including embedded TrueType/OpenType).
  • Vector graphics (lines, rectangles, ellipses).
  • Images (PNG, JPEG) with transparency.
  • Tables and complex formatting through manual positioning.
  • Pages of any size (A4, Letter, custom).


The library does not use external processes, making it ideal for embedding in web services, CLI utilities, or desktop applications in Rust.



Installation



Add to Cargo.toml:



[dependencies]printpdf = "0.7"


You will also need the rusttype crate for working with fonts (it is automatically pulled in as a dependency).



Basic Example: Creating a Simple Report



Let's write a program that creates a PDF with a title, text, and a line. Save the result to a file named report.pdf.



use printpdf::*;use std::fs::File;use std::io::BufWriter;

fn main() { // Create an A4 document let (doc, page1, layer1) = PdfDocument::new( "My Report", Mm(210.0), Mm(297.0), "Layer 1", );

// Get a font (built-in Helvetica) let font = doc.add_builtin_font(BuiltinFont::Helvetica).unwrap();

// Start drawing on the first layer let current_layer = doc.get_page(page1).get_layer(layer1);

// Text: title current_layer.use_text( "Sales Report", 24.0, Mm(20.0), Mm(270.0), &font, );

// Text: normal current_layer.use_text( "Report Date: 2025-03-15", 12.0, Mm(20.0), Mm(250.0), &font, );

// Draw a separator line current_layer.set_outline_color(Color::Rgb(Rgb::new(0.5, 0.5, 0.5))); current_layer.set_stroke_width(1.0); current_layer.add_line( Point::new(Mm(20.0), Mm(240.0)), Point::new(Mm(190.0), Mm(240.0)), );

// Save to file let file = File::create("report.pdf").unwrap(); let writer = BufWriter::new(file); doc.save(&mut std::io::BufWriter::new(writer)).unwrap();

println!("PDF report created: report.pdf");}


This code creates a single-page PDF with a title, date, and a gray line. All coordinates are specified in millimeters (points can also be used).



Adding a Table with Data



Tables are often needed for reports. printpdf does not have built-in table structures, but they are easy to implement manually:



use printpdf::*;use std::fs::File;use std::io::BufWriter;

fn draw_table( layer: &PdfLayerReference, font: &IndirectFontRef, data: Vec<[&str; 3]>, start_x: Mm, start_y: Mm,) { let col_widths = [Mm(40.0), Mm(60.0), Mm(50.0)]; let row_height = Mm(10.0); let header_bg = Color::Rgb(Rgb::new(0.9, 0.9, 0.9));

for (row_idx, row_data) in data.iter().enumerate() { let y = start_y - row_height * (row_idx as f32);

// Background for header if row_idx == 0 { layer.set_fill_color(header_bg); layer.add_rectangle( start_x, y - row_height,

Recommendations