【问题标题】:Jasper Report PDFs Are Not PDF/UA CompliantJasper 报告 PDF 不兼容 PDF/UA
【发布时间】:2019-06-12 15:46:26
【问题描述】:

我想让 Jasper 导出的 PDF 符合 PDF/UA 标准,但 Jasper 的限制阻止了我这样做。客户向我们施压,要求我们正确完成这项工作。

PDF/UA 有很多要求,包括但不限于显示标题和语言、嵌入字体以及在图像中添加替代文本。到目前为止,我已经在 J​​aspersoft Studio 中设置了所有 508 个 PDF 标记、设置属性以显示标题和语言、嵌入字体以及为图像添加 alt 文本。我还通过 Apache PDFBox 将 PDF/UA 标识符附加到输出 PDF(即生成 PDF 之后)。我们将 Jaspersoft Studio v6.6.0 与 Jasper Reports Library v6.4.0 和 Oracle 用于数据库。根据我的阅读,Jasper 在这方面的能力有限,因为 itext 由于许可问题被降级到 v2.1.7.js6。

<jasperReport xlmns=...>
        ... // other properties
        <property name="net.sf.jasperreports.awt.ignore.missing.font" value="false"/>
        <property name="net.sf.jasperreports.export.xls.detect.cell.type" value="false"/>
        <property name="net.sf.jasperreports.export.xls.sheet.names.all" value="REPORT SHEET NAME"/>
        <property name="net.sjasperreports.default.pdf.font.name" value="Times-Roman"/>
        <property name="net.sf.jasperreports.export.xls.ignore.graphics" value="false"/>
        <property name="net.sf.jasperreports.default.pdf.embedded" value="true"/>
        <property name="net.sf.jasperreports.export.pdf.metadata.title" value="MY REPORT TITLE"/>
        <property name="net.sf.jasperreports.export.pdf.display.metadata.title" value="true"/>
        <property name="net.sf.jasperreports.export.pdf.tagged" value="true"/>
        <property name="net.sf.jasperreports.export.pdf.tag.language" value="EN-US"/>
        ... // parameters, stored proc call, headings, etc.
        <!-- Possible PDF 508 tags to be set on text fields -->
        <property name="net.sf.jasperreports.export.pdf.tag.table" value="start"/>
        <property name="net.sf.jasperreports.export.pdf.tag.th" value="full"/>
        <property name="net.sf.jasperreports.export.pdf.tag.tr" value="start">
        <property name="net.sf.jasperreports.export.pdf.tag.td" value="full">
        <property name="net.sf.jasperreports.export.pdf.tag.tr" value="end">
        <property name="net.sf.jasperreports.export.pdf.tag.table" value="start"/>
        ...
</jasperReport>
... // other imports
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.XMPSchema;
import org.apache.xmpbox.xml.XmpSerializer;
... // more imports

public class ReportResult {
   ... // other methods

    /*
     * @param pdf - The pdf instance created from BAOS
     * @param title - Document
     * @return BAOS containing metadata (UA-identifier, title)
     */
    private ByteArrayOutputStream appendXMPMetaData(PDDocument pdf, String title) throws TransformerException, IOException {
        XMPMetadata xmp = XMPMetadata.createXMPMetadata();
        xmp.createAndAddDublinCoreSchema();
        xmp.getDublinCoreSchema().setTitle(title);
        xmp.getDublinCoreSchema().setDescription(title);
        xmp.createAndAddPDFAExtensionSchemaWithDefaultNS();
        xmp.getPDFExtensionSchema().addNamespace("http://www.aiim.org/pdfa/ns/schema#", "pdfaSchema");
        xmp.getPDFExtensionSchema().addNamespace("http://www.aiim.org/pdfa/ns/property#", "pdfaProperty");
        xmp.getPDFExtensionSchema().addNamespace("http://www.aiim.org/pdfua/ns/id/", "pdfuaid");

        XMPSchema uaSchema = new XMPSchema(XMPMetadata.createXMPMetadata(),
                "pdfaSchema", "pdfaSchema", "pdfaSchema");
        uaSchema.setTextPropertyValue("schema", "PDF/UA Universal Accessibility Schema");
        uaSchema.setTextPropertyValue("namespaceURI", "http://www.aiim.org/pdfua/ns/id/");
        uaSchema.setTextPropertyValue("prefix", "pdfuaid");

        XMPSchema uaProp = new XMPSchema(XMPMetadata.createXMPMetadata(),"pdfaProperty", "pdfaProperty", "pdfaProperty");
        uaProp.setTextPropertyValue("name", "part");
        uaProp.setTextPropertyValue("valueType", "Integer");
        uaProp.setTextPropertyValue("category", "internal");
        uaProp.setTextPropertyValue("description", "Indicates, which part of ISO 14289 standard is followed");
        uaSchema.addUnqualifiedSequenceValue("property", uaProp);

        xmp.getPDFExtensionSchema().addBagValue("schemas", uaSchema);
        xmp.getPDFExtensionSchema().setPrefix("pdfuaid");
        xmp.getPDFExtensionSchema().setTextPropertyValue("part", "1");

        XmpSerializer serializer = new XmpSerializer();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        serializer.serialize(xmp, baos, true);

        PDMetadata metadata = new PDMetadata(pdf);
        metadata.importXMPMetadata(baos.toByteArray());
        pdf.getDocumentCatalog().setMetadata(metadata);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        pdf.save(byteArrayOutputStream);
        pdf.close();

        return byteArrayOutputStream;
    } 

