dompdf.jsDocs
浏览器原生 · 矢量 PDFBrowser-native · Vector PDF

把 DOM 直接变成
真正的矢量 PDF。
Turn the DOM into
real vector PDFs.

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.

✓ 纯前端✓ Pure frontend✓ 可搜索文本✓ Searchable text✓ TypeScript✓ TypeScript✓ MIT License✓ MIT License
0 backend浏览器内完成导出Export runs in the browser
Vector文本可选择、复制、搜索Selectable, copyable, searchable
WASMRust 渲染核心Rust rendering core
~2s / 500p面向超长文档场景Built for very long documents

主要功能 Features

01

纯浏览器端 Runs in the browser

完全在浏览器端运行,不上传待导出的文档。 Runs entirely in the browser without uploading the document being exported.

02

矢量文本 Vector text

输出可搜索、可复制的矢量文本,而不是整页截图。 Produces searchable, selectable vector text instead of full-page screenshots.

03

Worker + WASM Worker + WASM

使用 Web Worker 和 WASM 执行 PDF 渲染,降低主线程阻塞。 Uses Web Workers and WASM for PDF rendering to reduce main-thread blocking.

04

为大规模文档优化 Built for large documents

面向大规模文档优化,典型测试约 2 秒生成 500 页,极限规模可达上万页。 About two seconds for 500 pages in representative benchmarks, with extreme workloads reaching tens of thousands of pages.

05

单页与分页 Continuous or paginated

支持单页长画布和标准纸张分页。 Supports both continuous single-page output and standard paginated documents.

06

纸张尺寸 Page sizes

支持 A/B/C 系列、Letter、Legal、Tabloid 等纸张,也可传入自定义尺寸。 Supports A/B/C series, Letter, Legal, Tabloid, and custom page sizes.

07

页眉与页脚 Headers and footers

支持页眉、页脚、页码占位符和逐页配置。 Supports headers, footers, page-number placeholders, and per-page configuration.

08

水印 Watermarks

支持文字水印、图片水印、上下层叠放和逐页控制。 Supports text and image watermarks, under/over layering, and per-page control.

09

字体与多语言 Fonts and fallback

支持 Unicode、TTF 字体、字体子集、粗体、斜体、图标字体和语言字体回退。 Supports Unicode, TTF fonts, font subsetting, bold, italic, icon fonts, and language-based font fallback.

10

视觉效果 Visual effects

支持图片、SVG、Canvas、背景、边框、圆角、阴影、透明度和渐变等常见视觉效果。 Supports images, SVG, Canvas, backgrounds, borders, rounded corners, shadows, opacity, gradients, and other common visual effects.

11

超链接注释 Link annotations

支持超链接 PDF 注释。 Preserves hyperlinks as PDF link annotations.

12

表单导出 Form export

支持静态表单外观和 PDF AcroForm 交互字段。 Supports static form appearance and interactive PDF AcroForm fields.

13

压缩与加密 Compression and encryption

支持 DEFLATE 压缩、用户密码、所有者密码和 PDF 权限控制。 Supports DEFLATE compression, user and owner passwords, and PDF permission flags.

14

完整 API 与类型 APIs and types

提供 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.

安装 Installation

npm

npm install dompdf.js

运行环境需要现代浏览器以及 Worker、WebAssembly、Blob 等 Web API。包本身要求 Node.js 18+ 用于安装、构建和开发,但导出 API 依赖 DOM,不能直接在纯 Node.js 环境中调用。

CDN

<script src="https://cdn.jsdelivr.net/npm/dompdf.js@latest/dist/dompdf.min.js"></script>

通过 <script> 引入后,API 会挂载到全局 dompdf。

npm

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.

CDN

<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.

快速开始 Quick start

默认导出函数返回 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.
The preview window needs time to load; revoking URL.revokeObjectURL too early can produce a blank page.

API API

常规业务优先使用下面五个高层 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.

recommended

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.

