移动端 PDF 在线预览
在 H5、微信内置浏览器环境直接打开 PDF,不同系统原生行为差异很大。iOS 微信可以直接预览 PDF,但不支持二次定制 UI、转发;安卓微信会直接触发文件下载,无法在线阅读。
主流解决方案:使用 Mozilla PDF.js,在前端把PDF解析渲染为 Canvas / SVG,实现在线预览,可自定义页面逻辑、分享转发。
Word文档无法前端直接解析,一般由后端预转换为PDF或图片序列。
微信内置浏览器原生PDF预览行为
| 系统 | 表现 | 限制 |
|---|---|---|
| iOS(iPhone) | 微信内置内核直接打开PDF,在线预览 | 页面是微信内置页面,无法自定义UI、无法直接做业务转发分享;复杂排版Word转PDF后偶有排版错乱 |
| Android | 点击PDF链接直接跳转下载,不会在线预览 | 用户必须下载文件后,使用本地APP打开阅读 |
原生预览能力不可控,业务需要统一体验、自定义交互(分享、水印、翻页)时,必须引入PDF.js。
Word文档预览说明:浏览器前端没有成熟方案直接解析Word。建议后端预处理,将Word转PDF,再交给前端PDF预览组件;也可以后端直接转图片序列展示。
注意:Linux服务端转换复杂图文文档,容易出现图文错位;Windows服务器转换成本高,第三方云转换服务有付费开销。
PDF.js 基础原理
PDF.js 是 Mozilla 开源、基于HTML5的PDF解析渲染库,不依赖任何浏览器插件。
- PDF解析的CPU密集计算放到 Web Worker 子线程执行,避免阻塞主线程造成页面卡顿;
- 解析PDF文档结构,逐页渲染输出为
Canvas位图 或者SVG矢量DOM; - 主线程拿到渲染结果,挂载DOM展示给用户。
API全部基于Promise异步,适合异步处理大体积PDF文件。
CDN 直接引入
<div id="pdf_viewer"></div>
<script type="module">
import * as pdfjsLib from 'https://cdn.jsdelivr.net/npm/pdfjs-dist@4/build/pdf.min.mjs';
// worker 路径必须与 pdfjs-dist 主库版本一致;v4 起文件名是 .mjs
pdfjsLib.GlobalWorkerOptions.workerSrc =
'https://cdn.jsdelivr.net/npm/pdfjs-dist@4/build/pdf.worker.min.mjs';
async function renderPdf(pdfUrl) {
// 加载pdf文档
const pdf = await pdfjsLib.getDocument(pdfUrl).promise;
const totalPages = pdf.numPages;
for(let pageNo = 1; pageNo <= totalPages; pageNo++) {
const page = await pdf.getPage(pageNo);
const scale = 1.5;
const viewport = page.getViewport({scale});
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.getElementById('pdf_viewer').appendChild(canvas);
canvas.width = viewport.width;
canvas.height = viewport.height;
await page.render({
canvasContext: ctx,
viewport
}).promise;
}
}
renderPdf("./demo.pdf");
</script>
npm 引入
安装依赖
npm install pdfjs-dist -S
import * as PDFJS from 'pdfjs-dist';
// 设置 worker 脚本路径,webpack、vite 需要处理 worker 资源
PDFJS.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url
).toString();
async function renderPdf(pdfUrl) {
const pdfDoc = await PDFJS.getDocument(pdfUrl).promise;
// 渲染逻辑和上面示例 renderPageAsync 保持一致
}
pdfh5:移动端封装PDF组件
pdfh5 底层封装pdf.js,存在两种渲染模式:
canvas模式:PDF页面渲染到Canvas画布(内存位图,浏览器DOM里的<canvas>元素,不是导出静态图片文件;如果DPR处理不当,移动端会模糊);svg模式:输出SVG矢量DOM节点,文本可复制。
两种模式区别:
- canvas模式:兼容性最好,支持印章、签名PDF;不开启textLayer时文本不可选;需要手动处理devicePixelRatio避免模糊。
- 旧版svg模式:矢量、文本可复制;但对带签名、印章、复杂混合图层PDF渲染会异常,新版已经移除该模式。
版本更新:v3.0.0(2025-10)支持官方 pdf.js v5.4.296,移除 svg 渲染模式,恢复懒加载,新增分段加载、沙箱集成(防止 JavaScript 注入等)、密码 PDF 预览;手势缩放仍在优化。v2.0.5 起已移除 jQuery 依赖。
CDN 直接引入(v2.x旧版本)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pdfh5@2/lib/pdfh5.css">
<div id="pdf_viewer"></div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pdfh5@2/lib/pdfh5.js"></script>
<script>
new window.Pdfh5('#pdf_viewer', {
pdfurl: "./demo.pdf",
// 处理中文CID字体,解决中文乱码
cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2/cmaps/",
renderType: "canvas" // v2.x可选 canvas / svg;v3.x已删除svg
})
</script>
注意:pdfh5 内部大量硬编码相对路径、动态 import、worker 加载逻辑,依赖脚本与页面同源;公共 CDN 是跨域来源,浏览器安全机制把相对路径全部解析坏掉,因此 CDN 外链会各种诡异报错。
建议:把资源完整下载,放到自己业务服务器同源目录,本地<script>引入,不要引用第三方 CDN 地址。
Vue 项目使用(v3 版本)
<template>
<div id="pdf_viewer"></div>
</template>
<script>
import Pdfh5 from "pdfh5";
import "pdfh5/css/pdfh5.css";
export default {
data() {
return {
pdfh5Ins: null
}
},
mounted() {
this.pdfh5Ins = new Pdfh5("#pdf_viewer", {
pdfurl: "./demo.pdf",
// v3 版本已无 renderType 配置,固定 canvas 渲染
textLayer: true, // 开启文本层,支持复制文字
workerSrc: "./pdf.worker.min.js",
cMapUrl: "./cmaps/"
});
// 监听渲染完成事件
this.pdfh5Ins.on("complete", (status, msg, time) => {
console.log("pdf渲染完成", status, msg);
})
},
beforeDestroy() {
// 销毁实例释放资源,防止内存泄漏
this.pdfh5Ins && this.pdfh5Ins.destroy();
}
}
</script>
v3 初始化时建议同时指定同源的 workerSrc、cMapUrl,并把仓库内的 cmaps、wasm、worker 拷到 public。是否还要单独 import CSS,以当前版本目录为准。
常见问题
PDF 跨域访问报错
PDF 文件如果和前端页面不在同一个域名,浏览器会触发 CORS 跨域拦截。
解决方案:
- 开发环境:webpack-dev-server 配置代理转发 PDF 资源
- 服务端、Nginx:PDF 资源服务器配置 CORS 响应头,允许前端域名访问;
- 后端接口代理:后端接收 PDF URL,把 PDF 读取为二进制
ArrayBuffer返回给前端,前端传入二进制数据给 PDF.js 解析,绕过浏览器跨域限制。
中文 PDF 乱码、文字方块
PDF 内部使用 CID 东亚字体,缺少 cmaps 字符映射资源,就会出现中文乱码。
- 配置参数
cMapUrl,指向 cmaps 字符映射资源; - 业务打包时不要把整份 cmaps 打进主 bundle。pdf.js 可从 CDN 拉 cmap;pdfh5 建议把 cmaps 放到与页面同源的静态目录,避免跨域和相对路径解析失败。
Canvas 渲染页面模糊
canvas 默认 CSS 像素渲染,手机高 DPR 屏幕会模糊。
需要根据 window.devicePixelRatio 设置 canvas 画布实际像素尺寸提升清晰度,pdfh5 内部 scale 参数可调节。
选型建议
- 快速业务开发,需要封装好手势缩放、分页:选用 pdfh5 v3.x,v3 已无 jQuery 依赖,仅 canvas 渲染;
- 需要深度自定义渲染逻辑:直接使用原生 pdfjs-dist;
- Word 文档:不要尝试前端解析,后端转换 PDF 或者图片序列。