    protected void getJasperPDFDoc(ReportConfig reportConfig) throws IOException, TransformerException {

        List<ReportParameter> reportParams = reportConfig.getReportParams();

        ... // cookies and printer config

        Map imagesMap = new HashMap();
        request.getSession(true).setAttribute("IMAGES_MAP", imagesMap);

        ByteArrayOutputStream bs = ReportAccess.Instance.getInstance().generateJasperReport(
                getCurrentUserId(), getCurrentUserName(), reportConfig, "PDF",
                 reportParams, getTmpImageUri(),
                 imagesMap, rptTemplateLoc);

        if (bs != null) {
            if (reportConfig.doPrint) {
                response.setContentType("text/html");
            } else {
                log.debug("Got PDF report data");
                String fileName = getReportFileName(reportConfig) + ".pdf";
                response.setContentType("application/pdf");
                String dispositionProperty = "attachment; filename=" + fileName;
                response.setHeader("Content-disposition", dispositionProperty);
            }

            PDDocument pdf = PDDocument.load(new ByteArrayInputStream(bs.toByteArray()));
            ByteArrayOutputStream baosWithMetaData = appendXMPMetaData(pdf, reportConfig.getDisplayName());

            response.setHeader("Content-length", Integer.toString(baosWithMetaData.size()));
            ServletOutputStream os = response.getOutputStream();
            baosWithMetaData.writeTo(os);

            os.flush();
            os.close();
        } else {
            displayError("PDF");
        }
     }

     ... // other methods
}
/* REPORT MANAGER CLASS */
private static void generatePDFDoc(JasperPrint jasperPrint, ByteArrayOutputStream f) {

        try {

            JasperPrint jr = moveTableOfContents(jasperPrint);
            JRPdfExporter exporter  = new JRPdfExporter();
            exporter.setExporterInput(new SimpleExporterInput(jr));
            exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(f));

            //configuration
            SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
            configuration.setCompressed(true);
            configuration.setTagged(true);
            configuration.setTagLanguage("EN-US");

            //set configuration
            exporter.setConfiguration(configuration);

            //export to PDF
            exporter.exportReport();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

我注意到 Adob​​e 的 Preflight 检查器以及我们的客户报告了一些错误,如下所列:

  1. 存在非标准标签
  2. 圆形角色图
  3. 每页左上角附加未知的锚单元
  4. 表格无法在表格编辑器视图中正确识别

Images 显示我的问题。非常感谢您提供这方面的任何帮助。

【问题讨论】:

  • 我删除了 PDFBox 标签。虽然您确实使用 PDFBox 来填充 xmp 内容,但您的问题与此无关。您提到的所有四个错误都表明结构树使用存在问题。出错的地方要么在 Jasper 中,要么在 itext 中。
  • JR 中使用的 iText 版本 2.1.7 是从对正确标记的 PDF 没有太大兴趣的时候开始的。所以即使它包含一些标记支持,我怀疑它是否经过适当的测试和强化。
  • 有什么方法可以添加单元格和相关的标题 ID?
  • 查看此处和 cmets 中的链接:stackoverflow.com/questions/56231681 但这样做真的很痛苦。我认为答案只是表面问题。
  • 所以我们必须完全重写我们的报告引擎才能实现这一点?

标签: jasper-reports itext pdf-generation


【解决方案1】:

如果您想让事情变得更简单,但方式不同,可以选择 PD4ML v4。页面上有一个简约样本:https://pd4ml.tech/pdf-ua/

它使用来自输入 HTML/CSS 的可用结构和元信息来生成有效的标记 PDF/UA。

如果目标只是通过 PDF/UA 文件格式验证(例如,通过 Adob​​e 的 Preflight 检查器),只需选择 Constants.PDFUA 作为输出格式就足够了。

pd4ml.writePDF(fos, Constants.PDFUA);

如果目标是生成符合马特宏协议的 PDF(并通过 PAC3 https://www.access-for-all.ch/en/pdf-lab/pdf-accessibility-checker-pac.html 的验证),您很可能还需要对齐输入的 HTML:添加 TITLE、ALT 和 LANG 属性,以确保表结构和标题层次结构一致等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 2016-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多