从 HTML 创建
提供两个入口:
Pdf::from_html(content)— 基础结构化 HTML(标题、段落、列表、代码、粗体/斜体)。不支持样式。每个绑定均可用。Pdf::from_html_css(html, css, font_bytes)— v0.3.37 新增的完整纯 Rust HTML+CSS 流水线。手写的 CSS 引擎(L3 + L4 选择器子集、级联、calc()/var()、@page/@media print)、由 Taffy 支撑的 block / flex / grid 布局、UAX #14 行断点、通过 rustybuzz 实现的 RTL 整形、::before/::after、page-break-*、<a href>→ 链接注释、<img>data-URI →/XObject,以及多字体级联。零 MPL 依赖。每个绑定均可用。
快速示例
Python
from pdf_oxide import Pdf
pdf = Pdf.from_html("<h1>Hello</h1><p>World</p>")
pdf.save("out.pdf")
WASM
import { WasmPdf } from "pdf-oxide-wasm";
import { writeFileSync } from "fs";
const pdf = WasmPdf.fromHtml("<h1>Hello</h1><p>World</p>");
writeFileSync("out.pdf", pdf.toBytes());
Rust
use pdf_oxide::api::Pdf;
let pdf = Pdf::from_html("<h1>Hello</h1><p>World</p>")?;
pdf.save("out.pdf")?;
Go
package main
import (
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
pdf, err := pdfoxide.FromHtml("<h1>Hello</h1><p>World</p>")
if err != nil { log.Fatal(err) }
defer pdf.Close()
if err := pdf.Save("out.pdf"); err != nil { log.Fatal(err) }
}
C#
using PdfOxide;
using var pdf = Pdf.FromHtml("<h1>Hello</h1><p>World</p>");
pdf.Save("out.pdf");
HTML + CSS 流水线(v0.3.37)
Pdf::from_html_css(html, css, font_bytes) 接受 HTML、CSS 样式表和 TTF/OTF 字体字节,返回分页后的 PDF。extract_text 能做到字节一致的往返,因此生成的 PDF 可直接进入既有的测试体系。
Rust:
use pdf_oxide::api::Pdf;
let font = std::fs::read("DejaVuSans.ttf")?;
let pdf = Pdf::from_html_css(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt } p { line-height: 1.5 }",
font,
)?;
pdf.save("out.pdf")?;
Python:
from pdf_oxide import Pdf
with open("DejaVuSans.ttf", "rb") as f:
font = f.read()
pdf = Pdf.from_html_css(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font,
)
pdf.save("out.pdf")
Node / TypeScript:
import { Pdf } from "pdf-oxide";
import { readFileSync } from "fs";
const font = readFileSync("DejaVuSans.ttf");
const pdf = Pdf.fromHtmlCss(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font,
);
pdf.save("out.pdf");
Go:
font, _ := os.ReadFile("DejaVuSans.ttf")
pdf, err := pdfoxide.FromHtmlCss(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font,
)
if err != nil { log.Fatal(err) }
defer pdf.Close()
_ = pdf.Save("out.pdf")
C#:
var font = File.ReadAllBytes("DejaVuSans.ttf");
using var pdf = Pdf.FromHtmlCss(
"<h1>Hello</h1><p>World</p>",
"h1 { color: blue; font-size: 24pt }",
font);
pdf.Save("out.pdf");
多字体级联
当文档需要混用多个字体族时,使用 Pdf::from_html_css_with_fonts(html, css, fonts)。任意元素上的 CSS font-family 会解析到已注册的字体族(不区分大小写,支持带引号 / 不带引号,以及多词不带引号)。未知的字体族会回退到第一个注册字体。
from pdf_oxide import Pdf
fonts = [
("DejaVu Sans", open("DejaVuSans.ttf", "rb").read()),
("Noto Sans CJK", open("NotoSansCJKtc-Regular.otf", "rb").read()),
]
pdf = Pdf.from_html_css_with_fonts(
'<h1 style="font-family: DejaVu Sans">English</h1>'
'<p style="font-family: \'Noto Sans CJK\'">中文段落</p>',
"h1 { font-size: 24pt }",
fonts,
)
pdf.save("multilang.pdf")
CJK 内容在输出时会自动子集化(v0.3.38 #385)— 从一个 ~17 MB 的 CJK 字体构建 5 个字符的 PDF,文件通常小于 100 KB。
支持的 CSS 范围
- 选择器 — L3 + L4 子集:
:is/:where/:not/:has,结构性伪类,带i/s标志的属性匹配器。 - 级联 — 按来源 / 特异性 / 源码顺序排序,继承,内联样式合并,自定义属性(带循环检测的
var())。 - 函数 —
calc()、min()、max()、clamp()。 - At-rules —
@media print(恒真)、(min/max-width)、带边距框的@page :first / :left / :right / :blank、@font-face、@import、@supports。 - 类型化值 — 颜色(约 150 个命名色、hex、rgb/rgba、hsl)、长度(所有 CSS Values L4 单位)、display、font-size / weight / style / family、margin / padding 简写、line-height。
- 计数器 —
counter/counters,counter-reset/-increment/-set,罗马 / 希腊 / 字母编号。 - 伪元素 —
::before/::after,支持字面字符串、attr(name)、open-quote/close-quote。 - 布局 — block、flex、grid(均通过 Taffy),外边距合并,多列(
column-count/column-width/column-gap),表格(auto + fixed 两种列算法)。 - 内联 — UAX #14 行断点、
text-align、white-space模式、硬换行、原子内联盒。 - 效果 —
opacity、transform: translate*()、page-break-before: always、page-break-after: always。 - HTML — HTML5 分词器,
<style>/<link rel="stylesheet">/ 内联style=""抽取,<img>data-URI 解码(/XObject),<a href>→ 带/URI的/Link注释,<ul>/<ol>列表标记。
不在支持范围
CSS 滤镜、3D 变换、动画、HTML 中的 SVG(每一个可用的 Rust SVG crate 都是 MPL)、MathML、hyphens: auto、shape-outside、JavaScript 执行、全矩阵 transform(缩放 / 旋转)、渐变、box-shadow。
许可证
cargo deny check licenses 通过,零 MPL 传递依赖。Mozilla 的 CSS 技术栈(cssparser、selectors、html5ever、lightningcss、stylo)均为 MPL-2.0;v0.3.37 手写了对应能力,使 pdf_oxide 完全保持在 MIT/Apache 下。
支持的 HTML 元素
| 元素 | 描述 |
|---|---|
<h1> through <h6> |
标题(映射到 PDF 标题大小) |
<p> |
带自动间距的段落 |
<b>, <strong> |
粗体文本 |
<i>, <em> |
斜体文本 |
<ul>, <ol>, <li> |
无序和有序列表 |
<pre>, <code> |
预格式化和行内代码 |
<blockquote> |
块引用 |
<br> |
换行 |
<hr> |
水平线 |
完整 API 参考
Pdf::from_html(content) (静态方法)
使用默认设置(Letter 页面、72pt 边距、12pt Helvetica 字体)从 HTML 内容创建 PDF。
Rust:
use pdf_oxide::api::Pdf;
let html = r#"
<h1>Product Specification</h1>
<p>This document describes the <strong>technical requirements</strong>
for the new product line.</p>
<h2>Requirements</h2>
<ul>
<li>Operating temperature: -20C to 60C</li>
<li>Power consumption: <5W</li>
<li>Weight: <200g</li>
</ul>
"#;
let pdf = Pdf::from_html(html)?;
pdf.save("spec.pdf")?;
JavaScript:
import { WasmPdf } from "pdf-oxide-wasm";
import { writeFileSync } from "fs";
const html = `
<h1>Product Specification</h1>
<p>This document describes the <strong>technical requirements</strong>
for the new product line.</p>
`;
const pdf = WasmPdf.fromHtml(html);
writeFileSync("spec.pdf", pdf.toBytes());
Python:
from pdf_oxide import Pdf
html = """
<h1>Product Specification</h1>
<p>This document describes the <strong>technical requirements</strong>
for the new product line.</p>
"""
pdf = Pdf.from_html(html)
pdf.save("spec.pdf")
Python 签名:
Pdf.from_html(
content: str,
title: str | None = None,
author: str | None = None
) -> Pdf
PdfBuilder::new().from_html(content) (构建器模式)
使用 PdfBuilder 控制页面大小、边距、字体大小和文档元数据。
Rust:
use pdf_oxide::api::PdfBuilder;
use pdf_oxide::writer::PageSize;
let pdf = PdfBuilder::new()
.title("Technical Specification")
.author("Engineering")
.page_size(PageSize::A4)
.margin(54.0)
.font_size(11.0)
.from_html("<h1>Spec</h1><p>Version 2.0</p>")?;
pdf.save("spec_a4.pdf")?;
高级示例
结构化报告
use pdf_oxide::api::Pdf;
let html = r#"
<h1>Incident Report</h1>
<h2>Summary</h2>
<p>On <em>2025-11-15</em>, a service disruption was detected in the
<strong>payment processing</strong> pipeline.</p>
<h2>Timeline</h2>
<ol>
<li>14:32 UTC - Alert triggered for elevated error rates</li>
<li>14:35 UTC - On-call engineer acknowledged</li>
<li>14:48 UTC - Root cause identified: database connection pool exhaustion</li>
<li>15:02 UTC - Fix deployed, services recovering</li>
<li>15:15 UTC - Full recovery confirmed</li>
</ol>
<h2>Root Cause</h2>
<p>A configuration change deployed at 14:00 UTC reduced the maximum
connection pool size from 100 to 10.</p>
<h2>Code Reference</h2>
<pre><code>max_connections: 10 # Should be 100
timeout_seconds: 30
</code></pre>
<h2>Action Items</h2>
<ul>
<li>Add validation for connection pool configuration</li>
<li>Implement canary deployment for config changes</li>
<li>Add alerting for connection pool utilization</li>
</ul>
"#;
let pdf = Pdf::from_html(html)?;
pdf.save("incident_report.pdf")?;
Python 动态 HTML
from pdf_oxide import Pdf
rows = [
("Widget A", "$12.99", 150),
("Widget B", "$24.50", 89),
("Widget C", "$7.25", 312),
]
html = "<h1>Inventory Report</h1>"
html += "<p>Generated on 2025-11-20</p>"
html += "<h2>Current Stock</h2><ul>"
for name, price, qty in rows:
html += f"<li><strong>{name}</strong> - {price} ({qty} units)</li>"
html += "</ul>"
pdf = Pdf.from_html(html, title="Inventory Report")
pdf.save("inventory.pdf")
从文件读取 HTML
from pdf_oxide import Pdf
with open("report.html") as f:
html = f.read()
pdf = Pdf.from_html(html, title="Report")
pdf.save("report.pdf")
import { WasmPdf } from "pdf-oxide-wasm";
import { readFileSync, writeFileSync } from "fs";
const html = readFileSync("report.html", "utf-8");
const pdf = WasmPdf.fromHtml(html);
writeFileSync("report.pdf", pdf.toBytes());
use pdf_oxide::api::Pdf;
let html = std::fs::read_to_string("report.html")?;
let pdf = Pdf::from_html(&html)?;
pdf.save("report.pdf")?;
相关页面
- 从 Markdown 创建 – 将 Markdown 转换为 PDF
- PdfBuilder 流畅 API – 完整构建器配置选项
- DocumentBuilder 底层 API – 程序化页面构建