Vue使用pdf-lib为文件流添加水印并预览

这篇具有很好参考价值的文章主要介绍了Vue使用pdf-lib为文件流添加水印并预览。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

之前也写过两篇预览pdf的,但是没有加水印,这是链接:Vue使用vue-pdf实现PDF文件预览,使用pdfobject预览pdf。这次项目中又要预览pdf了,要求还要加水印,做的时候又发现了一种预览pdf的方式,这种方式我觉的更好一些,并且还有个要求就是添加水印,当然水印后端也是可以加的,但是后端说了一堆...反正就是要让前端做,在我看来就是借口、不想做,最近也不忙,那我就给他搞出来好了。下面来介绍一下 

首先预览pdf就很简单了,我们只需要通过window.URL.createObjectURL(new Blob(file))转为一个路径fileSrc后,再通过window.open(fileSrc)就可以了,window.open方法第二个参数默认就是打开一个新页签,这样就可以直接预览了,很方便!就是下面这样子:

Vue使用pdf-lib为文件流添加水印并预览

并且右上角自动给我们提供了下载、打印等功能。 

但是要加上水印的话,可能会稍微复杂一点点,我也百度找了好多,发现好多都是在项目里直接预览的,也就是在当前页面或者一个div有个容器用来专门预览pdf的,然后水印的话也是appendChild到容器div中进行的。这不是我想要的,并且也跟我现在预览的方式不一样,所以我的思路就是如何给文件的那个二进制blob流上加上水印,这样预览的时候也是用这个文件流,以后不想预览了、直接下载也要水印也是很方便的。找来找去找到了pdf-lib库,然后就去https://www.npmjs.com/package/pdf-lib这里去看了下使用示例,看了两个例子,发现好像这个很合适哦,终于一波操作拿下了,这就是我想要的。

我这里添加水印共三种方式,第一种就是可以直接传入文本,将文本添加进去作为水印 ;第二种是将图片的ArrayBuffer传递进去,将图片作为水印;因为第一种方式直接传文本只能传英文,我传入汉字就报错了,npm官网好像也有写,这是不可避免的,所以才有了第三种方式,就是也是传入文本,不过我们通过canvas画出来,然后通过toDataURL转为base64路径,然后再通过XHR去加载该图片拿到图片的Blob,再调用Blob的arrayBuffer方法拿到buffer传递进去作为水印,其实第三种和第二种都是图片的形式,第三种会更灵活一些。下面上代码

1. 安装 

npm i pdf-lib

2. 引入 

//我的需求里只用到这么多就够了,其他的按需引入
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';

3. 添加水印使用 

 3.1 添加文本水印

import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';

// This should be a Uint8Array or ArrayBuffer
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const existingPdfBytes = ...

// Load a PDFDocument from the existing PDF bytes
const pdfDoc = await PDFDocument.load(existingPdfBytes)

// Embed the Helvetica font
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica)

// Get the first page of the document
const pages = pdfDoc.getPages()
const firstPage = pages[0]

// Get the width and height of the first page
const { width, height } = firstPage.getSize()

// Draw a string of text diagonally across the first page
firstPage.drawText('This text was added with JavaScript!', {
  x: 5,
  y: height / 2 + 300,
  size: 50,
  font: helveticaFont,
  color: rgb(0.95, 0.1, 0.1),
  rotate: degrees(-45),
})


// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc.save()

// For example, `pdfBytes` can be:
//   • Written to a file in Node
//   • Downloaded from the browser
//   • Rendered in an <iframe>

3.2 添加图片文本 

import { PDFDocument } from 'pdf-lib'

// These should be Uint8Arrays or ArrayBuffers
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const jpgImageBytes = ...
const pngImageBytes = ...

// Create a new PDFDocument
const pdfDoc = await PDFDocument.create()

// Embed the JPG image bytes and PNG image bytes
const jpgImage = await pdfDoc.embedJpg(jpgImageBytes)
const pngImage = await pdfDoc.embedPng(pngImageBytes)

// Get the width/height of the JPG image scaled down to 25% of its original size
const jpgDims = jpgImage.scale(0.25)

// Get the width/height of the PNG image scaled down to 50% of its original size
const pngDims = pngImage.scale(0.5)

// Add a blank page to the document
const page = pdfDoc.addPage()

// Draw the JPG image in the center of the page
page.drawImage(jpgImage, {
  x: page.getWidth() / 2 - jpgDims.width / 2,
  y: page.getHeight() / 2 - jpgDims.height / 2,
  width: jpgDims.width,
  height: jpgDims.height,
})

