Python PDF 加水印 和 Java PDF 加水印

24 篇文章 2 订阅
1 篇文章 0 订阅

最近项目有给PDF加水印的需求,目前使用的方法是:首先生成一个水印 PDF,再通过 PyPDF4 来把原件的每一页和 水印 PDF 合并,但耗时和页数成正比,耗时太长。

后来通过 JAVA 实现的方案是:读取原 PDF 后,在每一页的最外层直接添加文字,并且可以调整角度和透明度。

JAVA 方案耗时大概4000页在500毫秒,而相同文件在使用 Python 方案时耗时大概在 500 秒,JAVA 方案比 Python 方案快了 1000 倍。

然后就想到可能是 Merge 方案操作太耗时,就找了找 Python 里有没有 Java 方案的实现,目前还没找到一样的方案,但通过 PymuPDF 实现了个类似的方案:在每一页创建一个透明矩形,矩形可调整角度,然后在矩形里填充文字。

因为目前项目已经选用 JAVA 方案了,所以 PymuPDF 方案就没再找调整文字透明度的方法,诸君可以找找试试。

对了,PymuPDF 方案果然在耗时上提升了一大截,但还没 JAVA 方案那么变态的快,4000页大概耗时在3秒,耗时和创建几个矩形成正比。

好了,话不多说,给诸君上菜。

Python

import fitz
from PyPDF2 import PdfFileReader, PdfFileWriter


def add_watermark_by_merge_pdf(input_pdf, output, watermark):
    """PyPDF2 Merge 方案"""
    watermark_obj = PdfFileReader(watermark)
    watermark_page = watermark_obj.getPage(0)
    pdf_reader = PdfFileReader(input_pdf)
    pdf_writer = PdfFileWriter()

    for page in range(pdf_reader.getNumPages()):
        p = pdf_reader.getPage(page)
        p.mergePage(watermark_page)
        pdf_writer.addPage(page)

    with open(output, 'wb') as f:
        pdf_writer.write(f)


def add_watermark_by_text(input_path, output_path):
    """PymuPDF 矩形方案"""
    doc = fitz.open(input_path)
    text1 = "rotate=-90"
    red = (1, 0, 0)
    gray = (0, 0, 1)
    for page in doc:
        p1 = fitz.Point(page.rect.width - 25, page.rect.height - 25)
        shape = page.newShape()
        shape.drawCircle(p1, 1)
        shape.finish(width=0.3, color=red, fill=red)
        shape.insertText(p1, text1, rotate=-90, color=gray)
        shape.commit()
    doc.save(output_path)


if __name__ == "__main__":
    add_watermark_by_merge_pdf(
        './douluo.pdf', './watermark.pdf', './output.pdf')
    add_watermark_by_text('./douluo.pdf', './output.pdf')

JAVA

package test;

        import java.io.*;
        import java.net.URL;
        import java.net.URLConnection;

        import com.itextpdf.text.BaseColor;
        import com.itextpdf.text.Element;
        import com.itextpdf.text.pdf.BaseFont;
        import com.itextpdf.text.pdf.PdfContentByte;
        import com.itextpdf.text.pdf.PdfGState;
        import com.itextpdf.text.pdf.PdfReader;
        import com.itextpdf.text.pdf.PdfStamper;
        import test.utils.ConvectorUtils;

public class Watermark {
    /**
     * @param inputFile     你的PDF文件地址
     * @param outputFile    添加水印后生成PDF存放的地址
     * @param waterMarkName 你的水印
     * @return
     */
    public static boolean waterMark(String inputFile, String outputFile, String waterMarkName) {
        try {
            long s1 = System.currentTimeMillis();
            PdfReader reader = new PdfReader(inputFile);
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputFile));
            // 这里的字体设置比较关键,这个设置是支持中文的写法
            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);// 使用系统字体
            int total = reader.getNumberOfPages() + 1;

            PdfContentByte under;
            // Rectangle pageRect = null;
            long s2 = System.currentTimeMillis();
            System.out.println("读文件耗时:" + (s2 - s1));
            for (int i = 1; i < total; i++) {
                // 获得PDF最顶层
                under = stamper.getOverContent(i);
                // set Transparency
                PdfGState gs = new PdfGState();
                // 设置透明度为0.2
                gs.setFillOpacity(0.5f);
                under.setGState(gs);
                under.saveState();
                under.restoreState();
                under.beginText();
                under.setFontAndSize(base, 35);
                under.setTextMatrix(30, 30);
                under.setColorFill(BaseColor.GRAY);
                for (int y = 0; y < 3; y++) {
                    for (int x = 0; x < 2; x++) {
                        // 水印文字成45度角倾斜
                        under.showTextAligned(Element.ALIGN_LEFT, waterMarkName, 40+250 * x, 300 * y+ x * 50, 45);
                    }
                }
                // 添加水印文字
                under.endText();
                under.setLineWidth(1f);
                under.stroke();
            }
            stamper.close();
            System.out.println("转换文件耗时:" + (System.currentTimeMillis() - s2));
            return true;

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public static void main(String[] args) {
        String inputFile = args[0];
        String outputFile = args[1];
        String watermark = args[2];
        boolean flag = waterMark(inputFile, outputFile, watermark);
        if (flag){
            System.out.println("加水印成功");
        }else{
            System.out.println("加水印失败");
        }
    }
}

