【发布时间】:2019-06-12 15:46:26
【问题描述】:
我想让 Jasper 导出的 PDF 符合 PDF/UA 标准,但 Jasper 的限制阻止了我这样做。客户向我们施压,要求我们正确完成这项工作。
PDF/UA 有很多要求,包括但不限于显示标题和语言、嵌入字体以及在图像中添加替代文本。到目前为止,我已经在 Jaspersoft 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);
}
}
我注意到 Adobe 的 Preflight 检查器以及我们的客户报告了一些错误,如下所列:
- 存在非标准标签
- 圆形角色图
- 每页左上角附加未知的锚单元
- 表格无法在表格编辑器视图中正确识别
Images 显示我的问题。非常感谢您提供这方面的任何帮助。
【问题讨论】:
-
我删除了 PDFBox 标签。虽然您确实使用 PDFBox 来填充 xmp 内容,但您的问题与此无关。您提到的所有四个错误都表明结构树使用存在问题。出错的地方要么在 Jasper 中,要么在 itext 中。
-
JR 中使用的 iText 版本 2.1.7 是从对正确标记的 PDF 没有太大兴趣的时候开始的。所以即使它包含一些标记支持,我怀疑它是否经过适当的测试和强化。
-
有什么方法可以添加单元格和相关的标题 ID?
-
查看此处和 cmets 中的链接:stackoverflow.com/questions/56231681 但这样做真的很痛苦。我认为答案只是表面问题。
-
所以我们必须完全重写我们的报告引擎才能实现这一点?
标签: jasper-reports itext pdf-generation