Skip to content

pdf在线预览,实现文字可选择(深度研究)

在项目里做 PDF 在线预览,看起来是个很普通的需求。但只要你做过复杂的业务场景(比如电子合同、发票预览、公文系统),就会发现这东西里头的坑真不少:用户抱怨文字选不中没法复制、看合同发现盖的红章莫名其妙消失了、或者换个手机打开直接提示下载文件。

市面上“PDF 预览”的开源库挺多的,但这中间到底有什么区别?该怎么选?

试过一圈之后,我最后的结论依然是:直接用 Mozilla 的 PDF.js,而且最好直接上最新的 v6 版本


一、 市面上常见 PDF 预览方案的坑

在动手写代码之前,先看看大家常用的几种方案和各自的硬伤:

  1. PDFObject / 直接用 <embed><iframe> 嵌入

    • 原理:调用浏览器自带的 PDF 插件。
    • 坑点:PC 端 Chrome 看着挺好,但移动端(iOS / Android)几乎全军覆没,很多手机浏览器根本不帮你内嵌预览,而是直接弹下载。而且你没办法自定义任何 UI 界面。
  2. 后端转图片 / 纯 Canvas 绘制渲染

    • 原理:服务端把每一页 PDF 转成图片发给前端,或者前端用简易 Canvas 画出来。
    • 坑点:最致命的问题是文字根本选不上,没法复制,也无法 Ctrl+F 搜索。除了防截图防复制的特殊场景外,这种体验用户分分钟要吐槽。
  3. pdf2htmlEX

    • 原理:服务端用 C++ 把 PDF 精确转成 HTML 和 CSS。
    • 坑点:效果虽然不错,但需要你在后端跑一个 C++ 的转换服务,遇到几百页的大 PDF 服务端 CPU 直接飚满,没办法做到纯前端随载随看。
  4. 各种 React / Vue 的包装库 (如 vue-pdf、react-pdf 等)

    • 原理:基于 PDF.js 套了一层框架组件。
    • 坑点:这些第三方封装库更新极慢,经常绑死的是两三年前的老版本 PDF.js。老版本里的各种印章丢失、中文乱码 Bug 它们一个没少,出了问题你还很难改。

二、 为什么推荐直接用 PDF.js v6?

有些开发者早期用过 PDF.js 的旧版本,觉得它配置繁琐或者有 Bug。但到了 PDF.js v6 版本,官方修复和重构了几个非常核心的痛点。

1. 真正的文字可选择与搜索 (Text Layer)

有些库预览 PDF 只是给用户“看个图”,但 PDF.js 的做法是双层渲染

  • 底层用 Canvas 绘制清晰的图形和排版;
  • 上层用绝对定位生成一层完全透明的 HTML 文本层(TextLayer),刚好重合在 Canvas 上面。

在 v6 版本里,官方重构了 Text Layer 的字体测量和 DOM 节点重排算法。现在选文本拖拽极其顺滑,支持跨行选中、复制、直接 Ctrl + F 全文高亮搜索,体验和直接看常规网页几乎没区别。

2. 解决了讨厌的电子盖章/印章丢失问题

做合同或发票预览时,经常遇到页面内容都在,但右上角的红章不见了,或者变成了黑色方块

这是因为 PDF 里的印章通常属于 Stamp Annotation(盖章注释),并且带透明度混合与自定义字体。旧版 PDF.js 对这类注释处理得很粗暴,经常直接跳过。

PDF.js v6 重构了 AnnotationLayer,不仅把矢量印章、透明度渐变完美叠加了出来,还修复了盖章周围出现“白块”的尴尬 Bug。

3. Worker + WebAssembly 性能更好

解析 PDF 是个很吃 CPU 的活。v6 版本把文件解密、图像解码这些耗时计算全部扔进了 pdf.worker.js,结合 WebAssembly 跑在后台线程,就算加载几百页的大文档,主线程也不会被卡死掉帧。


三、 实际开发中的几个避坑点

要用好 PDF.js v6,这几个地方少走弯路:

  1. 别用老旧的 Wrapper 库:直接用官方 npm 包 pdfjs-dist (v6),或者直接把官方编译好的 viewer.html 拿来用 iframe 嵌入,最稳定。
  2. 一定要配 CMap 资源:如果你的 PDF 包含中文、日文等字体,必须把 cmaps/ 静态资源目录打包进去,并在代码里指定 cMapUrl。漏掉这个,中文字体或者印章文字就会变乱码。
  3. 记得开启这两个参数:手写渲染逻辑时,务必加上 renderTextLayer: truerenderAnnotationLayer: true,不然文字选不上、印章也显示不出来。

四、 总结

总结下来,在 Web 端做 PDF 预览,不用去搞那些花里胡哨的第三方封装。

如果你需要文字能选中复制、能 Ctrl+F 搜索、合同里的红章能正常显示,目前最省心、最靠谱的选择就是直接上 PDF.js v6。虽然刚开始配置 Worker 和 CMap 资源要花一点时间,但相比后续去处理各种印章不见了、中文变乱码的客诉,这绝对是最接地气、最靠谱的落地方案。

💡 内容推荐

MiniMax在线AI网页版入口(海螺AI)
介绍 MiniMax 在线 AI 网页版入口地址 https://agent.minimaxi.com/,盘点其支持的 MiniMax-M3、MiniMax-M2.7、MiniMax-M2.7 HighSpeed 模型以及深度“思考”模式开关功能特点。
AI 解决不了一切:从 3D 展示的效率神话到 Vue 2 监听失效的“抓瞎”
辩证探讨 AI 编程的优势与局限。既惊叹于 AI 1小时完成过去2周手写 3D 模型的恐怖效率,又记录其在复杂 Bug 排查时的死循环与“古法编程”的不可替代性。
普通人建网站和博客的最佳选择:零技术门槛的建站神器 Publii
没有编程基础和技术背景的普通人,想搭一个属于自己的个人博客或企业展示网站?本文介绍一款完全免费、像写 Word 一样建站的软件 Publii,帮你零成本、极简搭建与维护网站。
2026 年做网站还能赚钱吗?国内与海外市场对比及 AI 产品新机遇
深入分析 2026 年做网站的商业现状。为什么国内内容站变现维艰?为什么海外市场与 AI 产品(图片/视频生成、写作辅助)蕴含巨大机会?
NameSilo 坑人!Cloudflare 11 美元的 .net 域名它卖 16 美元,老站长避坑吐槽
吐槽长期使用 NameSilo 遭遇的价格被坑体验,对比 Cloudflare Registrar 的批发成本价及 .net/.io 域名的选购避坑指南。
Parcel 2 致命 Bug:link hreflang 构建后变成 Hash 字符串导致谷歌多语言页面大量 404 不收录
做多语言网站 SEO 时,在 HTML 中配置 link hreflang 是标准操作。但 Parcel 2 构建时会将 hreflang 的 href 误判为资源依赖并改写为带有 Hash 的 404 路径,导致 Google 爬虫抓取失败、GSC 大量报错。本文深入剖析此 Bug 及绕过方案。