如何用Rust的Printpdf 0.5.3 crate实现PDF文本自动换行格式化?
解决Printpdf中文本自动换行问题的方案
你在开发求职信PDF生成工具时,遇到Printpdf无法自动处理长文本换行的问题,以下是两种可行的解决思路:
方案一:保留Printpdf,结合textwrap实现手动换行
Printpdf本身不支持文本自动布局,但可以借助textwrap库将长文本拆分为适配页面宽度的多行,再逐行写入PDF。
步骤1:添加依赖
在Cargo.toml中加入textwrap:
[dependencies] printpdf = "0.5" textwrap = "0.16" # 可使用最新稳定版
步骤2:修改代码实现换行
核心是计算页面可用宽度,用textwrap拆分文本后逐行写入:
use std::io; use printpdf::*; use std::fs::File; use std::io::BufWriter; use textwrap::Wrapper; fn main() { // 读取用户输入 println!("Enter: Position"); let mut position = String::new(); io::stdin().read_line(&mut position).expect("failed to read"); println!("Enter: Company Name"); let mut coname = String::new(); io::stdin().read_line(&mut coname).expect("failed to read"); println!("Enter: Company Location"); let mut location = String::new(); io::stdin().read_line(&mut location).expect("failed to read"); let sample = "This is some random Sample text, This text should eventaully be a user input. Currently, this text is not a user input. this text is supposed to be a text of long string data that will eventually be added to the document via user input"; let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(216.0), Mm(279.0), "Layer 1"); let current_layer = doc.get_page(page1).get_layer(layer1); let font = doc.add_external_font(File::open("./fonts/TNR-Regular.ttf").unwrap()).unwrap(); // 写入抬头信息 current_layer.use_text(position.trim(), 14.0, Mm(25.0), Mm(250.0), &font); current_layer.use_text(coname.trim(), 14.0, Mm(25.0), Mm(240.0), &font); current_layer.use_text(location.trim(), 14.0, Mm(25.0), Mm(230.0), &font); current_layer.use_text(" ", 14.0, Mm(25.0), Mm(220.0), &font); // 计算页面可用宽度(左右边距各25mm) let available_width_mm = 216.0 - 25.0 * 2.0; // 转换为每行可容纳字符数:按14pt字体估算,每个字符约占8pt宽度 let char_per_line = (available_width_mm * 72.0 / 25.4 / 8.0) as usize; // 用textwrap拆分文本 let wrapper = Wrapper::new(char_per_line); let wrapped_lines = wrapper.wrap(sample); // 逐行写入文本 current_layer.begin_text_section(); current_layer.set_font(&font, 14.0); let mut y_pos = 210.0; let line_height = 18.0; // 行高略大于字号保证可读性 for line in wrapped_lines { current_layer.set_text_cursor(Mm(25.0), Mm(y_pos)); current_layer.write_text(line, &font); y_pos -= line_height; } current_layer.end_text_section(); doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap(); }
说明:通过计算页面可用宽度拆分文本,逐行控制Y坐标写入实现换行。若需更精准的字符宽度计算,可调用Printpdf字体的get_glyph_width方法逐个字符计算宽度后调整换行位置。
方案二:更换高层PDF生成库
若不想手动处理布局细节,可选择支持自动文本排版的高层库,比如genpdf,它封装了文本换行、边距、对齐等功能,更适合快速开发。
步骤1:添加依赖
[dependencies] genpdf = "0.2" genpdf-printpdf = "0.2" # 基于printpdf后端,也可选择其他后端
步骤2:简化代码实现
use genpdf::{Document, Element, Fonts, SimplePageDecorator}; use std::fs::File; use std::io; fn main() { // 读取用户输入 println!("Enter: Position"); let mut position = String::new(); io::stdin().read_line(&mut position).expect("failed to read"); println!("Enter: Company Name"); let mut coname = String::new(); io::stdin().read_line(&mut coname).expect("failed to read"); println!("Enter: Company Location"); let mut location = String::new(); io::stdin().read_line(&mut location).expect("failed to read"); let sample = "This is some random Sample text, This text should eventaully be a user input. Currently, this text is not a user input. this text is supposed to be a text of long string data that will eventually be added to the document via user input"; // 加载字体 let mut fonts = Fonts::new(); fonts.add_from_file("./fonts/TNR-Regular.ttf").unwrap(); // 配置文档:页面尺寸、边距 let mut doc = Document::new(fonts); doc.set_page_size(genpdf::PaperSize::Letter); // 对应216x279mm doc.set_margins(genpdf::Margins::mm(25.0)); doc.set_page_decorator(SimplePageDecorator::new()); // 添加内容,自动处理换行 doc.push(Element::Paragraph(position.trim().to_string())); doc.push(Element::Paragraph(coname.trim().to_string())); doc.push(Element::Paragraph(location.trim().to_string())); doc.push(Element::Paragraph(String::new())); // 空行 doc.push(Element::Paragraph(sample.to_string())); // 保存文档 doc.save(&mut File::create("cover_letter.pdf").unwrap()).unwrap(); }
说明:genpdf会自动处理文本换行、行间距和页面布局,代码更简洁,后续扩展GUI功能时也更容易集成。
总结
- 若想继续使用Printpdf,结合textwrap手动拆分换行是低成本解决方案;
- 若追求开发效率和更丰富的排版功能,更换genpdf等高层库更适合,尤其后续要做跨平台GUI工具时,高层库的封装会减少很多重复工作。
内容的提问来源于stack exchange,提问作者THRASHER-Li
相关产品推荐
相关产品推荐