JAVA POM

<dependencies>
    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itextpdf</artifactId>
      <version>5.5.10</version>
  	</dependency>
  	<dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itext-asian</artifactId>
      <version>5.2.0</version>
    </dependency>
  </dependencies>

有君想要Python 调用 Java的代码,如下:

import os

import jpype

# 一些基础路径
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LIB_PATH = os.path.join(BASE_PATH, 'lib')
# 使用的java包所在路径
WATERMARK_JAR_PATH = os.path.join(LIB_PATH, 'jwatermark.jar')


class JavaRunner:
    def __init__(self):
        jpype.startJVM(jpype.getDefaultJVMPath(), "-ea",
                       "-Djava.class.path=%s" % WATERMARK_JAR_PATH, convertStrings = False)
        Test = jpype.JClass('test.Test')
        self.t = Test()

    def addwatermark(self, input_path: str, output_path: str, watermark: str) -> bool:
        return self.t.addWarterMark(input_path, output_path, watermark)

    def tif2pdf(self, input_path:str, output_path:str) -> bool:
        return self.t.convertTifToPdf(input_path, output_path)

    # def __del__(self):
    #     jpype.shutdownJVM()

这块需要诸君使用IntelliJ IDEA这个IDEJAVA代码进行编译打成jar包,然后使用JPython启动JAVA虚拟机从而可以直接调用jar包中的Class,我这块把jar包放在了主目录的lib文件夹下,为了方便代码观察,我把从config导入的几个基础路径和jar路径COPY过来了,方便诸君进行自配

诸君若不想用JAVA虚拟机,可以尝试第二种方式,如下:

import subprocess


class File:
	……	……
	……	……
	@staticmethod
	def subcommand(args, pattern, timeout, error=TimeoutError):
	    p = subprocess.Popen(args, stderr=subprocess.PIPE,
	                         stdout=subprocess.PIPE, shell=False, encoding="utf-8")
	    p_start = time.time()
	    while True:
	        if p.poll() is not None:
	            break
	        if (time.time() - p_start) > timeout:
	            p.terminate()
	            raise error(f'{args}, {timeout}')
	        time.sleep(0.1)
	    command_return = p.stdout.read()
	    logger.info(f'[command_return] {command_return}')
	    success_str_find = re.findall(pattern, command_return)
	    return True if success_str_find else False

    def addwatermark(self):
        args = ["java", "-classpath", WATERMARK_JAR_PATH,
                "test.Watermark", self.convert_path,  WATERMARK_PATH, self.watermark]
        flag = self.subcommand(
            args, r'(加水印成功)', WATERMARK_TIMEOUT_NUM, WatermarkTimeoutError)
        if flag:
            self.watermark_path = WATERMARK_PATH
        else:
            raise WatermarkError('not get watermark_path')

这块代码也只是给个参考,具体方案是使用Python标准库subprocess开启子进程来运行java命令,就和在命令行直接运行java一样,如:java -classpath jwatermark.jar test.Watermark test.pdf output.pdf "www"
然后我因为有其他地方也都需要通过这种方式运行命令调用JAVA代码,而且设置超时返回是必须的,所有在这里把调用封成了subcommand方法,pattern参数的作用是捕获JAVA代码的输出里是不是存在调用成功的字符串,就像前面写的JAVA代码里加水印成功后会输出加水印成功,只要输出里存在这个字符串就是调用成功了。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python中给PDF文件添水印有多种方法。一种方法是使用PyPDF2库,在代码中将水印文件作为一个页面插入到其他PDF文件中。你可以使用以下代码实现这个功能: from os.path import splitext from PyPDF2 import PdfFileReader, PdfFileWriter def add_watermark(watermark, pdf_in): writer = PdfFileWriter() pdf_src = PdfFileReader(pdf_in) for i in range(pdf_src.getNumPages()): # 合并水印页和内容页,水印页在下,内容页在上 page = PdfFileReader(watermark).getPage(0) page.mergePage(pdf_src.getPage(i)) writer.addPage(page) # 生成结果文件 with open('_带水印'.join(splitext(pdf_in)), 'wb') as fp: writer.write(fp) add_watermark('水印文件.pdf', '需水印的文件.pdf') 另一种方法是使用Python-docx库创建一个docx文档,添自定义的艺术字水印,并将其转换为PDF文件。你可以按照以下步骤来实现: 1. 创建一个docx文档。 2. 在文档中添艺术字水印,并进行必要的样式设计。 3. 将docx文档保存为PDF文件。 这样,你就可以在PDF文件中看到添水印。 另外,你还可以使用python3-fitz模块来给PDF文件添水印。这个模块依赖MuPDF,所以请确保你已经安装了这个模块。使用python3-fitz模块,你可以遍历PDF文件中的所有页面,并插入水印图片。以下是一个示例代码: import fitz doc = fitz.open("需要添水印的文件.pdf") logo = "水印图片.png" for page in doc: rect = fitz.Rect(0, 0, 200, 200) # 水印图片的位置和大小 page.insertImage(rect, filename=logo) doc.save("带水印的文件.pdf") 这样,你就可以在带水印PDF文件中看到插入的水印图片。 希望这些方法可以帮助你在Python中给PDF文件添水印。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泡泡码客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值