前端用 pdf.js 将 PDF 渲染到 Canvas 再转图片,文字消失的坑

一、背景与业务场景

  • 业务流程:上传 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
相关推荐
崔庆才丨静觅20 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606121 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了21 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅21 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅21 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅21 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 天前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 天前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 天前
jwt介绍
前端
爱敲代码的小鱼1 天前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax