Генерация PDF отчётов на Rust с библиотекой printpdf
В современном мире программирования Rust всё чаще используется для создания высокопроизводительных серверных приложений и инструментов командной строки. Одной из востребованных задач является генерация PDF-отчётов — будь то аналитические дашборды, счета-фактуры или документация. Библиотека printpdf предоставляет лёгкий и надёжный способ создавать PDF-документы на чистом Rust без зависимостей от внешних систем (например, LaTeX или wkhtmltopdf).
Что такое printpdf?
printpdf — это pure-Rust библиотека для генерации PDF-документов с нуля. Она поддерживает:
- Текст с различными шрифтами (включая встраиваемые TrueType/OpenType).
- Векторную графику (линии, прямоугольники, эллипсы).
- Изображения (PNG, JPEG) с прозрачностью.
- Таблицы и сложное форматирование через ручное позиционирование.
- Страницы любого размера (A4, Letter, кастомные).
Библиотека не использует внешние процессы, что делает её идеальной для встраивания в веб-сервисы, CLI-утилиты или десктопные приложения на Rust.
Установка
Добавьте в Cargo.toml:
[dependencies]
printpdf = "0.7"
Также вам понадобится крейт rusttype для работы со шрифтами (он автоматически подтягивается как зависимость).
Базовый пример: создание простого отчёта
Напишем программу, которая создаёт PDF с заголовком, текстом и линией. Результат сохраним в файл report.pdf.
use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
fn main() {
// Создаём документ формата A4
let (doc, page1, layer1) = PdfDocument::new(
"Мой отчёт",
Mm(210.0),
Mm(297.0),
"Layer 1",
);
// Получаем шрифт (встроенный Helvetica)
let font = doc.add_builtin_font(BuiltinFont::Helvetica).unwrap();
// Начинаем рисовать на первом слое
let current_layer = doc.get_page(page1).get_layer(layer1);
// Текст: заголовок
current_layer.use_text(
"Отчёт о продажах",
24.0,
Mm(20.0),
Mm(270.0),
&font,
);
// Текст: обычный
current_layer.use_text(
"Дата отчёта: 2025-03-15",
12.0,
Mm(20.0),
Mm(250.0),
&font,
);
// Рисуем линию-разделитель
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)),
);
// Сохраняем в файл
let file = File::create("report.pdf").unwrap();
let writer = BufWriter::new(file);
doc.save(&mut std::io::BufWriter::new(writer)).unwrap();
println!("PDF отчёт создан: report.pdf");
}
Этот код создаёт одностраничный PDF с заголовком, датой и серой линией. Все координаты задаются в миллиметрах (можно использовать и пункты).
Добавление таблицы с данными
Для отчётов часто нужны таблицы. В printpdf нет готовых табличных структур, но их легко реализовать вручную:
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);
// Фон для заголовка
if row_idx == 0 {
layer.set_fill_color(header_bg);
layer.add_rectangle(
start_x,
y - row_height,
col_widths[0] + col_widths[1] + col_widths[2],
row_height,
);
layer.fill();
}
// Рисуем текст в каждой колонке
for (col_idx, cell_text) in row_data.iter().enumerate() {
let x = start_x
+ col_widths[..col_idx].iter().sum::<Mm Также в библиотеке
Валидация данных в Swift (iOS/macOS): библиотеки и подходы
Реактивное программирование в JavaScript (Node.js): библиотека RxJS
Redis Client (Jedis) для Java: полное руководство с примерами