最近項目有給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.FileOutputStream;
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;
public class Test {
/**
* @param inputFile 你的PDF文件地址
* @param outputFile 添加水印後生成PDF存放的地址
* @param waterMarkName 你的水印
* @return
*/
public static boolean waterMark(String inputFile, String outputFile, String waterMarkName) {
try {
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 s1 = System.currentTimeMillis();
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, 100 + 300 * x, 300 * y, 45);
}
}
// 添加水印文字
under.endText();
under.setLineWidth(1f);
under.stroke();
}
stamper.close();
System.out.println(System.currentTimeMillis() - s1);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static void main(String[] args) {
System.out.println(waterMark("./douluo.pdf", "./output.pdf", "中文水印 2020-05-05"));
}
}
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>