当前位置: 首页 > news >正文

【printpdf】readme.md文件详解

printpdf 是一个用于创建、读取、写入和渲染 PDF 文档的 Rust 库。

Website | Crates.io | Documentation | Donate

[!IMPORTANT]
HTML 到 PDF 的渲染功能仍处于实验阶段(存根 API),并且正在开发中。

目前,您需要手动定位 PDF 元素。

特性

  • 页面、书签、链接注释(读取 / 写入)
  • 图层(读取 / 写入)
  • 图形:线条、形状、贝塞尔曲线、SVG 内容(读取 / 写入)
  • 图像编码 / 解码(读取支持:实验性 / 写入支持需使用 image 库)
  • 嵌入式字体、Unicode 支持(读取支持:实验性 / 写入支持)
  • 最小化文件大小(自动子集化字体)
  • 高级图形 - 叠印、混合等
  • 高级排版 - 字符 / 单词缩放和间距、上标、下标等
  • 嵌入 SVG(内部使用 svg2pdf 库)

实验性特性:

  • 将 PDF 页面渲染为 SVG(每页一个独立的 SVG 文件)
    • 您可以使用生成的 SVG,通过 resvg 将 PDF 页面渲染为图像
  • 从 PDF 页面提取文本(自动解码 Unicode、换行符和文本位置)
  • 用于简单页面布局的最小化 XHTML 布局(使用 azul-layout + kuchiki HTML 解析器)
    • 目前这仅是一个存根 API:它可以编译,但不会产生可用的输出
    • azul-layout 获得更好的 HTML 求解器时,HTML 到 PDF 的管道将得到改进

不支持的功能:

  • 渐变
  • 图案
  • 文件附件
  • 开放印前接口
  • 半色调图像
  • 各种 PDF 标准的符合性 / 错误检查
  • 嵌入式 JavaScript

实时 WASM32 演示请参见:https://fschutt.github.io/printpdf

编写 PDF

基础示例

use printpdf::*;fn main() {let mut doc = PdfDocument::new("My first PDF");let page1_contents = vec![Op::Marker { id: "debugging-marker".to_string() }];let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);let pdf_bytes: Vec<u8> = doc.with_pages(vec![page1]).save(&PdfSaveOptions::default());
}

图形