// Draw the PNG image near the lower right corner of the JPG image
page.drawImage(pngImage, {
  x: page.getWidth() / 2 - pngDims.width / 2 + 75,
  y: page.getHeight() / 2 - pngDims.height,
  width: pngDims.width,
  height: pngDims.height,
})

// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc.save()

// For example, `pdfBytes` can be:
//   • Written to a file in Node
//   • Downloaded from the browser
//   • Rendered in an <iframe>

canvas那个也是用的这个这个通过图片添加水印 

上面这些都是官网上给的一些示例,我当时看到上面这两个例子,灵感瞬间就来了,然后测试,测试成功没问题,就开始整理代码,封装。结合自己的业务需求和可以复用通用的思想进行封装。下面贴一下最终的成功

3.3 封装previewPdf.js

import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
/**
 * 浏览器打开新页签预览pdf
 * blob(必选): pdf文件信息(Blob对象)【Blob】
 * docTitle(可选): 浏览器打开新页签的title  【String】
 * isAddWatermark(可选,默认为false): 是否需要添加水印 【Boolean】
 * watermark(必选):水印信息 【Object: { type: string, text: string, image:{ bytes: ArrayBuffer, imageType: string } }】
 * watermark.type(可选):类型 可选值:text、image、canvas
 * watermark.text(watermark.type为image时不填,否则必填):水印文本。注意:如果watermark.type值为text,text取值仅支持拉丁字母中的218个字符。详见:https://www.npmjs.com/package/pdf-lib
 * watermark.image(watermark.type为image时必填,否则不填):水印图片
 * watermark.image.bytes:图片ArrayBuffer
 * watermark.image.imageType:图片类型。可选值:png、jpg
 * Edit By WFT
 */
export default class PreviewPdf {
  constructor({ blob, docTitle, isAddWatermark = false, watermark: { type = 'text', text = 'WFT', image } }) {
    const _self = this
    if(!blob) {
      return console.error('[PDF Blob Is a required parameter]')
    }
    if(!isAddWatermark) { // 不添加水印
      _self.preView(blob, docTitle)
    } else {
      let bytes,imageType
      if(type == 'image') {
        if(!image) {
          return console.error('["image" Is a required parameter]')
        }
        bytes = image.bytes
        imageType = image.imageType
      }
      const map = {
        'text': _self.addTextWatermark.bind(_self),
        'image': _self.addImageWatermark.bind(_self),
        'canvas': _self.addCanvasWatermark.bind(_self)
      }
      blob.arrayBuffer().then(async buffer => {
        const existingPdfBytes = buffer
        const pdfDoc = await PDFDocument.load(existingPdfBytes)
        let params
        if(type == 'text') params = { pdfDoc, text, docTitle }
        if(type == 'image') params = { pdfDoc, bytes, imageType, docTitle }
        if(type == 'canvas') params = { pdfDoc, text, docTitle }
        map[type](params)
      }).catch(e => console.error('[Preview Pdf Error]:', e))
    }
  }

