如何使用iText或PdfBox扁平化PDF中的图章注释?
问题与解决方案
问题详情
现有一份添加了图章注释的PDF文档,目前仅能扁平化其中的AcroForm表单域,但图章注释无法被扁平化。在Mac的Preview应用中打开仅扁平化表单域后的PDF时,仍可移动、删除图章注释,需要实现表单域与图章注释的同时扁平化,以限制此类编辑操作。
添加图章的现有代码如下:
PdfReader reader1 = new PdfReader(file); ByteArrayOutputStream mos = new ByteArrayOutputStream(); PdfDocument docc = new PdfDocument(reader1, new PdfWriter(mos),new StampingProperties().useAppendMode()); String filePath = "rectangle.png"; ImageData img = ImageDataFactory.create(filePath); Rectangle rect=null; float w = img.getWidth(); float h = img.getHeight(); rect = new Rectangle(0,0,250,400); PdfFormXObject xObj = new PdfFormXObject(new Rectangle(w,h)); PdfCanvas canvas = new PdfCanvas(xObj, docc); canvas.addImageAt(img,0,0,false); PdfStampAnnotation stamp = new PdfStampAnnotation(rect); stamp.setNormalAppearance(xObj.getPdfObject()); docc.getPage(1).addAnnotation(stamp); docc.close(); file = new File("Annotation Added.pdf"); FileOutputStream fout1 = new FileOutputStream(file); fout1.write(mos.toByteArray()); fout1.close();
解决方案
要同时扁平化表单域和图章注释,需注意以下几点:
- 移除
useAppendMode()追加模式,该模式会保留原有注释结构,不利于扁平化操作; - 先处理表单域扁平化,再遍历页面注释,将注释的外观绘制到页面内容流后移除注释。
完整实现代码如下:
import com.itextpdf.kernel.pdf.*; import com.itextpdf.kernel.pdf.annot.PdfAnnotation; import com.itextpdf.kernel.pdf.annot.PdfStampAnnotation; import com.itextpdf.kernel.pdf.xobject.PdfFormXObject; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.geom.Rectangle; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.util.List; public class FlattenPdf { public static void main(String[] args) throws Exception { File inputFile = new File("input.pdf"); // 替换为你的输入PDF路径 File outputFile = new File("Flattened_Output.pdf"); PdfReader reader = new PdfReader(inputFile); ByteArrayOutputStream mos = new ByteArrayOutputStream(); // 不使用追加模式,使用正常 stamper 模式 PdfDocument doc = new PdfDocument(reader, new PdfWriter(mos)); // 1. 扁平化表单域 PdfAcroForm acroForm = PdfAcroForm.getAcroForm(doc, true); if (acroForm != null) { acroForm.flattenFields(); } // 2. 扁平化图章注释 int pageCount = doc.getNumberOfPages(); for (int i = 1; i <= pageCount; i++) { PdfPage page = doc.getPage(i); List<PdfAnnotation> annotations = page.getAnnotations(); // 倒序遍历,避免移除元素时索引混乱 for (int j = annotations.size() - 1; j >= 0; j--) { PdfAnnotation annot = annotations.get(j); if (annot instanceof PdfStampAnnotation) { PdfStampAnnotation stampAnnot = (PdfStampAnnotation) annot; PdfDictionary normalAppearance = stampAnnot.getNormalAppearance(); if (normalAppearance != null) { PdfFormXObject xObj = PdfFormXObject.makeXObject(normalAppearance, doc); Rectangle annotRect = stampAnnot.getRectangle().toRectangle(); // 获取页面内容画布,在现有内容上方绘制注释外观 PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), doc); canvas.addXObject(xObj, annotRect.getX(), annotRect.getY()); } // 移除注释 page.removeAnnotation(annot); } } } doc.close(); // 写入输出文件 FileOutputStream fout = new FileOutputStream(outputFile); fout.write(mos.toByteArray()); fout.close(); } }
关键说明
- 倒序遍历页面注释列表,避免移除元素时导致的索引越界问题;
- 使用
page.newContentStreamBefore()获取内容画布,确保注释外观绘制在页面原有内容之上; - 先处理表单扁平化,再处理注释扁平化,保证所有可编辑元素都被固化到页面内容中。
内容的提问来源于stack exchange,提问作者Akhil NagaSai
相关产品推荐
相关产品推荐

