vue项目中pdf.js动态生成水印,禁止下载,以及遇到的一些跨域问题

220 阅读4分钟

本周调研了pdf添加水印,需求是本项目中需要预览pdf,pdf上要加上水印,如果点击的时候后端会告诉我跳转到某一页。

  • 参考文章:

Vue项目中通过pdfjs实现PDF预览

PDF.js动态添加水印

# web项目引入PDF.js并添加水印禁止下载

pdf.js 跳到指定pdf页码以及实现高亮

按照以下思路

  • 首先看pdf.js的官网:mozilla.github.io/pdf.js/gett…
    如果要下载特定的版本访问:PDFjs 发布版本:github.com/mozilla/pdf… image.png 在发布版本中找到2.2.228, 我的项目中用的版本是2.2.228,下载完在vscode打开这个项目,下载的文件包含两个文件夹build和web文件夹,build文件夹是PDF.js核心文件,web下为展示PDF的视图界面viewer.html。选择viewer.html右击Open with Live Server就可以看到效果了。

  • 一些报错: pdf.js不支持跨域请求,所以会报错:file origin does not match viewer’s,简单粗暴的方法:需要在viewer.js中注释,代码如下:

// if (origin !== viewerOrigin && protocol !== 'blob:') { 
//    throw new Error('file origin does not match viewer\'s'); 
// } 
  • 在项目中预览pdf代码如下:
 const path = encodeURIComponent(后端返回的pdf地址)
 const preffix =process.env.NODE_ENV !== 'development' ? `/${process.env.VUE_APP_BASE_URL}` : ''
window.open(`${preffix}/pdf/web/viewer.html?file=` + path) // path是文件的全路径地址
  • 禁用一些多余的功能,(最主要是的是禁用下载,防止用户下载下来不带水印的原文件)
  1. 找到键盘组合按键打印的位置:
var hasAttachEvent = !!document.attachEvent;
window.addEventListener('keydown', function (event) {
  if (event.keyCode === 80 && (event.ctrlKey || event.metaKey) && !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
    window.print();

    if (hasAttachEvent) {
      return;
    }

    event.preventDefault();

    if (event.stopImmediatePropagation) {
      event.stopImmediatePropagation();
    } else {
      event.stopPropagation();
    }

    return;
  }
}, true);
  1. 改为如下内容:
var hasAttachEvent = !!document.attachEvent;
window.addEventListener('keydown', function (event) {
  if ((event.keyCode === 80 || event.keyCode === 83) && (event.ctrlKey || event.metaKey) && !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
    //window.print();

    if (hasAttachEvent) {
      return;
    }

    event.preventDefault();

    if (event.stopImmediatePropagation) {
      event.stopImmediatePropagation();
    } else {
      event.stopPropagation();
    }

    return;
  }
}, false);
  • 两种水印效果一种是一页生成一个大的水印一种是一页上面是密密麻麻的水印: 第一种一页生成一个大的水印的效果: image.png
  • 实现代码:
  1. 在viewer.js中找到如下内容,位置大概在11973行前后:
if (this.textLayerMode !== _ui_utils.TextLayerMode.DISABLE && this.textLayerFactory) {
   var textLayerDiv = document.createElement('div');
   textLayerDiv.className = 'textLayer';
   textLayerDiv.style.width = canvasWrapper.style.width;
   textLayerDiv.style.height = canvasWrapper.style.height;
   //---这里就是要插入水印的位置---
   if (this.annotationLayer && this.annotationLayer.div) {
      div.insertBefore(textLayerDiv, this.annotationLayer.div);
   } else {
      div.appendChild(textLayerDiv);
   }
   textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport, this.textLayerMode === _ui_utils.TextLayerMode.ENABLE_ENHANCE);
}
  1. 插入水印代码后内容如下:
