PDFbox内容流是按页面完成的,但是字段来自于来自目录的表单,该目录来自pdf
doc本身。因此,我不确定哪些字段位于哪些页面上,以及导致将文本写到错误的位置/页面上的原因。
即。我正在处理每页的字段,但不确定哪些字段在哪些页面上。
有没有办法告诉哪个字段在哪个页面上?或者,是否有办法只获取当前页面上的字段?
谢谢!
标记
代码段:
PDDocument pdfDoc = PDDocument.load(file);
PDDocumentCatalog docCatalog = pdfDoc.getDocumentCatalog();
PDAcroForm acroForm = docCatalog.getAcroForm();
// Get field names
List<PDField> fieldList = acroForm.getFields();
List<PDPage> pages = pdfDoc.getDocumentCatalog().getAllPages();
for (PDPage page : pages) {
PDPageContentStream contentStream = new PDPageContentStream(pdfDoc, page, true, true, true);
processFields(acroForm, fieldList, contentStream, page);
contentStream.close();
}
PDFbox内容流是按页面完成的,但是字段来自于来自目录的表单,该目录来自pdf doc本身。所以我不确定哪些字段在哪些页面上
其原因是PDF包含定义表单的全局html" target="_blank">对象结构。此结构中的表单字段在0、1或更多实际PDF页面上可能具有0、1或更多可视化效果。此外,在仅一个可视化的情况下,允许将现场对象和可视化对象合并。
不幸的是,PDFBox中的PDAcroForm
和PDField
对象仅表示此对象结构,无法轻松访问相关页面。但是,通过访问基础结构,您可以建立连接。
以下代码应明确说明如何执行此操作:
@SuppressWarnings("unchecked")
public void printFormFields(PDDocument pdfDoc) throws IOException {
PDDocumentCatalog docCatalog = pdfDoc.getDocumentCatalog();
List<PDPage> pages = docCatalog.getAllPages();
Map<COSDictionary, Integer> pageNrByAnnotDict = new HashMap<COSDictionary, Integer>();
for (int i = 0; i < pages.size(); i++) {
PDPage page = pages.get(i);
for (PDAnnotation annotation : page.getAnnotations())
pageNrByAnnotDict.put(annotation.getDictionary(), i + 1);
}
PDAcroForm acroForm = docCatalog.getAcroForm();
for (PDField field : (List<PDField>)acroForm.getFields()) {
COSDictionary fieldDict = field.getDictionary();
List<Integer> annotationPages = new ArrayList<Integer>();
List<COSObjectable> kids = field.getKids();
if (kids != null) {
for (COSObjectable kid : kids) {
COSBase kidObject = kid.getCOSObject();
if (kidObject instanceof COSDictionary)
annotationPages.add(pageNrByAnnotDict.get(kidObject));
}
}
Integer mergedPage = pageNrByAnnotDict.get(fieldDict);
if (mergedPage == null)
if (annotationPages.isEmpty())
System.out.printf("i Field '%s' not referenced (invisible).\n", field.getFullyQualifiedName());
else
System.out.printf("a Field '%s' referenced by separate annotation on %s.\n", field.getFullyQualifiedName(), annotationPages);
else
if (annotationPages.isEmpty())
System.out.printf("m Field '%s' referenced as merged on %s.\n", field.getFullyQualifiedName(), mergedPage);
else
System.out.printf("x Field '%s' referenced as merged on %s and by separate annotation on %s. (Not allowed!)\n", field.getFullyQualifiedName(), mergedPage, annotationPages);
}
}
当心 ,PDFBox PDAcroForm
表单字段处理中有两个缺点:
PDF规范允许定义表单的全局对象结构为一棵深树,即,实际字段不必是根的直接子代,而可以通过内部树节点来组织。PDFBox会忽略这一点,并希望这些字段是根的直接子代。
某些原始的PDF(最早的PDF)不包含域树,而仅通过可视化的窗口小部件注释引用页面中的域对象。PDFBox在其PDAcroForm.getFields
列表中看不到这些字段。
PS:
@mikhailvs在他的答案中正确显示,您可以使用来从字段窗口小部件检索页面对象,PDField.getWidget().getPage()
并使用确定其页码catalog.getAllPages().indexOf
。这种getPage()
方法虽然速度很快,但有一个缺点:它从小部件注释字典的
可选
条目中检索页面引用。因此,如果您处理的PDF是由填充该条目的软件创建的,那么一切都很好,但是,如果PDF创建者尚未填充该条目,那么您得到的只是一个null
页面。
在2.0.x中,用于访问有问题的元素的某些方法已更改,但整体情况没有改变,要安全地检索小部件的页面,您仍然必须遍历页面并找到引用注释的页面。
安全方法:
int determineSafe(PDDocument document, PDAnnotationWidget widget) throws IOException
{
COSDictionary widgetObject = widget.getCOSObject();
PDPageTree pages = document.getPages();
for (int i = 0; i < pages.getCount(); i++)
{
for (PDAnnotation annotation : pages.get(i).getAnnotations())
{
COSDictionary annotationObject = annotation.getCOSObject();
if (annotationObject.equals(widgetObject))
return i;
}
}
return -1;
}
快速方法
int determineFast(PDDocument document, PDAnnotationWidget widget)
{
PDPage page = widget.getPage();
return page != null ? document.getPages().indexOf(page) : -1;
}
用法:
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null)
{
for (PDField field : acroForm.getFieldTree())
{
System.out.println(field.getFullyQualifiedName());
for (PDAnnotationWidget widget : field.getWidgets())
{
System.out.print(widget.getAnnotationName() != null ? widget.getAnnotationName() : "(NN)");
System.out.printf(" - fast: %s", determineFast(document, widget));
System.out.printf(" - safe: %s\n", determineSafe(document, widget));
}
}
}
( 确定小部件页面.java)
(与1.8.x代码相反,这里的安全方法只是搜索单个字段的页面。如果在代码中必须确定许多小部件的页面,则应Map
像在1.8.x情况下那样创建查找。 )
快速方法失败的文档:aFieldTwice.pdf
快速方法适用的文档:test_duplicate_field2.pdf
HBase如何知道一行是否包含特定的列?例如,考虑以下情况: null 提前感谢!
问题内容: 如果我在Java(Android)中有两个日期(日期1和日期2),如何知道日期2是否在日期1的同一天内?(注意:如果Date2-Date1 <24小时则不是)。一些示例(支持相同的月份和年份) 谢谢 问题答案: 你为什么不使用? 您可以直接调用isSameDay之类的方法 检查Apache DateUtils。
有一个postgres表具有带有时区的时间戳(格式为'YYYY-MM-DD HH24:MI:SS.PPPP'表作为主键)。我想检查postgres表中是否存在特定的时间戳(没有时区)。
本文向大家介绍MongoDB文档中如何检查特定字段是否存在,包括了MongoDB文档中如何检查特定字段是否存在的使用技巧和注意事项,需要的朋友参考一下 使用MongoDB $exists 检查特定字段是否存在。如果文档中不存在该字段,则需要使用find()显示相同的文档。 让我们创建一个包含文档的集合- 在find()方法的帮助下显示集合中的所有文档- 这将产生以下输出- 以下是获取不包含特定字段
问题内容: 我想检测特定页面是否已在内存中映射。目的是能够在使用固定内存地址调用mmap之前执行此检查。以下代码说明了默认情况下的情况:mmap默默地重新映射原始内存页面。 我知道我可以打开并解析/ proc / self / maps来确定已分配的内存范围,并从中推断出以下内容:如果我可以使用mmap安全地请求特定的内存范围,但是我正在寻找合适的API:一个东西 ? 问题答案: msync(ad
问题内容: 如何知道一个字符串是否包含重音? 问题答案: 在列表中添加您想要的任何重音