Blob

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.

Uint8Array

downloadPDF(root, options?, filename?)

生成 PDF 并触发浏览器下载。默认文件名为 export.pdf。 Generates the PDF and triggers a browser download. The default filename is export.pdf.

download

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.

diagnostics
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 }));

高级快照 API

包还导出了以下底层函数:

  • 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 }));

Advanced snapshot APIs

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);

参数 Export options

以下参数均已实现并生效: 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 and page sizes

单页与分页模式

  • 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 到 a10
  • b0 到 b10
  • c0 到 c10
  • letter、government-letter
  • legal、junior-legal、government-legal
  • tabloid、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>

当元素本身高于一整页可用高度时,分页器仍可能拆分它,避免产生无法放置的内容。

Continuous and paginated modes

  • 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 a10
  • b0 through b10
  • c0 through c10
  • letter and government-letter
  • legal, junior-legal, and government-legal
  • tabloid and ledger

For 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.

Forced breaks and avoiding splits

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.

水印 Watermarks

文字水印

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',
    };
  },
});

Text watermarks

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.

Image watermarks

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.

Per-page watermarks

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',
    };
  },
});

字体与多语言 Fonts and multilingual text

非拉丁文本建议显式嵌入 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.

Language-based font fallback

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.

表单导出 Form export

表单值以导出开始时的 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、textarea
  • select
  • checkbox
  • radio

以下类型会保留静态外观,但不会生成等价的交互字段:

  • date、time、month、week、datetime-local
  • range、color、file
  • progress、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 appearance
  • interactive: create AcroForm fields for controls with a natural PDF mapping
  • hybrid: preserve the static appearance and add interactive fields

Current interactive-field mappings:

  • Text-like input elements and textarea
  • select
  • checkbox
  • radio

The following types retain a static appearance but do not produce equivalent interactive fields:

  • date, time, month, week, and datetime-local
  • range, color, and file
  • progress and meter

form: true still uses the default static mode. To generate interactive fields, explicitly set mode: 'interactive' or mode: 'hybrid'.

PDF 加密与权限 PDF encryption

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 printing
  • modify: allow modifications
  • copy: allow copying content
  • annot-forms: allow annotations and form filling

Unknown 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 文档属性 PDF metadata

设置 PDF 阅读器“文档属性”面板中显示的文档信息:

await dompdf(element, {
  metadata: {
    title: '季度报告',
    author: '刘发财',
    subject: '一季度汇总',
    keywords: ['报告', '财务'],
    creator: 'my-app',
    producer: 'my-app',
  },
});
  • 所有字段均可选;keywords 接受字符串或字符串数组(数组以空格连接)。
  • 提供 metadata 时,producer 默认为 dompdf.js,并自动将 creationDate/modDate 设为导出时间。
  • 属性值以 UTF-16BE 字符串写入,支持中文等非 ASCII 文本。
  • 未提供 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',
  },
});
  • All fields are optional; keywords accepts a string or a string array (arrays are joined with spaces).
  • When metadata is provided, producer defaults to dompdf.js, and creationDate/modDate are automatically set to the export time.
  • Values are written as UTF-16BE strings, so Chinese and other non-ASCII text is supported.
  • When metadata is omitted, no Info dictionary is written and the output stays byte-compatible with previous versions.

图片与跨域资源 Images and cross-origin resources

同源图片、data URL、Canvas 和可读取的 SVG 可以直接参与导出。跨域图片需要资源服务器返回正确的 CORS 响应头:

await dompdf(element, {
  useCORS: true,
  jpegQuality: 0.9,
});

注意:

  • useCORS: true 只会以匿名 CORS 方式请求图片,不能绕过服务器策略
  • 图片服务器通常需要返回 Access-Control-Allow-Origin
  • 导出前应等待图片和字体加载完成
  • 无法读取的图片可能被跳过或降级
  • proxy、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
  • The image server will normally need to return Access-Control-Allow-Origin
  • Wait for images and fonts to finish loading before exporting
  • Unreadable images may be skipped or degraded
  • Legacy html2canvas options such as 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 });
    });
  }),
);

