You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生成PDF/UA合规PDF遇PAC检查报错:Test Object Not Tagged

问题

我正在开发生成符合PDF/UA标准的PDF文件,核心目标是满足无障碍标准并通过PAC(PDF Accessibility Checker)工具检测。目前遇到的问题是,PAC工具持续标记我的PDF存在错误:"Test Object Not Tagged",这表明必要的标签可能缺失或实现不当,但我不清楚具体遗漏了哪些内容。

我已完成以下操作:

  • 定义文档结构元素
  • 在PDF内容生成代码中使用正确的标签
  • 应用我认为正确的无障碍元数据和设置

然而标签仍未被正确识别,似乎遗漏了某些核心内容。请问为实现PDF/UA合规的正确标签,我可能忽略了哪些步骤?若能指出我可能出错的环节,或需检查的关键标签元素,将不胜感激。

我的代码如下:

public void newPdf() {
        int mcidCounter = 0; // 从0开始
        int structParentCounter = 0; // 从0开始
        PDDocument document = new PDDocument();
        PDPage page = new PDPage(PDRectangle.A4);
        document.addPage(page);

        // 设置页面的StructParents条目
        page.setStructParents(structParentCounter); // structParentCounter为0

        PDPageContentStream contentStream = new PDPageContentStream(document, page);
        PDType0Font font = loadFont(FontEnum.BUNDES_SANS_WEB_REGULAR, document);

        // 将字体添加到页面资源
        PDResources resources = page.getResources();
        if (resources == null) {
            resources = new PDResources();
            page.setResources(resources);
        }
        resources.add(font);

        PDDocumentCatalog catalog = document.getDocumentCatalog();
        PDStructureTreeRoot structureTreeRoot = new PDStructureTreeRoot();
        catalog.setStructureTreeRoot(structureTreeRoot);

        catalog.setLanguage("de-DE"); // 将文档语言设置为德语

        PDMarkInfo markInfo = new PDMarkInfo();
        markInfo.setMarked(true);
        catalog.setMarkInfo(markInfo);

        // 创建文档结构元素
        PDStructureElement documentElement = new PDStructureElement(StandardStructureTypes.DOCUMENT, structureTreeRoot);
        structureTreeRoot.appendKid(documentElement);

        // 创建段落结构元素
        PDStructureElement paragraphElement = new PDStructureElement(StandardStructureTypes.P, documentElement);
        paragraphElement.setPage(page);
        documentElement.appendKid(paragraphElement);

        // 准备带MCID的标记内容
        COSDictionary markedContentDictionary = new COSDictionary();
        markedContentDictionary.setInt(COSName.MCID, mcidCounter);

        // 开始标记内容
        contentStream.beginMarkedContent(COSName.P, PDPropertyList.create(markedContentDictionary));
        contentStream.setFont(font, 12);
        contentStream.beginText();
        contentStream.newLineAtOffset(50, 700);
        contentStream.showText("Hallo Welt");
        contentStream.endText();
        contentStream.endMarkedContent();

        // 关闭内容流
        contentStream.close();

        // 创建父树并关联结构元素
        COSDictionary parentTreeRoot = new COSDictionary();
        PDNumberTreeNode parentTree = new PDNumberTreeNode(parentTreeRoot, COSBase.class);

        // 映射StructParent到结构元素
        Map<Integer, COSObjectable> parentTreeMap = new HashMap<>();
        parentTreeMap.put(structParentCounter, paragraphElement);
        parentTree.setNumbers(parentTreeMap);
        structureTreeRoot.setParentTree(parentTree);

        // 保存文档
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        document.save(outputStream);
        byte[] pdfBytes = outputStream.toByteArray();
        document.close();


        Path actualPath = Path.of("src/test/resources/pdf/small_test.pdf");
        Files.write(actualPath, pdfBytes, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
        

    }

PAC工具检测结果显示存在"Test Object Not Tagged"错误。


分析与解决方案

针对你遇到的PAC检测错误,以下是代码中遗漏的关键环节及修复方案:

1. 未关联标记内容(MCID)与结构元素

你创建了带MCID的标记内容,但段落结构元素没有绑定对应的MCID引用。PDF/UA要求结构元素必须明确关联页面中的标记内容,否则阅读器无法将结构标签与实际文本对应。

修复代码:在创建段落结构元素后添加MCID关联:

// 创建段落结构元素后添加这行
paragraphElement.appendKid(new PDMCID(mcidCounter));

2. 父树(Parent Tree)映射逻辑错误

父树的作用是将页面的StructParents值映射到顶层结构元素(DOCUMENT),而非直接映射到段落元素。当前代码把页面的StructParents绑定到段落元素,不符合PDF规范,导致结构树无法正确遍历。

修复代码:修正父树的映射目标:

Map<Integer, COSObjectable> parentTreeMap = new HashMap<>();
// 将页面的StructParents映射到文档根结构元素
parentTreeMap.put(structParentCounter, documentElement);
parentTree.setNumbers(parentTreeMap);
structureTreeRoot.setParentTree(parentTree);

3. 缺少显式的角色映射(Role Map)

虽然使用了标准结构标签,但显式声明角色映射能确保阅读器正确识别结构类型,这是PDF/UA的推荐要求。

添加代码:在设置结构树根后添加角色映射:

COSDictionary roleMap = new COSDictionary();
// 绑定标准结构类型到对应角色(可根据需求扩展)
roleMap.setItem(COSName.getPDFName("P"), COSName.getPDFName("Paragraph"));
roleMap.setItem(COSName.getPDFName("Document"), COSName.getPDFName("Document"));
structureTreeRoot.setRoleMap(roleMap);

4. 验证字体的无障碍属性

PDF/UA要求文本使用可嵌入的Unicode字体,确保屏幕阅读器能正确解析文本。检查loadFont方法是否正确嵌入字体,可添加以下代码确保字体嵌入:

// 确保字体被嵌入(子集化或完整嵌入)
font.setSubset(true);

5. 可选优化:添加文档大纲

添加大纲不仅能提升无障碍体验,还能帮助PAC工具确认结构完整性,可按需添加:

PDDocumentOutline outline = new PDDocumentOutline();
catalog.setDocumentOutline(outline);
PDOutlineItem rootItem = new PDOutlineItem();
rootItem.setTitle("文档内容");
outline.addFirst(rootItem);

内容的提问来源于stack exchange,提问作者CodeComposer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 21:04:54