PDF генерация в Rust: обзор библиотек и инструментов для создания PDF-документов

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

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

Начать курс

PDF генерация в Rust: обзор библиотек и инструментов

Язык Rust, известный своей производительностью и безопасностью, предлагает несколько мощных библиотек для создания PDF-документов. В отличие от Python, где доминируют reportlab и fpdf, экосистема Rust предоставляет легковесные, но функциональные решения. В этой статье мы рассмотрим три ключевые библиотеки: printpdf, genpdf и pdf-writer, а также затронем низкоуровневые подходы.

1. printpdf — базовая, но гибкая библиотека

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

Пример использования:

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

fn main() {
    let (doc, page1, layer1) = PdfDocument::new("PDF Документ", 
        Mm(210.0), Mm(297.0), "Layer 1");

    let font = doc.add_builtin_font(BuiltinFont::Helvetica).unwrap();

    // Добавляем текст
    doc.use_text("Привет, мир!", 24.0, 
        Mm(20.0), Mm(280.0), &font);

    // Рисуем линию
    let layer = doc.get_page(page1).get_layer(layer1);
    layer.set_color(Color::Rgb(Rgb::new(0.0, 0.0, 0.0), None));
    layer.add_line(Point::new(Mm(20.0), Mm(270.0)), 
                   Point::new(Mm(100.0), Mm(270.0)));

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

Плюсы:

  • Полный контроль над структурой PDF
  • Поддержка встраивания TrueType шрифтов
  • Работа с изображениями (PNG, JPEG)
  • Поддержка SVG-графики (через printpdf-svg)

Минусы:

  • Нет автоматической верстки текста
  • Необходимость вручную управлять позиционированием
  • Отсутствие поддержки таблиц и многостраничных макетов

2. genpdf — для простых документов с версткой

genpdf — это более высокоуровневая библиотека, которая автоматизирует процесс верстки. Она идеально подходит для создания отчетов, писем и других документов с простой структурой. Библиотека поддерживает перенос строк, выравнивание текста и базовые элементы дизайна.

Пример создания простого документа:

use genpdf::{Document, SimplePageSettings};
use genpdf::elements::{Paragraph, Break, Image};
use genpdf::fonts::FontFamily;

fn main() -> Result<(), Box> {
    let font_family = FontFamily::from_files(
        "fonts/", 
        "LiberationSans", 
        None
    )?;

    let mut doc = Document::new(font_family);
    doc.set_title("Пример документа");

    // Добавляем элементы
    doc.push(Paragraph::new("Заголовок").bold());
    doc.push(Break::new(5.0));
    doc.push(Paragraph::new("Основной текст с автоматическим переносом строк. "
        .to_string() + "Библиотека genpdf сама рассчитывает позиции."));

    // Сохраняем
    doc.render_to_file("output.pdf")?;
    Ok(())
}

Плюсы:

  • Автоматическая верстка текста
  • Поддержка переносов и выравнивания
  • Простой API для добавления элементов
  • Возможность создания многостраничных документов

Минусы:

  • Ограниченная поддержка графики
  • Нет встроенной поддержки таблиц
  • Меньше контроля над позиционированием

3. pdf-writer — низкоуровневая производительность

pdf-writer — это минималистичная библиотека для прямой записи PDF-объектов. Она предоставляет максимальную производительность и минимальный размер выходного файла, но требует глубокого понимания формата PDF.

Пример создания минимального PDF:

use pdf_writer::{PdfWriter, Writer, Finish, Rect, Name};

fn main() {
    let mut writer = PdfWriter::new();
    
    // Создаем страницу
    let page_id = writer.alloc_page_id();
    writer.pages(page_id);
    
    // Добавляем содержимое
    let content_id = writer.alloc_stream_id();
    writer.stream(content_id, |s|                    

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