if (this.textLayerMode !== _ui_utils.TextLayerMode.DISABLE && this.textLayerFactory) {
    var textLayerDiv = document.createElement('div');
    textLayerDiv.className = 'textLayer';
    textLayerDiv.style.width = canvasWrapper.style.width;
    textLayerDiv.style.height = canvasWrapper.style.height;
    //---------------------水印开始---------------------
    var cover = document.createElement('div');
    cover.className = "cover";
    cover.innerText = "内容保密,请勿复制或下载"; //这里就是水印内容,如果要按照不同的文件显示不同的水印,可参考pdf文件路径的传值方式,在viewer.jsp中head部位接收后台传值并在这里使用
    if (this.annotationLayer) {
        // annotationLayer needs to stay on top
        div.insertBefore(textLayerDiv, this.annotationLayer.div);
        div.appendChild(cover);
    } else {
        div.appendChild(textLayerDiv);
        div.appendChild(cover);
    }
    var coverEle = document.getElementsByClassName('cover'),size = 0,
        nowWidth = +canvasWrapper.style.width.split("p")[0],
        //714为100%时,每页的宽度。对比当前的宽度可以计算出页面变化后字体的数值
        size = 50 * nowWidth / 714 + "px";
    for(var i=0, len=coverEle.length; i<len; i++){
        coverEle[i].style.fontSize = size;
        coverEle[i].style.width = canvasWrapper.style.width;
        coverEle[i].style.height = canvasWrapper.style.height / 10;
    }
    //---------------------水印结束---------------------
    if (this.annotationLayer && this.annotationLayer.div) {
        div.insertBefore(textLayerDiv, this.annotationLayer.div);
    } else {
        div.appendChild(textLayerDiv);
    }

    textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport, this.textLayerMode === _ui_utils.TextLayerMode.ENABLE_ENHANCE);
}
  1. 最后在viewer.css文件开始位置添加水印的css样式完成水印显示:
