移动端显示h5我目前已经尝试了3种方法:
我使用的是vue的2.X版本
- 后端返回一个pdf的链接,然后前端直接使用
location.href = ‘http://www.XXX.com/test.pdf’
,这种形式苹果手机应该是没问题的,但是安卓手机默认会触发下载,所以不推荐。 - 使用pdfjs,这是pdfjs的下载地址,点击下载之后会下载一个压缩包,里面包含build,web文件夹和LICENSE文件,把他们解压之后放在static目录下面,这里给一个目录结构(我的项目是vue2.X项目):
| src
| static
| pdfjs
| build
| web
然后在使用的时候是这样的
<iframe ref="iframeRef" frameborder="0" width="100%" :src="/static/pdfView/web/viewer.html?file='XXX"/>
前面的这个/static/pdfView/web/viewer.html
是你的静态资源路径,例如你的网页地址是http://www.XXX.com
,那么就是http://www.XXX.com/static/pdfView/web/viewer.html
,至于这个viewer.html
是什么,他是pdfjs提供的一个渲染pdf的入口,也就是通过iframe内嵌viewer.html,然后通过file字段告诉viewer.html
一些细节。file可以是http://www.XXX.com/test.pdf
这种.pdf的形式,也可以是http://www.XXX.com/getPdfBlob?pdfName=123_指南
,这种blob二进制流形式。
但是经过测试,我发现vivo的部分手机和iponeX无法正常显示
- 使用pdfjs-dist (这个是最终方案),这里需要注意两个细节:
第一个是pdfjs-dist下载需要下载2.0.943版本
npm install --save pdfjs-dist@2.0.943
第二个是导入的时候
const pdfJS = require("pdfjs-dist");
pdfJS.GlobalWorkerOptions.workerSrc = require("pdfjs-dist/build/pdf.worker.entry");
尤其是一定要配置pdfJS.GlobalWorkerOptions.workerSrc
,pdfjs-dist
里面需要引入pdf.work.js
,但是静态资源会被重定向到index.html,所以一定要配置pdfJS.GlobalWorkerOptions.workerSrc
。
基本起步就是这些,我把我的一些代码粘贴到这里。我这里获取的pdf是二进制流的形式:
<template>
<div class="canvas-container">
<canvas class="pdf-container" v-for="page in numPages" :key="page" :ref="'myCanvas_' + page"> </canvas>
</div>
</template>
<script>
const pdfJS = require("pdfjs-dist");
import { getPDFBlob } from "@/api"
pdfJS.GlobalWorkerOptions.workerSrc = require("pdfjs-dist/build/pdf.worker.entry");
export default {
created() {
getPDFBlob("http://www.XXX.com/getPdfBlob?pdfName=123_指南")
.then((res) => {
let fileURL = URL.createObjectURL(res.data)
this.renderPage(fileURL);
})
.catch((error) => {
console.log("error", error);
});
},
data() {
return {
numPages: 0
}
},
methods: {
renderPage(pdfUrl) {
let pdfData = pdfJS.getDocument(pdfUrl);
pdfData.promise.then((pdf) => {
this.numPages = pdf.numPages
for (let i = 1; i <= this.numPages; i++) {
pdf.getPage(i).then((page) => {
let canvas = this.$refs['myCanvas_' + i][0];
let viewport = page.getViewport(3);
canvas.height = viewport.height;
canvas.width = viewport.width;
let ctx = canvas.getContext("2d");
let renderContext = {
canvasContext: ctx,
viewport: viewport,
};
page.render(renderContext)
});
}
});
},
}
};
</script>
<style scoped>
.canvas-container {
width: 100%;
overflow: hidden;
}
.pdf-container {
width: 100%;
}
</style>
这里有两个需要注意的:
第一个是import { getPDFBlob } from “@/api”
里面的getPDFBlob
是一个方法,他会拉取一个pdf的blob流。
第二个是let viewport = page.getViewport(3);
可以调整canvas的清晰度,如果感觉canvas显示的pdf模糊,可以尝试增大 page.getViewpor
入参的数值。
但是这里还是要说一句,pdf虽然正常显示了,但是交互不是太好,因为不支持双手捏合方法缩小,不支持双击放大缩小(当然你也可以直接开启css的touch-action属性)。如果支持,还需要监听双击和双指操作事件
后续
在我使用pdfjs-dist时,也就是我说的第三种方案的时候,我发现部分pdf出现了显示不全的问题,打冷眼看以为是乱码,放大仔细一看其实是大部分文字没显示出来,导致零零散散的文字看起来像乱码。并且后台有警告(警告大概意思是字体包没有提供)。
Warning: Error during font loading: The CMap “baseUrl” parameter must be specified, ensure that the “cMapUrl” and “cMapPacked” API parameters are provided.
我在这篇文章中获得了启发,我简单介绍一下我的思路。解决步骤:
- 如果找不到字体包就引入线上的
- 线上字体包的地址是 https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/
首先这个逻辑需要在源码里面修改,找到目标目录地址node_modules > pdfjs-dist>build>pdf.js
图片中画红框的内容是需要加的内容。我在下面给出代码的上下文,方便搜索和复制。
params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
params.CMapReaderFactory = params.CMapReaderFactory || _dom_utils.DOMCMapReaderFactory;
params.ignoreErrors = params.stopAtErrors !== true;
params.pdfBug = params.pdfBug === true;
params.cMapPacked = true
params.cMapUrl = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/'
var NativeImageDecoderValues = Object.values(_util.NativeImageDecoding);
改完之后就可以正确显示文字了
在这里大家可能看到引入的链接是https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/,通过这个链接感觉好像把pdfjs-dist升级到2.2.228版本就可以了?于是我删除了之前的包然后安装
npm install pdfjs-dist@2.2.228 -S
发现不行,还是会缺文字。