Apache PDFBox中如何让GraphicsState印章图显示在交易行上层
问题
我正在使用Apache PDFBox开发一个PDF项目,该项目展示交易列表,当前页面填满后会自动延伸至新页面。交易行采用紫色与白色交替的背景色。我已实现在每页底部添加印章,通过GraphicsState设置了印章图的透明度,但印章图目前像水印一样显示在交易行下方,我希望它能显示在交易行上层,请问该如何实现?
主代码
JsonArray transactionList = SampleDataGenerator .generateSampleData() .getJsonObject("data") .getJsonArray("transactionList"); for (int i = 0; i < transactionList.size(); i++) { JsonObject transaction = transactionList.getJsonObject(i); PDPageContentStream newPagesContentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true); if (dataYPosition - rowHeight < margin) { page = new PDPage(); document.addPage(page); dataYPosition = page.getMediaBox().getHeight() - 130f; // 调整新页面行高 PdfImageDrawer.drawColumnHeader(newPagesContentStream, HELVETICA, columnXPosition, columnYPosition + 85f); PdfImageDrawer.drawLogo(document, newPagesContentStream); } List<String> transactionValues = generateTransactionRow.doGenerateRow(transaction); for (String dataValues : transactionValues) { newPagesContentStream.setNonStrokingColor(i % 2 == 0 ? new Color(229, 229, 255) : Color.WHITE); newPagesContentStream.addRect(dataXPosition - 35f, dataYPosition, tableWidth, rectangleHeight); newPagesContentStream.fill(); newPagesContentStream.setNonStrokingColor( new Color(112, 112, 112)); newPagesContentStream.beginText(); newPagesContentStream.setFont(HELVETICA, 6.5f); newPagesContentStream.newLineAtOffset(dataXPosition, dataYPosition + 25f); newPagesContentStream.showText(dataValues); newPagesContentStream.endText(); dataXPosition += 100f; } dataXPosition += -600f; dataYPosition -= rowHeight; contentStream.saveGraphicsState(); PdfImageDrawer.drawTextStampInputStream(newPagesContentStream, stampImage); newPagesContentStream.close(); } //FOOTER PdfImageDrawer.drawTextStampInputStream(contentStream, stampImage); contentStream.close();
drawTextStampInputStream方法代码
public static void drawTextStampInputStream(PDPageContentStream contentStream, PDImageXObject stampImage) throws IOException { String text1 = "xxxxxxxxxxxxxxxxxxxxxxx"; String text2 = "xxxxxxxxxxxxxxxxxxxxxxx"; String text3 = "xxxxxxxxxxxxxxxxxxxxxxx"; PDType1Font HELVETICA = new PDType1Font(Standard14Fonts.FontName.HELVETICA); PDExtendedGraphicsState textGraphicsState = new PDExtendedGraphicsState(); textGraphicsState.setNonStrokingAlphaConstant(0.050f); contentStream.setGraphicsStateParameters(textGraphicsState); PdfTextWriter.doWrite(contentStream, HELVETICA, 8f, (700f) / 3f, 60f, 0, text1); PdfTextWriter.doWrite(contentStream, HELVETICA, 8f, 140f, 50f, 0, text2); PdfTextWriter.doWrite(contentStream, HELVETICA, 8f, 190f, 40f, 0, text3); PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState(); graphicsState.setNonStrokingAlphaConstant(0.10f); contentStream.setGraphicsStateParameters(graphicsState); contentStream.drawImage(stampImage, 480F, 10f, stampImage.getWidth() / 4F, stampImage.getHeight() / 4F); contentStream.restoreGraphicsState(); }
解决方案
PDF的内容层级由绘制顺序决定:先绘制的内容在底层,后绘制的内容在上层。你的印章被交易行覆盖,是因为循环内绘制完交易行后立即绘制印章,后续新增页面的交易行是在印章流之后绘制的,导致交易行叠在印章上方。
要让印章显示在交易行上层,需调整绘制顺序,确保所有页面的交易内容绘制完成后,再统一为每个页面追加绘制印章,具体修改如下:
调整后的主代码
JsonArray transactionList = SampleDataGenerator .generateSampleData() .getJsonObject("data") .getJsonArray("transactionList"); for (int i = 0; i < transactionList.size(); i++) { JsonObject transaction = transactionList.getJsonObject(i); PDPageContentStream newPagesContentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true); if (dataYPosition - rowHeight < margin) { page = new PDPage(); document.addPage(page); dataYPosition = page.getMediaBox().getHeight() - 130f; // 调整新页面行高 PdfImageDrawer.drawColumnHeader(newPagesContentStream, HELVETICA, columnXPosition, columnYPosition + 85f); PdfImageDrawer.drawLogo(document, newPagesContentStream); } List<String> transactionValues = generateTransactionRow.doGenerateRow(transaction); for (String dataValues : transactionValues) { newPagesContentStream.setNonStrokingColor(i % 2 == 0 ? new Color(229, 229, 255) : Color.WHITE); newPagesContentStream.addRect(dataXPosition - 35f, dataYPosition, tableWidth, rectangleHeight); newPagesContentStream.fill(); newPagesContentStream.setNonStrokingColor( new Color(112, 112, 112)); newPagesContentStream.beginText(); newPagesContentStream.setFont(HELVETICA, 6.5f); newPagesContentStream.newLineAtOffset(dataXPosition, dataYPosition + 25f); newPagesContentStream.showText(dataValues); newPagesContentStream.endText(); dataXPosition += 100f; } dataXPosition += -600f; dataYPosition -= rowHeight; // 移除循环内的印章绘制逻辑,仅关闭当前内容流 newPagesContentStream.close(); } // 所有交易内容绘制完成后,遍历每页追加印章 for (PDPage singlePage : document.getPages()) { try (PDPageContentStream stampStream = new PDPageContentStream(document, singlePage, PDPageContentStream.AppendMode.APPEND, true)) { PdfImageDrawer.drawTextStampInputStream(stampStream, stampImage); } } contentStream.close();
额外注意事项
- 使用
PDPageContentStream.AppendMode.APPEND模式打开页面流,确保印章绘制在现有内容之后。 - 用try-with-resources语法管理流,避免资源泄漏。
- 确保
drawTextStampInputStream中的restoreGraphicsState()调用正确,防止图形状态污染后续操作。
内容的提问来源于stack exchange,提问作者Plus Ultra
相关产品推荐
相关产品推荐

