1. 概述
iText 是一个用于创建和操作PDF文件的Java库。水印有助于保护机密信息。
在这个教程中,我们将使用iText PDF库来创建包含水印的新PDF文件,并向现有PDF添加水印。
2. Maven依赖项
在本教程中,我们将使用Maven管理依赖关系。我们需要iText依赖以开始使用iText PDF库。此外,我们还需要AssertJ依赖进行测试。我们将这两个依赖添加到我们的pom.xml
中:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.4</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
3. 水印
水印可以帮助在文档或图像文件上叠加或底层放置文本或logo。对于版权保护、数字产品的市场推广以及防止伪造等方面至关重要。
在这个教程中,我们将为生成的PDF添加保密水印,防止未经授权的使用:
4. 使用iText生成PDF
在这篇文章中,我们将编写一个故事并使用iText PDF库将其转换为PDF格式。我们将创建一个简单的程序StoryTime
。首先,我们声明两个String
类型的变量,将故事存储在声明的变量中:
public class StoryTime {
String aliceStory = "I am ...";
String paulStory = "I am Paul ..";
}
为了简洁,我们将String
值缩短。然后,声明一个String
类型的变量,用于存储生成的PDF的输出路径:
public static final String OUTPUT_DIR = "output/alice.pdf";
最后,创建一个方法,包含程序逻辑。我们将创建一个PdfWriter
实例,指定输出路径和名称。
接下来,我们将创建一个PdfDocument
实例来处理我们的PDF文件。为了将String
值添加到PDF文档中,我们将创建一个新的Document
实例:
public void createPdf(String output) throws IOException {
PdfWriter writer = new PdfWriter(output);
PdfDocument pdf = new PdfDocument(writer);
try (Document document = new Document(pdf, PageSize.A4, false)) {
document.add(new Paragraph(aliceSpeech)
.setFont(PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN)));
document.add(new Paragraph(paulSpeech)
.setFont(PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN)));
document.close();
}
}
我们的方法将生成一个新的PDF文件,并将其存储在OUTPUT_DIR
中。
5. 在生成的PDF上添加水印
在上一节中,我们使用iText PDF库生成了一个PDF文件。首先生成PDF有助于了解页面大小、旋转和页数,这有助于有效地添加水印。现在,让我们在简单程序中添加更多逻辑。我们的程序将在生成的PDF上添加水印。
首先,创建一个方法来指定水印的属性。我们将设置水印的Font
、fontSize
和Opacity
:
public Paragraph createWatermarkParagraph(String watermark) throws IOException {
PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA);
Text text = new Text(watermark);
text.setFont(font);
text.setFontSize(56);
text.setOpacity(0.5f);
return new Paragraph(text);
}
接下来,创建一个方法,包含将水印添加到PDF文档的逻辑。该方法将接受Document
、Paragraph
和offset
作为参数。我们将计算水印段落的位置和旋转:
public void addWatermarkToGeneratedPDF(Document document, int pageIndex,
Paragraph paragraph, float verticalOffset) {
PdfPage pdfPage = document.getPdfDocument().getPage(pageIndex);
PageSize pageSize = (PageSize) pdfPage.getPageSizeWithRotation();
float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
float y = (pageSize.getTop() + pageSize.getBottom()) / 2;
float xOffset = 100f / 2;
float rotationInRadians = (float) (PI / 180 * 45f);
document.showTextAligned(paragraph, x - xOffset, y + verticalOffset,
pageIndex, CENTER, TOP, rotationInRadians);
}
通过调用showTextAligned()
方法,我们将水印段落添加到文档中。接下来,编写一个方法,它将生成新的PDF并添加水印。我们将调用createWatermarkParagraph()
和addWatermarkToGeneratedPDF()
方法:
public void createNewPDF() throws IOException {
StoryTime storyTime = new StoryTime();
String waterMark = "CONFIDENTIAL";
PdfWriter writer = new PdfWriter(storyTime.OUTPUT_FILE);
PdfDocument pdf = new PdfDocument(writer);
try (Document document = new Document(pdf)) {
document.add(new Paragraph(storyTime.alice)
.setFont(PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN)));
document.add(new Paragraph(storyTime.paul));
Paragrapgh paragraph = storyTime.createWatermarkParagraph(waterMark);
for (int i = 1; i <= document.getPdfDocument().getNumberOfPages(); i++) {
storyTime.addWatermarkToGeneratedPDF(document, i, paragraph, 0f);
}
}
}
最后,编写一个单元测试来验证水印的存在:
@Test
public void givenNewTexts_whenGeneratingNewPDFWithIText() throws IOException {
StoryTime storyTime = new StoryTime();
String waterMark = "CONFIDENTIAL";
LocationTextExtractionStrategy extStrategy = new LocationTextExtractionStrategy();
try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(storyTime.OUTPUT_FILE))) {
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
String textFromPage = getTextFromPage(pdfDocument.getPage(i), extStrategy);
assertThat(textFromPage).contains(waterMark);
}
}
}
我们的测试验证了生成PDF中的水印存在。
6. 向现有PDF添加水印
iText PDF库使得向现有PDF添加水印变得容易。首先,我们需要将PDF文档加载到程序中,并使用iText库操纵现有的PDF。
首先,我们需要创建一个方法来添加水印段落。因为我们已经在上一节创建过,所以这里也可以使用。
接下来,创建一个方法,其中包含逻辑,帮助我们在现有PDF上添加水印。该方法将接受Document
、Paragraph
、PdfExtGState
、pageIndex
和offSet
作为参数。在方法中,我们将创建一个PdfCanvas
实例,用于向PDF内容流写入数据。
然后,我们将在PDF上计算水印的位置和旋转。我们将刷新文档并释放状态以提高性能:
public void addWatermarkToExistingPDF(Document document, int pageIndex,
Paragraph paragraph, PdfExtGState graphicState, float verticalOffset) {
PdfDocument pdfDocument = document.getPdfDocument();
PdfPage pdfPage = pdfDocument.getPage(pageIndex);
PageSize pageSize = (PageSize) pdfPage.getPageSizeWithRotation();
float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
float y = (pageSize.getTop() + pageSize.getBottom()) / 2;
PdfCanvas over = new PdfCanvas(pdfDocument.getPage(pageIndex));
over.saveState();
over.setExtGState(graphicState);
float xOffset = 14 / 2;
float rotationInRadians = (float) (PI / 180 * 45f);
document.showTextAligned(paragraph, x - xOffset, y + verticalOffset,
pageIndex, CENTER, TOP, rotationInRadians);
document.flush();
over.restoreState();
over.release();
}
最后,编写一个方法来向现有PDF添加水印。我们将调用createWatermarkParagraph()
来添加水印段落,并调用addWatermarkToExistingPDF()
来处理添加水印到页面的任务:
public void addWatermarkToExistingPdf() throws IOException {
StoryTime storyTime = new StoryTime();
String outputPdf = "output/aliceNew.pdf";
String watermark = "CONFIDENTIAL";
try (PdfDocument pdfDocument = new PdfDocument(new PdfReader("output/alice.pdf"),
new PdfWriter(outputPdf))) {
Document document = new Document(pdfDocument);
Paragraph paragraph = storyTime.createWatermarkParagraph(watermark);
PdfExtGState transparentGraphicState = new PdfExtGState().setFillOpacity(0.5f);
for (int i = 1; i <= document.getPdfDocument().getNumberOfPages(); i++) {
storyTime.addWatermarkToExistingPage(document, i, paragraph,
transparentGraphicState, 0f);
}
}
}
编写一个单元测试来验证水印的存在:
@Test
public void givenAnExistingPDF_whenManipulatedPDFWithITextmark() throws IOException {
StoryTime storyTime = new StoryTime();
String outputPdf = "output/aliceupdated.pdf";
String watermark = "CONFIDENTIAL";
LocationTextExtractionStrategy extStrategy
= new LocationTextExtractionStrategy();
try (PdfDocument pdfDocument = new PdfDocument(new PdfReader(outputPdf))) {
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
String textFromPage = getTextFromPage(pdfDocument.getPage(i), extStrategy);
assertThat(textFromPage).contains(watermark);
}
}
}
我们的测试验证了现有PDF中的水印存在。
7. 结论
在这篇教程中,我们探索了iText PDF库,生成了新的PDF,并向生成的PDF和现有PDF添加了水印。iText库在操作PDF方面显得非常强大。完整的代码可以在GitHub上找到。