当前位置:首页 » 《随便一记》 » 正文

前端使用vue-pdf、pdf-lib、canvas 给PDF文件添加水印,并预览与下载

29 人参与  2024年02月07日 10:31  分类 : 《随便一记》  评论

点击全文阅读


前端使用vue-pdf、pdf-lib 给pdf添加水印,并预览与下载

效果预览使用第三方插件安装依赖插件import 导入依赖 预览添加水印的pdf下载添加水印的pdf预览及下载总结完整代码

效果预览

在这里插入图片描述

使用第三方插件

安装依赖插件

npm i vue-pdf --savenpm i pdf-lib --savenpm install --save @pdf-lib/fontkit  //为 pdf-lib 加载自定义字体的工具

import 导入依赖

import pdf from "vue-pdf";import { degrees, PDFDocument, rgb, StandardFonts } from "pdf-lib";import fontkit from "@pdf-lib/fontkit"; 

预览添加水印的pdf

setWatermarkContent() {  let ele = document.createElement("canvas");  ele.width = 250;  ele.height = 200;  let objmsg = {    canvas: ele,    fontText: "张三-2023-01-01",    fontSize: 20,    fontFamily: "microsoft yahei",    fontcolor: "#dadbdc", //字体颜色   默认 #dadbdc    rotate: 25, //旋转角度   数字类型    textAlign: "left", //水印文字居中方式:left center right  默认 left  };  this.createWaterMark(objmsg);  this.drawWaterMark(ele);},// 创建canvas水印图片createWaterMark({ canvas, fontText, fontFamily = "microsoft yahei", fontSize = 30, fontcolor = "#dadbdc", rotate = 30, textAlign = "left" }) {  let ctx = canvas.getContext("2d");  ctx.font = `${fontSize}px ${fontFamily}`;  ctx.rotate((-rotate * Math.PI) / 180);  ctx.fillStyle = fontcolor;  ctx.textAlign = textAlign;  ctx.textBaseline = "Middle";  ctx.fillText(fontText, canvas.width / 6, canvas.height / 2);},// 给pdf增加水印遮罩层drawWaterMark(ele) {  let div = document.createElement("div");  div.style.pointerEvents = "none";  div.style.top = "0";  div.style.left = "0px";  div.style.position = "absolute";  div.style.background = "url(" + ele.toDataURL("image/png") + ") left top repeat";  let width = document.getElementById("pdfBox").clientWidth || 700;  let height = document.getElementById("pdfBox").clientHeight || 700;  div.style.width = width + "px";  div.style.height = height + "px";  document.getElementById("myIframe").appendChild(div);},

下载添加水印的pdf

原理就是给显示pdf 的容器增加一层水印遮罩层

// 处理PDFasync downFile() {  /*2.获取pdf文件的arrarybuffer文件流  可请求后台接口返回的base64文件流,然后转成arrayBuffer类型  可访问前端项目中的本地文件,不能直接访问服务器链接文件,会有跨域问题*/  try {    // 1.通过url获取pdf文件的arrarybuffer文件流    const existingPdfBytes = await fetch(this.fileUrl).then((res) => res.arrayBuffer());    // 2.将arraybuffer数据转成pdf文档    const pdfDoc = await PDFDocument.load(existingPdfBytes);    // 3.1  内置字体(不支持中文), 如果水印中不包含中文可直接用内置字体(不支持中文)    // const fontkitFile = await pdfDoc.embedFont(StandardFonts.Helvetica);    // 3.2 自定义字体,如不需要使用自定义字体可以将这一段全部注释掉,也不用下载自定义字体文件和自定义字体工具fontkit    // 将自己下载好的.ttf文件放置项目中,然后访问文件路径(不支持访问本地文件)    const fontBytes = await fetch("/fonts/SourceHanSansCN-Normal.ttf").then((res) => res.arrayBuffer());    pdfDoc.registerFontkit(fontkit); // 自定义字体挂载、fontkit为自定义字体注册工具    const fontkitFile = await pdfDoc.embedFont(fontBytes);    //  4. 为每页pdf添加文字水印    const pages = pdfDoc.getPages();    for (let i = 0; i < pages.length; i++) {      const noPage = pages[i];      const { width, height } = noPage.getSize();      for (let i = 0; i < 10; i++) {        for (let j = 0; j < 3; j++) {          noPage.drawText("张三-2023-01-01", {            x: 230 * j,            y: (height / 4) * i,            size: 20,            font: fontkitFile, //字体(内置/自定义)            color: rgb(0.46, 0.53, 0.6),            rotate: degrees(45),            opacity: 0.3,          });        }      }    }    //5. 保存pdf文件的unit64Arrary文件流    const pdfBytes = await pdfDoc.save();    this.saveByteArray(this.waterFile.fileName + ".pdf", pdfBytes);  } catch (error) {    this.$message.warning("文件下载失败!");  }},// 下载文件saveByteArray(reportName, byte) {  var blob = new Blob([byte], { type: "application/pdf" });  var link = document.createElement("a");  link.href = window.URL.createObjectURL(blob);  var fileName = reportName;  link.download = fileName;  link.click();},

预览及下载总结

下载:

通过url获取pdf文件的arrarybuffer文件流将arraybuffer数据转成pdf文档添加水印字体(内置/自定义)为每页pdf添加文字水印保存pdf文件的unit64Arrary文件流

预览:

创建canvas容器(用于显示水印文字)创建水印canvas将水印canvas遮罩层定位到pdf容器中

完整代码

<template>  <div>    <div class="content">      <div id="myIframe" style="max-width: 700px; min-height: 550px; position: relative; margin: 0 auto">        <pdf id="pdfBox" :page="pageNum" :src="fileUrl" @progress="loadedRatio = $event" @num-pages="totalPages = $event"></pdf>      </div>      <el-button v-if="false" type="primary" @click="downFile" plain style="width: 300px">保存并下载pdf</el-button>    </div>    <span slot="footer" class="dialog-footer">      <div class="btnGroup" v-if="totalPages">        <div class="pageNum">{{ pageNum }} / {{ totalPages }}</div>        <el-button-group>          <el-button round plain type="primary" icon="el-icon-arrow-left" size="mini" @click="prePage">上一页</el-button>          <el-button round plain type="primary" size="mini" @click="nextPage">下一页<i class="el-icon-arrow-right el-icon--right"></i></el-button>        </el-button-group>      </div>    </span>  </div></template><script>/* npm i vue-pdf --savenpm install --save @pdf-lib/fontkitnpm i pdf-lib --save*/import pdf from "vue-pdf";import { degrees, PDFDocument, rgb, StandardFonts } from "pdf-lib";import fontkit from "@pdf-lib/fontkit"; //为 pdf-lib 加载自定义字体的工具export default {  components: {    pdf,  },  data() {    return {      pageNum: 1, //显示第一页      loadedRatio: 0, // 当前页面的加载进度,范围是0-1 ,等于1的时候代表当前页已经完全加载完成了      totalPages: 0, //pdf总页数      fileUrl:"XXXXX.pdf",    };  },  mounted() {    this.getPageNum();  },  methods: {   // 获取PDF总页数    getPageNum() {      let loadingTask = pdf.createLoadingTask(this.fileUrl);      loadingTask.promise        .then((pdf) => {          this.totalPages = pdf.numPages;          this.$nextTick(() => {            this.setWatermarkContent();          });        })        .catch((err) => {          this.$message.warning("pdf加载失败");        });    },    // 上一页    prePage() {      let page = this.pageNum;      page = page > 1 ? page - 1 : this.totalPages;      this.pageNum = page;      window.scrollTo(0, 0);    },    // 下一页    nextPage() {      let page = this.pageNum;      page = page < this.totalPages ? page + 1 : 1;      this.pageNum = page;      window.scrollTo(0, 0);    },    setWatermarkContent() {      // 1.创建canvas容器(用于显示水印文字)      let ele = document.createElement("canvas");      ele.width = 250;      ele.height = 200;      let objmsg = {        canvas: ele,        fontText: "张三-2023-01-01", // String        fontSize: 20,        fontFamily: "microsoft yahei",        fontcolor: "#dadbdc", //字体颜色   默认 #dadbdc        rotate: 25, //旋转角度   数字类型        textAlign: "left", //水印文字居中方式:left center right  默认 left      };      // 2.创建水印canvas      this.createWaterMark(objmsg);       // 2.将水印canvas遮罩层定位到pdf容器中      this.drawWaterMark(ele);    },    // 创建canvas水印图片    createWaterMark({ canvas, fontText, fontFamily = "microsoft yahei", fontSize = 30, fontcolor = "#dadbdc", rotate = 30, textAlign = "left" }) {      let ctx = canvas.getContext("2d");      ctx.font = `${fontSize}px ${fontFamily}`;      ctx.rotate((-rotate * Math.PI) / 180);      ctx.fillStyle = fontcolor;      ctx.textAlign = textAlign;      ctx.textBaseline = "Middle";      ctx.fillText(fontText, canvas.width / 6, canvas.height / 2);    },    // 给pdf增加水印遮罩层    drawWaterMark(ele) {      let div = document.createElement("div");      div.style.pointerEvents = "none";      div.style.top = "0";      div.style.left = "0px";      div.style.position = "absolute";      div.style.background = "url(" + ele.toDataURL("image/png") + ") left top repeat";      let width = document.getElementById("pdfBox").clientWidth || 700;      let height = document.getElementById("pdfBox").clientHeight || 700;      div.style.width = width + "px";      div.style.height = height + "px";      document.getElementById("myIframe").appendChild(div);    },    // PDF 下载    async downFile() {      /*2.获取pdf文件的arrarybuffer文件流       可请求后台接口返回的base64文件流,然后转成arrayBuffer类型       可访问前端项目中的本地文件,不能直接访问服务器链接文件,会有跨域问题*/      try {        // 1.通过url获取pdf文件的arrarybuffer文件流        const existingPdfBytes = await fetch(this.fileUrl).then((res) => res.arrayBuffer());        // 2.将arraybuffer数据转成pdf文档        const pdfDoc = await PDFDocument.load(existingPdfBytes);        // 3.1  内置字体(不支持中文), 如果水印中不包含中文可直接用内置字体(不支持中文)        // const fontkitFile = await pdfDoc.embedFont(StandardFonts.Helvetica);        // 3.2 自定义字体,如不需要使用自定义字体可以将这一段全部注释掉,也不用下载自定义字体文件和自定义字体工具fontkit        // 将自己下载好的.ttf文件放置项目中,然后访问文件路径(不支持访问本地文件)        const fontBytes = await fetch("/fonts/SourceHanSansCN-Normal.ttf").then((res) => res.arrayBuffer());        pdfDoc.registerFontkit(fontkit); // 自定义字体挂载、fontkit为自定义字体注册工具        const fontkitFile = await pdfDoc.embedFont(fontBytes);        //  4. 为每页pdf添加文字水印        const pages = pdfDoc.getPages();        for (let i = 0; i < pages.length; i++) {          const noPage = pages[i];          const { width, height } = noPage.getSize();          for (let i = 0; i < 10; i++) {            for (let j = 0; j < 3; j++) {              noPage.drawText('张三-2023-01-01', {                x: 230 * j,                y: (height / 4) * i,                size: 20,                font: fontkitFile, //字体(内置/自定义)                color: rgb(0.46, 0.53, 0.6),                rotate: degrees(45),                opacity: 0.3,              });            }          }        }        //5. 保存pdf文件的unit64Arrary文件流        const pdfBytes = await pdfDoc.save();        this.saveByteArray( "水印PDF.pdf", pdfBytes);      } catch (error) {        this.$message.warning("文件下载失败!");      }    },    // 下载文件    saveByteArray(fileName, byte) {      var blob = new Blob([byte], { type: "application/pdf" });      var link = document.createElement("a");      link.href = window.URL.createObjectURL(blob);      var fileName = reportName;      link.download = fileName;      link.click();    },  },};</script>

点击全文阅读


本文链接:http://zhangshiyu.com/post/67673.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1