进度反馈 Progress reporting

onProgress 会报告以下阶段: onProgress reports these stages:

  • collecting:采集 DOM 和资源
  • countingPages:为逐页页眉、页脚或水印计算总页数
  • rendering:WASM 正在生成 PDF
  • done:导出完成
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 resources
  • countingPages: determining the total page count for per-page headers, footers, or watermarks
  • rendering: generating the PDF in WASM
  • done: export completed
await 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.

工作原理 How it works

  1. 主线程遍历目标元素,读取浏览器计算后的布局、样式、文本、图片和表单状态。
  2. TypeScript 将采集结果编码成紧凑的二进制快照。
  3. 快照通过可转移对象发送到 Web Worker。
  4. Rust/WASM 完成分页、字体子集、绘制和 PDF 对象写入。
  5. 主线程收到 PDF 字节,并按调用方式返回 Uint8Array、Blob 或触发下载。

这套结构避免将整个文档先绘制成一张 Canvas,同时让主要 PDF 生成工作离开主线程。

  1. The main thread walks the target element and reads browser-computed layout, styles, text, images, and form state.
  2. TypeScript encodes the collected result into a compact binary snapshot.
  3. The snapshot is transferred to a Web Worker.
  4. Rust/WASM performs pagination, font subsetting, drawing, and PDF object generation.
  5. The main thread receives the PDF bytes and returns a 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.

兼容性与限制 Compatibility and limitations

浏览器兼容性

运行时依赖:

  • DOM 和 CSSOM
  • Web Worker
  • WebAssembly
  • Blob、URL.createObjectURL
  • TextEncoder、TextDecoder

建议使用较新的 Chromium、Firefox 或 Safari。该库不支持直接在 Node.js、SSR 服务端或没有 DOM 的 Worker 中采集页面。Next.js、Nuxt 等 SSR 项目应仅在客户端调用导出 API。

旧版兼容参数

项目已经从 html2canvas + jsPDF 迁移到 DOM 快照 + Worker + WASM 。以下参数会被类型和运行时接受,但当前不会提供旧版等价行为,部分参数会输出 warning:

  • onJspdfReady、onJspdfFinish
  • foreignObjectRendering、allowTaint、proxy、imageTimeout
  • logging、cache
  • windowWidth、windowHeight、scrollX、scrollY
  • x、y、width、height、scale
  • canvas、removeContainer、onclone
  • pdfFileName、floatPrecision、orientation、putOnlyUsedFonts

其中 ignoreElements 已真正生效,可以替代一部分旧版 clone 阶段的元素过滤逻辑。完整迁移建议见 旧版 API 迁移说明。

已知边界

  • 动画、视频、iframe 和浏览器插件内容不能按动态状态完整导出
  • 部分复杂滤镜、混合模式、遮罩和高级 CSS 会被降级或栅格化
  • 超高分辨率图片和完整 CJK 字体会增加采集时间、内存和 PDF 体积
  • fontUrl 和 putOnlyUsedFonts 目前只有兼容签名;字体 URL 需要由调用方先行加载
  • PageRegionConfig.content 的 renderer 回调属于兼容签名,当前不提供可操作的 jsPDF 实例
  • 依赖 jsPDF 插件或在导出结束后直接修改 jsPDF 实例的代码需要迁移

Browser compatibility

The runtime depends on:

  • DOM and CSSOM
  • Web Workers
  • WebAssembly
  • Blob and URL.createObjectURL
  • TextEncoder and TextDecoder

Use 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.

Legacy compatibility options

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 onJspdfFinish
  • foreignObjectRendering, allowTaint, proxy, and imageTimeout
  • logging and cache
  • windowWidth, windowHeight, scrollX, and scrollY
  • x, y, width, height, and scale
  • canvas, removeContainer, and onclone
  • pdfFileName, 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.