/* 水印遮罩层 */
.cover{
  z-index: 100;
  position: absolute;
  top: 41%;
  left: 1%;
  transform: rotate(330deg);
  text-align: center;
  font-size: 310%;
  padding-left: 30px;
  letter-spacing: 18px;
  color:rgba(162, 162, 162, 0.4);
}
  • 第二种页生成密密麻麻水印的实现代码:
  1. 在viewer.js中同样找到:if (this.textLayerMode !== _ui_utils.TextLayerMode.DISABLE && this.textLayerFactory),在 this.textLayer = textLayer这个上面添加结束
 if (
                  this.textLayerMode !== _ui_utils.TextLayerMode.DISABLE &&
                  this.textLayerFactory
                ) {
                  var textLayerDiv = document.createElement('div')
                  textLayerDiv.className = 'textLayer'
                  textLayerDiv.style.width = canvasWrapper.style.width
                  textLayerDiv.style.height = canvasWrapper.style.height

                  // 开始开始复制---------------注意放的位置------------------------------------------
                  var cover = document.createElement('div')
                  cover.className = 'cover'
                  const userInfo = JSON.parse(localStorage.getItem('userInfo'))
                  var waterName = userInfo.nickname + '(' + userInfo.id + ')'
                  var defaultSettings = {
                    watermark_txt: waterName,
                    watermark_x: 0, //水印起始位置x轴坐标
                    watermark_y: 0, //水印起始位置Y轴坐标
                    watermark_rows: 30, //水印行数
                    watermark_cols: 30, //水印列数
                    watermark_x_space: 100, // 水印x轴间隔
                    watermark_y_space: 100, //水印y轴间隔
                    watermark_color: '#D8D8D8', // 'grey', //水印字体颜色
                    watermark_alpha: 1, // 0.4水印透明度
                    watermark_fontsize: '24px', //水印字体大小
                    watermark_font: '微软雅黑', //水印字体
                    watermark_width: 120, //水印宽度
                    watermark_height: 60, //水印长度
                    watermark_angle: 40 //水印倾斜度数
                  }
                  var oTemp = document.createDocumentFragment()

                  //获取页面最大宽度
                  var page_width = Math.max(document.body.scrollWidth, document.body.clientWidth)
                  //获取页面最大长度
                  var page_height = Math.max(document.body.scrollHeight, document.body.clientHeight)
                  //如果将水印列数设置为0,或水印列数设置过大,超过页面最大宽度,则重新计算水印列数和水印x轴间隔
                  if (
                    defaultSettings.watermark_cols == 0 ||
                    parseInt(
                      defaultSettings.watermark_x +
                        defaultSettings.watermark_width * defaultSettings.watermark_cols +
                        defaultSettings.watermark_x_space * (defaultSettings.watermark_cols - 1)
                    ) > page_width
                  ) {
                    defaultSettings.watermark_cols = parseInt(
                      (page_width -
                        defaultSettings.watermark_x +
                        defaultSettings.watermark_x_space) /
                        (defaultSettings.watermark_width + defaultSettings.watermark_x_space)
                    )
                    defaultSettings.watermark_x_space = parseInt(
                      (page_width -
                        defaultSettings.watermark_x -
                        defaultSettings.watermark_width * defaultSettings.watermark_cols) /
                        (defaultSettings.watermark_cols - 1)
                    )
                  }
                  //如果将水印行数设置为0,或水印行数设置过大,超过页面最大长度,则重新计算水印行数和水印y轴间隔
                  //这一段可以注释,因为可能会每页只加一半水印
                  if (
                    defaultSettings.watermark_rows == 0 ||
                    parseInt(
                      defaultSettings.watermark_y +
                        defaultSettings.watermark_height * defaultSettings.watermark_rows +
                        defaultSettings.watermark_y_space * (defaultSettings.watermark_rows - 1)
                    ) > page_height
                  ) {
                    defaultSettings.watermark_rows = parseInt(
                      (defaultSettings.watermark_y_space +
                        page_height -
                        defaultSettings.watermark_y) /
                        (defaultSettings.watermark_height + defaultSettings.watermark_y_space)
                    )
                    defaultSettings.watermark_y_space = parseInt(
                      (page_height -
                        defaultSettings.watermark_y -
                        defaultSettings.watermark_height * defaultSettings.watermark_rows) /
                        (defaultSettings.watermark_rows - 1)
                    )
                  }
                  var x
                  var y
                  for (var i = 0; i < defaultSettings.watermark_rows; i++) {
                    y =
                      defaultSettings.watermark_y +
                      (defaultSettings.watermark_y_space + defaultSettings.watermark_height) * i
                    for (var j = 0; j < defaultSettings.watermark_cols; j++) {
                      x =
                        defaultSettings.watermark_x +
                        (defaultSettings.watermark_width + defaultSettings.watermark_x_space) * j

                      var mask_div = document.createElement('div')
                      mask_div.id = 'mask_div' + i + j
                      mask_div.className = 'mask_div'
                      mask_div.appendChild(document.createTextNode(defaultSettings.watermark_txt))
                      //设置水印div倾斜显示
                      mask_div.style.webkitTransform =
                        'rotate(-' + defaultSettings.watermark_angle + 'deg)'
                      mask_div.style.MozTransform =
                        'rotate(-' + defaultSettings.watermark_angle + 'deg)'
                      mask_div.style.msTransform =
                        'rotate(-' + defaultSettings.watermark_angle + 'deg)'
                      mask_div.style.OTransform =
                        'rotate(-' + defaultSettings.watermark_angle + 'deg)'
                      mask_div.style.transform =
                        'rotate(-' + defaultSettings.watermark_angle + 'deg)'
                      mask_div.style.visibility = ''
                      mask_div.style.position = 'absolute'
                      //奇偶行错开,这样水印就不对齐,显的不呆板
                      if (i % 2 != 0) {
                        mask_div.style.left = x + 100 + 'px'
                      } else {
                        mask_div.style.left = x + 'px'
                      }
                      mask_div.style.top = y + 'px'
                      mask_div.style.overflow = 'hidden'
                      mask_div.style.opacity = defaultSettings.watermark_alpha
                      mask_div.style.fontSize = defaultSettings.watermark_fontsize
                      mask_div.style.fontFamily = defaultSettings.watermark_font
                      mask_div.style.color = defaultSettings.watermark_color
                      mask_div.style.textAlign = 'center'
                      mask_div.style.width = canvasWrapper.style.width + 'px'
                      mask_div.style.height = canvasWrapper.style.height + 'px'
                      mask_div.style.display = 'block'
                      oTemp.appendChild(mask_div)
                    }
                  }
                  cover.appendChild(oTemp)
                  // 结束

                  if (this.annotationLayer && this.annotationLayer.div) {
                    // annotationLayer needs to stay on top
                    div.insertBefore(textLayerDiv, this.annotationLayer.div)
                    // 这一段添加进来 开始
                    div.appendChild(cover)
                    // 这一段添加进来 结束
                  } else {
                    div.appendChild(textLayerDiv)
                    // 这一段添加进来 开始
                    div.appendChild(cover)
                    // 这一段添加进来 结束
                  }
                  textLayer = this.textLayerFactory.createTextLayerBuilder(
                    textLayerDiv,
                    this.id - 1,
                    this.viewport,
                    this.enhanceTextSelection
                  )
                }
                // 这一段添加进来 开始
                var cover = document.getElementsByClassName('cover')
                for (var i = 0, len = cover.length; i < len; i++) {
                  cover[i].style.width = canvasWrapper.style.width
                  cover[i].style.height = canvasWrapper.style.height
                }

2.在css里添加

.cover{
  position:absolute;
  top:0;
  overflow:hidden
}

注意pdf.js 跳到指定pdf页码以及实现高亮该方法中跳转页,如果是文件里只有一页会空白显示。要对方法进行小的判断改写很简单。