纯浏览器端 Runs in the browser
完全在浏览器端运行,不上传待导出的文档。 Runs entirely in the browser without uploading the document being exported.
dompdf.js 是一个纯前端 DOM 转 PDF 引擎,由 TypeScript、Web Worker、Rust 和 WebAssembly 驱动。它直接读取浏览器已经计算好的 DOM 布局,在浏览器内生成以矢量文本为主的 PDF,不依赖服务端、jsPDF 或截图式 PDF 流水线。 dompdf.js is a pure-frontend DOM-to-PDF engine powered by TypeScript, Web Workers, Rust, and WebAssembly. It reads the layout already computed by the browser and generates primarily vector-based PDFs directly in the browser, without a server, jsPDF, or a screenshot-based PDF pipeline.
它适合需要可选中文本、长文档分页、中文和自定义字体、页眉页脚、水印、表单、压缩或加密的浏览器端导出场景。 It is designed for browser-side exports that need selectable text, long-document pagination, Chinese and custom fonts, headers and footers, watermarks, forms, compression, or encryption.
在典型长文档测试中,dompdf.js 可在约 2 秒内生成 500 页
PDF;在内容结构和设备资源允许的情况下,单次导出规模可扩展到上万页。实际耗时和可生成页数会受到
DOM 复杂度、图片数量、字体体积、压缩配置、浏览器及设备性能影响。
In representative long-document benchmarks, dompdf.js can
generate a 500-page PDF in about two seconds. With suitable document
structure and sufficient device resources, a single export can scale to
tens of thousands of pages. Actual rendering time and page limits depend
on DOM complexity, image count, font size, compression settings, browser,
and device performance.
完全在浏览器端运行,不上传待导出的文档。 Runs entirely in the browser without uploading the document being exported.
输出可搜索、可复制的矢量文本,而不是整页截图。 Produces searchable, selectable vector text instead of full-page screenshots.
使用 Web Worker 和 WASM 执行 PDF 渲染,降低主线程阻塞。 Uses Web Workers and WASM for PDF rendering to reduce main-thread blocking.
面向大规模文档优化,典型测试约 2 秒生成 500 页,极限规模可达上万页。 About two seconds for 500 pages in representative benchmarks, with extreme workloads reaching tens of thousands of pages.
支持单页长画布和标准纸张分页。 Supports both continuous single-page output and standard paginated documents.
支持 A/B/C 系列、Letter、Legal、Tabloid 等纸张,也可传入自定义尺寸。 Supports A/B/C series, Letter, Legal, Tabloid, and custom page sizes.
支持页眉、页脚、页码占位符和逐页配置。 Supports headers, footers, page-number placeholders, and per-page configuration.
支持文字水印、图片水印、上下层叠放和逐页控制。 Supports text and image watermarks, under/over layering, and per-page control.
支持 Unicode、TTF 字体、字体子集、粗体、斜体、图标字体和语言字体回退。 Supports Unicode, TTF fonts, font subsetting, bold, italic, icon fonts, and language-based font fallback.
支持图片、SVG、Canvas、背景、边框、圆角、阴影、透明度和渐变等常见视觉效果。 Supports images, SVG, Canvas, backgrounds, borders, rounded corners, shadows, opacity, gradients, and other common visual effects.
支持超链接 PDF 注释。 Preserves hyperlinks as PDF link annotations.
支持静态表单外观和 PDF AcroForm 交互字段。 Supports static form appearance and interactive PDF AcroForm fields.
支持 DEFLATE 压缩、用户密码、所有者密码和 PDF 权限控制。 Supports DEFLATE compression, user and owner passwords, and PDF permission flags.
提供 Blob、Uint8Array、直接下载和底层快照
API,并提供 TypeScript 类型声明。
Provides Blob, Uint8Array,
direct-download, and low-level snapshot APIs, plus TypeScript
declarations.
dompdf.js
使用浏览器计算后的几何位置来还原页面,因此 CSS 仍由浏览器负责布局。PDF
渲染器会尽量保留常见视觉效果,但它不是完整的浏览器排版引擎,复杂滤镜、动画、视频和部分高级
CSS 可能被栅格化或降级。
CSS layout stays the browser's job.dompdf.js
reconstructs the page from geometry already computed by the browser, so
CSS layout remains the browser's responsibility. The PDF renderer
preserves common visual effects where possible, but it is not a complete
browser rendering engine. Complex filters, animations, video, and some
advanced CSS may be rasterized or degraded.
npm install dompdf.js
运行环境需要现代浏览器以及 Worker、WebAssembly、Blob
等 Web API。包本身要求 Node.js 18+ 用于安装、构建和开发,但导出 API 依赖
DOM,不能直接在纯 Node.js 环境中调用。
<script src="https://cdn.jsdelivr.net/npm/dompdf.js@latest/dist/dompdf.min.js"></script>
通过 <script> 引入后,API 会挂载到全局
dompdf。
npm install dompdf.js
The runtime requires a modern browser with Web Workers, WebAssembly,
Blob, and related Web APIs. The package requires Node.js 18+
for installation, builds, and development, but its export APIs depend on
the DOM and cannot be called directly in a plain Node.js environment.
<script src="https://cdn.jsdelivr.net/npm/dompdf.js@latest/dist/dompdf.min.js"></script>
When loaded through a <script> tag, the API is exposed
globally as dompdf.
默认导出函数返回 Promise<Blob>。下面三个标签页分别对应获取
Blob、直接下载和通过 CDN 引入。
The default export returns a Promise<Blob>. The three
tabs below cover getting a Blob, downloading directly, and using the CDN
build.
import dompdf from 'dompdf.js';
const element = document.querySelector('#capture');
if (!element) throw new Error('没有找到 #capture');
const blob = await dompdf(element, {
format: 'a4',
pagination: true,
backgroundColor: '#ffffff',
});
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
setTimeout(() => URL.revokeObjectURL(url), 30_000);
window.open 之后立刻释放 URL。URL.revokeObjectURL,否则可能得到空白页面。
Do not revoke the URL right after window.open.URL.revokeObjectURL too early can produce a blank page.
常规业务优先使用下面五个高层 API。底层 Snapshot API 更适合调试、分页覆盖层或自定义流水线。 Prefer the five high-level APIs below for application code. The low-level snapshot APIs are intended for diagnostics, pagination overlays, and custom pipelines.
dompdf(root, options?)
默认导出,也是最简入口。行为与 exportPDF 相同。
The default export and simplest entry point. It behaves the same as
exportPDF.
exportPDF(root, options?)
采集 DOM、生成 PDF,并返回 MIME 类型为
application/pdf 的 Blob。适合预览、上传或自行下载。
Collects the DOM, generates the PDF, and returns a
Blob with the MIME type
application/pdf. Use it for previews, uploads, or a custom
download flow.
renderToBytes(root, options?)返回 PDF 原始字节。适合交给文件系统 API、上传接口或其他二进制处理流程。 Returns the raw PDF bytes. Use it with the File System API, an upload endpoint, or another binary-processing pipeline.
downloadPDF(root, options?, filename?)
生成 PDF 并触发浏览器下载。默认文件名为 export.pdf。
Generates the PDF and triggers a browser download. The default filename
is export.pdf.
inspect(root, options?)返回 WASM 侧的快照摘要,包含节点、图片、字体和页数等信息。它主要用于诊断,不会生成可下载的 PDF。 Returns a WASM-side snapshot summary containing node, image, font, and page counts. It is intended for diagnostics and does not create a downloadable PDF.
function dompdf(
root: HTMLElement,
options?: ExportOptions,
): Promise<Blob>;
import dompdf, { exportPDF, renderToBytes, downloadPDF, inspect } from 'dompdf.js';
// 默认导出:返回 Blob
const blob = await dompdf(element, options);
// 与默认导出等价
const sameBlob = await exportPDF(element, options);
// 原始字节
const bytes: Uint8Array = await renderToBytes(element, options);
// 触发浏览器下载
await downloadPDF(element, options, 'invoice.pdf');
// 诊断摘要
console.log(await inspect(element, { pagination: true }));
包还导出了以下底层函数:
collectSnapshot(root, options?) -> Promise<Uint8Array>:采集并编码
DOM 快照
collectSnapshotData(root, options?):返回编码前的中间数据encodeSnapshot(data, perPageHF, perPageWatermark?) -> Uint8Array:编码中间数据
computePageBreaks(root, options?) -> number[]:计算相对根元素顶部的分页 Y 坐标
pageConfigNeedsPerPageResolution、watermarkNeedsPerPageResolution:判断配置是否需要逐页解析
resolvePerPageHF、resolveStaticPageConfigHF:解析逐页页眉页脚
resolvePerPageWatermark、resolveStaticWatermarkPages:解析逐页水印
这些接口面向调试、可视化分页和自定义流水线。快照二进制格式属于项目内部协议,普通业务代码应优先使用
dompdf、exportPDF、renderToBytes 或
downloadPDF。
import dompdf, { exportPDF, renderToBytes, downloadPDF, inspect } from 'dompdf.js';
// Default export: returns a Blob
const blob = await dompdf(element, options);
// Equivalent to the default export
const sameBlob = await exportPDF(element, options);
// Raw bytes
const bytes: Uint8Array = await renderToBytes(element, options);
// Triggers a browser download
await downloadPDF(element, options, 'invoice.pdf');
// Diagnostic summary
console.log(await inspect(element, { pagination: true }));
The package also exports these lower-level functions:
collectSnapshot(root, options?) -> Promise<Uint8Array>:
collect and encode a DOM snapshot
collectSnapshotData(root, options?): return the intermediate
data before encoding
encodeSnapshot(data, perPageHF, perPageWatermark?) -> Uint8Array:
encode intermediate data
computePageBreaks(root, options?) -> number[]: calculate
page-break Y coordinates relative to the root element
pageConfigNeedsPerPageResolution and
watermarkNeedsPerPageResolution: determine whether a
configuration needs per-page resolution
resolvePerPageHF and resolveStaticPageConfigHF:
resolve per-page headers and footers
resolvePerPageWatermark and
resolveStaticWatermarkPages: resolve per-page watermarks
These APIs are intended for diagnostics, pagination overlays, and custom
pipelines. The snapshot binary format is an internal protocol; normal
application code should prefer dompdf, exportPDF,
renderToBytes, or downloadPDF.
默认导出对象也附带了常用方法,因此下面两种写法等价: Common methods are also attached to the default export, so these forms are equivalent:
import dompdf, { renderToBytes } from 'dompdf.js';
const bytesA = await dompdf.renderToBytes(element);
const bytesB = await renderToBytes(element);
以下参数均已实现并生效: The following options are implemented and take effect:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
format |
string | [number, number] |
'a4' |
纸张名称或 [宽, 高],单位为 pt |
pageWidthPt |
number |
由 format 决定 |
直接覆盖页面宽度 |
pageHeightPt |
number |
由 format 决定 |
直接覆盖页面高度 |
marginPt |
number | [上, 右, 下, 左] |
0 |
PDF 页边距,单位为 pt |
pagination |
boolean |
false |
是否按纸张高度分页 |
backgroundColor |
string | null |
null |
页面背景色;null 表示透明 |
precision |
number |
2 |
PDF 坐标保留的小数位数 |
compress |
boolean |
false |
使用 DEFLATE 压缩 PDF 流 |
jpegQuality |
number |
0.85 |
图片转 JPEG 时的质量 |
useCORS |
boolean |
false |
跨域图片按匿名 CORS 方式加载 |
ignoreElements |
(element) => boolean |
无 | 返回 true 时跳过该元素及其内容 |
fontConfig |
FontConfig | FontConfig[] |
无 | 注册自定义字体 |
langFontConfig |
FontConfig[] |
无 | 按 Unicode 范围配置字体回退 |
pageConfig |
对象或逐页函数 | 见下文 | 页眉和页脚配置 |
watermark |
对象或逐页函数 | 无 | 文字或图片水印 |
form |
boolean | FormOptions |
静态模式 | 表单控件导出方式 |
encryption |
PdfEncryptionOptions |
无 | PDF 密码和权限配置 |
metadata |
PdfMetadataOptions |
无 | PDF 文档属性(Info 字典) |
onProgress |
(progress) => void |
无 | 导出进度回调 |
完整 TypeScript 类型以 src/snapshot.ts
和发布包内的 dist/types 为准。
| Option | Type | Default | Description |
|---|---|---|---|
format |
string | [number, number] |
'a4' |
Page-size name or [width, height] in pt |
pageWidthPt |
number |
From format |
Override page width directly |
pageHeightPt |
number |
From format |
Override page height directly |
marginPt |
number | [top, right, bottom, left] |
0 |
PDF margins in pt |
pagination |
boolean |
false |
Paginate using the configured page height |
backgroundColor |
string | null |
null |
Page background; null means transparent |
precision |
number |
2 |
Decimal places retained for PDF coordinates |
compress |
boolean |
false |
Compress PDF streams with DEFLATE |
jpegQuality |
number |
0.85 |
JPEG quality used when converting images |
useCORS |
boolean |
false |
Load cross-origin images with anonymous CORS |
ignoreElements |
(element) => boolean |
None |
Skip an element and its content when it returns true
|
fontConfig |
FontConfig | FontConfig[] |
None | Register custom fonts |
langFontConfig |
FontConfig[] |
None | Configure Unicode-range font fallback |
pageConfig |
Object or per-page function | See below | Header and footer configuration |
watermark |
Object or per-page function | None | Text or image watermark |
form |
boolean | FormOptions |
Static mode | Form-control export behavior |
encryption |
PdfEncryptionOptions |
None | PDF passwords and permissions |
metadata |
PdfMetadataOptions |
None | PDF document properties (Info dictionary) |
onProgress |
(progress) => void |
None | Export progress callback |
See src/snapshot.ts and the
published package's dist/types directory for the complete
TypeScript definitions.
pagination: false:生成一张内容驱动高度的长页面,适合票据、截图替代和连续文档
pagination: true:根据纸张、页边距、页眉和页脚的可用区域生成多页 PDF
await dompdf(element, {
format: 'a4',
pagination: true,
marginPt: [36, 36, 36, 36],
});
为了获得稳定的分页结果,建议让待导出容器的 CSS 宽度接近目标纸张的内容宽度。A4 在 96 DPI 下为约
794px × 1123px;如果设置了页边距,还要从中减去对应宽度。
常见纸张尺寸见
page_sizes.md。当前支持:
a0 到 a10b0 到 b10c0 到 c10letter、government-letterlegal、junior-legal、government-legaltabloid、ledger自定义横向 A4 可以直接交换宽高:
await dompdf(element, {
format: [842.25, 595.5],
pagination: true,
});
也可以使用 pageWidthPt 和 pageHeightPt 覆盖
format。orientation 目前只是旧版兼容参数,不会自动交换纸张宽高。
在元素上添加 pageBreak 属性,可要求分页器在该元素前换页:
<section>第一页内容</section>
<section pageBreak>从新页面开始</section>
添加 divisionDisable 属性,可尽量避免整个元素跨页拆分:
<article divisionDisable>
这部分内容会尽量保持在同一页。
</article>
当元素本身高于一整页可用高度时,分页器仍可能拆分它,避免产生无法放置的内容。
pagination: false: create one content-driven continuous page,
useful for receipts, screenshot replacement, and continuous documents
pagination: true: create a multi-page PDF using the available
area left by the page size, margins, header, and footer
await dompdf(element, {
format: 'a4',
pagination: true,
marginPt: [36, 36, 36, 36],
});
For stable pagination, keep the export container's CSS width close to the
target page's content width. A4 is approximately
794px x 1123px at 96 DPI; configured margins must be subtracted
from that width.
See
page_sizes.md
for common dimensions. Supported names include:
a0 through a10b0 through b10c0 through c10letter and government-letterlegal, junior-legal, and
government-legal
tabloid and ledgerFor landscape A4, swap the dimensions explicitly:
await dompdf(element, {
format: [842.25, 595.5],
pagination: true,
});
You can also override format with pageWidthPt and
pageHeightPt. The orientation option is currently
retained only for legacy compatibility and does not swap the page dimensions
automatically.
Add a pageBreak attribute to request a page break before an element:
<section>First-page content</section>
<section pageBreak>Start on a new page</section>
Add a divisionDisable attribute to keep an element on one page
where possible:
<article divisionDisable>
This block will be kept together when possible.
</article>
An element taller than the entire available page area may still be split so that the content remains placeable.
await dompdf(element, {
pagination: true,
watermark: {
text: '内部资料 ${currentPage}/${totalPages}',
color: 'rgba(185, 28, 28, 0.14)',
fontFamily: 'Helvetica',
fontSize: 28,
fontWeight: 700,
angle: -35,
spacing: [180, 130],
offset: [40, 40],
layer: 'under',
excludePages: [1],
},
});
文字水印默认值包括:角度 35、字号 28px、颜色
rgba(0, 0, 0, 0.12)、间距 [160, 120]、偏移
[36, 36]、层级 under。
await dompdf(element, {
pagination: true,
useCORS: true,
watermark: {
imageUrl: '/assets/company-mark.png',
imageWidth: 120,
opacity: 0.1,
angle: -30,
spacing: [220, 160],
layer: 'over',
},
});
图片水印支持 imageWidth、imageHeight 和
opacity。只指定宽或高时会按照原图比例计算另一边。
await dompdf(element, {
pagination: true,
watermark(pageNum) {
if (pageNum === 1) return null;
return {
text: pageNum % 2 === 0 ? '偶数页' : '奇数页',
color: 'rgba(30, 64, 175, 0.12)',
layer: 'under',
};
},
});
await dompdf(element, {
pagination: true,
watermark: {
text: 'INTERNAL ${currentPage}/${totalPages}',
color: 'rgba(185, 28, 28, 0.14)',
fontFamily: 'Helvetica',
fontSize: 28,
fontWeight: 700,
angle: -35,
spacing: [180, 130],
offset: [40, 40],
layer: 'under',
excludePages: [1],
},
});
Text-watermark defaults include angle 35, font size
28px, color rgba(0, 0, 0, 0.12), spacing
[160, 120], offset [36, 36], and layer
under.
await dompdf(element, {
pagination: true,
useCORS: true,
watermark: {
imageUrl: '/assets/company-mark.png',
imageWidth: 120,
opacity: 0.1,
angle: -30,
spacing: [220, 160],
layer: 'over',
},
});
Image watermarks support imageWidth, imageHeight,
and opacity. If only width or height is supplied, the other
dimension is calculated from the source aspect ratio.
await dompdf(element, {
pagination: true,
watermark(pageNum) {
if (pageNum === 1) return null;
return {
text: pageNum % 2 === 0 ? 'EVEN PAGE' : 'ODD PAGE',
color: 'rgba(30, 64, 175, 0.12)',
layer: 'under',
};
},
});
非拉丁文本建议显式嵌入 TTF 字体。最可靠的方式是先加载字体,再通过
fontBytes 传入:
import dompdf from 'dompdf.js';
const fontBuffer = await fetch('/fonts/SourceHanSansSC-Regular.ttf').then((response) => {
if (!response.ok) throw new Error(`字体加载失败:${response.status}`);
return response.arrayBuffer();
});
await dompdf(element, {
fontConfig: {
fontFamily: 'SourceHanSansSC-Regular',
fontBytes: new Uint8Array(fontBuffer),
fontStyle: 'normal',
fontWeight: 400,
},
});
fontConfig 可传单个字体或数组。主要字段:
| 字段 | 说明 |
|---|---|
fontFamily |
必填,应与导出元素的 CSS font-family 对应 |
fontBytes |
已解码的 TTF 字节,推荐使用 |
fontBase64 |
Base64 编码的 TTF 数据 |
fontStyle |
normal 或 italic |
fontWeight |
字重,如 400、700 |
iconFont |
是否作为图标字体处理 |
类型中保留了 fontUrl,但当前采集器不会主动读取它。需要从 URL
加载字体时,请先 fetch 并转换为 Uint8Array。
langFontConfig 可以通过 Unicode 范围选择字体,并设置默认回退字体:
await dompdf(element, {
langFontConfig: [
{
fontFamily: 'SourceHanSansSC-Regular',
fontBytes: chineseFontBytes,
charRange: [[0x3400, 0x9fff]],
},
{
fontFamily: 'NotoSans-Regular',
fontBytes: fallbackFontBytes,
isDefault: true,
},
],
});
仓库提供了
examples/SourceHanSansSC-Regular.ttf,可用于本地演示和验证。生产项目应确认所用字体的授权范围,并避免重复注册体积较大的完整字体。
For non-Latin text, explicitly embed a TTF font. The most reliable approach is
to load the font first and pass it through fontBytes:
import dompdf from 'dompdf.js';
const fontBuffer = await fetch('/fonts/SourceHanSansSC-Regular.ttf').then((response) => {
if (!response.ok) throw new Error(`Font request failed: ${response.status}`);
return response.arrayBuffer();
});
await dompdf(element, {
fontConfig: {
fontFamily: 'SourceHanSansSC-Regular',
fontBytes: new Uint8Array(fontBuffer),
fontStyle: 'normal',
fontWeight: 400,
},
});
fontConfig accepts one font or an array. Its main fields are:
| Field | Description |
|---|---|
fontFamily |
Required; should match the exported element's CSS font-family |
fontBytes |
Decoded TTF bytes; recommended |
fontBase64 |
Base64-encoded TTF data |
fontStyle |
normal or italic |
fontWeight |
A weight such as 400 or 700 |
iconFont |
Whether to treat the font as an icon font |
The type definition retains fontUrl, but the current collector
does not fetch it. Load URL-based fonts yourself and pass them as a
Uint8Array.
langFontConfig selects fonts by Unicode range and can define a
default fallback:
await dompdf(element, {
langFontConfig: [
{
fontFamily: 'SourceHanSansSC-Regular',
fontBytes: chineseFontBytes,
charRange: [[0x3400, 0x9fff]],
},
{
fontFamily: 'NotoSans-Regular',
fontBytes: fallbackFontBytes,
isDefault: true,
},
],
});
The repository includes
examples/SourceHanSansSC-Regular.ttf
for local demos and verification. In production, verify the font's license
and avoid registering duplicate copies of large complete fonts.
表单值以导出开始时的 DOM 当前状态为准,而不是初始 HTML 属性值。
await dompdf(element, {
pagination: true,
form: {
mode: 'hybrid',
include: [
'text',
'textarea',
'select',
'checkbox',
'radio',
'date-time',
'range',
'color',
'file',
'progress',
'meter',
],
},
});
模式说明:
static:默认模式,只保留控件当前的静态视觉外观interactive:为具备自然 PDF 映射的控件生成 AcroForm 字段hybrid:保留静态视觉,同时附加交互字段当前交互字段映射:
input、textareaselectcheckboxradio以下类型会保留静态外观,但不会生成等价的交互字段:
date、time、month、week、datetime-localrange、color、fileprogress、meter
form: true 与默认行为一样仍是静态模式;要生成交互字段,必须显式设置
mode: 'interactive' 或 mode: 'hybrid'。
Form values are captured from the DOM state at the start of the export, rather than from the initial HTML attributes.
await dompdf(element, {
pagination: true,
form: {
mode: 'hybrid',
include: [
'text',
'textarea',
'select',
'checkbox',
'radio',
'date-time',
'range',
'color',
'file',
'progress',
'meter',
],
},
});
Modes:
static: the default; preserve only the control's current visual appearanceinteractive: create AcroForm fields for controls with a natural PDF mappinghybrid: preserve the static appearance and add interactive fieldsCurrent interactive-field mappings:
input elements and textareaselectcheckboxradioThe following types retain a static appearance but do not produce equivalent interactive fields:
date, time, month, week, and datetime-localrange, color, and fileprogress and meter
form: true still uses the default static mode. To generate
interactive fields, explicitly set mode: 'interactive' or
mode: 'hybrid'.
await dompdf(element, {
encryption: {
userPassword: 'reader-password',
ownerPassword: 'owner-password',
userPermissions: ['print', 'copy'],
},
});
可用权限:
print:允许打印modify:允许修改copy:允许复制内容annot-forms:允许注释和填写表单传入未知权限会抛出错误。PDF 权限最终是否严格执行还取决于阅读器;权限标志不能替代对敏感数据的访问控制。
await dompdf(element, {
encryption: {
userPassword: 'reader-password',
ownerPassword: 'owner-password',
userPermissions: ['print', 'copy'],
},
});
Available permissions:
print: allow printingmodify: allow modificationscopy: allow copying contentannot-forms: allow annotations and form fillingUnknown permission names cause an error. Whether PDF permissions are strictly enforced ultimately depends on the reader; permission flags are not a substitute for access control over sensitive data.
设置 PDF 阅读器“文档属性”面板中显示的文档信息:
await dompdf(element, {
metadata: {
title: '季度报告',
author: '刘发财',
subject: '一季度汇总',
keywords: ['报告', '财务'],
creator: 'my-app',
producer: 'my-app',
},
});
keywords 接受字符串或字符串数组(数组以空格连接)。
metadata 时,producer 默认为
dompdf.js,并自动将 creationDate/modDate
设为导出时间。
metadata 时不写入 Info 字典,输出与之前版本字节级一致。
Set document properties shown in the PDF reader's document info panel:
await dompdf(element, {
metadata: {
title: 'Quarterly Report',
author: 'Alice',
subject: 'Q1 summary',
keywords: ['report', 'finance'],
creator: 'my-app',
producer: 'my-app',
},
});
keywords accepts a string or a string
array (arrays are joined with spaces).
metadata is provided, producer defaults to
dompdf.js, and creationDate/modDate
are automatically set to the export time.
metadata is omitted, no Info dictionary is written and the
output stays byte-compatible with previous versions.
同源图片、data URL、Canvas 和可读取的 SVG 可以直接参与导出。跨域图片需要资源服务器返回正确的 CORS 响应头:
await dompdf(element, {
useCORS: true,
jpegQuality: 0.9,
});
注意:
useCORS: true 只会以匿名 CORS 方式请求图片,不能绕过服务器策略
Access-Control-Allow-Originproxy、allowTaint、imageTimeout
等旧 html2canvas 参数目前不会提供原版行为
可以在导出前等待页面字体和图片:
await document.fonts.ready;
await Promise.all(
Array.from(element.querySelectorAll('img')).map((image) => {
if (image.complete) return Promise.resolve();
return new Promise<void>((resolve) => {
image.addEventListener('load', () => resolve(), { once: true });
image.addEventListener('error', () => resolve(), { once: true });
});
}),
);
Same-origin images, data URLs, Canvas content, and readable SVGs can participate directly in an export. Cross-origin images require correct CORS response headers from the resource server:
await dompdf(element, {
useCORS: true,
jpegQuality: 0.9,
});
Notes:
useCORS: true only requests images with anonymous CORS; it
cannot bypass server policy
Access-Control-Allow-Origin
proxy,
allowTaint, and imageTimeout do not provide their
original behavior
You can wait for document fonts and images before exporting:
await document.fonts.ready;
await Promise.all(
Array.from(element.querySelectorAll('img')).map((image) => {
if (image.complete) return Promise.resolve();
return new Promise<void>((resolve) => {
image.addEventListener('load', () => resolve(), { once: true });
image.addEventListener('error', () => resolve(), { once: true });
});
}),
);
onProgress 会报告以下阶段:
onProgress reports these stages:
collecting:采集 DOM 和资源countingPages:为逐页页眉、页脚或水印计算总页数rendering:WASM 正在生成 PDFdone:导出完成await dompdf(element, {
pagination: true,
onProgress(progress) {
switch (progress.stage) {
case 'collecting':
console.log('正在采集页面');
break;
case 'countingPages':
console.log(`正在计算页数:${progress.totalPages ?? '...'}`);
break;
case 'rendering':
console.log(
`正在生成:${progress.currentPage ?? 0}/${progress.totalPages ?? '?'}`,
);
break;
case 'done':
console.log(`导出完成,共 ${progress.totalPages ?? 1} 页`);
break;
}
},
});
并非每次导出都会出现 countingPages 阶段;只有需要先知道总页数的配置才会触发它。
collecting: collecting the DOM and resourcescountingPages: determining the total page count for per-page
headers, footers, or watermarks
rendering: generating the PDF in WASMdone: export completedawait dompdf(element, {
pagination: true,
onProgress(progress) {
switch (progress.stage) {
case 'collecting':
console.log('Collecting the document');
break;
case 'countingPages':
console.log(`Counting pages: ${progress.totalPages ?? '...'}`);
break;
case 'rendering':
console.log(
`Rendering: ${progress.currentPage ?? 0}/${progress.totalPages ?? '?'}`,
);
break;
case 'done':
console.log(`Export complete: ${progress.totalPages ?? 1} pages`);
break;
}
},
});
Not every export emits a countingPages stage. It is used only
when a configuration needs the total page count in advance.
Uint8Array、Blob
或触发下载。
这套结构避免将整个文档先绘制成一张 Canvas,同时让主要 PDF 生成工作离开主线程。
Uint8Array, a Blob, or starts a download,
depending on the API used.
This architecture avoids drawing the entire document into one Canvas and keeps the main PDF-generation work off the main thread.
运行时依赖:
Blob、URL.createObjectURLTextEncoder、TextDecoder建议使用较新的 Chromium、Firefox 或 Safari。该库不支持直接在 Node.js、SSR 服务端或没有 DOM 的 Worker 中采集页面。Next.js、Nuxt 等 SSR 项目应仅在客户端调用导出 API。
项目已经从 html2canvas + jsPDF 迁移到
DOM 快照 + Worker + WASM
。以下参数会被类型和运行时接受,但当前不会提供旧版等价行为,部分参数会输出 warning:
onJspdfReady、onJspdfFinishforeignObjectRendering、allowTaint、proxy、imageTimeout
logging、cachewindowWidth、windowHeight、scrollX、scrollY
x、y、width、height、scalecanvas、removeContainer、onclonepdfFileName、floatPrecision、orientation、putOnlyUsedFonts
其中 ignoreElements
已真正生效,可以替代一部分旧版 clone 阶段的元素过滤逻辑。完整迁移建议见
旧版 API 迁移说明。
fontUrl 和 putOnlyUsedFonts
目前只有兼容签名;字体 URL 需要由调用方先行加载
PageRegionConfig.content 的 renderer
回调属于兼容签名,当前不提供可操作的 jsPDF 实例
The runtime depends on:
Blob and URL.createObjectURLTextEncoder and TextDecoderUse a recent Chromium, Firefox, or Safari release. The library cannot collect a page directly in Node.js, during server-side rendering, or inside a Worker without a DOM. Next.js, Nuxt, and other SSR applications should call the export APIs only on the client.
The project has migrated from html2canvas + jsPDF to
DOM snapshot + Worker + WASM. The following options are accepted
by the types and runtime, but do not currently provide equivalent legacy
behavior. Some emit a warning:
onJspdfReady and onJspdfFinishforeignObjectRendering, allowTaint,
proxy, and imageTimeout
logging and cachewindowWidth, windowHeight, scrollX,
and scrollY
x, y, width, height, and
scale
canvas, removeContainer, and onclonepdfFileName, floatPrecision,
orientation, and putOnlyUsedFonts
ignoreElements is fully implemented and can replace some legacy
clone-stage filtering. See the
migration notes for the full
migration path.
fontUrl and putOnlyUsedFonts currently have
compatibility signatures only; callers must load font URLs themselves
PageRegionConfig.content is a
compatibility signature and does not expose an operable jsPDF instance
浏览器字体不会自动嵌入 PDF。请通过 fontConfig.fontBytes
注册包含相应字符的 TTF 字体,并让元素的 CSS font-family 与
fontFamily 对应。
确保导出容器宽度与目标纸张的内容宽度接近,并在导出前等待字体和图片完成加载。页边距、页眉和页脚都会减少每页可用区域。
使用自定义 [宽, 高] 或 pageWidthPt/pageHeightPt。不要依赖目前仅用于兼容的
orientation。
设置 useCORS: true
,并确认图片服务器允许跨域读取。仅在前端设置 CORS 选项不能绕过服务器响应头限制。
启用 compress: true,避免使用远超显示尺寸的大图,适当降低
jpegQuality,并尽量只注册实际需要的字体和字重。
Browser fonts are not embedded into the PDF automatically. Register a TTF
containing the required characters through fontConfig.fontBytes,
and ensure the element's CSS font-family matches
fontFamily.
Keep the export container width close to the target page's content width, and wait for fonts and images to load before exporting. Margins, headers, and footers all reduce the usable area on each page.
Use a custom [width, height] pair or
pageWidthPt/pageHeightPt. Do not rely on the currently
compatibility-only orientation option.
Set useCORS: true and verify that the image server permits
cross-origin reads. A frontend CORS option alone cannot bypass missing server
response headers.
Enable compress: true, avoid images far larger than their
displayed dimensions, reduce jpegQuality where appropriate, and
register only the fonts and weights the document needs.
wasm32-unknown-unknown targetrustup target add wasm32-unknown-unknown
npm install
npm run build
npm test
npm run serve
常用脚本:
| 命令 | 作用 |
|---|---|
npm run build:wasm |
编译 Rust/WASM 模块 |
npm run build |
构建发布包和 TypeScript 类型声明 |
npm run dev |
监听源码并持续构建 |
npm test |
构建 WASM 并运行 PDF 冒烟验证 |
npm run verify |
与 npm test 相同,执行验证脚本 |
npm run serve |
在 8080 端口启动静态文件服务 |
npm test 当前会执行
scripts/verify.mjs,验证基础分页、PDF 结构、图片、中文字体子集、透明度和复合字形。
wasm32-unknown-unknown targetrustup target add wasm32-unknown-unknown
npm install
npm run build
npm test
npm run serve
Common scripts:
| Command | Purpose |
|---|---|
npm run build:wasm |
Compile the Rust/WASM module |
npm run build |
Build the published bundles and TypeScript declarations |
npm run dev |
Watch the source and rebuild continuously |
npm test |
Build WASM and run the PDF smoke verification |
npm run verify |
Same as npm test; run the verification script |
npm run serve |
Start a static server on port 8080 |
npm test currently runs
scripts/verify.mjs, which
verifies basic pagination, PDF structure, images, Chinese font subsetting,
opacity, and composite glyphs.
examples/index.html:综合功能演示examples/comparison.html:与其他前端 PDF 方案对比
examples/markdown-editor.html:Markdown 编辑和实时导出
userscript/dompdf-page-exporter.user.js:支持整页、单节点和排除元素的油猴导出脚本
在仓库根目录运行 npm run serve
后访问对应页面。不要直接通过 file://
打开示例,否则模块、字体和跨域资源可能受浏览器安全策略限制。
dompdf.js/
├── src/ # TypeScript API、DOM 采集、Worker 和 WASM 连接层
├── wasm/ # Rust 分页、字体、压缩、加密和 PDF 写入器
├── dist/ # 构建后的发布产物
├── examples/ # 浏览器演示页面和示例字体
├── docs/ # 迁移说明与 PDF 差异系统文档
└── scripts/ # 构建、验证和 PDF 差异工具
examples/index.html: comprehensive
feature demo
examples/comparison.html: comparison with other frontend PDF approaches
examples/markdown-editor.html: Markdown editing and live export
userscript/dompdf-page-exporter.user.js: userscript for
full-page, single-node, and exclusion-based exports
Run npm run serve from the repository root before opening an
example. Do not open examples directly through file://, because
browser security policies may block modules, fonts, and cross-origin
resources.
dompdf.js/
|-- src/ # TypeScript API, DOM collection, Worker, and WASM bridge
|-- wasm/ # Rust pagination, fonts, compression, encryption, and PDF writer
|-- dist/ # Built package artifacts
|-- examples/ # Browser demos and example fonts
|-- docs/ # Migration notes and PDF-diff documentation
`-- scripts/ # Build, verification, and PDF-diff tooling
提交 PR 前请阅读
CONTRIBUTING.md,并至少运行:
npm test
npm run build
安全问题请按照
SECURITY.md
中的方式反馈,不要在公开 Issue 中披露尚未修复的漏洞。
近期版本变化见
CHANGELOG.md。
本项目基于 MIT License 开源。
Read CONTRIBUTING.md before
submitting a pull request, and run at least:
npm test
npm run build
Report security issues as described in
SECURITY.md. Do not disclose an unpatched vulnerability in a public issue.
See CHANGELOG.md for recent version
changes.
This project is licensed under the MIT License.