前言

这里主要使用到了两个库,pdf的展示是开源库pdf.js,和canvas结合使用;生成pdf的开源库是jsPDF。

pdf展示的demo: http://121.4.85.237/

pdf.js

这个开源库很牛的,详细内容看官网就行了,我这里就简单的贴一些例子和pdf.js的两种使用方式。

官网:https://github.com/mozilla/pdf.js

这个pdf.js还是有一个官网demo的可以玩一下。

https://mozilla.github.io/pdf.js/web/viewer.html

我这里主要说一下两种使用方式,第一种是以npm的形式引入项目中,去进行生产;第二种是以构建包的形式扔到服务器上进行使用。

引入项目使用

引入的是pdf-dist npm包,此方式的缺点是官方demo中的工具栏等一些东西是没有的,想要的话需要自己去实现。

安装:npm i pdfjs-dist

直接上只展示一页(此处是第一页)demo的核心代码

<canvas id="theCanvas" style="border: 1px solid black; direction: ltr;"></canvas>

const pdfjsLib = require("pdfjs-dist");
import ss from "pdfjs-dist/build/pdf.worker.entry";
const pdfPath = "helloworld.pdf"; // 此pdf要放在静态文件夹中
pdfjsLib.GlobalWorkerOptions.workerSrc = ss; // 这里使用好别扭,固定写法

mounted() {
  const loadingTask = pdfjsLib.getDocument(pdfPath);
  loadingTask.promise.then(function(pdfDocument) {
    return pdfDocument.getPage(1).then(function(pdfPage) {
      const viewport = pdfPage.getViewport({ scale: 1.5 }); // 我自己设的1.5倍
      const canvas = document.getElementById("theCanvas");
      canvas.width = viewport.width;
      canvas.height = viewport.height;
      const ctx = canvas.getContext("2d");
      const renderTask = pdfPage.render({
        canvasContext: ctx,
        viewport,
      });
      return renderTask.promise;
    })
  }).catch(function(reason) {
    console.log('error', reason)
  })
}

还可以实现展示多页,不过我试了下,单页展示时的页面响应式还不错,多页时有点闪眼,我这里就把resize事件去掉了。

<canvas
      v-for="item in totalPage"
      :key="item"
      :id="'theCanvas' + item"
      class="canvas-box"
></canvas>
    
const that = this;
this.loadingTask = pdfjsLib.getDocument(pdfPath);
this.winW = document.documentElement.clientWidth;
this.loadingTask.promise
  .then(function (pdfDocument) {
    let nums = pdfDocument.numPages;
    for (let i = 1; i <= numPages; i++) {
      pdfDocument
        .getPage(i)
        .then(function (pdfPage) {
          let viewport = pdfPage.getViewport({ scale: 1.5 });
          let scale = (that.winW / viewport.width).toFixed(2);
          let scaledViewport = pdfPage.getViewport({ scale });
          const canvas = document.getElementById("theCanvas" + i);
          canvas.width = scaledViewport.width;
          canvas.height = scaledViewport.height;
          const ctx = canvas.getContext("2d");
          const renderTask = pdfPage.render({
            canvasContext: ctx,
            viewport: scaledViewport,
          });
          return renderTask.promise;
        })
        .catch((err) => {
          console.log("errr", err);
        });
    }
  })
  .catch(function (reason) {
    console.log("error", reason);
  });
  },

构建包

首先是拉代码进行构建包,不想自己构建就在官网下载构建包。

// 自己构建
git clone https://github.com/mozilla/pdf.js.git
cd pdf.js
npm install -g gulp-cli
npm install
gulp generic-legacy

// 或者官网直接下载构建包
https://mozilla.github.io/pdf.js/getting_started/#download

自己操作的构建包在build文件夹中,可将构建包抽出,运行web文件夹下的viewer.html,使用open with live server,会默认查看compressed.tracemonkey-pldi-09.pdf 。

默认查看的pdf是在viewer.js中做了配置:defaultUrl 属性。想查看其它pdf文档,需要将pdf放入web文件夹中,和compressed.tracemonkey-pldi-09.pdf同层级,并在浏览器中拼接file,类似于http://127.0.0.1:5500/web/viewer.html?file=helloworld.pdf

对于其它服务器的pdf文档,我没有试验,应该也是支持的,感觉也是在file后拼接url路径,可去看网上大佬文章进行学习。这里推荐几篇文章,讲了下后端返回流文件的处理

https://juejin.cn/post/6844903843088564238
https://juejin.cn/post/6988051826058723364

构建包的使用

我将构建包扔在我的服务器80端口上,写个简单的index.html,html里使用了iframe嵌套了一个,点击切换按钮展示另一个pdf,很好用。

简单代码如下:

<iframe
      width="100%"
      height="100%"
      src="http://121.4.85.237/legacy/web/viewer.html"

></iframe>
>
let btn = document.getElementById("button");
btn.onclick = function () {
  window.open(
    "http://121.4.85.237/legacy/web/viewer.html?file=helloworld.pdf"
  );
};

jsPDF

此开源库是将html元素导出为pdf文档,或者不根据元素自己new一个pdf文档出来,使用简单。

官方文档: http://raw.githack.com/MrRio/jsPDF/master/docs/index.html
安装: npm install jspdf --save

一些基础使用

import { jsPDF } from "jspdf";
const doc = new jsPDF(); // Default export is a4 paper
doc.text("Hello world!", 10, 10);
doc.addFont("MyFont.ttf", "MyFont", "normal");
doc.setFont("MyFont");
doc.setFontSize(40)
doc.save("a4.pdf");

导出为一页或多页pdf文档可参考这篇博客:

https://www.jianshu.com/p/651c40d565e4

此库的缺点很多,如有一点模糊感,分页时会造成文字或表格截断等。看网上的处理方式有自己拿捏css布局的,也有遍历节点做判断处理的。

我的建议是在简单场景下或者不需要分页时可以使用此库生成pdf,复杂场景下直接让后端去生成pdf,后端有更好的处理方式。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