Known boundaries

  • Animations, video, iframes, and browser-plugin content cannot be exported with their full dynamic behavior
  • Some complex filters, blend modes, masks, and advanced CSS are degraded or rasterized
  • Very high-resolution images and complete CJK fonts increase collection time, memory use, and PDF size
  • fontUrl and putOnlyUsedFonts currently have compatibility signatures only; callers must load font URLs themselves
  • The renderer callback accepted by PageRegionConfig.content is a compatibility signature and does not expose an operable jsPDF instance
  • Code that depends on jsPDF plugins or modifies a live jsPDF instance after export must be migrated

常见问题 FAQ

为什么中文为空白或显示成方框?

浏览器字体不会自动嵌入 PDF。请通过 fontConfig.fontBytes 注册包含相应字符的 TTF 字体,并让元素的 CSS font-family 与 fontFamily 对应。

为什么分页位置和页面预览不一致?

确保导出容器宽度与目标纸张的内容宽度接近,并在导出前等待字体和图片完成加载。页边距、页眉和页脚都会减少每页可用区域。

如何导出横向页面?

使用自定义 [宽, 高] 或 pageWidthPt/pageHeightPt。不要依赖目前仅用于兼容的 orientation。

为什么跨域图片没有出现在 PDF 中?

设置 useCORS: true ,并确认图片服务器允许跨域读取。仅在前端设置 CORS 选项不能绕过服务器响应头限制。

如何减小 PDF 体积?

启用 compress: true,避免使用远超显示尺寸的大图,适当降低 jpegQuality,并尽量只注册实际需要的字体和字重。

Why is Chinese text blank or rendered as boxes?

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.

Why do page breaks differ from the browser preview?

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.

How do I export a landscape page?

Use a custom [width, height] pair or pageWidthPt/pageHeightPt. Do not rely on the currently compatibility-only orientation option.

Why is a cross-origin image missing from the PDF?

Set useCORS: true and verify that the image server permits cross-origin reads. A frontend CORS option alone cannot bypass missing server response headers.

How can I reduce PDF size?

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.

本地开发 Local development

环境要求

  • Node.js 18+
  • Rust 工具链
  • wasm32-unknown-unknown target
rustup 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 结构、图片、中文字体子集、透明度和复合字形。

Requirements

  • Node.js 18+
  • Rust toolchain
  • wasm32-unknown-unknown target
rustup 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 and project structure

示例页面

在仓库根目录运行 npm run serve 后访问对应页面。不要直接通过 file:// 打开示例,否则模块、字体和跨域资源可能受浏览器安全策略限制。

项目结构

dompdf.js/
├── src/                  # TypeScript API、DOM 采集、Worker 和 WASM 连接层
├── wasm/                 # Rust 分页、字体、压缩、加密和 PDF 写入器
├── dist/                 # 构建后的发布产物
├── examples/             # 浏览器演示页面和示例字体
├── docs/                 # 迁移说明与 PDF 差异系统文档
└── scripts/              # 构建、验证和 PDF 差异工具

Examples

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.

Project structure

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

社区与许可 Community and license

参与贡献

提交 PR 前请阅读 CONTRIBUTING.md,并至少运行:

npm test
npm run build

安全报告

安全问题请按照 SECURITY.md 中的方式反馈,不要在公开 Issue 中披露尚未修复的漏洞。

变更记录

近期版本变化见 CHANGELOG.md。

许可证

本项目基于 MIT License 开源。

dompdf.js 交流群

dompdf.js 微信交流群二维码

Contributing

Read CONTRIBUTING.md before submitting a pull request, and run at least:

npm test
npm run build

Security

Report security issues as described in SECURITY.md. Do not disclose an unpatched vulnerability in a public issue.

Changelog

See CHANGELOG.md for recent version changes.

License

This project is licensed under the MIT License.