当前位置: 首页 > 科技观察

如何给PDF批量添加水印?

时间:2023-03-18 21:00:18 科技观察

我们有时需要将一些机密文件发送给多个客户。为了防止客户泄露文件,我们会给机密文件加水印。每个客户收到的文件内容相同,只是水印不同。这样,如果信息泄露了,通过水印就可以知道是从谁那里泄露的。今天有市场的朋友问我关于PDF加水印的问题,如下图:他有一个Excel文件,里面有10000个经销商的名字,他想把价格表的PDF发给这些经销商。每个经销商收到的PDF文件上的水印是经销商自己的名字。这需要人工操作很累。但是如果你用Python来做,那就很简单了。代码不应超过30行。为了准备满足此要求的环境,需要安装两个模块,称为reportlab和pikepdf。使用pip安装即可:python3-mpipinstallreportlabpikepdf然后,你需要找到.ttf或.ttc格式的中文字体。您可以直接从Internet下载中文字体文件。也可以使用系统自带的中文字体。下面是在macOS系统中查找默认Arial的示例。macOS系统字体在/System/Library/Fonts,松体对应的.ttc文件地址在/System/Library/Fonts/Supplemental/Songti.ttc。对于系统默认字体,我们只需要知道它对应的文件名为Songti.ttc即可。如果是从网上下载的第三方字体,需要使用相对于项目代码的绝对路径或相对路径。获取经销商名称对应的列表由于这位朋友不会使用pandas,我们尝试使用Python的native方法获取经销商名称列表。假设经销商信息对应的Excel如下图所示:我们先将这个Excel文件导出成csv文件:然后,我们用Python读取csv文件,获取经销商名称列表:importcsvwithopen('经销商信息.csv')asf:reader=csv.DictReader(f)name_list=[x['经销商名称']forxinreader]print(name_list)运行效果如下图:生成水印PDF一般来说,我们不能直接将一段文字作为水印添加到另一个PDF文件中。我们只需要先将这段文字生成图片或者水印PDF文件,然后将图片或者水印PDF作为“图层”覆盖在目标PDF上即可。因此,现在需要为每个经销商生成对应的带水印的PDF文件。此PDF仅包含带水印的文本。效果如下图所示:对应代码create_watermark.py如下:Information.csv')asf:reader=csv.DictReader(f)name_list=[x['Dealername']forxinreader]pdfmetrics.registerFont(TTFont('Songti','Songti.ttc'))#LoadChinesefontwater_mark_folder=Path('water_pdf')#使用一个文件夹存放所有水印PDFwater_mark_folder.mkdir(exist_ok=True)fornameinname_list:path=str(water_mark_folder/Path(f'{name}.pdf'))c=canvas.Canvas(path,pagesize=(200*units.mm,200*units.mm))#生成一个长宽为200mm的画布c.translate(0.1*200*units.mm,0.1*200*units.mm)c.rotate(45)#将水印文本旋转45°c.setFont('Songti',35)#字体大小c.setStrokeColorRGB(0,0,0)#设置字体颜色c.setFillColorRGB(0,0,0)#设置填充颜色c.setFillAlpha(0.3)#设置透明度,越小越透明c.drawString(0,0,f'{name}特价表,严禁泄露!')具体c.save()函数的代码,已经写在注释中运行后会在当前项目根目录生成water_pdf文件夹,里面包含生成的水印PDF。合并水印和目标PDF最后一步是将每个经销商的水印PDF与目标PDF合并。带水印的PDF作为图层叠加在目标PDF之上。使用pikepdf来完成这项工作非常简单。使用以下代码编写一个combine.py文件:true)col=2#每页有多少列水印row=3#每页有多少行水印forpathinwater_pdf_list:target=Pdf.open('./PythonisinstanceGolang.pdf')#每次都必须使用Re-打开PDF,因为加水印是inplace操作file=Path(path)name=file.stemwater_mark_pdf=Pdf.open(path)water_mark=water_mark_pdf.pages[0]forpageintarget.pages:forxinrange(col):#每行显示多少列水印foryinrange(row):#每页显示多少行PDFpage.add_overlay(water_mark,Rectangle(page.trimbox[2]*x/col,page.trimbox[3]*y/row,page.trimbox[2]*(x+1)/col,page.trimbox[3]*(y+1)/row))result_name=Path('结果',f'{name}_addwatermark.pdf')target.save(str(result_name))运行后,会在项目根目录下生成一个result文件夹,里面是添加了水印的PDF文件,如图下图:这里有必要对代码中的一些地方进行说明带有行号的代码如下图所示:代码的第21行和第22行,有两个for循环,它们的作用是在其中添加多个水印页面。请注意下图中我圈出的地方:每页有6个水印,分为3行2列。这3行对应于变量行的值。第2列对应于变量col的值。您也可以根据需要修改这两个数字。甚至每一页水印的位置都是随机变化的,以防止被去水印程序删除。page.trimbox是PDF页面的宽度,page.trimbox是PDF页面的高度。总结一下大家的关注这篇文章我把任务分为三个部分,分别是:Excel转CSV,让Python轻松读取Python读取CSV生成水印PDF水印PDF和目标PDF文件合并这三个部分的代码可以合并放到一个.py文件里,但是我没有这么做,因为问这个问题的同学不是程序员,Python的水平只是入门。如果合并在一起,代码量太大后,不知道有没有问题。怎么了。在计算机领域,所有的问题都可以通过将问题拆分成多个部分分别运行或者增加几个中间层来解决。今天使用的方法是拆分问题。对于初学者来说,每一步都是相对独立的,效果立竿见影。第二步只需要依赖第一步的结果,第三步只需要依赖第二步的结果,这样每一步的输入输出都非常清晰,可以显着降低复杂度的问题。如果报错,也更容易知道问题出在哪里。