我有一个对段落有评论的PDF。这一段很艰难。我的要求是从特定页面删除命令。
以下代码应从我的PDF中删除特定注释,但不会删除。
PDDocument document = PDDocument.load(...File...);
List<PDAnnotation> annotations = new ArrayList<>();
PDPageTree allPages = document.getDocumentCatalog().getPages();
for (int i = 0; i < allPages.getCount(); i++) {
PDPage page = allPages.get(i);
annotations = page.getAnnotations();
List<PDAnnotation> annotationToRemove = new ArrayList<PDAnnotation>();
if (annotations.size() < 1)
continue;
else {
for (PDAnnotation annotation : annotations) {
if (annotation.getContents() != null && annotation.getContents().equals("Sample Strikethrough")) {
annotationToRemove.add(annotation);
}
}
annotations.removeAll(annotationToRemove);
}
}
删除特定评论并在评论所针对的文字上保留删除线的最佳方法是什么?
答案 0 :(得分:2)
删除特定评论并在评论所针对的文字上保留删除线的最佳方法是什么?
您实际找到的注释是子类型 StrikeOut 的文本标记注释,即此注释的主要外观是删除线。因此,您不能删除此注释。相反,您应该删除生成注释的其他外观(悬停文本)的数据。
这可以这样做:
final COSName POPUP = COSName.getPDFName("Popup");
PDDocument document = PDDocument.load(resource);
List<PDAnnotation> annotations = new ArrayList<>();
PDPageTree allPages = document.getDocumentCatalog().getPages();
List<COSObjectable> objectsToRemove = new ArrayList<>();
for (int i = 0; i < allPages.getCount(); i++) {
PDPage page = allPages.get(i);
annotations = page.getAnnotations();
for (PDAnnotation annotation : annotations) {
if ("StrikeOut".equals(annotation.getSubtype()))
{
COSDictionary annotationDict = annotation.getCOSObject();
COSBase popup = annotationDict.getItem(POPUP);
annotationDict.removeItem(POPUP); // popup annotation
annotationDict.removeItem(COSName.CONTENTS); // plain text comment
annotationDict.removeItem(COSName.RC); // rich text comment
annotationDict.removeItem(COSName.T); // author
if (popup != null)
objectsToRemove.add(popup);
}
}
annotations.removeAll(objectsToRemove);
}
(RemoveStrikeoutComment.java test testRemoveLikeStephanImproved
)
作为查看此问题的副作用,PDFBox错误变得明显:OP的原始代码应该完全删除 StrikeOut 注释,但它什么也没做。原因是在页面注释的上下文中使用COSArrayList
类的错误。
page.getAnnotations()
返回的页面注释列表是COSArrayList
的实例。此类带有一个COS对象列表,它们出现在页面 Annots 数组中,并带有这些条目的包装器列表(在必要时解析间接引用之后)。
removeAll
方法(明智地)检查其参数集合是否包含此类包装器,并从前一个集合和参数集合中删除实际的COS对象,而不是包装器,从后者(即使用包装器)。
这适用于 Annots 数组中的直接对象,但是前一个列表中作为间接引用的条目未正确删除,因为代码尝试删除已解析的注释词典,而该列表实际上包含间接引用。
在手头的情况下导致删除不被回写。在更一般的情况下,结果甚至可能更怪异,因为现在两个列表具有不同的大小。因此,面向索引的方法现在可以操作列表的非对应对象......
(顺便说一句,在我上面的代码中,我删除了一个间接引用,而不是一个包装器,也使列表处于混乱状态,因为这次只删除了前者的一个条目,而不是后一个列表;可能这也应该是处理得更安全。)
retainAll
方法中出现类似的问题。
另一个小故障:COSArrayList.lastIndexOf
使用所包含列表的indexOf
。
使用当前3.0.0-SNAPSHOT分析的PDFBox源代码,但所有版本2.0.0 - 2.0.7都会出现错误,因此它们的代码也很可能包含这些错误。