使用Apache PDFBox删除PDF多余图片后如何去除遗留的空白区域
问题原因
你当前的代码只是删除了页面资源字典中的图片XObject对象,并没有移除页面内容流中对应的图片绘制指令,也没有对删除图片位置下方的内容做上移排版,所以才会留下空白区域。
具体解决步骤
- 第一步:解析并修改页面内容流,移除对应图片的绘制指令
你需要遍历页面的内容流运算符,识别出绘制待删除图片的Do指令,直接移除这些指令,避免渲染时预留图片位置。 - 第二步:调整剩余内容的排版(可选,按需开启)
如果需要删除空白后下方内容自动上移填充空缺,你需要先记录被删除图片的坐标和尺寸,再遍历所有后续内容元素(文本、图形、其他图片等)的坐标,将Y轴坐标减去对应空白区域的高度,完成内容上移。
可参考的修改后代码
import org.apache.pdfbox.contentstream.PDFStreamEngine; import org.apache.pdfbox.contentstream.operator.Operator; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdfwriter.ContentStreamWriter; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; // 移除图片及对应的绘制指令 private ByteArrayOutputStream clearimages(ByteArrayOutputStream destinationStream) throws IOException { PDDocument document = PDDocument.load(destinationStream.toByteArray()); AtomicInteger keepImage = new AtomicInteger(0); List<COSName> toRemoveImageNames = new ArrayList<>(); // 第一步:先标记需要删除的图片资源名 for (PDPage page : document.getPages()) { PDResources pdResources = page.getResources(); for (COSName propertyName : pdResources.getXObjectNames()) { if(!pdResources.isImageXObject(propertyName)) { continue; } keepImage.incrementAndGet(); if (keepImage.get() > 4) { toRemoveImageNames.add(propertyName); } } // 第二步:遍历内容流,移除对应图片的Do绘制指令 List<Object> newTokens = new ArrayList<>(); PDFStreamEngine engine = new PDFStreamEngine() {}; List<Object> tokens = engine.parsePage(page); for (int i = 0; i < tokens.size(); i++) { Object token = tokens.get(i); if (token instanceof Operator && ((Operator) token).getName().equals("Do")) { COSName imgName = (COSName) tokens.get(i - 1); if (toRemoveImageNames.contains(imgName)) { // 移除Do指令和前面的资源名参数 newTokens.remove(newTokens.size() - 1); continue; } } newTokens.add(token); } // 把修改后的内容流写回页面 try (OutputStream out = page.getContents().createOutputStream()) { ContentStreamWriter writer = new ContentStreamWriter(out); writer.writeTokens(newTokens); } // 第三步:删除资源字典中标记的图片对象 for (COSName name : toRemoveImageNames) { pdResources.getCOSObject().getCOSDictionary(COSName.XOBJECT).removeItem(name); } toRemoveImageNames.clear(); } ByteArrayOutputStream out = new ByteArrayOutputStream(); document.save(out); document.close(); return out; }
注意:如果需要完全消除空白、让下方内容上移,还需要额外计算每张被删图片的占位高度,遍历所有内容元素的Y坐标做对应偏移,该逻辑需要根据你的PDF排版规则调整,不同PDF的坐标系原点设置可能存在差异。
内容的提问来源于stack exchange,提问作者SRam
相关产品推荐
相关产品推荐

