一、背景与业务场景
- 业务流程:上传 PDF → 使用 pdf.js 渲染页面为
canvas → canvas.toDataURL()
→ 上传为图片 → 页面展示图片。
- 现象对比:
- WPS 创建的 PDF:渲染为图片后,文字正常显示。
- "网页转 PDF"的文件:渲染为图片后,文字不显示(图片里空白或只有图形/线条,没有文字)。
二、问题现象
- 代码位置(节选):
- 使用
pdfjsLib.getDocument
加载 PDF 数据后,在 renderPdfToImages
中调用 page.render
渲染到 canvas
,之后 canvas.toDataURL()
上传。
- 异常点:
- 相同的流程下,只有"网页转 PDF"的文档渲染后的图片出现文字缺失。
三、排查线索
- 之前未为 pdf.js 显式配置 CMaps、标准字体路径等参数。
- "网页转 PDF"常见特点:使用 CID/子集字体,依赖 CMap 资源映射字形;若运行时找不到对应资源,pdf.js 会出现文字无法正确绘制到 canvas 的情况。
- WPS 导出的 PDF 通常内嵌常用字体,即使不配置 CMaps 也能绘制出字形,不容易暴露问题。
四、根因分析
- pdf.js 在渲染涉及 CJK(中文/日/韩)与 CID 字体的 PDF 时,需要 CMap 和标准字体资源辅助解析。未配置 cMapUrl / cMapPacked (以及在部分场景下的 standardFontDataUrl )会导致文本无法正确绘制。
- 个别环境中直接将 ArrayBuffer 传入 getDocument 可能存在解析不稳定的问题,转为 Uint8Array 更稳妥。
- 渲染 intent 使用默认值时,在某些 PDF 文本/矢量渲染质量欠佳;使用
intent: 'print'
可提高文本渲染稳定性和清晰度。
五、解决方案(分步骤)
1)准备静态资源(CMaps 与标准字体)
- 将 node_modules 中的标准字体资源复制到 public (
public/lib/pdfjs-dist/web/cmaps/
) 这样运行时就可以通过 URL 加载这些静态资源,不会被打包工具"吃掉"。
2)为 getDocument 显式配置 CMaps/(可选)标准字体,并把 ArrayBuffer 转为 Uint8Array
- 要点:
data
:使用 Uint8Array (由 ArrayBuffer 转),提升兼容性。
cMapUrl
:指向 public 下的 cmaps/ 目录。
cMapPacked
: true :使用打包的 .bcmap 文件。
standardFontDataUrl
:可选。如果极个别 PDF 仍有文字缺失,可打开这一项。
js
复制代码
// 将 ArrayBuffer 转成 Uint8Array,并配置 CMaps/标准字体资源(可选)
// 这样可解决"网页转 PDF"使用 CID/中文字体时文字不显示的问题
const pdfBinary = pdfData instanceof ArrayBuffer ? new Uint8Array(pdfData) : pdfData
// 显式配置 pdf.js 的 CMaps 路径与打包模式(.bcmap)
const loadingTask = pdfjsLib.getDocument({
data: pdfBinary,
cMapUrl:'/lib/pdfjs-dist/web/cmaps/',
cMapPacked: true,
})
const pdf = await loadingTask.promise
3)渲染时增加 intent: 'print'
- 要点:提升文字/矢量渲染清晰度与稳定性,减少文字丢失或模糊。
js
复制代码
// 设置渲染意图为 'print',可提升文字/矢量渲染清晰度与稳定性
await page.render({
canvasContext: context,
viewport: viewport,
intent: 'print' // 关键:使用"打印渲染"路径
}).promise
总结
- "WPS PDF 正常、网页转 PDF 异常"的根因多在字体/CMap 资源缺失。
- 落地方案核心:
- 给 pdf.js 配置
cMapUrl / cMapPacked (必要时 standardFontDataUrl )
;
- 渲染时使用
intent: 'print'
;
- 将
ArrayBuffer
转为 Uint8Array
。