Генерация PDF отчётов на Rust: библиотека printpdf

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Генерация PDF отчётов на Rust с библиотекой printpdf

В современном веб-разработке создание PDF-документов остаётся востребованной задачей — от формирования счетов и отчётов до экспорта данных. Если ваш проект написан на Rust или вы используете его для бэкенда, библиотека printpdf станет надёжным инструментом для генерации PDF. В этой статье мы разберём возможности printpdf, покажем примеры кода и обсудим, как интегрировать её в образовательный проект.

Что такое printpdf?

printpdf — это чистая Rust-библиотека для создания PDF-документов без внешних зависимостей. Она позволяет:

  • Создавать многостраничные документы с произвольным содержимым
  • Добавлять текст с различными шрифтами (включая TrueType и OpenType)
  • Рисовать линии, прямоугольники, эллипсы и сложные пути
  • Встраивать изображения (JPEG, PNG)
  • Работать с таблицами и сетками
  • Управлять цветовыми пространствами (RGB, CMYK, Grayscale)

Главное преимущество — полная автономность: не требуется установка LaTeX, wkhtmltopdf или других систем. Это делает printpdf идеальным выбором для образовательного сайта pythonlib.ru, где важна простота развёртывания и воспроизводимость примеров.

Установка и первый документ

Добавьте зависимость в Cargo.toml:

[dependencies]
printpdf = "0.7"

Создадим простейший PDF с текстом:

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

fn main() {
    // Создаём документ формата A4
    let (doc, page1, layer1) = PdfDocument::new(
        "Мой первый PDF",
        Mm(210.0),
        Mm(297.0),
        "Layer 1"
    );

    // Получаем шрифт по умолчанию
    let font = doc.add_builtin_font(BuiltinFont::Helvetica).unwrap();

    // Добавляем текст
    let current_layer = doc.get_page(page1).get_layer(layer1);
    current_layer.use_text(
        "Привет, PythonLib!",
        24.0,
        Mm(10.0),
        Mm(280.0),
        &font
    );

    // Сохраняем файл
    let file = File::create("hello.pdf").unwrap();
    let writer = BufWriter::new(file);
    doc.save(&mut writer).unwrap();

    println!("PDF создан: hello.pdf");
}

Этот код создаёт одностраничный PDF с надписью. Обратите внимание: use_text принимает координаты в миллиметрах, а размер шрифта — в пунктах.

Работа с графикой и цветом

printpdf поддерживает рисование примитивов. Создадим отчёт с линией и закрашенным прямоугольником:

use printpdf::*;

fn create_report() -> (PdfDocumentReference, PdfPageIndex, PdfLayerIndex) {
    let (doc, page, layer) = PdfDocument::new(
        "Отчёт",
        Mm(210.0),
        Mm(297.0),
        "Data"
    );

    let layer = doc.get_page(page).get_layer(layer);

    // Рисуем красную линию
    let points = vec![
        (Point::new(Mm(20.0), Mm(250.0)), false),
        (Point::new(Mm(190.0), Mm(250.0)), false),
    ];
    let line = Line {
        points,
        is_closed: false,
    };
    layer.set_color(Color::Rgb(Rgb::new(1.0, 0.0, 0.0)));
    layer.add_line(line);

    // Закрашенный синий прямоугольник
    let rect = Polygon {
        vertices: vec![
            (Point::new(Mm(50.0), Mm(200.0)), false),
            (Point::new(Mm(150.0), Mm(200.0)), false),
            (Point::new(Mm(150.0), Mm(180.0)), false),
            (Point::new(Mm(50.0), Mm(180.0)), false),
        ],
        is_closed: true,
    };
    layer.set_color(Color::Rgb(Rgb::new(0.0, 0.0, 1.0)));
    layer.add_polygon(rect);

    (doc, page, layer)
}

Цвета задаются в диапазоне 0.0–1.0. Для CMYK используйте Color::Cmyk(Cmyk::new(0.0, 1.0, 0.0, 0.0)).

Встраивание изображений

Добавим логотип в отчёт:

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

fn add_image(doc: &mut PdfDocumentReference, page: PdfPageIndex, layer: PdfLayerIndex) {
    let image = Image::from_reader(File::open("logo.png").unwrap()).unwrap();
    // Масштабируем до 50x50 мм
    let size = image.image.into_image_size();
    image.add_to_layer(
        doc.get_page(page).get_layer(layer                    

Рекомендаций