预览PDF
述语
在h5中预览PDF,最开始使用的是vue-PDF 发现在渲染PDF时 是 乱序渲染,不是从头渲染,而且 每次渲染都要读取一遍pdf流,这样如果在查看PDF 时, 假如在 PDF未渲染完某页 就返回时 会报错,导致下一次打开PDF 不会渲染出来。导致效果不好
所以 更改 PDF插件,使用PDFjs-dist,但是注意点还是要注意以上。
但是我还是觉得 将后台将pdf转换成图片的base64流,传给前台,前台直接渲染图片会更简单
基本代码
先下载pdfjs-dist
以下是预览PDF的组件,其中url可以是路径也可以是base流,
canvas 绘制
缺点:放大后会模糊
svg 绘制
canvas 按像素绘制 svg按弧度 转角...绘制
包含 canvas 绘制 和svg绘制 PDF
<template>
<div class="view-wrapper">
<van-nav-bar title="查看PDF" left-arrow @click-left="goHome()" fixed />
<div class='box' style="background: #fff">
//-------------canvas 绘制
<div>
<canvas v-for="page in pages" :id="'the-canvas'+page" :key="page"></canvas>
</div>
//----END
//----------svg 绘制
<div class="view" id="canvas-wrap"></div>
//----END
</div>
<van-loading v-show="loadding" type="spinner" />
</div>
</template>
<script>
import PDFJS from 'pdfjs-dist';
import workerSrc from 'pdfjs-dist/build/pdf.worker.entry';
PDFJS.workerSrc = workerSrc;
// PDFJS.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.943/build/pdf.worker.min.js';
//请注意,这里的cdn worker地址是贴的网上的,并且签章那部分代码是没有注释的,直接拿去用是达不到效果的,我也没有现成的资源可提供,只是贴一下方法
export default {
data() {
return {
pdfDoc: null,
pages: 0,
pdfUrl:'',
src:'',
loadding:true,
file:true,
isDestory:true
}
},
metaInfo: {
meta: [
{ name: 'viewport', content: 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=5,user-scalable=yes' }
]
},
watch: {
},
methods: {
_renderPage (num) {
// getPage 处理每个页面
// 返回单页内容实例(页面引索) pdf.getPage(index)
this.pdfDoc.getPage(num).then((page) => {
// canvas 绘制 PDF
let canvas = document.getElementById('the-canvas' + num)
let ctx = canvas.getContext('2d');
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
let dpr = window.devicePixelRatio || 1
let bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1
let ratio = dpr / bsr
// 返回页面内容(比例) page.getViewport({scale:2.0})语法改这么写
let viewport = page.getViewport({scale:screen.availWidth / page.getViewport({scale:1.0}).width});//这是让pdf文件的大小等于视口的大小
canvas.width = viewport.width * ratio
canvas.height = viewport.height * ratio//这里会进行压缩,解决模糊问题
canvas.style.width = viewport.width + 'px'
canvas.style.height = viewport.height + 'px'
let renderContext = {
canvasContext: ctx,
viewport: viewport,
transform: [ratio, 0, 0, ratio, 0, 0]//这里会进行放大,解决模糊问题
}
const that = this
page.render(renderContext).promise.then(function(){
// 防止 在 PDF未渲染完某页 就返回时 会报错,导致下一次打开PDF 不会渲染出来
if(that.isDestory){
console.log('xuanran')
// page.getTextContent()
if (that.pages > num) {
that._renderPage(num + 1)
}
}
});
//-----------END----------
//svg绘制 PDF
/* svg实现方式 */
let viewport = page.getViewport({scale: vm.scale})
let container = document.createElement('div')
container.id = 'canvas_' + num
container.className = 'pageContainer'
container.style.width = viewport.width + 'px'
container.style.height = viewport.height + 'px'
document.getElementById('canvas-wrap').appendChild(container)
return page.getOperatorList().then(function(opList) {
let svgGfx = new PDFJS.SVGGraphics(page.commonObjs, page.objs)
return svgGfx.getSVG(opList, viewport).then(function(svg) {
container.appendChild(svg)
})
})
//-----------END----------
})
},
_loadFile (url) {
// 获取整个 文档
PDFJS.getDocument({
url,
// cMapUrl: 'https://unpkg.zhimg.com/pdfjs-dist@1.9.426/cmaps/',//这里同样要引入字体解决水印问题,需自己提供
// 注意:如果PDF的水印是收费字体,则需要 通过cMapUrl引入对应的字体,如果是免费字体,则不需要引入字体 水印会直接渲染出来
cMapPacked: true
}).promise.then((pdf) => {
this.pdfDoc = pdf
this.pages = this.pdfDoc.numPages
this.loadding = false
const that = this
if(that.isDestory){
this.$nextTick(() => {
this._renderPage(1)
})
}
},(err) => {
if(err.name == 'MissingPDFException'){
this.$toast('无效的PDF链接')
}
// reject(err);
})
}
},
mounted() {
this._loadFile('http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf');
},
beforeDestroy(){
console.log('destory')
this.isDestory = false
this._renderPage = null
}
}
</script>
<style lang="scss" scoped>
.view-wrapper {
padding-top:1.22667rem;
}
.view-wrapper /deep/ .van-nav-bar .van-icon {
color: #333;
font-size: 18px;
margin-right: 3px;
}
.view-wrapper /deep/ .van-loading {
position: absolute;
top: 50%;
left: 46%;
}
</style>
引入pdfjs-dist 报错 总是引入不进来
const PDFJS = require('pdfjs-dist')
PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.entry')
有些字体加载不出来
如果pdf中有商用字体,对于商用字体,默认是不支持的,想要渲染出商用字体,需要引入对应的字体:一般是全部引入
cMapUrl: 'https://unpkg.zhimg.com/pdfjs-dist@1.9.426/cmaps/',
//这里同样要引入字体解决水印问题,需自己提供
// 注意:如果PDF的水印是收费字体,则需要 通过cMapUrl引入对应的字体,如果是免费字体,则不需要引入字体 水印会直接渲染出来
cMapPacked: true
查看 签章 电子签名
在node_moduels pdfjs-dist 的 pdf.worker.js 文件中,将以下代码注释:
if (data.fieldType === 'Sig') {
this.setFlags(AnnotationFlag.HIDDEN);
}
缩放pdf
用的是 <meta name="viewport" content="width=device-width,initial-scale=1.0"> 在查看pdf页 设置 成可以缩放,在其他页面设置成不可以缩放
在vue中更改 html的meta 可以 通过v-meta插件 设置
metaInfo: {
//和data是平级
meta: [
{ name: 'viewport', content: 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=5,user-scalable=yes' }
]
},
contains 属性冲突
PDFJS 里给Aray添加了contains属性,Array.prototype.contains,所以如果在项目中同时添加contains属性会报错