前端实现pdf转图片

10,648 阅读3分钟

1. 需求背景

在用户注册过程中,需要下载一份模版打印后手填信息进行上传人工审核。 目前存在的情况是用户有些手填信息模糊或填写不规范导致人工审核不通过,占据了大部分比例的注册失败。

因此我们在H5应用上所填写的一些信息希望能够填充到模版中去,然后再打印下来补全未填的信息。

按照产品的意思是希望能够生成图片,让用户进行保存下载。该操作比pdf的操作简易方便,不会出现下载pdf文件后用户不好找的情况。因此希望能够产出图片。

在后端处理填充模版时,他们目前只支持了生成pdf的文件。假如需要生成图片直接返回给后端还需要去做调研寻找开源库,当然我们前端也确实是同样的情况,最后还是我们前端来做这件事了。

2. 方案调研

网络上存在很多的有关pdf的npm包,但我看来看去最终其实他们都是用到了github上star最多的pdf.js。

在pdfjs的github上有说道

image.png

翻译过来是:PDF.js 由社区驱动并由 Mozilla 提供支持。 我们的目标是创建一个通用的、基于 Web 标准的平台,用于解析和呈现 PDF。

那就用它了吧!然后开始在网上找各种有关pdfjs具体实践的文章!

最终应该是能够实现我的需求了!

以下是我的核心源码实现!在这里我的项目使用的是vue框架!

首先安装使用npm或yarn安装pdfjs-dist包

参考pdfjs的github github.com/mozilla/pdf…

image.png

npm i pdfjs-dist --save-dev@指定版本

yarn add pdfjs-dist@指定版本 --dev

严重建议参考一下版本,最新版可能会有问题!!!!! image.png

在这里我使用的是2.5.207版本

import * as PDFJS from 'pdfjs-dist/build/pdf'
import PDFJSWorker from 'pdfjs-dist/build/pdf.worker.entry.js'
PDFJS.GlobalWorkerOptions.workerSrc = PDFJSWorker
const pdfUrl = '后端返回给你的pdf-url地址'
const CMapReaderFactory = {
  url: pdfUrl,
  // cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/',
  // cMapPacked: true
}
    data() {
      return {
        pdfInstance: null,
        pdfPages: 0,
        imgUrl: "",
      };
    },
    async getPdfInfo() {
      const loadingTask = PDFJS.getDocument(CMapReaderFactory);
      const pdfInfo = await loadingTask.promise; // 获取pdf实例
      this.pdfInstance = pdfInfo; // 设置pdf实例
      this.pdfPages = pdfInfo.numPages; // 获取解析pdf的页数 在该项目中模版pdf只有一页 故跟网上大多写法不太一样
    },
    async createCanvas(pdfPages) {
      const canvas = document.createElement("canvas"); // 创建canvas标签
      console.log(canvas)
      const page = await this.pdfInstance.getPage(pdfPages); // 根据pdf实例和解析出pdf的页数得出一个page信息

      console.log(page)

      const viewport = page.getViewport({ scale: 1 }); // 获取page视口信息

      const context = canvas.getContext("2d"); // 返回一个用于在画布上绘图的环境
      canvas.width = viewport.width; // 设置画布宽度
      canvas.height = viewport.height; // 设置画布高度
      
      const renderContext = {
        canvasContext: context,
        viewport: viewport,
      };

      const renderTask =  page.render(renderContext); 
      renderTask.promise.then(() => {
        const imgUrl = canvas.toDataURL('image/jpeg')
        this.imgUrl = imgUrl
      })
    }

3. 调研过程中碰见的问题

image.png

参考解决方案地址:github.com/mozilla/pdf…

在这里我并没有使用cdn引入的方式

image.png

该情况是有些字体并不能识别出来。

参考解决方案地址: www.codeprj.com/blog/b37f86… blog.csdn.net/m0_37903882…

在这里我的解决方案是找后端沟通让他在填充信息的时候统一字体格式!

  • 没有选择采用CDN的方式或增加包体积大小。
  • 一是因为CDN有可能出现不稳定或挂了的情况我们没有办法改变。
  • 二是为了少来依赖包和项目包体积大小。

image.png

4. 寻找更加合适的解决方案

欢迎大家给点意见啊~ 也希望我的这篇文章能够帮助到正在找相关解决方案的各位!

参考链接:blog.csdn.net/xxjiushini/…

www.jianshu.com/p/c4a885e67…

5. 最新解决方案

通过cdn方式,在需要用到该方法时动态创建script标签且引入。

      const arr = ['三个cdn链接']
      const fragment = document.createDocumentFragment();
      for (let i = 0; i < 3; i++) {
        const script = document.createElement("script");
        script.src = arr[i];
        if (i == 2) {
          script.onload = this.showPdf;
        }
        fragment.appendChild(script);
      }
      document.getElementsByTagName("body")[0].appendChild(fragment);

这里使用了DocumentFragment,参考链接developer.mozilla.org/zh-CN/docs/…

该方案能够避免在H5项目中依赖包体积过大的问题,公司内部生成cdn链接避免了外链有可能挂了的情况。