PDFBox 2.0.24添加PDAnnotationText报未嵌套在Annot结构元素错误如何解决
PDF/A UA-1/1a规范下嵌套PDAnnotationText对象报错问题
在构造同时符合PDF/A UA-1 / PDF/A-1a规范的PDF文件时,嵌套PDAnnotationText对象时触发无障碍检查工具报错,涉及工具包含3版、2021版PAC以及VeraPDF。
报错详情
- PAC 2021报错
- 注解未嵌套在"Annot"结构元素内
- 存在疑似空对象引用问题,提示
对象引用未设置到对象实例
- VeraPDF报错
- 除Widget、Popup、Link子类型的注解外,其余注解均需嵌套在Annot标签内
- PDFDebugger结构查看结果
已通过PDFDebugger查看注解结构、对应结构树根节点,可复现问题。示例文件包含文本内容,第一、二行各有一个注解可触发上述报错。
规范要求
根据PDF协会发布的结构元素最佳实践指南(第31页),注解需遵循以下构造规则:
Annot结构元素用于包裹除链接和组件外的所有注解,注解分为两类:
- 标记注解或用作标记注解的类型:
Annot结构元素需包裹标记内容和指向实际注解的对象引用,支持嵌套。- 其他注解:
Annot结构元素通常仅需包裹指向实际注解的引用。示例:
<Annot>
内容 OBJR (指向<Annot>类型的Highlight)创建规则
若标记注解对应的待标记内容尚未被结构元素包裹,无法直接与注解关联,会出现适配问题。这种情况下需要先识别并隔离待标记内容,将其包裹在标记内容序列中,再与实际注解一同关联到结构元素。
可能需要对标记内容周边的其他标记内容序列和结构元素做调整。消费规则
向用户展示标记内容时,应包含以下信息:
- 被标记的内容
- 内容被标记的提示及标记注解的类型
- 注解content字段的内容
- 所有回复该注解的其他注解
虽PDF/UA-1未强制要求兼容阅读器实现该能力,但处理器还应支持展示注解的属性,例如作者、主题、状态、时间、勾选状态等。
现有实现说明
具备Java开发经验,因PDFBox公开文档较少,参考了公开的无障碍PDF构造示例实现代码,尝试了将注解代码嵌套在内容块内、单独拆分结构等方案均未解决问题,未排查到错误点。相关实现代码如下:
public PDStructureElement drawElement(Cell textCell, float x, float y, int height, PDStructureElement parent, String structType, int pageIndex, PDPage page) throws IOException { // 初始化带MCID的标记内容元素,创建对应的结构元素容器 PDPageContentStream contentsElement = new PDPageContentStream(pdf, pages.get(pageIndex), PDPageContentStream.AppendMode.APPEND, false); currentElem = addContentToParent(pdf, null, structType, pages.get(pageIndex), parent, pageIndex); setNextMarkedContentDictionary(); contentsElement.beginMarkedContent(COSName.P, PDPropertyList.create(currentMarkedContentDictionary)); // 示例使用Old English字体渲染内容 drawCellText(textCell, (PAGE_WIDTH - (int) (templateRegularFont.getStringWidth(textCell.getDefaultText()) / 1000 * textCell.getFontSize())) / 2, y + height + textCell.getFontSize(), contentsElement); // 结束标记内容,将P结构元素追加到父容器 contentsElement.endMarkedContent(); addContentToParent(pdf, COSName.P, null, pages.get(pageIndex), currentElem, pageIndex); contentsElement.close(); // 存在文本注解时的处理逻辑 if (textCell.getAnnotationText() != null) { float stringWidth = 0.00f; stringWidth = templateRegularFont .getStringWidth(textCell.getDefaultText().substring(0, textCell.getDefaultText().length())) / 1000 * textCell.getFontSize(); annotations = pages.get(pageIndex).getAnnotations(); PDRectangle rect = new PDRectangle(x + 5, PAGE_HEIGHT - height - y - textCell.getFontSize() - 5, stringWidth, textCell.getFontSize() + 5); PDPageContentStream contentsAnnot = new PDPageContentStream(pdf, pages.get(pageIndex), PDPageContentStream.AppendMode.APPEND, false); currentElem = addContentToParent(pdf, null, structType, pages.get(pageIndex), parent, pageIndex); setNextMarkedContentDictionary(); contentsAnnot.beginMarkedContent(COSName.ANNOT, PDPropertyList.create(currentMarkedContentDictionary)); // 构造文本注解对象 PDAnnotationText annotText = new PDAnnotationText(); annotText.setAnnotationName(textCell.getDefaultText()); annotText.setContents(textCell.getDefaultText()); annotText.setHidden(false); annotText.setInvisible(false); annotText.setName("Note"); annotText.setNoRotate(true); annotText.setNoView(false); annotText.setNoZoom(true); annotText.setOpen(false); annotText.setPage(pages.get(pageIndex)); annotText.setPrinted(true); annotText.setRectangle(rect); annotText.setStructParent(currentStructParent); PDAppearanceDictionary appearance = new PDAppearanceDictionary(); appearance.getCOSObject().setDirect(true); PDAppearanceStream appearanceStream = new PDAppearanceStream(pdf); appearanceStream.setBBox(rect); appearance.setNormalAppearance(appearanceStream); annotText.setAppearance(appearance); PDFTemplateStructure pdfStructure = new PDFTemplateStructure(); pdfStructure.setAppearanceDictionary(appearance); annotations.add(annotText); pages.get(pageIndex).setAnnotations(annotations); PDStructureElement fieldElem = new PDStructureElement(StandardStructureTypes.P, parent); fieldElem.setPage(pages.get(pageIndex)); COSArray kArray = new COSArray(); kArray.add(COSInteger.get(currentMCID)); fieldElem.getCOSObject().setItem(COSName.K, kArray); // 添加注解的对象引用用于标记 PDObjectReference objectReference = new PDObjectReference(); objectReference.setReferencedObject(annotText); annotationRefs.add(objectReference); addWidgetContent(pdf, annotationRefs.get(annotationRefs.size() - 1), fieldElem, StandardStructureTypes.P, pageIndex, pages, page); contentsAnnot.endMarkedContent(); addContentToParent(pdf, COSName.ANNOT, null, pages.get(pageIndex), currentElem, pageIndex); contentsAnnot.close(); COSDictionary dict = pages.get(pageIndex).getCOSObject(); while (dict.containsKey(COSName.PARENT)) { COSBase COSParent = dict.getDictionaryObject(COSName.PARENT); if (COSParent instanceof COSDictionary) { dict = (COSDictionary) COSParent; dict.setNeedToBeUpdated(true); } } } return currentElem; }
若需查看其他代码块可补充提供。
内容的提问来源于stack exchange,提问作者MartinLondonUK1977
相关产品推荐
相关产品推荐