  // 添加 Text 水印
  async addTextWatermark({ pdfDoc, text, docTitle }) {
    // console.log(StandardFonts, 'StandardFonts-->>') // 字体
    const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica)
    const pages = pdfDoc.getPages()
    for(let i = 0; i < pages.length; i++) {
      let page = pages[i]
      let { width, height } = page.getSize()
      for(let i = 0; i < 6; i++) {
        for(let j = 0; j < 6; j++) {
          page.drawText(text, {
            x: j * 100,
            y: height / 5 + i * 100,
            size: 30,
            font: helveticaFont,
            color: rgb(0.95, 0.1, 0.1),
            opacity: 0.2,
            rotate: degrees(-35),
          })
        }
      }
    }
    // 序列化为字节
    const pdfBytes = await pdfDoc.save()
    this.preView(pdfBytes, docTitle)
  }

  // 添加 image 水印
  async addImageWatermark({ pdfDoc, bytes, imageType, docTitle }) {
    // 嵌入JPG图像字节和PNG图像字节
    let image
    const maps = {
      'jpg': pdfDoc.embedJpg.bind(pdfDoc),
      'png': pdfDoc.embedPng.bind(pdfDoc)
    }
    image = await maps[imageType](bytes)
    // 将JPG图像的宽度/高度缩小到原始大小的50%
    const dims = image.scale(0.5)
    const pages = pdfDoc.getPages()
    for(let i = 0; i < pages.length; i++) {
      let page = pages[i]
      let { width, height } = page.getSize()
      for(let i = 0; i < 6; i++) {
        for(let j = 0; j < 6; j++) {
          page.drawImage(image, {
            x: width / 5 - dims.width / 2 + j * 100,
            y: height / 5 - dims.height / 2 + i * 100,
            width: dims.width,
            height: dims.height,
            rotate: degrees(-35)
          })
        }
      }
    }
    // 序列化为字节
    const pdfBytes = await pdfDoc.save()
    this.preView(pdfBytes, docTitle)
  }

  // 添加 canvas 水印
  addCanvasWatermark({ pdfDoc, text, docTitle }) {
    // 旋转角度大小
    const rotateAngle = Math.PI / 6;

    // labels是要显示的水印文字,垂直排列
    let labels = new Array();
    labels.push(text);

    const pages = pdfDoc.getPages()

    const size = pages[0].getSize()

    let pageWidth = size.width
    let pageHeight = size.height

    let canvas = document.createElement('canvas');
    let canvasWidth = canvas.width = pageWidth;
    let canvasHeight = canvas.height = pageHeight;

    const context = canvas.getContext('2d');
    context.font = "15px Arial";

    // 先平移到画布中心
    context.translate(pageWidth / 2, pageHeight / 2 - 250);
    // 在绕画布逆方向旋转30度
    context.rotate(-rotateAngle);
    // 在还原画布的坐标中心
    context.translate(-pageWidth / 2, -pageHeight / 2);

    // 获取文本的最大长度
    let textWidth = Math.max(...labels.map(item => context.measureText(item).width));

    let lineHeight = 15, fontHeight = 12, positionY, i
    i = 0, positionY = 0
    while (positionY <= pageHeight) {
      positionY = positionY + lineHeight * 5
      i++
    }
    canvasWidth += Math.sin(rotateAngle) * (positionY + i * fontHeight) // 给canvas加上画布向左偏移的最大距离
    canvasHeight = 2 * canvasHeight
    for (positionY = 0, i = 0; positionY <= canvasHeight; positionY = positionY + lineHeight * 5) {
      // 进行画布偏移是为了让画布旋转之后水印能够左对齐;
      context.translate(-(Math.sin(rotateAngle) * (positionY + i * fontHeight)), 0);
      for (let positionX = 0; positionX < canvasWidth; positionX += 2 * textWidth) {
        let spacing = 0;
        labels.forEach(item => {
          context.fillText(item, positionX, positionY + spacing);        
          context.fillStyle = 'rgba(187, 187, 187, .8)'; // 字体颜色
          spacing = spacing + lineHeight;
        })
      }
      context.translate(Math.sin(rotateAngle) * (positionY + i * fontHeight), 0);
      context.restore();
      i++
    }
    // 图片的base64编码路径
    let dataUrl = canvas.toDataURL('image/png');
    // 使用Xhr请求获取图片Blob
    let xhr = new XMLHttpRequest();
    xhr.open("get", dataUrl, true);
    xhr.responseType = "blob";
    xhr.onload = res => {
      const imgBlob = res.target.response
      // 获取Blob图片Buffer
      imgBlob.arrayBuffer().then(async buffer => {
        const pngImage = await pdfDoc.embedPng(buffer)
        for(let i = 0; i < pages.length; i++) {
          pages[i].drawImage(pngImage)
        }
        // 序列化为字节
        const pdfBytes = await pdfDoc.save()
        this.preView(pdfBytes, docTitle)
      })
    }
    xhr.send();
  }

  // 预览
  preView(stream, docTitle) {
    const URL = window.URL || window.webkitURL;
    const href = URL.createObjectURL(new Blob([stream], { type: 'application/pdf;charset=utf-8' }))
    const wo = window.open(href)
    // 设置新打开的页签 document title
    let timer = setInterval(() => {
      if(wo.closed) {
        clearInterval(timer)
      } else {
        wo.document.title = docTitle
      }
    }, 500)
  }
}

3.4 调用使用 

我这里将上面文件放在src/utils下 

3.4.1  预览(添加文本水印)

代码: 

// 引入
import PreviewPdf from '@/utils/previewPdf'

// script
// 实例化进行添加水印 并预览
// file.raw 是要预览的pdf文件流 Blob
new PreviewPdf({
  blob: file.raw,
  docTitle: 'window.open docTitle',
  isAddWatermark: true, // 是否需要添加水印
  watermark: { // watermark必填 里面可以不填
    type: 'text',
    text: 'WFT'
  }
})

效果:

Vue使用pdf-lib为文件流添加水印并预览

