前端使用vue-pdf、pdf-lib 给pdf添加水印,并预览与下载
- 效果预览
- 使用第三方插件
- 安装依赖插件
- import 导入依赖
- 预览添加水印的pdf
- 下载添加水印的pdf
- 预览及下载总结
- 完整代码
效果预览
使用第三方插件
安装依赖插件
npm i vue-pdf --save
npm i pdf-lib --save
npm 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 的容器增加一层水印遮罩层
// 处理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(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 --save
npm install --save @pdf-lib/fontkit
npm 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>