vue 在线阅读PDF

项目中使用到在线阅读PDF的功能很常见,平时的话,我都用的window.open或者下面这俩属性,都可以直接展示出来


 
 
  

但是用户那边有要求,不能右键下载,复制之类的,本来想用pdf.js去处理的,在途中突然发现vue-pdf这个东西,发现还挺不错的,简单记录一下,直接上效果图和代码
vue 在线阅读PDF_第1张图片

安装:npm install --save vue-pdf
引入:import pdf from 'vue-pdf'
 components:{
           pdf
       },

demo代码:







参考地址:https://www.cnblogs.com/shaozhu520/p/12801184.html

这上面存在一个问题,就是我用代码处理过的PDF是带有水印的,但是这里水印却不显示了,百度相关却没有找到好的解决方案,然后通过前端处理了一下。
首先自定义一下水印指令 新建watermark.js

import Vue from "vue";

Vue.directive("watermark", (el, binding) => {
  (function (str, container, id, font) {
    setTimeout(function () {
      var containerWidth = container.offsetWidth || "400"; // 获取父容器宽
      var containerHeight = container.offsetHeight || "100"; // 获取父容器高
      container.style.position = "relative"; // 设置布局为相对布局

      // 创建canvas元素(先制作一块背景图)
      const can = document.createElement("canvas");
      can.width = containerWidth; // 设置每一块的宽度
      can.height = containerHeight; // 高度
      const cans = can.getContext("2d"); // 获取canvas画布
      cans.rotate((-20 * Math.PI) / 270); // 逆时针旋转π/9
      cans.font = font || "20px PingFang-SC-Regular"; // 设置字体
      cans.fillStyle = "rgba(219,8,8, 0.3)"; // 设置字体的颜色
      cans.textAlign = "left"; // 文本对齐方式
      cans.textBaseline = "Middle"; // 文本基线
      cans.fillText(str, 0, (4 * can.height) / 5); // 绘制文字
       cans.fillText(str, 0, (4 * can.height) / 10); // 绘制文字

      // 创建一个div元素
      const div = document.createElement("div");
      div.id = id; // 设置id
      div.style.pointerEvents = "none"; // 取消所有事件
      div.style.top = "-15px";
      div.style.left = "80px";
      div.style.position = "absolute";
      div.style.zIndex = "100000";
      div.style.width = containerWidth + "px";
      div.style.height = containerHeight + "px";
      div.style.background =
        "url(" + can.toDataURL("image/png") + ") left top repeat";
      container.appendChild(div); // 追加到页面
    }, 300);
  })(binding.value.text, el, binding.value.id, binding.value.font);
});

然后在main.js引入import "@/api/file/watermark";
使用如下:

waterMark 是你动态传入的水印值

参考地址:https://www.cnblogs.com/zhaomeizi/p/13577701.html

如果打包运行出现 ***worker.js 404 错误,
在这里插入图片描述

解决方案:
找到该目录 node_modules/worker-loader/dist/index.js
修改

const filename = _loaderUtils2.default.interpolateName(this, options.name || '[hash].worker.js', {
	    context: options.context || this.rootContext || this.options.context,
	    regExp: options.regExp
  	});

修改后

const filename = _loaderUtils2.default.interpolateName(this, options.name || 'static/js/[hash].worker.js', {
	   	 context: options.context || this.rootContext || this.options.context,
   		regExp: options.regExp
  	});

成功解决

20211026处理打印乱码
官方提供的解决方案:https://github.com/FranckFreiburger/vue-pdf/pull/130/commits/253f6186ff0676abf9277786087dda8d95dd8ea7
但是我按照这个修改后会出现多的空白页,也不能直接复制过来使用,所幸看到一个老哥也有过同样问题,他贴出来了修改后的代码,复制过来直接使用,没问题了

https://blog.csdn.net/qq_42249552/article/details/112860593

修改后的代码

import {PDFLinkService} from 'pdfjs-dist/es5/web/pdf_viewer';
 
var pendingOperation = Promise.resolve();
 
