矢量 PDF 导出 plugin-api-pdf-vector
约 1910 字大约 6 分钟
...
2026-09-28
插件说明
@sv-print/plugin-api-pdf-vector 为打印模板实例挂载 toVectorPdf 方法,导出矢量 PDF。基于 html2realpdf(Zig + WebAssembly 实现),导出的 PDF:
- 文字可选中、可搜索、可复制(含 Unicode 映射,LLM/工具可直接读取文本,无需 OCR)
- 字体子集嵌入(内置 Noto Sans 拉丁字体 + 阿拉伯/希伯来回退,支持注册自定义 TTF 中文字体)
- 原生矢量图形:边框、背景、渐变、阴影、表格、支持范围内的 SVG 均为矢量绘制
- 链接保留:
http/https/mailto/tel/ftp链接转成 PDF 注释
与截图方案(plugin-api-pdf)相比,不存在像素化、放大模糊的问题,文字密集型文档体积通常更小。
第三方依赖:@imggion/html2realpdf(peerDependency,需一并安装)。
npm install @sv-print/plugin-api-pdf-vector @imggion/html2realpdf示例代码
唯一注册入口 hiprint.register(异步),须在创建模板之前注册:
import pluginApiPdfVector from '@sv-print/plugin-api-pdf-vector';
await hiprint.register({
// 须在创建模板之前注册
plugins: [pluginApiPdfVector()], // 工厂函数,可传 config
});
const template = new hiprint.PrintTemplate(options); // 须在 register 之后创建模板
// "text" 是元素的字段名(field)
const printData = { text: '这是打印时显示的文本' };
// 直接下载
await template.toVectorPdf(printData, { name: 'pdf名称' });
// 获取 blob 自行处理
const res = await template.toVectorPdf(printData, {
name: 'pdf名称',
isDownload: false, // 不自动下载
type: 'blob', // 默认 blob;支持 blob、arraybuffer、bloburl、datauristring
onProgress: (cur, total, phase) => {
// phase: "snapshot" | "wasm" | "complete" 三阶段
console.log('toVectorPdf 进度', phase, cur, total);
},
});
console.log('toVectorPdf', res); // Blob高级用法
与 plugin-api-pdf 的对比
| plugin-api-pdf | plugin-api-pdf-vector | |
|---|---|---|
| 渲染方式 | 每页 DOM 截图为 JPEG 再嵌入 PDF | 浏览器快照 → WASM 矢量排版 |
| 文字 | 不可选中(图片) | 可选中/可搜索/可复制 |
| 体积 | 较大(整页 JPEG) | 较小(矢量 + 字体子集) |
| 清晰度 | 受 pixelRatio/quality 限制 | 无限放大不失真 |
| 图片资源 | 需同源/可跨域加载 | 需同源/可跨域加载(或 resourceResolver) |
| 兼容性 | 纯 JS | 需支持 WebAssembly 的浏览器 |
参数命名与 plugin-api-pdf 保持一致(name/isDownload/type/onProgress 等),调用方法为 toVectorPdf(printData, options?)。
注册中文字体(含中文内容时必需)
html2realpdf 内置字体仅覆盖拉丁(Noto Sans)+ 阿拉伯/希伯来文,中文等 CJK 字符必须注册 TTF 字体,否则导出报 MissingGlyph 错误:
const fontData = await (await fetch('/fonts/SourceHanSansCN-Regular.ttf')).arrayBuffer();
const res = await template.toVectorPdf(printData, {
name: '中文名称.pdf',
fonts: [{ family: 'SourceHanSansCN', data: fontData }], // 含中文内容时必需,否则导出报 MissingGlyph 错误
});- 注册字体会自动追加为整个模板的
font-family兜底:元素自身字体优先,未覆盖的字符(如中文)回落到注册字体,无需修改模板 - 可同时注册多个字体(如宋体 + 黑体);同一 family 注册不同
weight/style可实现粗体/斜体 - 字体文件需为可嵌入的 TrueType(.ttf),otf/woff 不支持
data支持直接传ArrayBuffer,也支持async () => ArrayBuffer异步函数(首次导出时自动加载,适合按需拉取字体文件)- 也可在插件初始化时全局注册:
pluginApiPdfVector({ fonts: [...] })
常用参数(与 plugin-api-pdf 兼容)
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
name | string | sv-print-时间戳.pdf | 文件名(自动补 .pdf) |
isDownload | boolean | true | 是否自动下载 |
type | string | 'blob' | 输出类型:blob、arraybuffer、bloburl、datauristring |
onProgress | function | - | 进度回调 (cur, total, phase) |
paperWidth / paperHeight | number | 面板尺寸 | 纸张尺寸覆盖(mm) |
orientation | string | 自动 | 'portrait' / 'landscape'(传入 paperWidth/Height 时无效) |
矢量渲染相关参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
fonts | FontRegistration[] | - | 注册嵌入 TTF 字体(含中文内容时必需) |
metadata | object | - | PDF 元数据 { title, author, subject, keywords, creator } |
enableLinks | boolean | true | 是否保留链接注释 |
canvasToSvg | function | - | canvas 图表转矢量回调(如 echarts) |
fallback | string | 'rasterize-subtree' | 不支持的 SVG 降级策略;设 'error' 则不做 SVG 转图片,严格保留矢量并按错误中断导出 |
svgToImage | function | - | SVG 转图片回调,自定义处理内联 <svg> |
resourcePolicy | string | 'error' | 图片加载失败策略:'omit' 忽略并继续 |
resourceResolver | function | - | 跨域/受保护资源解析器 |
baseUrl | string | 页面 URL | 相对资源解析基准 |
execution | string | 'worker' | WASM 执行位置:'worker' / 'main' |
wasmUrl | string | 包内路径 | wasm 资源地址覆盖(CDN 部署时使用) |
signal | AbortSignal | - | 中断渲染 |
debug | boolean | true | 控制台输出渲染诊断信息 |
data | any | - | 打印数据(与第一参数等效) |
插件初始化配置(config)
工厂函数入参,调用参数会覆盖 config:
pluginApiPdfVector({
execution: 'worker', // 默认;Worker 不可用环境可设为 "main"
wasmUrl: 'https://cdn.example.com/libhtml2realpdf.wasm', // CDN 部署时覆盖
fonts: [{ family: 'SourceHanSansCN', data: fontBuffer }], // 全局注册字体
});echarts 图表保持矢量
echarts 元素默认以 SVG renderer 输出(plugin-ele-echarts 使用 renderer: "svg"),天然矢量。若使用 canvas renderer,可通过 canvasToSvg 让图表库导出 SVG:
const res = await template.toVectorPdf(printData, {
name: '图表.pdf',
canvasToSvg: (canvas) => {
const chart = echarts.getInstanceByDom(canvas);
return chart ? chart.renderToSVGString() : null;
},
});SVG 处理与 svgToImage 回调
导出时模板内的所有内联 <svg> 默认转为图片(canvas 栅格化为 PNG),避开底层 WASM 解析器不支持的特性(<use> 引用、渐变 stroke、非法 preserveAspectRatio 等)导致的 InvalidSvg 中断。需要自定义处理时通过 svgToImage 回调接管:
const res = await template.toVectorPdf(printData, {
name: '条码.pdf',
// 每个 <svg> 调用一次;返回图片源,返回 null 回落默认栅格化
svgToImage: async (svg, { width, height, viewBox }) => {
const png = await serverRender(svg.outerHTML); // 如交给服务端渲染
return png; // 支持 data URL / URL / Blob / <canvas> / <img>
},
});svgToImage返回null→ 走默认 canvas 栅格化;抛错 → 原样透传给toVectorPdf(Promise reject)canvasToSvg仅用于canvas元素(echarts canvas renderer 等),与svgToImage互不影响<img src="*.svg">、CSS 背景 SVG 等仍交由底层处理(不支持的子树自动栅格化)
Vite 开发服务器(dev)注意事项
Vite 的依赖预构建(esbuild)会丢失包内 new URL('./worker.js', import.meta.url) 等资源引用,导致 dev 模式下报 PDF Worker initialization failed。解决办法(二选一):
// vite.config.ts
export default defineConfig({
optimizeDeps: {
// 方案一(推荐):排除预构建,让 Vite 正确重写资源路径
exclude: ['@imggion/html2realpdf'],
},
});// 方案二:插件已内置降级 —— Worker 初始化失败时自动回退 main 线程渲染(控制台会有 warn 提示)
// 也可显式指定:
template.toVectorPdf(printData, { execution: 'main' });生产构建(vite build)不受影响,Rollup 会正确处理资源引用。
注意事项
- 中文字体:内置字体不含 CJK 字形,未注册中文字体时导出直接报
MissingGlyph错误,务必通过fonts注册 - 跨域图片:图片资源需允许跨域(CORS),否则渲染报错;可用
resourcePolicy: "omit"跳过失败图片或resourceResolver自行解析 - CSS 兼容:底层渲染引擎有自己的 CSS 支持矩阵(见 css-support.md),不支持的属性会产生诊断信息(
debug: true输出) - 水印:
watermarkOptions由面板 CSS 绘制时可正常矢量渲染 - 旧浏览器:需要浏览器支持 WebAssembly 与 module Worker(所有现代浏览器均支持)
- 所有 api 插件依赖
globalThis.$(jQuery Deferred)与globalThis.hinnn,由 sv-print 内核提供
版权所有
版权归属:sv-print
