PDF预览
背景介绍:
最近在做小程序发票相关的东西,与后端调试过程中发现,给的发票是pdf格式的,看到这想完了,pdf预览在移动端一直是老大难的问题,心里想着后端就不能转成图片给我一个图片格式的嘛,但是转念一想,对呀,移动端预览pdf一直是个问题不能总是逃避呀,不能每次都依靠后端,带着这种想法,就开启了pdf预览之路。
正文:
思路:使用pdf.js将pdf加载到canvas里,然后将canvas转成图片
环境:"vue": "^2.6.10",
先附上正确的完整的代码步骤
(一)准备pdf.js、pdf.work.js、pdf.work.entry.js这三个文件
- 注意版本:这里使用的是2.0.288(如果实在找不到可以在项目中使用npm install pdfjs-dist@2.0.288,然后到node_modules里去找到相应的文件)
(二)将这三个文件放到public文件夹里面
(三)在public文件夹下的index.html文件中引入pdf.js和pdf.work.js这两个文件
注意:不要使用cdn的地址,后面我们需要需改pdf.work.js
(四)在发票预览的页面写相应的处理操作
- template
-
<div class="img-con" id="img-con"> <canvas id="myCanvas" canvas-id="myCanvas" style="display: none"></canvas> </div>
-
- js
-
this.invoiceUrl = 'http://localhost:8080/dev/cnr-web/江苏移动.pdf'; let canvasWidth = document.body.clientWidth - 40; PDFJS.cMapUrl = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/'; //字体解析 PDFJS.cMapPacked = true; PDFJS.workerSrc = 'http://localhost:8080/dev/cnr-web/pdf.worker.entry.js'; let loadingTask = PDFJS.getDocument(this.invoiceUrl); loadingTask.promise.then( function (pdf) { console.log('PDF loaded'); let pageNumber = 1; pdf.getPage(pageNumber).then(function (page) { console.log('Page loaded'); let scale = 2; let viewport = page.getViewport(scale); console.log(viewport, 'viewport'); let canvas = document.getElementById('myCanvas'); let context = canvas.getContext('2d'); viewport.viewBox.forEach((e, index) => { viewport.viewBox[index] = 0; }); canvas.height = viewport.height; canvas.width = viewport.width; let renderContext = { canvasContext: context, viewport: viewport, }; let renderTask = page.render(renderContext); renderTask.then(function () { let image = new Image(); image.src = canvas.toDataURL('image/png'); image.width = canvasWidth; let ImgCon = document.getElementById('img-con'); ImgCon.appendChild(image); }); }); }, function (reason) { console.error(reason); });
-
注意:
1.cmaps版本不对会影响pdf内容显示
PDFJS.cMapUrl = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/'; //字体解析
2. pdf.worker.entry.js不要使用相对路径,使用绝对路径 。
原因:构建之后pdf不显示
PDFJS.workerSrc = 'http://localhost:8080/dev/cnr-web/pdf.worker.entry.js';
3.需要将canvas转成图片,不然不好在手机上显示
let renderTask = page.render(renderContext); renderTask.then(function () { let image = new Image(); image.src = canvas.toDataURL('image/png'); image.width = canvasWidth; let ImgCon = document.getElementById('img-con'); ImgCon.appendChild(image); });
(五)修改pdf.work.js文件,原因是个别发票的印章出不来
// if (data.fieldType === 'Sig') {
// _this2.setFlags(_util.AnnotationFlag.HIDDEN);
// }
到此,就结束了........
碰到的问题:
- 使用npm install pdfjs-dist,在import PDFJS from 'pdfjs-dist'时报错, 应该是pdfjs-dist版本问题,最后选择的是2.0.288的版本
- 使用script引入pdf.js和pdf.work.js时,发票显示不出来,需要通过pdf.work.entry.js引入pdf.work.js
- 个别发票印章显示不出来,将pdf.work.js中的印章代码注释掉,参考(五)
- 代码构建之后发票显示不出来,PDFJS.workerSrc要用绝对路径,不要使用相对路径
最终效果:
下面附上我的历程:
由于是在小程序端开发,首先就是考虑小程序自带的方法,使用wx.openDocument,但是这个是直接打开,我们的需求是,如下图,这个样子的,摇头~,然后又用了web-view,嗯,web-view也是占满整个屏幕的,就跟H5或者PC端使用iframe展示效果一样,也是不行,后来又看到用canvas加载pdf,就网上找各种办法。哎,第一步就不行了,使用npm install pdfjs-dist后,import PDFJS from 'pdfjs-dist',就开始报错,百度又说是版本问题,但是换了几个版本还是不行,想着说找个html文件用scipt标签引入cdn地址好吧,可惜小程序中没有这么一个html文件让我引入,在网上查找了好久,还是不行,陷入了死局,这可咋整。睡了一觉醒来想着说先不在小程序里写了,先写一个纯html,js的,这种不会因为是小程序环境还是H5环境而影响,说干就干,嗯,一个简单的demo就出来了,但是发票内容有些显示不出来,百度说cMapUrl地址不对,有改过版本有下载到本地.....网上说的方法都试过还是不行,又陷入了死局,后来用safari浏览器打开,发票显示的非常完美,啥问题都没有,难道是个别浏览器不支持这种字体,抱着在手机上显示正常的侥幸心理,打算把它迁移到项目里。想着小程序里不行,咋办呢,突然灵机一动H5吧,H5环境比小程序好点,然后就开始了迁移之路,因为是vue项目第一想法还是使用npm install pdfjs-dist,然后根据demo中引入的是2.0.288的cdn地址,又联想到小程序测试的时候安装使用的时候报错,就果断选择了2.0.288的版本安装。这次没报错,将代码移植过去之后发票没有显示,又陷入了死局,后来想着js能显示,移过来咋就不行了呢,就比对了下,区别就是一个使用的cdn地址一个是npm install的呗,行那咱就换,我还就不信了。换了之后,显示了但是没显示全,而且跟demo显示不全的还不一样,然后又陷入了死局。后来在来回切换使用的是npm还是script引入的时候,混搭了一下,pdf.js使用的是Script标签,pdf.work.js使用的是npm,发票内容竟然出来了,但是个别发票的印章没有出来,之前在研究发票内容显示不全的时候看到过这个问题,就是把pdf.work.js里的印章的代码注释掉,但是这个使用npm安装的呀,要改node_modules。的确,注释掉就可以了。但这也不是个好办法,我们使用的是jekins部署的,自动打包,不好修改node_modules,如果这样的话就只能手动打包了。然后就想着继续研究研究,发现,script标签引入的pdf.work.js,而node_modules引入的是pdf.work.entry.js。然后把node_modules里的俩文件copy出来放在了本地,放在vue文件同级时,会报require is undefined;然后就放到了main.js的同级,但是pdf.js中会报错,里面会用到pdf.work.js里的东西,由于pdf.js是放在了public里面,在public下面的index.html中引入的,会先加载,这个时候pdf.work.js还没有加载,所以会报错,后来把pdf.work.js也放到了public 里,pdf.work.entry.js还是放到了main.js的同级,将pdf.work.entry.js里的pdf.work.js指向了public下的pdf.work.js,然后注释pdf.work.js里的签名代码,发票完美的显示好了。真的是太开心了。可是,好景不长,后来发现构建之后发票不显示,在控制台发现报错了,如下图,这个错误之前在本地调试是也有过,但是由于没有影响发票展示就没在意。这下好了,为啥呢,map文件之前也见过就想着把这俩map文件也放到public里,再次构建,嗯不报错了,啥报错也没有,就是不展示,后来经过再次使用npm install pdfjs-dist,最新版的、2.3.000版的、2.0.288版的调试,还是会出现各种各样的问题,最后想到之前混搭使用npm install pdfjs-dist@2.0.288的时候只是印章不出来,就想着这种条件下构建呢。说干就干,发现构建之后,也只是印章显示不出来,这就说明是pdf.work.entry.js的路径有问题,这里我写的是相对路径,想着改成http开头的路径呢,构建了一下,发现果然发票有完美的出来了,到此,结束,目前还没发现有什么问题。