export default function (PDFJS) {
 
  function isPDFDocumentLoadingTask(obj) {
 
    return typeof (obj) === 'object' && obj !== null && obj.__PDFDocumentLoadingTask === true;
    // or: return obj.constructor.name === 'PDFDocumentLoadingTask';
  }
 
  function createLoadingTask(src, options) {
 
    var source;
    if (typeof (src) === 'string')
      source = {url: src};
    else if (src instanceof Uint8Array)
      source = {data: src};
    else if (typeof (src) === 'object' && src !== null)
      source = Object.assign({}, src);
    else
      throw new TypeError('invalid src type');
 
    // source.verbosity = PDFJS.VerbosityLevel.INFOS;
    // source.pdfBug = true;
    // source.stopAtErrors = true;
 
    if (options && options.withCredentials)
      source.withCredentials = options.withCredentials;
 
    var loadingTask = PDFJS.getDocument(source);
    loadingTask.__PDFDocumentLoadingTask = true; // since PDFDocumentLoadingTask is not public
 
    if (options && options.onPassword)
      loadingTask.onPassword = options.onPassword;
 
    if (options && options.onProgress)
      loadingTask.onProgress = options.onProgress;
 
    return loadingTask;
  }
 
 
  function PDFJSWrapper(canvasElt, annotationLayerElt, emitEvent) {
 
    var pdfDoc = null;
    var pdfPage = null;
    var pdfRender = null;
    var canceling = false;
 
    canvasElt.getContext('2d').save();
 
    function clearCanvas() {
 
      canvasElt.getContext('2d').clearRect(0, 0, canvasElt.width, canvasElt.height);
    }
 
    function clearAnnotations() {
 
      while (annotationLayerElt.firstChild)
        annotationLayerElt.removeChild(annotationLayerElt.firstChild);
    }
 
    this.destroy = function () {
 
      if (pdfDoc === null)
        return;
 
      // Aborts all network requests and destroys worker.
      pendingOperation = pdfDoc.destroy();
      pdfDoc = null;
    }
 
    this.getResolutionScale = function () {
 
      return canvasElt.offsetWidth / canvasElt.width;
    }
 
    this.printPage = function (dpi, pageNumberOnly) {
 
      if (pdfPage === null)
        return;
 
      // 1in == 72pt
      // 1in == 96px
      var PRINT_RESOLUTION = dpi === undefined ? 150 : dpi;
      var PRINT_UNITS = PRINT_RESOLUTION / 72.0;
      var CSS_UNITS = 96.0 / 72.0;
 
      var printContainerElement = document.createElement('div');
      printContainerElement.setAttribute('id', 'print-container')
 
      function removePrintContainer() {
 
        printContainerElement.parentNode.removeChild(printContainerElement);
      }
 
      new Promise(function (resolve, reject) {
 
        printContainerElement.frameBorder = '0';
        printContainerElement.scrolling = 'no';
        printContainerElement.width = '0px;'
        printContainerElement.height = '0px;'
        printContainerElement.style.cssText = 'position: absolute; top: 0; left: 0';
 
        window.document.body.appendChild(printContainerElement);
        resolve(window);
      })
        .then(function (win) {
 
          win.document.title = '';
 
          return pdfDoc.getPage(1)
            .then(function (page) {
 
              var viewport = page.getViewport({scale: 1});
              win.document.head.appendChild(win.document.createElement('style')).textContent =
                '@supports ((size:A4) and (size:1pt 1pt)) {' +
                '.print-canvas { display: none }' +
                '@media print {' +
                '* { margin: 0 ;padding: 0}' +
                '@page { margin: 3mm; size: ' + ((viewport.width * PRINT_UNITS) / CSS_UNITS) + 'pt ' + ((viewport.height * PRINT_UNITS) / CSS_UNITS) + 'pt; }' +
                '.print-canvas { page-break-before: avoid; page-break-after: always; page-break-inside: avoid; display: block }' +
                'body > *:not(#print-container) { display: none; }' +
                '}'
              return win;
            })
        })
        .then(function (win) {
 
          var allPages = [];
 
          for (var pageNumber = 1; pageNumber <= pdfDoc.numPages; ++pageNumber) {
 
            if (pageNumberOnly !== undefined && pageNumberOnly.indexOf(pageNumber) === -1)
              continue;
 
            allPages.push(
              pdfDoc.getPage(pageNumber)
                .then(function (page) {
 
 
                  var viewport = page.getViewport({scale: 1});
 
                  var printCanvasElt = printContainerElement.appendChild(win.document.createElement('canvas'));
                  printCanvasElt.setAttribute('class', 'print-canvas')
                  printCanvasElt.width = (viewport.width * PRINT_UNITS);
                  printCanvasElt.height = (viewport.height * PRINT_UNITS);
 
                  return page.render({
                    canvasContext: printCanvasElt.getContext('2d'),
                    transform: [ // Additional transform, applied just before viewport transform.
                      PRINT_UNITS, 0, 0,
                      PRINT_UNITS, 0, 0
                    ],
                    viewport: viewport,
                    intent: 'print'
                  }).promise;
                })
            );
          }
 
          Promise.all(allPages)
            .then(function () {
              win.focus(); // Required for IE
              if (win.document.queryCommandSupported('print')) {
                win.document.execCommand('print', false, false);
              } else {
                win.print();
              }
              removePrintContainer();
            })
            .catch(function (err) {
 
              removePrintContainer();
              emitEvent('error', err);
            })
        })
    }
 
    this.renderPage = function (rotate) {
      if (pdfRender !== null) {
 
        if (canceling)
          return;
        canceling = true;
        pdfRender.cancel();
        return;
      }
 
      if (pdfPage === null)
        return;
 
      var pageRotate = (pdfPage.rotate === undefined ? 0 : pdfPage.rotate) + (rotate === undefined ? 0 : rotate);
 
      var scale = canvasElt.offsetWidth / pdfPage.getViewport({scale: 1}).width * (window.devicePixelRatio || 1);
      var viewport = pdfPage.getViewport({scale: scale, rotation: pageRotate});
 
      emitEvent('page-size', viewport.width, viewport.height, scale);
 
      canvasElt.width = viewport.width;
      canvasElt.height = viewport.height;
 
      pdfRender = pdfPage.render({
        canvasContext: canvasElt.getContext('2d'),
        viewport: viewport
      });
 
      annotationLayerElt.style.visibility = 'hidden';
      clearAnnotations();
 
      var viewer = {
        scrollPageIntoView: function (params) {
          emitEvent('link-clicked', params.pageNumber)
        },
      };
 
      var linkService = new PDFLinkService();
      linkService.setDocument(pdfDoc);
      linkService.setViewer(viewer);
 
      pendingOperation = pendingOperation.then(function () {
 
        var getAnnotationsOperation =
          pdfPage.getAnnotations({intent: 'display'})
            .then(function (annotations) {
 
              PDFJS.AnnotationLayer.render({
                viewport: viewport.clone({dontFlip: true}),
                div: annotationLayerElt,
                annotations: annotations,
                page: pdfPage,
                linkService: linkService,
                renderInteractiveForms: false
              });
            });
 
        var pdfRenderOperation =
          pdfRender.promise
            .then(function () {
 
              annotationLayerElt.style.visibility = '';
              canceling = false;
              pdfRender = null;
            })
            .catch(function (err) {
 
              pdfRender = null;
              if (err instanceof PDFJS.RenderingCancelledException) {
 
                canceling = false;
                this.renderPage(rotate);
                return;
              }
              emitEvent('error', err);
            }.bind(this))
 
        return Promise.all([getAnnotationsOperation, pdfRenderOperation]);
      }.bind(this));
    }
 
 
    this.forEachPage = function (pageCallback) {
 
      var numPages = pdfDoc.numPages;
 
      (function next(pageNum) {
 
        pdfDoc.getPage(pageNum)
          .then(pageCallback)
          .then(function () {
 
            if (++pageNum <= numPages)
              next(pageNum);
          })
      })(1);
    }
 
 
    this.loadPage = function (pageNumber, rotate) {
 
      pdfPage = null;
 
      if (pdfDoc === null)
        return;
 
      pendingOperation = pendingOperation.then(function () {
 
        return pdfDoc.getPage(pageNumber);
      })
        .then(function (page) {
 
          pdfPage = page;
          this.renderPage(rotate);
          emitEvent('page-loaded', page.pageNumber);
        }.bind(this))
        .catch(function (err) {
 
          clearCanvas();
          clearAnnotations();
          emitEvent('error', err);
        });
    }
 
    this.loadDocument = function (src) {
 
      pdfDoc = null;
      pdfPage = null;
 
      emitEvent('num-pages', undefined);
 
      if (!src) {
 
        canvasElt.removeAttribute('width');
        canvasElt.removeAttribute('height');
        clearAnnotations();
        return;
      }
 
      // wait for pending operation ends
      pendingOperation = pendingOperation.then(function () {
 
        var loadingTask;
        if (isPDFDocumentLoadingTask(src)) {
 
          if (src.destroyed) {
 
            emitEvent('error', new Error('loadingTask has been destroyed'));
            return
          }
 
          loadingTask = src;
        } else {
 
          loadingTask = createLoadingTask(src, {
            onPassword: function (updatePassword, reason) {
 
              var reasonStr;
              switch (reason) {
                case PDFJS.PasswordResponses.NEED_PASSWORD:
                  reasonStr = 'NEED_PASSWORD';
                  break;
                case PDFJS.PasswordResponses.INCORRECT_PASSWORD:
                  reasonStr = 'INCORRECT_PASSWORD';
                  break;
              }
              emitEvent('password', updatePassword, reasonStr);
            },
            onProgress: function (status) {
 
              var ratio = status.loaded / status.total;
              emitEvent('progress', Math.min(ratio, 1));
            }
          });
        }
 
        return loadingTask.promise;
      })
        .then(function (pdf) {
 
          pdfDoc = pdf;
          emitEvent('num-pages', pdf.numPages);
          emitEvent('loaded');
        })
        .catch(function (err) {
 
          clearCanvas();
          clearAnnotations();
          emitEvent('error', err);
        })
    }
 
    annotationLayerElt.style.transformOrigin = '0 0';
  }
 
  return {
    createLoadingTask: createLoadingTask,
    PDFJSWrapper: PDFJSWrapper,
  }
}

2022-2-16 补更 涉及到打印问题了,火狐这边很难搞,最终选定pdf.js,由于火狐版本比较低,所以选择使用2.0.x版本的pdf.js去使用,感觉效果比较赞。
官网地址:http://mozilla.github.io/pdf.js/ 老版本自己找找吧,下载完解压后如下
vue 在线阅读PDF_第2张图片
把这三个文件放到public下面
vue 在线阅读PDF_第3张图片

页面使用: