在 Rust 中使用 printpdf 库生成 PDF 报告

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

在 Rust 中使用 printpdf 库生成 PDF 报告



在现代编程世界中,Rust 越来越多地被用于创建高性能的服务器应用程序和命令行工具。其中一项常见的任务是生成 PDF 报告——无论是分析仪表板、发票还是文档。printpdf 库提供了一种轻量且可靠的方式,在纯 Rust 环境中创建 PDF 文档,无需依赖外部系统(例如 LaTeX 或 wkhtmltopdf)。



什么是 printpdf?



printpdf 是一个纯 Rust 库,用于从头生成 PDF 文档。它支持:

  • 使用各种字体(包括嵌入的 TrueType/OpenType 字体)的文本。
  • 矢量图形(线条、矩形、椭圆)。
  • 带有透明度的图像(PNG、JPEG)。
  • 通过手动定位实现的表格和复杂排版。
  • 任意尺寸的页面(A4、Letter、自定义尺寸)。


该库不使用外部进程,因此非常适合嵌入到 Web 服务、CLI 工具或 Rust 桌面应用程序中。



安装



在 Cargo.toml 中添加:



[dependencies]printpdf = "0.7"


您还需要 rusttype crate 来处理字体(它会作为依赖项自动引入)。



基本示例:创建简单报告



让我们编写一个程序,创建一个包含标题、文本和线条的 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,

推荐