use printpdf::*;fn main() {let mut doc = PdfDocument::new("My first PDF");let line = Line {// 二次形状。"false" 决定下一个(后续)点是否是贝塞尔控制点(用于曲线)// 如果您想要孔洞,只需将点的缠绕顺序从顺时针重新排序为逆时针。points: vec![(Point::new(Mm(100.0), Mm(100.0)), false),(Point::new(Mm(100.0), Mm(200.0)), false),(Point::new(Mm(300.0), Mm(200.0)), false),(Point::new(Mm(300.0), Mm(100.0)), false),],is_closed: true,};// 三角形形状let polygon = Polygon {rings: vec![vec![(Point::new(Mm(150.0), Mm(150.0)), false),(Point::new(Mm(150.0), Mm(250.0)), false),(Point::new(Mm(350.0), Mm(250.0)), false),]],mode: PaintMode::FillStroke,winding_order: WindingOrder::NonZero,};// 图形配置let fill_color = Color::Cmyk(Cmyk::new(0.0, 0.23, 0.0, 0.0, None));let outline_color = Color::Rgb(Rgb::new(0.75, 1.0, 0.64, None));let mut dash_pattern = LineDashPattern::default();dash_pattern.dash_1 = Some(20);let extgstate = ExtendedGraphicsStateBuilder::new().with_overprint_stroke(true).with_blend_mode(BlendMode::multiply()).build();let page1_contents = vec![// 添加 line1(正方形)Op::SetOutlineColor { col: Color::Rgb(Rgb::new(0.75, 1.0, 0.64, None)) },Op::SetOutlineThickness { pt: Pt(10.0) },Op::DrawLine { line: line },// 添加 line2(三角形)Op::SaveGraphicsState,Op::LoadGraphicsState { gs: doc.add_graphics_state(extgstate) },Op::SetLineDashPattern { dash: dash_pattern },Op::SetLineJoinStyle { join: LineJoinStyle::Round },Op::SetLineCapStyle { cap: LineCapStyle::Round },Op::SetFillColor { col: fill_color },Op::SetOutlineThickness { pt: Pt(15.0) },Op::SetOutlineColor { col: outline_color },Op::DrawPolygon { polygon: polygon },Op::RestoreGraphicsState,];let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);let pdf_bytes: Vec<u8> = doc.with_pages(vec![page1]).save(&PdfSaveOptions::default());
}

图像

  • 图像仅在发布模式下被压缩。在调试模式下,您可能会得到巨大的 PDF(6 MB 或更多)。
  • 为了使这个过程更快,请使用 BufReader 而不是直接从文件读取。
  • 图像的缩放是隐式完成的,以适应 300 dpi 下的一像素等于一点。
use printpdf::*;fn main() {let mut doc = PdfDocument::new("My first PDF");let image_bytes = include_bytes!("assets/img/BMP_test.bmp");let image = RawImage::decode_from_bytes(image_bytes).unwrap(); // 需要 --feature bmp// 在 PDF 中,图像是一个 `XObject`,由唯一的 `ImageId` 标识let image_xobject_id = doc.add_image(image);let page1_contents = vec![Op::UseXobject { id: image_xobject_id.clone(), transform: XObjectTransform::default() }];let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);let pdf_bytes: Vec<u8> = doc.with_pages(vec![page1]).save(&PdfSaveOptions::default());
}

字体

use printpdf::*;fn main() {let mut doc = PdfDocument::new("My first PDF");let roboto_bytes = include_bytes!("assets/fonts/RobotoMedium.ttf").unwrap()let font_index = 0;let mut warnings = Vec::new();let font = ParsedFont::from_bytes(&roboto_bytes, font_index, &mut warnings).unwrap();// 如果您需要自定义文本整形(内部使用 `allsorts` 字体整形器)// let glyphs = font.shape(text);// printpdf 自动跟踪 PDF 中使用的字体let font_id = doc.add_font(&font);let text_pos = Point {x: Mm(10.0).into(),y: Mm(100.0).into(),}; // 从左下角开始let page1_contents = vec![Op::SetLineHeight { lh: Pt(33.0) },Op::SetWordSpacing { pt: Pt(33.0) },Op::SetCharacterSpacing { multiplier: 10.0 },Op::SetTextCursor { pos: text_pos },// Op::WriteCodepoints { ... }// Op::WriteCodepointsWithKerning { ... }Op::WriteText {items: vec![TextItem::Text("Lorem ipsum".to_string())],font: font_id.clone(),},Op::AddLineBreak,Op::WriteText {items: vec![TextItem::Text("dolor sit amet".to_string())],font: font_id.clone(),},Op::AddLineBreak,];let save_options = PdfSaveOptions {subset_fonts: true, // 保存时自动子集化字体..Default::default()};let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);let mut warnings = Vec::new();let pdf_bytes: Vec<u8> = doc.with_pages(vec![page1]).save(&save_options, &mut warnings);
}

表格、HTML

为了创建表格等,printpdf 使用了一个基本的布局系统,类似于 wkhtmltopdf(尽管在功能方面更有限)。它对于基本的页面布局、书籍渲染和报告 / 表单 / 等来说已经足够好。包括自动分页。

由于 printpdf 支持 WASM,因此有一个交互式演示在 https://fschutt.github.io/printpdf - 尝试使用 XML。

有关 XML 语法描述,请参见 SYNTAX.md。

// 需要 --features="html"
use printpdf::*;fn main() {// 参见 https://fschutt.github.io/printpdf 获取交互式 WASM 演示!let html = r#"<html><!-- printpdf 自动将内容分页 --><body style="padding:10mm"><p style="color: red; font-family: sans-serif;" data-chapter="1" data-subsection="First subsection">Hello!</p><div style="width:200px;height:200px;background:red;" data-chapter="1" data-subsection="Second subsection"><p>World!</p></div></body><!-- 为每个页面配置页眉和页脚 --><head><header><h4 style="color: #2e2e2e;min-height: 8mm;">Chapter {attr:chapter} * {attr:subsection}</h4><p style="position: absolute;top:5mm;left:5mm;">{builtin:pagenum}</p></header><footer><hr/><footer/></head></html>"#;let options = XmlRenderOptions {// 在 HTML 中使用的命名图像,例如 ["image1.png" => DecodedImage(image1_bytes)]images: BTreeMap::new(),// 在 HTML 中使用的命名字体,例如 ["Roboto" => DecodedImage(roboto_bytes)]fonts: BTreeMap::new(),// 默认页面宽度,printpdf 将自动分页page_width: Mm(210.0),// 默认页面高度page_height: Mm(297.0),};let pdf_bytes = PdfDocument::new("My PDF").with_html(html, &options).unwrap().save(&PdfSaveOptions::default());
}

路线图

printpdf 的目标是成为一个通用 PDF 库,例如 libharu 或类似的库。由 printpdf 生成的 PDF 应始终遵循 PDF 标准,除非您将其关闭。目前,仅涵盖了标准 PDF/X-3:2002(即根据 Adobe Acrobat 的有效 PDF)。随着时间的推移,将支持更多标准。

printpdf 维基实时更新于:https://github.com/fschutt/printpdf/wiki

以下是我在开发这个库时找到的一些资源:

  • PDFXPlorer,显示 PDF 的 DOM 树,需要 .NET 2.0
  • 官方 PDF 1.7 参考
  • [德语] 如何在 PDF 中嵌入 Unicode 字体
  • PDF X/1-a 验证器
  • PDF X/3 技术说明

许可证 / 支持

本库采用 MIT 许可证授权。

您可以在 https://github.com/sponsors/fschutt 进行捐赠(一次性或定期)。谢谢!

http://www.dtcms.com/a/544256.html

相关文章:

  • 环形缓冲区实现共享内存
  • Spring AI 搭建 RAG 个人知识库
  • 网站地址是什么用淘宝域名做网站什么效果
  • SPSSAU「质量控制」模块:从可视化监控到过程优化,一站式搞定质量难题
  • 基于健康指标的自动驾驶全系统运行时安全分析方法
  • 从 0 到 1 开发 Rust 分布式日志服务:高吞吐设计 + 存储优化,支撑千万级日志采集
  • 如何做好网站的推广工作成都百度爱采购
  • [无人机sdk] Open Protocol | 协议包构造验证
  • 【Vscode】解决ssh远程开发时Formatter失效的问题
  • TCP 如何保证传输的可靠性?
  • 亲子娱乐升级!Docker 电视盒子 ADB 安装助手,儿童 APP 一键装满电视
  • Microsoft 365 Copilot 扩展至应用和工作流构建功能
  • 【Latex】本地部署latex+vscode
  • 注册中心(环境隔离、分级模型、Eureka)、远程调用负载均衡、服务保护原理分析
  • 有没有专门做建筑造价的私单网站网站开发风险
  • LSTM模型做二分类(PyTorch实现)
  • Linux 文件变动监控工具:原理、设计与实用指南(C/C++代码实现)
  • 建站之星怎么用做视频解析网站犯法吗
  • LibreTV无广告观影实测:聚合全网资源,远程访问家庭影院新方案!
  • 仓颉中的 UTF-8 编码处理:从 DFA 解码、错误策略到流式与字素迭代的工程实战
  • 【React】打卡笔记,入门学习02:react-router
  • Latex 转 word 在线
  • 【OD刷题笔记】- 可以组成网络的服务器
  • 《算法闯关指南:优选算法--前缀和》--27.寻找数组的中心下标,28.除自身以外数组的乘积
  • linux arm64平台上协议栈发包报文长度溢出导致系统挂死举例
  • 深入理解 Rust `HashMap` 的哈希算法与冲突解决机制
  • 彩票网站开发做一个网站价格
  • 《C++ 继承》三大面向对象编程——继承:派生类构造、多继承、菱形虚拟继承概要
  • 医疗AI白箱编程:从理论到实践指南(代码部分)
  • Spring Cache 多级缓存中 hash 类型 Redis 缓存的自定义实现与核心功能