3.4.2 预览(添加图片水印) 

 代码:

// 引入
import PreviewPdf from '@/utils/previewPdf'

// script
const watermarkImage = require('@/assets/img/watermark.png') // 水印图片
let xhr = new XMLHttpRequest();
xhr.open("get", watermarkImage, true);
xhr.responseType = "blob";
xhr.onload = function (res) {
  const imgBlob = res.target.response // 水印图片的Blob流
  imgBlob.arrayBuffer().then(buffer => { //get arraybuffer
    // 添加水印 预览
    new PreviewPdf({
      blob: file.raw,
      docTitle: file.name,
      isAddWatermark: true,
      watermark: {
        type: 'image',
        image: {
          bytes: buffer,
          imageType: 'png'
        }
      }
    })
  })
}
xhr.send();

效果:

Vue使用pdf-lib为文件流添加水印并预览

3.4.3 预览(添加文本canvas绘制水印) 

 代码:

// 引入
import PreviewPdf from '@/utils/previewPdf'

// script
new PreviewPdf({
  blob: file.raw,
  docTitle: file.name,
  isAddWatermark: true,
  watermark: {
    type: 'canvas',
    text: 'WFT-CANVAS'
  }
})

效果: 

Vue使用pdf-lib为文件流添加水印并预览

因为有些样式调的不太好,就我目前写的我更偏向使用canvas这个,当然都是可以使用的,样式都是可以调整的。 

注意:里面的属性 isAddWatermark 设置为false或者不传该字段将不添加水印,还有watermark这个字段是必须的,穿个空对象也行像watermark:{}这样,因为我上面类中构造方法将参数结构了,可以按需调整。

整体的封装使用就是上面这样子了, 希望可以帮到有需要的伙伴~~~


再给大家一个直接往某个dom元素里面添加水印的方法 

不传参数默认为整个body添加水印 文章来源地址https://www.toymoban.com/news/detail-434104.html

function waterMark(text = 'WFT', dom = document.body) {
  if (document.getElementById('waterMark')) return
  // 旋转角度大小
  var rotateAngle = Math.PI / 6;

  // labels是要显示的水印文字,垂直排列
  var labels = new Array();
  labels.push(text);

  let pageWidth = dom.clientWidth
  let pageHeight = dom.clientHeight

  let canvas = document.createElement('canvas');
  let canvasWidth = canvas.width = pageWidth;
  let canvasHeight = canvas.height = pageHeight;

  var context = canvas.getContext('2d');
  context.font = "15px Arial";

  // 先平移到画布中心
  context.translate(pageWidth / 2, pageHeight / 2 - 250);
  // 在绕画布逆方向旋转30度
  context.rotate(-rotateAngle);
  // 在还原画布的坐标中心
  context.translate(-pageWidth / 2, -pageHeight / 2);

  // 获取文本的最大长度
  let textWidth = Math.max(...labels.map(item => context.measureText(item).width));

  let lineHeight = 15, fontHeight = 12, positionY, i
  i = 0, positionY = 0
  while (positionY <= pageHeight) {
    positionY = positionY + lineHeight * 5
    i++
  }
  canvasWidth += Math.sin(rotateAngle) * (positionY + i * fontHeight) // 给canvas加上画布向左偏移的最大距离
  canvasHeight = 2 * canvasHeight
  for (positionY = 0, i = 0; positionY <= canvasHeight; positionY = positionY + lineHeight * 5) {
    // 进行画布偏移是为了让画布旋转之后水印能够左对齐;
    context.translate(-(Math.sin(rotateAngle) * (positionY + i * fontHeight)), 0);
    for (let positionX = 0; positionX < canvasWidth; positionX += 2 * textWidth) {
      let spacing = 0;
      labels.forEach(item => {
        context.fillText(item, positionX, positionY + spacing);
        spacing = spacing + lineHeight;
      })
    }
    context.translate(Math.sin(rotateAngle) * (positionY + i * fontHeight), 0);
    context.restore();
    i++
  }
  let dataUrl = canvas.toDataURL('image/png');
  let waterMarkPage = document.createElement('div');
  waterMarkPage.id = "waterMark"
  let style = waterMarkPage.style;
  style.position = 'fixed';
  style.overflow = "hidden";
  style.left = 0;
  style.top = 0;
  style.opacity = '0.4';
  style.background = "url(" + dataUrl + ")";
  style.zIndex = 999;
  style.pointerEvents = "none";

  style.width = '100%';
  style.height = '100vh';
  dom.appendChild(waterMarkPage);
}

