前端使用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>