使用pdf-lib实现pdf固定位置增加水印
功能描述
上传pdf文件后,前端对文件进行处理,要求输出的文件在每一页右上角固定位置增加编号。
前言
第一次用这个代码编写功能,再加上代码在虚拟机内无法粘贴出来,所以格式上有些错误请谅解。
关于pdf-lib
官网描述的很清楚,这里不赘述
官网入口 链接
https://pdf-lib.js.org/
实现逻辑
上传pdf => 转化为base64 => 调整pdf尺寸为标准A4尺寸 => 添加水印 => 调用下载pdf
实现难点
在接到需求后,搜索了很多pdf-lib的用法。实现增加水印功能并不困难。
我主要遇到了两个难点:
1、上传文件后直接使用pdf-lib处理报错
2、不同pdf尺寸不同,导致添加水印位置不固定
上传文件后直接使用pdf-lib处理报错
上传这里我是用的element的el-upload,在上传成功后使用pdf-lib的 PDFDocument类下的load方法载入文件进行处理。
可以看到官网对load方法入参格式规定如下:
我这里使用的第一种,将文件流转化为base64编码
httpRequest(data){
//调用转方法base64
this.getBase64(data.file).then((resBase64)=>{
//获取文件,不带data:application/pdf;base64,前缀
this.file = resBase64.split(",")[1];
// 在pdf中添加水印
this.getAddFile()
}).catch((err)=>{
console.log(err);
)
getBase64(file){
return new Promise((resolve,reject) => {
const reader =new FileReader();
let fileResult = "".
reader.readAsDataURL(file);
// 开始转码
reader.onload=()=>
fileResult = reader.result;
};
// 转码失败
reader.onerror = (error) => {
reject(error);
}
// 转码结束
reader.onloadend=()=>
resolve(fileResult);
};
})
}
对不同尺寸的pdf处理逻辑
因为在调试过程中,使用的是标准A4纸大小的pdf进行调试,所以在后面给不同pdf添加水印时发现问题。
在能给pdf添加水印后,尝试了给不同的pdf添加水印,结果发现只有标准A4纸水印位置是按照设定的位置进行添加,其他的pdf位置,缩放等都有问题。
下面说到的方法在官网都有详细的描述,包括一些方法在什么情况下调用都有说明。总之看官网会更详细,我这里只写了一写思路。
下面说一下处理逻辑,所有代码放在最后。
1、拿到上传的pdf的尺寸,与标准A4纸像素大小进行比较,获得缩放比例。注意,这里pdf的宽和高都要计算A4的比例,使用A4的宽高分别除对应pdf的宽高,取两个值中的最小值,这样才能保证缩放为标准尺寸后pdf内容不会丢失
const pageEle = pages[i]
const pageHeight = pageEle.getsize().height
const pagewidth= pageEle.getsize().width
// 计算缩放比例
let scale=1
let scaleX = Math.min(A4_WIDTH / pagewidth, 1);
let scaleY = Math.min(A4_HEIGHT/pageHeight, 1);
scale = Math.min(scaleX,scaleY)
2、根据上面的比例,使用setSize设置页面大小
//缩放页面大小
pageEle.setsize(pageEle.getwidth()*scale,pageEle.getHeight()*scale)
3、setSize不会改变内容缩放,所以还需使用scaleContent进行内容缩放
//缩放页面内容
pageEle.scaleContent(scale,scale);
4、在上面一系列操作后,本以为可以了,but,发现添加的位置还是不对,原因是添加水印时传的位置,还是按照缩放前的坐标轴原点添加的,所以还需要使用resetPosition进行页面位置重置
//重置页面位置,防止添加文字错位
pageEle.resetPosition()
一些未解决的问题
处理后如何传给后端
因为这个需求一开始是让前端来加水印,但是中间又说要直接让后端加,所有我现在只写到了这一步,后面文件处理成功后如何传给后端还没有研究
横版pdf处理问题
如果是横版的pdf,正常来说我们认为他的宽度是大于高度的。例如宽300,高200,是我们认为的横版pdf
但是使用getSize()获取的高度和宽度,宽度就是两个值中较小的那个,高度是较大的那个也就是300,宽度是200.
这样会导致我们无法判断他是横版还是竖版。
按照我写的这个逻辑,竖版处理完的pdf大概如下(黑色是pdf,红色是添加的水印)
横版的会这样,我们想要的是右上角,但是他会转成竖版再在右上角添加。因为刚才说的获取宽高的方法返回值的问题,我们也无法判断pdf的横竖,所以无法处理,后面没时间找到解决办法。
代码
<template>
<el-upload
:multiple="false"
accept=".pdf"
:action="url'
:show-file-list="false"
:http-request="httpRequest
<el-button size="mini" type="primary">点击上传</e1-button>
</el-upload>
</template>
<script>
import INTERFACE from "../../../../assets/interface/pjs/myIssue.js".
import { degrees, PDFDocument, rgb ,standardFonts} from 'pdf-lib';
export default {
data(){
return {
filelist:[],
file:"",
url:INTERFACE.uploadFile,
}
},
methods:{
handleRemove(file,fileList){
console.log(file, filelist);
},
handlePreview(file){
console.log(file);
},
handleExceed(files,filelist){
this.$message.warning(当前限制选择3个文件,本次选择了 ${files.length}个文件,共选择了 ${files.length +filelist.length} 个文件”);
},
beforeRemove(files,filelist){
this.$message.warning(当前限制选择3个文件,本次选择了 ${files.length}个文件,共选择了 ${files.length +filelist.length} 个文件”);
},
httpRequest(data){
//调用转方法base64
this.getBase64(data.file).then((resBase64)=>{
//获取文件,不带data:application/pdf;base64,前缀
this.file = resBase64.split(",")[1];
// 在pdf中添加水印
this.getAddFile()
}).catch((err)=>{
console.log(err);
)
getBase64(file){
return new Promise((resolve,reject) => {
const reader =new FileReader();
let fileResult = "".
reader.readAsDataURL(file);
// 开始转码
reader.onload=()=>
fileResult = reader.result;
};
// 转码失败
reader.onerror = (error) => {
reject(error);
}
// 转码结束
reader.onloadend=()=>
resolve(fileResult);
};
})
}
async getAddFile(){
// A4大小
const A4_WIDTH= 595.28;
const A4_HEIGHT = 841.89;
// 载入文件
const pdfDoc =await PDFDocument.load(this.file);
const pages = pdfDoc.getPages();
// 获取字体
const timesRomanFont = await pdfDoc.embedFont(Standardfonts.TimesRoman)
//遍历每页加文本
for(let i=0 ;i< pages.length ;i++){
//当前页面宽高
const pageEle = pages[i]
const pageHeight = pageEle.getsize().height
const pagewidth= pageEle.getsize().width
// 计算缩放比例
let scale=1
let scaleX = Math.min(A4_WIDTH / pagewidth, 1);
let scaleY = Math.min(A4_HEIGHT/pageHeight, 1);
scale = Math.min(scaleX,scaleY)
//缩放页面大小
pageEle.setsize(pageEle.getwidth()*scale,pageEle.getHeight()*scale)
//缩放页面内容
pageEle.scaleContent(scale,scale);
//重置页面位置,防止添加文字错位
pageEle.resetPosition()
// 添加文字 文字内容 文字信息
pageEle.drawText('124234YT-63',{
x:500,
y: 800,
size:8,
font:
timesRomanFont,
rgb(0,6,0),
color:rgb(0,6,0),
}
}
//下载文件
const pdfBytes = await pdfDoc.save();
this.saveByteArray(" 添加水印"+'.pdf', pdfBytes);
}
//保存pdf并下载
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();
}