到了这里,关于Vue使用pdf-lib为文件流添加水印并预览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue前端预览pdf并加水印、ofd文件,控制打印、下载、另存,vue-pdf的使用方法以及在开发中所踩过的坑合集

    根据公司的实际项目需求,要求实现对pdf和ofd文件的预览,并且需要限制用户是否可以下载、打印、另存pdf、ofd文件,如果该用户可以打印、下载需要控制每个用户的下载次数以及可打印的次数。正常的预览pdf很简单,直接调用浏览器的预览就可以而且功能也比较全,但是一

    2024年02月16日
    浏览(131)
  • Aspose.Pdf使用教程:在PDF文件中添加水印

    Aspose.PDF  是一款高级PDF处理API,可以在跨平台应用程序中轻松生成,修改,转换,呈现,保护和打印文档。无需使用Adobe Acrobat。此外,API提供压缩选项,表创建和处理,图形和图像功能,广泛的超链接功能,图章和水印任务,扩展的安全控件和自定义字体处理。本文将为你

    2024年02月01日
    浏览(52)
  • 【Vue】vue2使用pdfjs预览pdf文件,在线预览方式一,pdfjs文件包打开新窗口预览pdf文件

    【Vue】vue2预览显示quill富文本内容,vue-quill-editor回显页面,v-html回显富文本内容 【Vue】vue2项目使用swiper轮播图2023年8月21日实战保姆级教程 【Vue】vue2使用pdfjs预览pdf文件,在线预览方式一,pdfjs文件包打开新窗口预览pdf文件 提示:这里可以添加本文要记录的大概内容: vue

    2024年02月07日
    浏览(39)
  • Vue中使用pdf.js实现在线预览pdf文件流

    以下是在Vue中使用pdf.js实现在线预览pdf文件流的步骤: 在需要使用的组件中,使用以下代码引入pdf.js: 使用pdf.js的 getDocument() 方法加载pdf文件流。可以将文件流作为Blob对象传递给该方法。例如,可以使用axios从服务器获取pdf文件流: 在 loadPdf() 方法中,使用 getDocument() 方法

    2024年02月09日
    浏览(47)
  • java实现pdf文件添加水印,下载到浏览器

    添加itextpdf依赖 根据需求,不需要指定路径可以删除对应的输出流 效果如下:代码中的相对路径在src平级目录下,test.pdf是PdfStamper里面fileOutputStream生成的,test1.pdf是fos生成的 浏览器下载的如下: 生成的pdf内容如下(红框里面是pdf原来的内容,可以自己调整代码中注释掉的设

    2024年02月05日
    浏览(46)
  • 使用PyMuPDF添加PDF水印

    使用Python添加PDF水印的博客文章。 C:pythoncodenewpdfwatermark.py 在日常工作中,我们经常需要对PDF文件进行处理。其中一项常见的需求是向PDF文件添加水印,以保护文件的版权或标识文件的来源。本文将介绍如何使用Python编程语言和PyMuPDF库在PDF文件中添加水印。 在开始之前,确

    2024年02月11日
    浏览(59)
  • 使用itext7为pdf文档添加水印

    iText7是一款功能强大的开源PDF处理库,用于创建、编辑和处理PDF文档。相比于iTextSharp,iText7具有更先进的功能和更好的性能。 添加水印是iText7的一个常见应用场景。水印可以用于保护文档的版权,标识文档的状态或来源等。使用iText7添加水印可以通过以下步骤实现: 导入

    2024年04月22日
    浏览(23)
  • 【vue-pdf】PDF文件预览插件

    1 插件安装 vue-pdf GitHub:https://github.com/FranckFreiburger/vue-pdf#readme 参考文档:https://www.cnblogs.com/steamed-twisted-roll/p/9648255.html catch报错:vue-pdf组件报错vue-pdf Cannot read properties of undefined (reading ‘catch‘)_你看我像是会的样子吗?的博客-CSDN博客 2 代码示例 Example.01 超简单分页预览 E

    2024年02月14日
    浏览(34)
  • vue-pdf实现pdf文件在线预览

    在日常的工作中在线预览 PDF 文件的需求是很多的,下面介绍一下使用 vue-pdf 实现pdf文件在线预览 使用 npm 安装 vue-pdf npm install vue-pdf 使用 vue-pdf 显示 PDF 文件 此时页面中就会显示我们提供的 PDF 文件了,但是此时只显示了 PDF 文件的第一页 按页显示 PDF 文件 使用 vue-pdf 能满足

    2024年02月13日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包