使用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(060)color:rgb(060)}
   	}
   	//下载文件
   	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();
   }
### 实现 Vue使用 `vue-pdf` 和 `pdf-lib` 给 PDF 文件添加水印实现预览和下载 #### 安装依赖包 为了在 Vue 项目中集成这些功能,需要安装必要的 npm 包: ```bash npm install vue-pdf pdf-lib buffer file-saver ``` - `vue-pdf`: 提供基于 PDF.js 的组件用于渲染 PDF 文档。 - `pdf-lib`: 轻量级 JavaScript 库,允许创建、编辑现有的 PDFs 并支持操作如添加文字或图像作为水印。 #### 创建 PDF 加载器服务 可以构建一个简单的加载器服务来处理 PDF 数据流以及应用水印逻辑。这里展示了一个简化版的服务函数: ```javascript import * as fs from 'file-saver'; import { PDFDocument, rgb } from 'pdf-lib'; import fetchPdfBytes from '@/utils/fetchPdfBytes'; // 自定义方法获取PDF字节数据 async function addWatermarkToPdf(url, text) { const existingPdfBytes = await fetchPdfBytes(url); const pdfDoc = await PDFDocument.load(existingPdfBytes); const pages = pdfDoc.getPages(); for (let i = 0; i < pages.length; ++i) { const page = pages[i]; const fontSize = 30; const textColor = rgb(0.95, 0.1, 0.1); // 设置颜色为红色 // 计算位置使文本居中显示于页面上角 const { width, height } = page.getSize(); const x = width / 2 - (text.length * fontSize) / 4; const y = height - fontSize; page.drawText(text, { x, y, size: fontSize, color: textColor, rotate: Math.PI / 4, // 斜向放置 }); } const modifiedPdfBytes = await pdfDoc.save(); return new Blob([modifiedPdfBytes], { type: "application/pdf" }); } ``` 此代码片段展示了如何读取远程 URL 上的 PDF 文件,并通过循环遍历每一页,在指定的位置绘制倾斜的文字水印。 #### 构建 Vue 组件以提供 UI 功能 接下来是在 Vue 组件内利用上述工具和服务的方法之一: ```html <template> <div class="container"> <!-- 显示原始PDF --> <embed :src="originalUrl" type="application/pdf" /> <!-- 下载按钮触发事件 --> <button @click="downloadWithWatermark">Download with Watermark</button> <!-- 展示带有水印效果后的PDF预览链接(可选)--> <a v-if="watermarkedBlobUrl" :href="watermarkedBlobUrl" target="_blank">Preview With Watermark</a> </div> </template> <script> export default { data() { return { originalUrl: '/path/to/your/file.pdf', watermarkedBlobUrl: null }; }, methods: { async downloadWithWatermark() { try { let blob = await addWatermarkToPdf(this.originalUrl, 'COPYRIGHT'); this.watermarkedBlobUrl = URL.createObjectURL(blob); saveAs(blob, 'document_with_watermark.pdf'); // 使用FileSaver保存文件 } catch(error){ console.error('Failed to apply watermark:', error.message); } } } }; </script> ``` 这段模板包含了三个主要部分:嵌入式 `<embed>` 标签用来呈现原生 PDF;一个按钮调用 `addWatermarkToPdf()` 方法生成含水印的新 PDF 版本;最后是一个条件性的锚点标签指向新生成的内容以便即时查看[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值