diff --git a/UI/org.eclipse.birt.report.designer.core/src/org/eclipse/birt/report/designer/core/model/schematic/TableHandleAdapter.java b/UI/org.eclipse.birt.report.designer.core/src/org/eclipse/birt/report/designer/core/model/schematic/TableHandleAdapter.java index 73822082f44..9dd0971e762 100644 --- a/UI/org.eclipse.birt.report.designer.core/src/org/eclipse/birt/report/designer/core/model/schematic/TableHandleAdapter.java +++ b/UI/org.eclipse.birt.report.designer.core/src/org/eclipse/birt/report/designer/core/model/schematic/TableHandleAdapter.java @@ -59,7 +59,7 @@ public class TableHandleAdapter extends ReportItemtHandleAdapter { private static final String NAME_NULL = ""; //$NON-NLS-1$ private static final String NAME_DETAIL = Messages.getString("TableHandleAdapter.name.detail"); //$NON-NLS-1$ private static final String NAME_FOOTER = Messages.getString("TableHandleAdapter.name.footer"); //$NON-NLS-1$ - private static final String NAME_HEADRER = Messages.getString("TableHandleAdapter.name.header"); //$NON-NLS-1$ + private static final String NAME_HEADER = Messages.getString("TableHandleAdapter.name.header"); //$NON-NLS-1$ private static final String TRANS_LABEL_NOT_INCLUDE = Messages .getString("TableHandleAdapter.transLabel.notInclude"); //$NON-NLS-1$ private static final String TRANS_LABEL_INCLUDE = Messages.getString("TableHandleAdapter.transLabel.include"); //$NON-NLS-1$ @@ -1502,7 +1502,7 @@ public void deleteRowInSlotHandle(int id) throws SemanticException { protected static String getOperationName(int id) { switch (id) { case HEADER: - return NAME_HEADRER; + return NAME_HEADER; case FOOTER: return NAME_FOOTER; case DETAIL: diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/.project b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/.project new file mode 100644 index 00000000000..d8555feba04 --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/.project @@ -0,0 +1,12 @@ + + + pdf-ua + + + + + + + org.eclipse.birt.report.designer.ui.reportprojectnature + + diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkHTML.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkHTML.rptdesign new file mode 100644 index 00000000000..ffea1559595 --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkHTML.rptdesign @@ -0,0 +1,381 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + Das ist der Titel + in + /templates/blank_report.gif + ltr + 96 + de_DE + 1.7 + PDF.A1A + PDF.UA-1 + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTCODE + dimension + PRODUCTCODE + PRODUCTCODE + + + PRODUCTNAME + dimension + PRODUCTNAME + PRODUCTNAME + + + PRODUCTDESCRIPTION + dimension + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + + + BUYPRICE + measure + BUYPRICE + BUYPRICE + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + HTMLDESCRIPTION + dimension + HTMLDESCRIPTION + HTMLDESCRIPTION + + + + + + + 1 + PRODUCTCODE + string + + + 2 + PRODUCTNAME + string + + + 3 + PRODUCTDESCRIPTION + string + + + 4 + BUYPRICE + float + + + 5 + PRODUCTLINE + string + + + 6 + HTMLDESCRIPTION + string + + + + 100 + CM + + + 1 + PRODUCTCODE + PRODUCTCODE + string + 12 + + + 2 + PRODUCTNAME + PRODUCTNAME + string + 12 + + + 3 + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + string + 12 + + + 4 + BUYPRICE + BUYPRICE + float + 8 + + + 5 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 6 + HTMLDESCRIPTION + HTMLDESCRIPTION + string + 2005 + + + + + + 2.0 + + + + + + + PRODUCTCODE + 1 + + 12 + 15 + 0 + Nullable + + PRODUCTCODE + + + + PRODUCTCODE + + 15 + + + + + + + PRODUCTNAME + 2 + + 12 + 70 + 0 + Nullable + + PRODUCTNAME + + + + PRODUCTNAME + + 70 + + + + + + + PRODUCTLINE + 3 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + PRODUCTSCALE + 4 + + 12 + 10 + 0 + Nullable + + PRODUCTSCALE + + + + PRODUCTSCALE + + 10 + + + + + + + PRODUCTVENDOR + 5 + + 12 + 50 + 0 + Nullable + + PRODUCTVENDOR + + + + PRODUCTVENDOR + + 50 + + + + + + + PRODUCTDESCRIPTION + 6 + + 12 + 32700 + 0 + Nullable + + PRODUCTDESCRIPTION + + + + PRODUCTDESCRIPTION + + 32700 + + + + + + + QUANTITYINSTOCK + 7 + + 4 + 10 + 0 + Nullable + + QUANTITYINSTOCK + + + + QUANTITYINSTOCK + + 11 + + + + + + + BUYPRICE + 8 + + 8 + 15 + 0 + Nullable + + BUYPRICE + + + + BUYPRICE + + 24 + + + + + + + MSRP + 9 + + 8 + 15 + 0 + Nullable + + MSRP + + + + MSRP + + 24 + + + + + + + +]]> + + + + + + + + a4 + + + + + '<p>Text with <a href="https://github.com/eclipse-birt/birt">hyperlink</a></p>' + html + + + diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkImage.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkImage.rptdesign new file mode 100644 index 00000000000..b68fe079e97 --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkImage.rptdesign @@ -0,0 +1,56 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + Das ist der Titel + in + /templates/blank_report.gif + ltr + 96 + de_DE + 1.7 + PDF.A1A + PDF.UA-1 + true + + + + + + a4 + + + + + + + + + + 5.171717171717172in + 5.171717171717172in + "Eine gemalte Sonne mit Gesicht" + file + ./sonne.png + + + hyperlink + Die Website der Eclipse Stiftung + https://eclipse.org + _blank + + + + + diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkLabel.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkLabel.rptdesign new file mode 100644 index 00000000000..d22fc4387a2 --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/HyperlinkLabel.rptdesign @@ -0,0 +1,390 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + Das ist der Titel + in + /templates/blank_report.gif + ltr + 96 + de_DE + 1.7 + PDF.A3A + PDF.UA-1 + false + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTCODE + dimension + PRODUCTCODE + PRODUCTCODE + + + PRODUCTNAME + dimension + PRODUCTNAME + PRODUCTNAME + + + PRODUCTDESCRIPTION + dimension + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + + + BUYPRICE + measure + BUYPRICE + BUYPRICE + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + HTMLDESCRIPTION + dimension + HTMLDESCRIPTION + HTMLDESCRIPTION + + + + + + + 1 + PRODUCTCODE + string + + + 2 + PRODUCTNAME + string + + + 3 + PRODUCTDESCRIPTION + string + + + 4 + BUYPRICE + float + + + 5 + PRODUCTLINE + string + + + 6 + HTMLDESCRIPTION + string + + + + 100 + CM + + + 1 + PRODUCTCODE + PRODUCTCODE + string + 12 + + + 2 + PRODUCTNAME + PRODUCTNAME + string + 12 + + + 3 + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + string + 12 + + + 4 + BUYPRICE + BUYPRICE + float + 8 + + + 5 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 6 + HTMLDESCRIPTION + HTMLDESCRIPTION + string + 2005 + + + + + + 2.0 + + + + + + + PRODUCTCODE + 1 + + 12 + 15 + 0 + Nullable + + PRODUCTCODE + + + + PRODUCTCODE + + 15 + + + + + + + PRODUCTNAME + 2 + + 12 + 70 + 0 + Nullable + + PRODUCTNAME + + + + PRODUCTNAME + + 70 + + + + + + + PRODUCTLINE + 3 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + PRODUCTSCALE + 4 + + 12 + 10 + 0 + Nullable + + PRODUCTSCALE + + + + PRODUCTSCALE + + 10 + + + + + + + PRODUCTVENDOR + 5 + + 12 + 50 + 0 + Nullable + + PRODUCTVENDOR + + + + PRODUCTVENDOR + + 50 + + + + + + + PRODUCTDESCRIPTION + 6 + + 12 + 32700 + 0 + Nullable + + PRODUCTDESCRIPTION + + + + PRODUCTDESCRIPTION + + 32700 + + + + + + + QUANTITYINSTOCK + 7 + + 4 + 10 + 0 + Nullable + + QUANTITYINSTOCK + + + + QUANTITYINSTOCK + + 11 + + + + + + + BUYPRICE + 8 + + 8 + 15 + 0 + Nullable + + BUYPRICE + + + + BUYPRICE + + 24 + + + + + + + MSRP + 9 + + 8 + 15 + 0 + Nullable + + MSRP + + + + MSRP + + 24 + + + + + + + +]]> + + + + + + + + a4 + + + + + + diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTable.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTable.rptdesign index db732c82cd4..0842ed3612e 100644 --- a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTable.rptdesign +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTable.rptdesign @@ -1,13 +1,13 @@ Henning von Bargen - Eclipse BIRT Designer Version 4.17.0.qualifier Build <@BUILD@> - Das ist der Titel + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + A table with a group header in /templates/blank_report.gif ltr 96 - de_DE + en_US 1.7 PDF.A1A PDF.UA-1 diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTableHTML.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTableHTML.rptdesign new file mode 100644 index 00000000000..25a34f373ae --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complexTableHTML.rptdesign @@ -0,0 +1,500 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + This is the document title + in + /templates/blank_report.gif + ltr + 96 + en_US + 1.7 + PDF.A1A + PDF.UA-1 + true + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTCODE + dimension + PRODUCTCODE + PRODUCTCODE + + + PRODUCTNAME + dimension + PRODUCTNAME + PRODUCTNAME + + + PRODUCTDESCRIPTION + dimension + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + + + BUYPRICE + measure + BUYPRICE + BUYPRICE + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + HTMLDESCRIPTION + dimension + HTMLDESCRIPTION + HTMLDESCRIPTION + + + + + + + 1 + PRODUCTCODE + string + + + 2 + PRODUCTNAME + string + + + 3 + PRODUCTDESCRIPTION + string + + + 4 + BUYPRICE + float + + + 5 + PRODUCTLINE + string + + + 6 + HTMLDESCRIPTION + string + + + + 100 + CM + + + 1 + PRODUCTCODE + PRODUCTCODE + string + 12 + + + 2 + PRODUCTNAME + PRODUCTNAME + string + 12 + + + 3 + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + string + 12 + + + 4 + BUYPRICE + BUYPRICE + float + 8 + + + 5 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 6 + HTMLDESCRIPTION + HTMLDESCRIPTION + string + 2005 + + + + + + 2.0 + + + + + + + PRODUCTCODE + 1 + + 12 + 15 + 0 + Nullable + + PRODUCTCODE + + + + PRODUCTCODE + + 15 + + + + + + + PRODUCTNAME + 2 + + 12 + 70 + 0 + Nullable + + PRODUCTNAME + + + + PRODUCTNAME + + 70 + + + + + + + PRODUCTLINE + 3 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + PRODUCTSCALE + 4 + + 12 + 10 + 0 + Nullable + + PRODUCTSCALE + + + + PRODUCTSCALE + + 10 + + + + + + + PRODUCTVENDOR + 5 + + 12 + 50 + 0 + Nullable + + PRODUCTVENDOR + + + + PRODUCTVENDOR + + 50 + + + + + + + PRODUCTDESCRIPTION + 6 + + 12 + 32700 + 0 + Nullable + + PRODUCTDESCRIPTION + + + + PRODUCTDESCRIPTION + + 32700 + + + + + + + QUANTITYINSTOCK + 7 + + 4 + 10 + 0 + Nullable + + QUANTITYINSTOCK + + + + QUANTITYINSTOCK + + 11 + + + + + + + BUYPRICE + 8 + + 8 + 15 + 0 + Nullable + + BUYPRICE + + + + BUYPRICE + + 24 + + + + + + + MSRP + 9 + + 8 + 15 + 0 + Nullable + + MSRP + + + + MSRP + + 24 + + + + + + + +]]> + + + + + + + + a4 + + + + + + + + Products + + + PRODUCTNAME + PRODUCTNAME + dataSetRow["PRODUCTNAME"] + string + + + PRODUCTLINE + PRODUCTLINE + dataSetRow["PRODUCTLINE"] + string + + + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + dataSetRow["PRODUCTDESCRIPTION"] + string + + + PRODUCTCODE + PRODUCTCODE + dataSetRow["PRODUCTCODE"] + string + + + BUYPRICE + BUYPRICE + dataSetRow["BUYPRICE"] + float + + + HTMLDESCRIPTION + HTMLDESCRIPTION + dataSetRow["HTMLDESCRIPTION"] + string + + + 9999 + This is the table caption! + + + +
+ + #80FF00 + + "product" + + + + "descr" + + + + "price" + + + +
+ + TDProductLine + row["PRODUCTLINE"] + row["PRODUCTLINE"] + + row["PRODUCTLINE"] + + false + false +
+ + auto + + 3 + 1 + "bm-" + row["PRODUCTLINE"] + #80FFFF + + row["HTMLDESCRIPTION"] + html + + + +
+
+ + + auto + + "bm-" + row["PRODUCTLINE"] + ",product" + + PRODUCTNAME + + + + "bm-" + row["PRODUCTLINE"] + ",descr" + + PRODUCTDESCRIPTION + + + + "bm-" + row["PRODUCTLINE"] + ",price" + + BUYPRICE + + + + +
+ +
diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complextTable.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complextTable.rptdesign new file mode 100644 index 00000000000..8312cda7a76 --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/complextTable.rptdesign @@ -0,0 +1,499 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + Example of a table + in + /templates/blank_report.gif + ltr + 96 + en_US + 1.7 + PDF.A1A + PDF.UA-1 + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTCODE + dimension + PRODUCTCODE + PRODUCTCODE + + + PRODUCTNAME + dimension + PRODUCTNAME + PRODUCTNAME + + + PRODUCTDESCRIPTION + dimension + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + + + BUYPRICE + measure + BUYPRICE + BUYPRICE + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + HTMLDESCRIPTION + dimension + HTMLDESCRIPTION + HTMLDESCRIPTION + + + + + + + 1 + PRODUCTCODE + string + + + 2 + PRODUCTNAME + string + + + 3 + PRODUCTDESCRIPTION + string + + + 4 + BUYPRICE + float + + + 5 + PRODUCTLINE + string + + + 6 + HTMLDESCRIPTION + string + + + + 100 + CM + + + 1 + PRODUCTCODE + PRODUCTCODE + string + 12 + + + 2 + PRODUCTNAME + PRODUCTNAME + string + 12 + + + 3 + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + string + 12 + + + 4 + BUYPRICE + BUYPRICE + float + 8 + + + 5 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 6 + HTMLDESCRIPTION + HTMLDESCRIPTION + string + 2005 + + + + + + 2.0 + + + + + + + PRODUCTCODE + 1 + + 12 + 15 + 0 + Nullable + + PRODUCTCODE + + + + PRODUCTCODE + + 15 + + + + + + + PRODUCTNAME + 2 + + 12 + 70 + 0 + Nullable + + PRODUCTNAME + + + + PRODUCTNAME + + 70 + + + + + + + PRODUCTLINE + 3 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + PRODUCTSCALE + 4 + + 12 + 10 + 0 + Nullable + + PRODUCTSCALE + + + + PRODUCTSCALE + + 10 + + + + + + + PRODUCTVENDOR + 5 + + 12 + 50 + 0 + Nullable + + PRODUCTVENDOR + + + + PRODUCTVENDOR + + 50 + + + + + + + PRODUCTDESCRIPTION + 6 + + 12 + 32700 + 0 + Nullable + + PRODUCTDESCRIPTION + + + + PRODUCTDESCRIPTION + + 32700 + + + + + + + QUANTITYINSTOCK + 7 + + 4 + 10 + 0 + Nullable + + QUANTITYINSTOCK + + + + QUANTITYINSTOCK + + 11 + + + + + + + BUYPRICE + 8 + + 8 + 15 + 0 + Nullable + + BUYPRICE + + + + BUYPRICE + + 24 + + + + + + + MSRP + 9 + + 8 + 15 + 0 + Nullable + + MSRP + + + + MSRP + + 24 + + + + + + + +]]> + + + + + + + + a4 + + + + + + + + Products + + + PRODUCTNAME + PRODUCTNAME + dataSetRow["PRODUCTNAME"] + string + + + PRODUCTLINE + PRODUCTLINE + dataSetRow["PRODUCTLINE"] + string + + + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + dataSetRow["PRODUCTDESCRIPTION"] + string + + + PRODUCTCODE + PRODUCTCODE + dataSetRow["PRODUCTCODE"] + string + + + BUYPRICE + BUYPRICE + dataSetRow["BUYPRICE"] + float + + + HTMLDESCRIPTION + HTMLDESCRIPTION + dataSetRow["HTMLDESCRIPTION"] + string + + + 9999 + This is the table caption! + + + +
+ + #80FF00 + + "product" + + + + "descr" + + + + "price" + + + +
+ + TDProductLine + row["PRODUCTLINE"] + row["PRODUCTLINE"] + + row["PRODUCTLINE"] + + false + false +
+ + auto + + 3 + 1 + "bm-" + row["PRODUCTLINE"] + #80FFFF + + row["HTMLDESCRIPTION"] + html + + + +
+
+ + + auto + + "bm-" + row["PRODUCTLINE"] + ",product" + + PRODUCTNAME + + + + "bm-" + row["PRODUCTLINE"] + ",descr" + + PRODUCTDESCRIPTION + + + + "bm-" + row["PRODUCTLINE"] + ",price" + + BUYPRICE + + + + +
+ +
diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/grid_only.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/grid_only.rptdesign new file mode 100644 index 00000000000..0cf6a9dbd3d --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/grid_only.rptdesign @@ -0,0 +1,672 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + Just a grid + in + /templates/blank_report.gif + ltr + 96 + en_US + 1.7 + PDF.A1A + PDF.UA-1 + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTCODE + dimension + PRODUCTCODE + PRODUCTCODE + + + PRODUCTNAME + dimension + PRODUCTNAME + PRODUCTNAME + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + PRODUCTSCALE + dimension + PRODUCTSCALE + PRODUCTSCALE + + + PRODUCTVENDOR + dimension + PRODUCTVENDOR + PRODUCTVENDOR + + + PRODUCTDESCRIPTION + dimension + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + + + QUANTITYINSTOCK + measure + QUANTITYINSTOCK + QUANTITYINSTOCK + + + BUYPRICE + measure + BUYPRICE + BUYPRICE + + + MSRP + measure + MSRP + MSRP + + + + + + + 1 + PRODUCTCODE + string + + + 2 + PRODUCTNAME + string + + + 3 + PRODUCTLINE + string + + + 4 + PRODUCTSCALE + string + + + 5 + PRODUCTVENDOR + string + + + 6 + PRODUCTDESCRIPTION + string + + + 7 + QUANTITYINSTOCK + integer + + + 8 + BUYPRICE + float + + + 9 + MSRP + float + + + + 50 + CM + + + 1 + PRODUCTCODE + PRODUCTCODE + string + 12 + + + 2 + PRODUCTNAME + PRODUCTNAME + string + 12 + + + 3 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 4 + PRODUCTSCALE + PRODUCTSCALE + string + 12 + + + 5 + PRODUCTVENDOR + PRODUCTVENDOR + string + 12 + + + 6 + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + string + 12 + + + 7 + QUANTITYINSTOCK + QUANTITYINSTOCK + integer + 4 + + + 8 + BUYPRICE + BUYPRICE + float + 8 + + + 9 + MSRP + MSRP + float + 8 + + + + + + 2.0 + + + + + + + PRODUCTCODE + 1 + + 12 + 15 + 0 + Nullable + + PRODUCTCODE + + + + PRODUCTCODE + + 15 + + + + + + + PRODUCTNAME + 2 + + 12 + 70 + 0 + Nullable + + PRODUCTNAME + + + + PRODUCTNAME + + 70 + + + + + + + PRODUCTLINE + 3 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + PRODUCTSCALE + 4 + + 12 + 10 + 0 + Nullable + + PRODUCTSCALE + + + + PRODUCTSCALE + + 10 + + + + + + + PRODUCTVENDOR + 5 + + 12 + 50 + 0 + Nullable + + PRODUCTVENDOR + + + + PRODUCTVENDOR + + 50 + + + + + + + PRODUCTDESCRIPTION + 6 + + 12 + 32700 + 0 + Nullable + + PRODUCTDESCRIPTION + + + + PRODUCTDESCRIPTION + + 32700 + + + + + + + QUANTITYINSTOCK + 7 + + 4 + 10 + 0 + Nullable + + QUANTITYINSTOCK + + + + QUANTITYINSTOCK + + 11 + + + + + + + BUYPRICE + 8 + + 8 + 15 + 0 + Nullable + + BUYPRICE + + + + BUYPRICE + + 24 + + + + + + + MSRP + 9 + + 8 + 15 + 0 + Nullable + + MSRP + + + + MSRP + + 24 + + + + + + + +]]> + + + + + + + + a4 + + + + + + + + Products + + + PRODUCTCODE + PRODUCTCODE + dataSetRow["PRODUCTCODE"] + string + + + PRODUCTNAME + PRODUCTNAME + dataSetRow["PRODUCTNAME"] + string + + + PRODUCTLINE + PRODUCTLINE + dataSetRow["PRODUCTLINE"] + string + + + PRODUCTSCALE + PRODUCTSCALE + dataSetRow["PRODUCTSCALE"] + string + + + PRODUCTVENDOR + PRODUCTVENDOR + dataSetRow["PRODUCTVENDOR"] + string + + + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + dataSetRow["PRODUCTDESCRIPTION"] + string + + + QUANTITYINSTOCK + QUANTITYINSTOCK + dataSetRow["QUANTITYINSTOCK"] + integer + + + BUYPRICE + BUYPRICE + dataSetRow["BUYPRICE"] + float + + + MSRP + MSRP + dataSetRow["MSRP"] + float + + + + + + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + + + 3 + 1 + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + "<h1>Super!</h1><p>This is some HTML including <em>emphasized text</em> and <strong>strong text</strong>.</p>" + html + + + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + PRODUCTCODE + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + MSRP + + + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + PRODUCTNAME + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + + + solid + thin + solid + thin + solid + thin + solid + thin + 6pt + 6pt + 6pt + 6pt + + PRODUCTSCALE + + + + + + diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/headingsAndComplexTableHTML.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/headingsAndComplexTableHTML.rptdesign new file mode 100644 index 00000000000..c2abb8bbc1c --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/headingsAndComplexTableHTML.rptdesign @@ -0,0 +1,569 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + Das ist der Titel + in + /templates/blank_report.gif + ltr + 96 + de_DE + 1.7 + PDF.A1A + PDF.UA-1 + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTCODE + dimension + PRODUCTCODE + PRODUCTCODE + + + PRODUCTNAME + dimension + PRODUCTNAME + PRODUCTNAME + + + PRODUCTDESCRIPTION + dimension + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + + + BUYPRICE + measure + BUYPRICE + BUYPRICE + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + HTMLDESCRIPTION + dimension + HTMLDESCRIPTION + HTMLDESCRIPTION + + + + + + + 1 + PRODUCTCODE + string + + + 2 + PRODUCTNAME + string + + + 3 + PRODUCTDESCRIPTION + string + + + 4 + BUYPRICE + float + + + 5 + PRODUCTLINE + string + + + 6 + HTMLDESCRIPTION + string + + + + 50 + CM + + + 1 + PRODUCTCODE + PRODUCTCODE + string + 12 + + + 2 + PRODUCTNAME + PRODUCTNAME + string + 12 + + + 3 + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + string + 12 + + + 4 + BUYPRICE + BUYPRICE + float + 8 + + + 5 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 6 + HTMLDESCRIPTION + HTMLDESCRIPTION + string + 2005 + + + + + + 2.0 + + + + + + + PRODUCTCODE + 1 + + 12 + 15 + 0 + Nullable + + PRODUCTCODE + + + + PRODUCTCODE + + 15 + + + + + + + PRODUCTNAME + 2 + + 12 + 70 + 0 + Nullable + + PRODUCTNAME + + + + PRODUCTNAME + + 70 + + + + + + + PRODUCTLINE + 3 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + PRODUCTSCALE + 4 + + 12 + 10 + 0 + Nullable + + PRODUCTSCALE + + + + PRODUCTSCALE + + 10 + + + + + + + PRODUCTVENDOR + 5 + + 12 + 50 + 0 + Nullable + + PRODUCTVENDOR + + + + PRODUCTVENDOR + + 50 + + + + + + + PRODUCTDESCRIPTION + 6 + + 12 + 32700 + 0 + Nullable + + PRODUCTDESCRIPTION + + + + PRODUCTDESCRIPTION + + 32700 + + + + + + + QUANTITYINSTOCK + 7 + + 4 + 10 + 0 + Nullable + + QUANTITYINSTOCK + + + + QUANTITYINSTOCK + + 11 + + + + + + + BUYPRICE + 8 + + 8 + 15 + 0 + Nullable + + BUYPRICE + + + + BUYPRICE + + 24 + + + + + + + MSRP + 9 + + 8 + 15 + 0 + Nullable + + MSRP + + + + MSRP + + 24 + + + + + + + +]]> + + + + + + + + + + + a4 + + + + + + + + + + + + + + + Products + + + PRODUCTNAME + PRODUCTNAME + dataSetRow["PRODUCTNAME"] + string + + + PRODUCTLINE + PRODUCTLINE + dataSetRow["PRODUCTLINE"] + string + + + PRODUCTDESCRIPTION + PRODUCTDESCRIPTION + dataSetRow["PRODUCTDESCRIPTION"] + string + + + PRODUCTCODE + PRODUCTCODE + dataSetRow["PRODUCTCODE"] + string + + + BUYPRICE + BUYPRICE + dataSetRow["BUYPRICE"] + float + + + HTMLDESCRIPTION + HTMLDESCRIPTION + dataSetRow["HTMLDESCRIPTION"] + string + + + 9999 + This is the table caption! + + + +
+ + #80FF00 + + "product" + + + + "descr" + + + + "price" + + + +
+ + TDProductLine + row["PRODUCTLINE"] + row["PRODUCTLINE"] + + row["PRODUCTLINE"] + + false +
+ + + 3 + 1 + "bm-" + row["PRODUCTLINE"] + TH + #80FFFF + + row["HTMLDESCRIPTION"] + html + + + +
+
+ + + auto + + "bm-" + row["PRODUCTLINE"] + ",product" + + PRODUCTNAME + + + + "bm-" + row["PRODUCTLINE"] + ",descr" + + PRODUCTDESCRIPTION + + + + "bm-" + row["PRODUCTLINE"] + ",price" + + BUYPRICE + + + + +
+ + + + + '<p>This is some HTML including <u>underlined text</u>.</p>' + html + + + + + all + true + + + '<p>This is some more HTML including a hyperlink to <a href="https://github.com/eclipse-birt/birt">the BIRT project page </a> on GitHub.</p>' + html + + +
diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/hello_world.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/hello_world.rptdesign index 07d2684e1ee..0b5f39150e7 100644 --- a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/hello_world.rptdesign +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/hello_world.rptdesign @@ -1,7 +1,7 @@ Henning von Bargen - Eclipse BIRT Designer Version 4.17.0.qualifier Build <@BUILD@> + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> Das ist der Titel in /templates/blank_report.gif @@ -9,8 +9,9 @@ 96 de_DE 1.7 - PDF.A1A + PDF.A3A PDF.UA-1 + false true + + + + custom + 60mm + 210mm + + + + + + + + Products + + + PRODUCTNAME + PRODUCTNAME + dataSetRow["PRODUCTNAME"] + string + + + Productname + row["PRODUCTNAME"] + string + true + + +
+ +
+ + + Productname + + +
+ +
diff --git a/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/pageNumbering.rptdesign b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/pageNumbering.rptdesign new file mode 100644 index 00000000000..a29abb5eb91 --- /dev/null +++ b/UI/org.eclipse.birt.report.designer.samplereports/samplereports/Reporting Feature Examples/Accessibility/pageNumbering.rptdesign @@ -0,0 +1,410 @@ + + + Henning von Bargen + Eclipse BIRT Designer Version 4.18.0.qualifier Build <@BUILD@> + This is the document title + in + /templates/blank_report.gif + ltr + 96 + en_US + 1.7 + PDF.A3A + PDF.UA-1 + false + true + + + + + contentBidiFormatStr + ILYNN + + + metadataBidiFormatStr + ILYNN + + + org.eclipse.birt.report.data.oda.sampledb.Driver + jdbc:classicmodels:sampledb + ClassicModels + + + + + nulls lowest + + + PRODUCTLINE + dimension + PRODUCTLINE + PRODUCTLINE + + + TEXTDESCRIPTION + dimension + TEXTDESCRIPTION + TEXTDESCRIPTION + + + HTMLDESCRIPTION + dimension + HTMLDESCRIPTION + HTMLDESCRIPTION + + + IMAGE + attribute + IMAGE + IMAGE + + + + + + 1 + PRODUCTLINE + string + + + 2 + TEXTDESCRIPTION + string + + + 3 + HTMLDESCRIPTION + string + + + 4 + IMAGE + blob + + + + 20 + CM + + + 1 + PRODUCTLINE + PRODUCTLINE + string + 12 + + + 2 + TEXTDESCRIPTION + TEXTDESCRIPTION + string + 12 + + + 3 + HTMLDESCRIPTION + HTMLDESCRIPTION + string + 2005 + + + 4 + IMAGE + IMAGE + blob + 2004 + + + + + + 2.0 + + + + + + + PRODUCTLINE + 1 + + 12 + 50 + 0 + Nullable + + PRODUCTLINE + + + + PRODUCTLINE + + 50 + + + + + + + TEXTDESCRIPTION + 2 + + 12 + 4000 + 0 + Nullable + + TEXTDESCRIPTION + + + + TEXTDESCRIPTION + + 4000 + + + + + + + HTMLDESCRIPTION + 3 + + 2005 + 2147483647 + 0 + Nullable + + HTMLDESCRIPTION + + + + HTMLDESCRIPTION + + 2147483647 + + + + + + + IMAGE + 4 + + 2004 + 2147483647 + 0 + Nullable + + IMAGE + + + + IMAGE + + 2147483647 + + + + + + + +]]> + + + + + + + + a4 + + + + + + + + + + + + + + + + inline + plain + + + + inline + page-number + + + inline + plain + + + + inline + total-page + + + + + + + + + + + + + + + + + + + Productlines + + + PRODUCTLINE + PRODUCTLINE + dataSetRow["PRODUCTLINE"] + string + + + HTMLDESCRIPTION + HTMLDESCRIPTION + dataSetRow["HTMLDESCRIPTION"] + string + + + This is the table caption + + 20mm + + +
+ + + col + solid + thin + solid + thin + solid + thin + solid + thin + + + + col + solid + thin + solid + thin + solid + thin + solid + thin + + + +
+ + + + solid + thin + solid + thin + solid + thin + solid + thin + + PRODUCTLINE + + + + solid + thin + solid + thin + solid + thin + solid + thin + + row["HTMLDESCRIPTION"] + html + + + + +
+ + + 2 + 1 + yellow + solid + thin + solid + thin + solid + thin + solid + thin + + + +
+
+ +
diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPage.java b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPage.java index 49d1fc1ae08..91fbd60b1ee 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPage.java +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPage.java @@ -19,7 +19,6 @@ import java.awt.print.PageFormat; import java.awt.print.Paper; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -37,7 +36,6 @@ import org.eclipse.birt.report.engine.layout.pdf.font.FontInfo; import org.eclipse.birt.report.engine.nLayout.area.style.AreaConstants; import org.eclipse.birt.report.engine.nLayout.area.style.TextStyle; -import org.eclipse.birt.report.engine.util.FlashFile; import org.eclipse.birt.report.engine.util.SvgFile; import org.w3c.dom.css.CSSValue; @@ -53,7 +51,6 @@ import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfDestination; import com.lowagie.text.pdf.PdfDictionary; -import com.lowagie.text.pdf.PdfFileSpecification; import com.lowagie.text.pdf.PdfName; import com.lowagie.text.pdf.PdfRectangle; import com.lowagie.text.pdf.PdfString; @@ -85,7 +82,7 @@ public class PDFPage extends AbstractPage { protected PDFPageDevice pageDevice; - protected boolean inArtifact = false; + protected short artifactDepth = 0; /** * font size must greater than minimum font . if not,illegalArgumentException @@ -154,7 +151,9 @@ protected void drawBackgroundColor(Color color, float x, float y, float width, f return; } y = transformY(y, height); - beginArtifact(); + PdfDictionary properties = new PdfDictionary(); + properties.put(PdfNames.TYPE, PdfNames.BACKGROUND); + beginArtifact(properties); contentByte.saveState(); contentByte.setColorFill(color); contentByte.concatCTM(1, 0, 0, 1, x, y); @@ -167,7 +166,9 @@ protected void drawBackgroundColor(Color color, float x, float y, float width, f @Override protected void drawBackgroundImage(float x, float y, float width, float height, float imageWidth, float imageHeight, int repeat, String imageUrl, byte[] imageData, float offsetX, float offsetY) throws Exception { - beginArtifact(); + PdfDictionary properties = new PdfDictionary(); + properties.put(PdfNames.TYPE, PdfNames.BACKGROUND); + beginArtifact(properties); contentByte.saveState(); clip(x, y, width, height); @@ -231,24 +232,17 @@ protected void drawBackgroundImage(float x, float y, float width, float height, protected void drawImage(String imageId, byte[] imageData, String extension, float imageX, float imageY, float height, float width, String helpText, Map params) throws Exception { - // Flash - if (FlashFile.isFlash(null, null, extension)) { - embedFlash(null, imageData, imageX, imageY, height, width, helpText, params); - return; - } - - if (isTagged && !inArtifact) { - PdfDictionary dict = new PdfDictionary(); - pageDevice.structureCurrentLeaf.put(new PdfName("Alt"), new PdfString(helpText)); + if (isTagged && artifactDepth == 0) { + pageDevice.structureCurrentNode.put(PdfNames.ALT, new PdfString(helpText)); - PdfDictionary attributes = pageDevice.structureCurrentLeaf.getAsDict(PdfName.A); + PdfDictionary attributes = pageDevice.structureCurrentNode.getAsDict(PdfName.A); if (attributes == null) { attributes = new PdfDictionary(); - pageDevice.structureCurrentLeaf.put(PdfName.A, attributes); + pageDevice.structureCurrentNode.put(PdfName.A, attributes); } attributes.put(PdfName.BBOX, new PdfRectangle(imageX, imageY, width, height)); - contentByte.beginMarkedContentSequence(pageDevice.structureCurrentLeaf); + contentByte.beginMarkedContentSequence(pageDevice.structureCurrentNode); } // Cached Image @@ -259,7 +253,7 @@ protected void drawImage(String imageId, byte[] imageData, String extension, flo } if (template != null) { drawImage(template, imageX, imageY, height, width, helpText); - if (isTagged && !inArtifact) { + if (isTagged && artifactDepth == 0) { contentByte.endMarkedContentSequence(); } return; @@ -286,7 +280,7 @@ protected void drawImage(String imageId, byte[] imageData, String extension, flo if (imageId == null) { // image without imageId, not able to cache. drawImage(image, imageX, imageY, height, width, helpText); - if (isTagged && !inArtifact) { + if (isTagged && artifactDepth == 0) { contentByte.endMarkedContentSequence(); } return; @@ -302,7 +296,7 @@ protected void drawImage(String imageId, byte[] imageData, String extension, flo drawImage(template, imageX, imageY, height, width, helpText); } - if (isTagged && !inArtifact) { + if (isTagged && artifactDepth == 0) { contentByte.endMarkedContentSequence(); } @@ -365,16 +359,26 @@ protected void drawLine(float startX, float startY, float endX, float endY, floa contentByte.restoreState(); } + protected void drawDecorationLine(float textX, float textY, float width, float lineWidth, float verticalOffset, + Color color, boolean artifact) { + if (artifact) { + // FIXME: Can we really treat a strike-through line as an artifact? + // Probably one should mark the text itself as "deleted" instead, but how can we + // do this? + PdfDictionary properties = new PdfDictionary(); + properties.put(PdfNames.TYPE, PdfNames.LAYOUT); + beginArtifact(properties); + super.drawDecorationLine(textX, textY, width, lineWidth, verticalOffset, color); + endArtifact(); + } else { + super.drawDecorationLine(textX, textY, width, lineWidth, verticalOffset, color); + } + } + @Override protected void drawDecorationLine(float textX, float textY, float width, float lineWidth, float verticalOffset, Color color) { - - // FIXME: Can we really treat a strike-through line as an artifact? - // Probably one should mark the text itself as "deleted" instead, but how can we - // do this? - beginArtifact(); - super.drawDecorationLine(textX, textY, width, lineWidth, verticalOffset, color); - endArtifact(); + drawDecorationLine(textX, textY, width, lineWidth, verticalOffset, color, true); } @Override @@ -384,6 +388,13 @@ protected void drawText(String text, float textX, float textY, float baseline, f convertToPoint(textStyle.getLetterSpacing()), convertToPoint(textStyle.getWordSpacing()), textStyle.getColor(), textStyle.getAlign()); if (textStyle.isHasHyperlink() && textStyle.isHasHyperlinkDecoration()) { + // FIXME ATM, the underline is marked as Artifact (see drawDecorationLine). + // I think this is not quite correct and not necessary. + // LibreOffice does not do it this way. + // The underline decoration should be included in the /Link element. + // For this, we probably have to handle the beginMarkedContentSequence and + // endMarkedContentSequence stuff right here instead of in drawDecorationLine + // and the overloaded drawText methods. FontInfo fontInfo = textStyle.getFontInfo(); float lineWidth = fontInfo.getLineWidth(); Color color = textStyle.getColor(); @@ -414,7 +425,16 @@ public void drawTotalPage(String text, int textX, int textY, int width, int heig PdfContentByte tempCB = this.contentByte; this.containerHeight = template.getHeight(); this.contentByte = template; + int artifactDepthOld = artifactDepth; + if (artifactDepthOld == 0) { + PdfDictionary properties = new PdfDictionary(); + properties.put(PdfNames.TYPE, PdfNames.PAGINATION); + beginArtifact(); + } drawText(text, textX, textY, width, height, textInfo); + if (artifactDepthOld == 0) { + endArtifact(); + } this.contentByte = tempCB; this.containerHeight = pageHeight; } @@ -438,7 +458,7 @@ private void createBookmark(String bookmark, float y) { } /** - * Create the hyperlinks + * Create a hyperlink. * * @param hyperlink * @param bookmark @@ -448,18 +468,23 @@ private void createBookmark(String bookmark, float y) { * @param y * @param width * @param height + * + * @return a new PdfAnnotation describing the hyperlink. */ - public void createHyperlink(String hyperlink, String bookmark, String targetWindow, int type, int x, int y, + public PdfAnnotation createHyperlink(String hyperlink, String bookmark, String targetWindow, int type, int x, int y, int width, int height) { - createHyperlink(hyperlink, bookmark, targetWindow, type, convertToPoint(x), convertToPoint(y), + return createHyperlink(hyperlink, bookmark, targetWindow, type, convertToPoint(x), convertToPoint(y), convertToPoint(width), convertToPoint(height)); } - private void createHyperlink(String hyperlink, String bookmark, String targetWindow, int type, float x, float y, + private PdfAnnotation createHyperlink(String hyperlink, String bookmark, String targetWindow, int type, float x, + float y, float width, float height) { y = transformY(y, height); - writer.addAnnotation(new PdfAnnotation(writer, x, y, x + width, y + height, - createPdfAction(hyperlink, bookmark, targetWindow, type))); + PdfAnnotation annotation = new PdfAnnotation(writer, x, y, x + width, y + height, + createPdfAction(hyperlink, bookmark, targetWindow, type)); + writer.addAnnotation(annotation); + return annotation; } /** @@ -509,14 +534,14 @@ private void drawRawLine(float startX, float startY, float endX, float endY, flo startY = transformY(startY); endY = transformY(endY); contentByte.concatCTM(1, 0, 0, 1, startX, startY); + if (null != color && !Color.BLACK.equals(color)) { + contentByte.setColorStroke(color); + } contentByte.moveTo(0, 0); contentByte.lineTo(endX - startX, endY - startY); contentByte.setLineWidth(width); - if (null != color && !Color.BLACK.equals(color)) { - contentByte.setColorStroke(color); - } contentByte.stroke(); } @@ -530,8 +555,8 @@ private void drawText(String text, float textX, float textY, FontInfo fontInfo, // start drawing the text content contentByte.beginText(); - if (isTagged && !inArtifact) { - contentByte.beginMarkedContentSequence(pageDevice.structureCurrentLeaf); + if (isTagged && artifactDepth == 0) { + contentByte.beginMarkedContentSequence(pageDevice.structureCurrentNode); } if (null != color && !Color.BLACK.equals(color)) { @@ -552,6 +577,7 @@ private void drawText(String text, float textX, float textY, FontInfo fontInfo, String defaultFontPdfA = this.pageDevice.getDefaultFontPdfA(); if (defaultFontPdfA != null) { font = BaseFont.createFont(defaultFontPdfA, BaseFont.IDENTITY_H, true); + font.setIncludeCidSet(this.pageDevice.isIncludeCidSet()); } logger.log(Level.WARNING, "PDF/A: " + fontInfo.getFontName() + " not embeddable, fallback font used."); @@ -572,7 +598,7 @@ private void drawText(String text, float textX, float textY, FontInfo fontInfo, if (wordSpacing != 0) { contentByte.setWordSpacing(wordSpacing); } - setTextMatrix(contentByte, fontInfo, textX, transformY(textY, 0, containerHeight)); + setTextMatrix(contentByte, fontInfo); if ((font.getFontType() == BaseFont.FONT_TYPE_TTUNI) && CSSValueConstants.JUSTIFY_VALUE.equals(align) && wordSpacing > 0) { int idx = text.indexOf(' '); @@ -594,7 +620,7 @@ private void drawText(String text, float textX, float textY, FontInfo fontInfo, } else { contentByte.showText(text); } - if (isTagged && !inArtifact) { + if (isTagged && artifactDepth == 0) { contentByte.endMarkedContentSequence(); } contentByte.endText(); @@ -641,7 +667,7 @@ private PdfAction createPdfAction(String hyperlink, String bookmark, String targ } } - private void setTextMatrix(PdfContentByte cb, FontInfo fi, float x, float y) { + private void setTextMatrix(PdfContentByte cb, FontInfo fi) { if (!fi.getSimulation()) { cb.setTextMatrix(0, 0); @@ -733,20 +759,6 @@ protected void drawImage(Image image, float imageX, float imageY, float height, contentByte.restoreState(); } - protected void embedFlash(String flashPath, byte[] flashData, float x, float y, float height, float width, - String helpText, Map params) throws IOException { - y = transformY(y, height); - contentByte.saveState(); - PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(writer, flashPath, helpText, flashData); - PdfAnnotation annot = PdfAnnotation.createScreen(writer, new Rectangle(x, y, x + width, y + height), helpText, - fs, "application/x-shockwave-flash", true); - writer.addAnnotation(annot); - if (helpText != null) { - showHelpText(x, y, width, height, helpText); - } - contentByte.restoreState(); - } - protected PdfTemplate generateTemplateFromSVG(byte[] svgData, float height, float width) throws Exception { return transSVG(null, svgData, height, width); @@ -774,32 +786,58 @@ protected PdfTemplate transSVG(String svgPath, byte[] svgData, float height, flo return template; } + /** + * Mark the beginning of an artifact. Artifact content is whatever is not + * essential for the reader, such as page headers and footers, or repeated table + * headers, or graphical elements like lines or boxes which do not really have a + * meaning. + */ public void beginArtifact() { if (!isTagged) { return; } - if (!inArtifact) { - contentByte.beginMarkedContentSequence(new PdfName("Artifact")); - inArtifact = true; + if (artifactDepth == 0) { + contentByte.beginMarkedContentSequence(PdfNames.ARTIFACT); } - else { - logger.warning("beginArtifact called inside artifact!"); + artifactDepth++; + } + + /** + * Mark the beginning of an artifact. Artifact content is whatever is not + * essential for the reader, such as page headers and footers, or repeated table + * headers, or graphical elements like lines or boxes which do not really have a + * meaning. + * + * @param properties additional properties which are used in the call to + * beginMarkedContentSequence. + */ + public void beginArtifact(PdfDictionary properties) { + if (!isTagged) { + return; + } + if (artifactDepth == 0) { + contentByte.beginMarkedContentSequence(PdfNames.ARTIFACT, properties, true); } + artifactDepth++; } + /** + * Mark the end of and artifact. + */ public void endArtifact() { if (!isTagged) { return; } - if (inArtifact) { + artifactDepth--; + if (artifactDepth == 0) { contentByte.endMarkedContentSequence(); - inArtifact = false; - } else { - logger.warning("endArtifact called outside of an artifact!"); } } + /** + * @return if we are currently in an artifact or not. + */ public boolean isInArtifact() { - return inArtifact; + return artifactDepth > 0; } } diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPageDevice.java b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPageDevice.java index 14ff5d7faf9..62e81c2519a 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPageDevice.java +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFPageDevice.java @@ -38,8 +38,10 @@ import org.eclipse.birt.report.engine.api.ITOCTree; import org.eclipse.birt.report.engine.api.TOCNode; import org.eclipse.birt.report.engine.api.script.IReportContext; +import org.eclipse.birt.report.engine.content.IBandContent; import org.eclipse.birt.report.engine.content.IReportContent; import org.eclipse.birt.report.engine.content.impl.CellContent; +import org.eclipse.birt.report.engine.content.impl.RowContent; import org.eclipse.birt.report.engine.i18n.EngineResourceHandle; import org.eclipse.birt.report.engine.i18n.MessageConstants; import org.eclipse.birt.report.engine.internal.util.BundleVersionUtil; @@ -48,6 +50,7 @@ import org.eclipse.birt.report.engine.layout.emitter.IPageDevice; import org.eclipse.birt.report.engine.nLayout.area.IArea; import org.eclipse.birt.report.engine.nLayout.area.impl.CellArea; +import org.eclipse.birt.report.engine.nLayout.area.impl.ContainerArea; import com.ibm.icu.util.ULocale; import com.lowagie.text.Document; @@ -76,6 +79,7 @@ import com.lowagie.text.xml.xmp.PdfA1Schema; import com.lowagie.text.xml.xmp.PdfSchema; import com.lowagie.text.xml.xmp.XmpBasicSchema; +import com.lowagie.text.xml.xmp.XmpSchema; import com.lowagie.text.xml.xmp.XmpWriter; /** @@ -97,19 +101,36 @@ public class PDFPageDevice implements IPageDevice { private static final String PDF_VERSION_1_6 = "1.6"; /** PDF version 1.7 */ private static final String PDF_VERSION_1_7 = "1.7"; + /** PDF version 2.0 */ + private static final String PDF_VERSION_2_0 = "2.0"; /** PDFX/PDFA conformance */ /** PDF conformance PDF Standard */ private static final String PDF_CONFORMANCE_STANDARD = "PDF.Standard"; - /** PDF conformance PDF X-3:2002 */ + /** PDF conformance PDF/X-3:2002 */ private static final String PDF_CONFORMANCE_X32002 = "PDF.X32002"; - /** PDF conformance PDF A1A */ + /** PDF conformance PDF/A-1a */ private static final String PDF_CONFORMANCE_A1A = "PDF.A1A"; - /** PDF conformance PDF A1B */ + /** PDF conformance PDFA-1b */ private static final String PDF_CONFORMANCE_A1B = "PDF.A1B"; + /** PDF conformance PDF/A-2a */ + private static final String PDF_CONFORMANCE_A2A = "PDF.A2A"; + /** PDF conformance PDFA-2b */ + private static final String PDF_CONFORMANCE_A2B = "PDF.A2B"; + /** PDF conformance PDF/A-3a */ + private static final String PDF_CONFORMANCE_A3A = "PDF.A3A"; + /** PDF conformance PDFA-3b */ + private static final String PDF_CONFORMANCE_A3B = "PDF.A3B"; + /** PDF conformance PDF/A-3u */ + private static final String PDF_CONFORMANCE_A3U = "PDF.A3U"; + /** PDF conformance PDF/A-4f */ + private static final String PDF_CONFORMANCE_A4F = "PDF.A4F"; + /** PDF conformance PDF X-1a:2001, unsupported (TODO: CMYK of PDF.X1A2001 */ private static final String PDF_UA_CONFORMANCE_1 = "PDF.UA-1"; + private static final String PDF_UA_CONFORMANCE_2 = "PDF.UA-2"; + private static final String PDF_UA_CONFORMANCE_NONE = "none"; /** PDF ICC color profile */ /** PDF ICC default color profile RGB */ @@ -132,7 +153,7 @@ public class PDFPageDevice implements IPageDevice { // The StructureTree defines the logical structure of the content. PdfStructureTreeRoot structureRoot = null; PdfStructureElement structureDocument = null; - PdfStructureElement structureCurrentLeaf = null; + PdfStructureElement structureCurrentNode = null; protected IReportContext context; @@ -280,7 +301,8 @@ public PDFPageDevice(OutputStream output, String title, String author, String su throw new BirtException("The report needs a locale property for PDF/UA!"); } Locale locale = new Locale(localeString); - String language = locale.toLanguageTag(); + String language = locale.toString(); + language = language.replace('_', '-'); // 'de_de' is invalid, it should be 'de-DE'. doc.setDocumentLanguage(language); // In order to declare the main language of the document, // we need to use the extraCatalog. That way we don't need to @@ -324,7 +346,7 @@ public PDFPageDevice(OutputStream output, String title, String author, String su // iterate over the list, and create a file inputstream for each file location. for (String s : list.split(",")) { // If there is an exception creating the input stream, don't stop execution. - // Just graceffully let the user know that there was an error with the variable. + // Just gracefully let the user know that there was an error with the variable. try { String fileName = s.trim().replace("\\", "\\\\"); File f = new File(fileName); @@ -346,15 +368,13 @@ public PDFPageDevice(OutputStream output, String title, String author, String su } // The other is a "Named Expression", which is basically a user property that is - // the result - // of an expression instead of a string literal. This should be set as an - // arraylist through - // BIRT script + // the result of an expression instead of a string literal. This should be set + // as an arraylist through BIRT script if (result instanceof ArrayList) { ArrayList pdfList = (ArrayList) result; - for (String fileName : pdfList) { + for (String fileName : pdfList) { // If there is an exception creating the input stream, don't stop execution. - // Just graceffully let the user know that there was an error with the variable. + // Just gracefully let the user know that there was an error with the variable. fileName = fileName.replace("\\", "\\\\"); try { File f = new File(fileName); @@ -389,11 +409,14 @@ public PDFPageDevice(OutputStream output, String title, String author, String su } } + /** + * Initialize the attributes for the PDF tag tree structure. + */ public void initStructure() { structureRoot = writer.getStructureTreeRoot(); structureDocument = new PdfStructureElement(structureRoot, new PdfName("Document")); - structureCurrentLeaf = structureDocument; + structureCurrentNode = structureDocument; } @@ -500,22 +523,21 @@ public void close() throws Exception { // iterate over the list, and create a fileinputstream for each file location. for (String s : list.split(",")) { // If there is an exception creating the input stream, don't stop execution. - // Just graceffully let the user know that there was an error with the variable. + // Just gracefully let the user know that there was an error with the variable. try { String fileName = s.trim().replace("\\", "\\\\"); - ; - File f = new File(fileName); - if (f.exists()) { - FileInputStream fis = new FileInputStream(f); - pdfs.add(fis); - } else { - // get the file using context.getResource() for relative or universal paths - URL url = context.getResource(fileName); - InputStream is = new BufferedInputStream(url.openStream()); - pdfs.add(is); + File f = new File(fileName); + if (f.exists()) { + FileInputStream fis = new FileInputStream(f); + pdfs.add(fis); + } else { + // get the file using context.getResource() for relative or universal paths + URL url = context.getResource(fileName); + InputStream is = new BufferedInputStream(url.openStream()); + pdfs.add(is); } - } catch (Exception e) { - logger.log(Level.WARNING, e.getMessage(), e); + } catch (Exception e) { + logger.log(Level.WARNING, e.getMessage(), e); } } } @@ -524,14 +546,13 @@ public void close() throws Exception { // option 2: "Named Expression", which is basically a user property that is the // result of an expression instead of a string literal. This should be set as an - // arraylist through - // BIRT script + // arraylist through BIRT script if (result instanceof ArrayList) { ArrayList pdfList = (ArrayList) result; for (String fileName : pdfList) { // If there is an exception creating the input stream, don't stop execution. - // Just graceffully let the user know that there was an error with the variable. + // Just gracefully let the user know that there was an error with the variable. fileName = fileName.replace("\\", "\\\\"); try { File f = new File(fileName); @@ -555,7 +576,7 @@ public void close() throws Exception { } } - if (this.isPdfAFormat) { + if (this.isPdfAFormat || this.isPdfUAFormat) { // PDF/A: set color profile and metadata this.setPdfIccXmp(); } @@ -702,8 +723,10 @@ public void concatPDFs(List streamOfPDFFiles, boolean paginate) { /** * Set the PDF version based on the user property + * + * @throws BirtException */ - private void setPdfVersion() { + private void setPdfVersion() throws BirtException { String userPdfVersion; // PDF version based on user property if (this.userProperties != null && this.userProperties.containsKey(PDFPageDevice.PDF_VERSION)) { @@ -718,8 +741,9 @@ private void setPdfVersion() { * Set the PDF version * * @param version key to set the PDF version (e.g. 1.7) + * @throws BirtException */ - public void setPdfVersion(String version) { + public void setPdfVersion(String version) throws BirtException { switch (version) { case PDFPageDevice.PDF_VERSION_1_3: this.pdfVersion = PdfWriter.VERSION_1_3; @@ -736,6 +760,10 @@ public void setPdfVersion(String version) { case PDFPageDevice.PDF_VERSION_1_7: this.pdfVersion = PdfWriter.VERSION_1_7; break; + case PDFPageDevice.PDF_VERSION_2_0: + // this.pdfVersion = PdfWriter.VERSION_2_0; + // OpenPDF does not yet support creation of PDF 2.0 + throw new BirtException("OpenPDF does not yet support creation of PDF 2.0"); } // version only set if the PDF version exists if (this.pdfVersion != '0') { @@ -760,7 +788,7 @@ private void setPdfUAConformance() { /** * Set the PDF version * - * @param conformance key to set the PDF version (e.g. 1.7) + * @param conformance key to set the PDF/UA version (e.g. PDF/UA-1) */ public void setPdfUAConformance(String conformance) { switch (conformance) { @@ -769,6 +797,11 @@ public void setPdfUAConformance(String conformance) { this.isPdfUAFormat = true; writer.setTagged(); break; + case PDFPageDevice.PDF_UA_CONFORMANCE_2: + this.pdfUAConformance = 2; + this.isPdfUAFormat = true; + writer.setTagged(); + break; } } @@ -781,8 +814,10 @@ public String getPdfUAConformance() { switch (this.pdfUAConformance) { case 1: return PDFPageDevice.PDF_UA_CONFORMANCE_1; + case 2: + return PDFPageDevice.PDF_UA_CONFORMANCE_2; default: - return PDFPageDevice.PDF_CONFORMANCE_STANDARD; + return PDFPageDevice.PDF_UA_CONFORMANCE_NONE; } } @@ -820,6 +855,7 @@ private void setPdfConformance() { } else { userPdfConformance = (String) getReportDesignConfiguration(this.report, PDFPageDevice.PDF_CONFORMANCE); } + switch (userPdfConformance) { case PDFPageDevice.PDF_CONFORMANCE_X32002: this.pdfConformance = PdfWriter.PDFX32002; @@ -833,6 +869,30 @@ private void setPdfConformance() { this.pdfConformance = PdfWriter.PDFA1B; this.isPdfAFormat = true; break; + case PDFPageDevice.PDF_CONFORMANCE_A2A: + this.pdfConformance = PDFPageDevice.PDFA2A; + this.isPdfAFormat = true; + break; + case PDFPageDevice.PDF_CONFORMANCE_A2B: + this.pdfConformance = PDFPageDevice.PDFA2B; + this.isPdfAFormat = true; + break; + case PDFPageDevice.PDF_CONFORMANCE_A3A: + this.pdfConformance = PDFPageDevice.PDFA3A; + this.isPdfAFormat = true; + break; + case PDFPageDevice.PDF_CONFORMANCE_A3B: + this.pdfConformance = PDFPageDevice.PDFA3B; + this.isPdfAFormat = true; + break; + case PDFPageDevice.PDF_CONFORMANCE_A3U: + this.pdfConformance = PDFPageDevice.PDFA3U; + this.isPdfAFormat = true; + break; + case PDFPageDevice.PDF_CONFORMANCE_A4F: + this.pdfConformance = PDFPageDevice.PDFA4F; + this.isPdfAFormat = true; + break; default: this.pdfConformance = PdfWriter.PDFXNONE; this.isPdfAFormat = false; @@ -843,11 +903,11 @@ private void setPdfConformance() { // PDFA: overwrite to get the document title independent of the openPDF // issue of PDF/A-conformance if (this.userProperties != null && this.userProperties.containsKey(PDFPageDevice.PDFA_ADD_DOCUMENT_TITLE)) { - this.addPdfADocumentTitle = Boolean.parseBoolean(this.userProperties.get(PDFPageDevice.PDFA_ADD_DOCUMENT_TITLE).toString()); - } else { this.addPdfADocumentTitle = Boolean - .parseBoolean( - (String) getReportDesignConfiguration(this.report, PDFPageDevice.PDFA_ADD_DOCUMENT_TITLE)); + .parseBoolean(this.userProperties.get(PDFPageDevice.PDFA_ADD_DOCUMENT_TITLE).toString()); + } else { + this.addPdfADocumentTitle = Boolean.parseBoolean( + (String) getReportDesignConfiguration(this.report, PDFPageDevice.PDFA_ADD_DOCUMENT_TITLE)); } } @@ -858,11 +918,26 @@ private void setPdfConformance() { */ public void setPdfConformance(int pdfConformance) { writer.setPDFXConformance(pdfConformance); - if (pdfConformance == PdfWriter.PDFA1A) { + switch (pdfConformance) { + case PdfWriter.PDFA1A: + case PDFA2A: + case PDFA3A: writer.setTagged(); + break; + default: + // do nothing } } + // FIXME The existence of these constants is a workaround for the fact that + // OpenPDF does not yet support PDF/A-2 or newer. + private static final int PDFA2A = 5; + private static final int PDFA2B = 6; + private static final int PDFA3A = 7; + private static final int PDFA3B = 8; + private static final int PDFA3U = 9; + private static final int PDFA4F = 10; + /** * Get the PDF conformance * @@ -876,6 +951,18 @@ public String getPdfConformance() { return PDFPageDevice.PDF_CONFORMANCE_A1A; case PdfWriter.PDFA1B: return PDFPageDevice.PDF_CONFORMANCE_A1B; + case PDFPageDevice.PDFA2A: + return PDFPageDevice.PDF_CONFORMANCE_A2A; + case PDFPageDevice.PDFA2B: + return PDFPageDevice.PDF_CONFORMANCE_A2B; + case PDFPageDevice.PDFA3A: + return PDFPageDevice.PDF_CONFORMANCE_A3A; + case PDFPageDevice.PDFA3B: + return PDFPageDevice.PDF_CONFORMANCE_A3B; + case PDFPageDevice.PDFA3U: + return PDFPageDevice.PDF_CONFORMANCE_A3U; + case PDFPageDevice.PDFA4F: + return PDFPageDevice.PDF_CONFORMANCE_A4F; default: return PDFPageDevice.PDF_CONFORMANCE_STANDARD; } @@ -900,21 +987,23 @@ public boolean isPDFUAFormat() { } /** - * We need to override getXmlns because we have to define the pdfuaid namespace. + * PDF XMP schema for declaring that the PDF is PDF/UA conforming. + * + * @since 4.18 + * */ - private static class DublinCoreAccessibleSchema extends DublinCoreSchema { + private static class PDFUASchema extends XmpSchema { - public DublinCoreAccessibleSchema() { - super(); - } + private static final long serialVersionUID = -6990512370284803429L; - @Override - public String getXmlns() { - return super.getXmlns() + " xmlns:pdfuaid=\"http://www.aiim.org/pdfua/ns/id/\""; + public PDFUASchema() { + super("xmlns:pdfuaid=\"http://www.aiim.org/pdfua/ns/id/\""); } /** * This is what declares the document to be PDF/UA-1, so it must be called. + * + * @param version the PDF/UA version. Valid values are 1 or 2. */ public void addPdfUAId(int version) { setProperty("pdfuaid:part", String.valueOf(version)); @@ -923,8 +1012,57 @@ public void addPdfUAId(int version) { } /** - * Create the XML for the XMPMetadata. We use the same method from PdfWriter as - * a template and add what is neeeded for PDF/UA. + * PDF XMP schema for declaring that the PDF is PDF/A conforming. + * + * Since the document can be PDF/UA conforming at the same time, the PDF/A + * specification requires that this is other schema is also described here. + * + * @since 4.18 + * + */ + private static class PDFAExtensionSchema extends XmpSchema { + + private static final long serialVersionUID = 6654512771721220538L; + + public PDFAExtensionSchema() { + super("xmlns:pdfaExtension=\"http://www.aiim.org/pdfa/ns/extension/\" xmlns:pdfaProperty=\"http://www.aiim.org/pdfa/ns/property#\" xmlns:pdfaSchema=\"http://www.aiim.org/pdfa/ns/schema#\""); + } + + public String toString() { + return " \r\n" + " \r\n" + + " \r\n" + + " http://www.aiim.org/pdfua/ns/id/\r\n" + + " pdfuaid\r\n" + + " PDF/UA identification schema\r\n" + + " \r\n" + " \r\n" + + " \r\n" + + " internal\r\n" + + " PDF/UA version identifier\r\n" + + " part\r\n" + + " Integer\r\n" + + " \r\n" + + " \r\n" + + " internal\r\n" + + " PDF/UA amendment identifier\r\n" + + " amd\r\n" + + " Text\r\n" + + "\r\n" + " \r\n" + + " \r\n" + + " internal\r\n" + + " PDF/UA corrigenda identifier\r\n" + + " corr\r\n" + + " Text\r\n" + + " \r\n" + " \r\n" + + " \r\n" + " \r\n" + + " \r\n" + " \r\n" + ""; + } + } + + /** + * Create the XML for the XMPMetadata. + * + * We use the same method from {@link PdfWriter} as a template and add what is + * needed for PDF/UA. * * @return an XmpMetadata byte array */ @@ -939,7 +1077,7 @@ private byte[] createXmpMetadataBytes() { // Not tested. int PdfXConformance = writer.getPDFXConformance(); XmpWriter xmp = new XmpWriter(baos, "UTF-8", 4); - DublinCoreAccessibleSchema dc = new DublinCoreAccessibleSchema(); + DublinCoreSchema dc = new DublinCoreSchema(); PdfSchema p = new PdfSchema(); XmpBasicSchema basic = new XmpBasicSchema(); @@ -982,10 +1120,6 @@ private byte[] createXmpMetadataBytes() { basic.addModDate(((PdfDate) obj).getW3CDate()); } } - if (this.isPDFUAFormat()) { - // Declare the document to be PDF/UA conformant. - dc.addPdfUAId(this.pdfUAConformance); - } if (dc.size() > 0) xmp.addRdfDescription(dc); @@ -994,13 +1128,56 @@ private byte[] createXmpMetadataBytes() { if (basic.size() > 0) xmp.addRdfDescription(basic); - // Declare the document to be PDF/A conformant, if requested by the developer. - if (PdfXConformance == PdfWriter.PDFA1A || PdfXConformance == PdfWriter.PDFA1B) { + if (this.isPDFUAFormat()) { + // Declare the document to be PDF/UA conforming. + PDFUASchema ua = new PDFUASchema(); + ua.addPdfUAId(this.pdfUAConformance); + xmp.addRdfDescription(ua); + } + + // Declare the document to be PDF/A conforming, if requested by the developer. + if (isPdfAFormat) { + + // If the document is also PDF/UA conforming, we need to add a description of + // that schema as an PDF/A extension. + if (isPDFUAFormat()) { + PDFAExtensionSchema pdfaext = new PDFAExtensionSchema(); + xmp.addRdfDescription(pdfaext); + } + PdfA1Schema a1 = new PdfA1Schema(); - if (PdfXConformance == PdfWriter.PDFA1A) + switch (PdfXConformance) { + case PdfWriter.PDFA1A: + case PdfWriter.PDFA1B: + a1.addPart("1"); + break; + case PDFA2A: + case PDFA2B: + a1.addPart("2"); + break; + case PDFA3A: + case PDFA3B: + case PDFA3U: + a1.addPart("3"); + break; + case PDFA4F: + a1.addPart("4"); + break; + } + switch (PdfXConformance) { + case PdfWriter.PDFA1A: + case PDFA2A: + case PDFA3A: a1.addConformance("A"); - else + break; + case PdfWriter.PDFA1B: + case PDFA2B: + case PDFA3B: a1.addConformance("B"); + break; + case PDFA4F: + a1.addConformance("F"); + } xmp.addRdfDescription(a1); } @@ -1012,7 +1189,7 @@ private byte[] createXmpMetadataBytes() { } /** - * Set the PDF icc color profile and the XMP meta data + * Set the PDF ICC color profile and the XMP meta data */ private void setPdfIccXmp() { @@ -1031,10 +1208,13 @@ private void setPdfIccXmp() { String fullFileNameIcc = ""; if (this.userProperties != null && userProperties.containsKey(PDFPageDevice.PDF_ICC_PROFILE_EXTERNAL_FILE)) { - fullFileNameIcc = userProperties.get(PDFPageDevice.PDF_ICC_PROFILE_EXTERNAL_FILE).toString().trim(); + fullFileNameIcc = userProperties.get(PDFPageDevice.PDF_ICC_PROFILE_EXTERNAL_FILE).toString(); } else { fullFileNameIcc = ((String) getReportDesignConfiguration(this.report, - PDFPageDevice.PDF_ICC_PROFILE_EXTERNAL_FILE)).trim(); + PDFPageDevice.PDF_ICC_PROFILE_EXTERNAL_FILE)); + } + if (fullFileNameIcc != null) { + fullFileNameIcc = fullFileNameIcc.trim(); } if (fullFileNameIcc != null && fullFileNameIcc.length() > 0) { try { @@ -1186,70 +1366,192 @@ private Object getReportDesignConfiguration(IReportContent reportContent, String } /** - * @param tagType + * Open a tag in the tag tree structure. + * + * Basically this means: Create a new child node for structureCurrentNode and + * let structureCurrentNode point to this child node. But several edge cases + * need special handling. For example, when we are in an artifact, we don't want + * to open a new tag. And containers need special handling for page-breaking to + * avoid the creation of unnecessary tags, e.g. a table that spans two pages + * must still a single table in the tag tree. + * + * If the PDF emitter is not configured to create tagged PDF, then this method + * is a no-op. + * + * @param tagType the tag type. Note that we use some special tag types AUTO, + * PAGE_HEADER and PAGE_FOOTER which are not actually PDF tags, + * but plcaeholders which trigger special handling here. + * @param area the area for which we create a tag. */ - public void pushTag(String tagType, IArea area) { - if (!writer.isTagged()) { + public void openTag(String tagType, IArea area) { + if (!writer.isTagged() || tagType == null) { return; } - if ("pageHeader".equals(tagType)) { - currentPage.beginArtifact(); - } else if ("pageFooter".equals(tagType)) { - currentPage.beginArtifact(); - } else if (currentPage.isInArtifact()) { - ; - } else { - structureCurrentLeaf = new PdfStructureElement(structureCurrentLeaf, new PdfName(tagType)); - // FIXME Adding attributes should be made a method of the IArea classes. - if ("Figure".equals(tagType)) { - // Top-Level figure elements must have a placement attribute. - if (PdfName.DOCUMENT.equals(structureCurrentLeaf.getParent().get(PdfName.S))) { - PdfDictionary attributes = structureCurrentLeaf.getAsDict(PdfName.A); - if (attributes == null) { - attributes = new PdfDictionary(); - structureCurrentLeaf.put(PdfName.A, attributes); - } - attributes.put(new PdfName("Placement"), new PdfName("Block")); - attributes.put(PdfName.O, new PdfName("Layout")); - } - } - if ("TD".equals(tagType) || "TH".equals(tagType)) { - if (area instanceof CellArea) { - CellArea cellArea = (CellArea) area; - int rowspan = cellArea.getRowSpan(); - int colspan = cellArea.getColSpan(); - String scope = ((CellContent) (cellArea.getContent())).getScope(); - String bookmark = cellArea.getBookmark(); - if (bookmark != null) { - structureCurrentLeaf.put(PdfName.ID, new PdfString(bookmark)); - } - - String headers = ((CellContent) (cellArea.getContent())).getHeaders(); - if (rowspan != 1 || colspan != 1 || scope != null || headers != null) { - PdfDictionary attributes = structureCurrentLeaf.getAsDict(PdfName.A); - if (attributes == null) { - attributes = new PdfDictionary(); - attributes.put(PdfName.O, PdfName.TABLE); - structureCurrentLeaf.put(PdfName.A, attributes); - } - if (rowspan != 1) { - attributes.put(new PdfName("RowSpan"), new PdfNumber(rowspan)); - } - if (colspan != 1) { - attributes.put(new PdfName("ColSpan"), new PdfNumber(colspan)); + PdfDictionary properties = null; + switch (tagType) { + case PdfTag.AUTO: + System.err.println("TODO: auto TagType found for area: " + area); + break; + case PdfTag.PAGE_HEADER: + properties = new PdfDictionary(); + properties.put(PdfNames.TYPE, PdfNames.PAGINATION); + properties.put(PdfNames.SUBTYPE, PdfNames.HEADER); + currentPage.beginArtifact(properties); + break; + case PdfTag.PAGE_FOOTER: + properties = new PdfDictionary(); + properties.put(PdfNames.TYPE, PdfNames.PAGINATION); + properties.put(PdfNames.SUBTYPE, PdfNames.FOOTER); + currentPage.beginArtifact(properties); + break; + default: + if (area instanceof ContainerArea && ((ContainerArea) area).isArtifact()) { + properties = new PdfDictionary(); + // FIXME: It is not clear if Pagination or Page is the appropriate type for + // repeated header rows. + properties.put(PdfNames.TYPE, PdfNames.PAGINATION); + currentPage.beginArtifact(properties); + } else if (currentPage.isInArtifact()) { + // Do not open a tag inside artifacts. + } else { + if (area instanceof ContainerArea) { + final ContainerArea container = (ContainerArea) area; + if (container.isFirstPart()) { + if (PdfTag.TR.equals(tagType)) { + beforeOpenTableSectionTag(container); } - if (scope != null && "TH".equals(tagType)) { - attributes.put(new PdfName("Scope"), pdfScope((scope))); + structureCurrentNode = new PdfStructureElement(structureCurrentNode, new PdfName(tagType)); + try { + container.setStructureElement(structureCurrentNode); + } catch (BirtException be) { + be.printStackTrace(); + structureCurrentNode = new PdfStructureElement(structureCurrentNode, new PdfName(tagType)); } - if (headers != null) { - attributes.put(new PdfName("Headers"), commaSeparatedToPdfByteStringArray((headers))); + } else { + structureCurrentNode = container.getFirstPart().getStructureElement(); + PdfName restored = structureCurrentNode.getAsName(PdfName.S); + if (PdfName.TABLE.equals(restored)) { + // Also restore the table section, e.g. TBody. + PdfArray children = structureCurrentNode.getAsArray(PdfName.K); // K means "kids" in this + // context + if (children != null && children.size() > 0) { + structureCurrentNode = (PdfStructureElement) children.getAsDict(children.size() - 1); + } } } + } else { + structureCurrentNode = new PdfStructureElement(structureCurrentNode, new PdfName(tagType)); + } + if (PdfTag.FIGURE.equals(tagType)) { + addFigureAttributes(); + } + if (PdfTag.TD.equals(tagType) || PdfTag.TH.equals(tagType)) { + addTableCellAttributes(tagType, area); } } } } + /** + * Open a tag for the table section THead, TBody, TFoot when necessary. + * + * When opening a row, we want to open e.g. a TBody tag if it is the first row + * of the table body. If we are still in the wrong section, we have to close + * that section tag first before opening the new section tag. + * + * @param row the RowArea. + */ + private void beforeOpenTableSectionTag(final ContainerArea row) { + PdfName currentTag = structureCurrentNode.getAsName(PdfName.S); + RowContent rowContent = (RowContent) row.getContent(); + PdfName inject = null; + boolean closeSection = false; + switch (rowContent.getBand().getBandType()) { + case IBandContent.BAND_HEADER: + // THead + if (!currentTag.equals(PdfName.THEAD)) { + inject = PdfName.THEAD; + } + break; + case IBandContent.BAND_FOOTER: + // TFoot + if (!currentTag.equals(PdfName.TFOOT)) { + inject = PdfName.TFOOT; + closeSection = !currentTag.equals(PdfName.TABLE); + } + break; + default: + // TBody + if (!currentTag.equals(PdfName.TBODY)) { + inject = PdfName.TBODY; + closeSection = !currentTag.equals(PdfName.TABLE); + } + } + if (closeSection) { + structureCurrentNode = (PdfStructureElement) structureCurrentNode.getParent(); + } + if (inject != null) { + structureCurrentNode = new PdfStructureElement(structureCurrentNode, inject); + } + } + + /** + * Add attributes for a table cell. + * + * They are necessary to connect TD cells with their corresponding TH cells. + */ + private void addTableCellAttributes(String tagType, IArea area) { + if (area instanceof CellArea) { + CellArea cellArea = (CellArea) area; + int rowspan = cellArea.getRowSpan(); + int colspan = cellArea.getColSpan(); + String scope = ((CellContent) (cellArea.getContent())).getScope(); + String bookmark = cellArea.getBookmark(); + if (bookmark != null) { + structureCurrentNode.put(PdfName.ID, new PdfString(bookmark)); + } + + String headers = ((CellContent) (cellArea.getContent())).getHeaders(); + if (rowspan != 1 || colspan != 1 || scope != null || headers != null) { + PdfDictionary attributes = structureCurrentNode.getAsDict(PdfName.A); + if (attributes == null) { + attributes = new PdfDictionary(); + attributes.put(PdfName.O, PdfName.TABLE); + structureCurrentNode.put(PdfName.A, attributes); + } + if (rowspan != 1) { + attributes.put(PdfNames.ROWSPAN, new PdfNumber(rowspan)); + } + if (colspan != 1) { + attributes.put(PdfNames.COLSPAN, new PdfNumber(colspan)); + } + if (scope != null && PdfTag.TH.equals(tagType)) { + attributes.put(PdfNames.SCOPE, pdfScope((scope))); + } + if (headers != null) { + attributes.put(PdfNames.HEADERS, commaSeparatedToPdfByteStringArray((headers))); + } + } + } + } + + /** + * Add necessary attributes for figure tags. + * + * Top-Level figure elements must have a placement attribute. + */ + private void addFigureAttributes() { + if (PdfName.DOCUMENT.equals(structureCurrentNode.getParent().get(PdfName.S))) { + PdfDictionary attributes = structureCurrentNode.getAsDict(PdfName.A); + if (attributes == null) { + attributes = new PdfDictionary(); + structureCurrentNode.put(PdfName.A, attributes); + } + attributes.put(PdfNames.PLACEMENT, PdfNames.BLOCK); + attributes.put(PdfName.O, PdfNames.LAYOUT); + } + } + /** * Split a comma-separated string into a PDF bytestring array. Note that blanks * are not stripped and empty values are allowed. @@ -1274,35 +1576,53 @@ private PdfName pdfScope(String scope) { return null; } if ("col".equals(scope)) { - return new PdfName("Column"); + return PdfNames.COLUMN; } if ("row".equals(scope)) { - return new PdfName("Row"); + return PdfNames.ROW; } logger.warning("Unsupported scope: " + scope); return null; } /** - * @param tagType + * This is the opposite of openTag. + * + * Every tag that has been opened must be closed exactly once. + * + * + * If the PDF emitter is not configured to create tagged PDF, then this method + * is a no-op. + * + * @param tagType must be the same as in the call to openTag. + * @param area must be the same as in the call to openTag. */ - public void popTag(String tagType) { - if (!writer.isTagged()) { + public void closeTag(String tagType, IArea area) { + if (!writer.isTagged() || tagType == null) { return; } if ("pageHeader".equals(tagType)) { currentPage.endArtifact(); } else if ("pageFooter".equals(tagType)) { currentPage.endArtifact(); + } else if (area instanceof ContainerArea && ((ContainerArea) area).isArtifact()) { + currentPage.endArtifact(); } else if (currentPage.isInArtifact()) { - ; + // do nothing } else { - structureCurrentLeaf = (PdfStructureElement) structureCurrentLeaf.getParent(); + if (PdfTag.TABLE.equals(tagType)) { + PdfName currentTag = structureCurrentNode.getAsName(PdfName.S); + if (!currentTag.equals(PdfNames.TR)) { + // Close the THead/TBody/TFoot tag also + structureCurrentNode = (PdfStructureElement) structureCurrentNode.getParent(); + } + } + structureCurrentNode = (PdfStructureElement) structureCurrentNode.getParent(); } } /** - * Is the writer is expected to create tagged PDF or not? + * @return Is the writer is expected to create tagged PDF or not? */ public boolean isTagged() { return writer.isTagged(); diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFRender.java b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFRender.java index 40622312ca1..f2d9c7b7e09 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFRender.java +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PDFRender.java @@ -37,11 +37,19 @@ import org.eclipse.birt.report.engine.nLayout.area.IImageArea; import org.eclipse.birt.report.engine.nLayout.area.ITemplateArea; import org.eclipse.birt.report.engine.nLayout.area.ITextArea; +import org.eclipse.birt.report.engine.nLayout.area.impl.InlineTextArea; import org.eclipse.birt.report.engine.nLayout.area.impl.TableArea; import org.eclipse.birt.report.engine.nLayout.area.style.TextStyle; import org.eclipse.birt.report.model.api.ReportDesignHandle; +import com.lowagie.text.pdf.PdfAnnotation; +import com.lowagie.text.pdf.PdfArray; +import com.lowagie.text.pdf.PdfDictionary; +import com.lowagie.text.pdf.PdfIndirectReference; import com.lowagie.text.pdf.PdfName; +import com.lowagie.text.pdf.PdfNumber; +import com.lowagie.text.pdf.PdfObject; +import com.lowagie.text.pdf.PdfString; import com.lowagie.text.pdf.PdfTemplate; /** @@ -65,8 +73,6 @@ public class PDFRender extends PageDeviceRender { protected HashSet bookmarks = new HashSet<>(); - private PdfName ARTIFACT = new PdfName("Artifact"); - /** * Constructor * @@ -99,18 +105,32 @@ protected void newPage(IContainerArea page) { public void visitImage(IImageArea imageArea) { int imageX = currentX + getX(imageArea); int imageY = currentY + getY(imageArea); + IHyperlinkAction hlAction = imageArea.getAction(); + if (null != hlAction) { + currentPageDevice.openTag(PdfTag.LINK, imageArea); + } super.visitImage(imageArea); createBookmark(imageArea, imageX, imageY); - createHyperlink(imageArea, imageX, imageY); + if (null != hlAction) { + createHyperlink(imageArea, imageX, imageY); + currentPageDevice.closeTag(PdfTag.LINK, imageArea); + } } @Override public void visitText(ITextArea textArea) { + IHyperlinkAction hlAction = textArea.getAction(); + if (null != hlAction) { + currentPageDevice.openTag(PdfTag.LINK, textArea); + } super.visitText(textArea); int x = currentX + getX(textArea); int y = currentY + getY(textArea); createBookmark(textArea, x, y); - createHyperlink(textArea, x, y); + if (null != hlAction) { + createHyperlink(textArea, x, y); + currentPageDevice.closeTag(PdfTag.LINK, textArea); + } } @Override @@ -155,11 +175,22 @@ public void end(IReportContent rc) { @Override protected void drawContainer(IContainerArea container) { + IHyperlinkAction hlAction = container.getAction(); + if (container instanceof InlineTextArea) { + // A Hyperlink is created for the text already, we don't need it here. + hlAction = null; + } + if (null != hlAction) { + currentPageDevice.openTag(PdfTag.LINK, container); + } super.drawContainer(container); int x = currentX + getX(container); int y = currentY + getY(container); createBookmark(container, x, y); - createHyperlink(container, x, y); + if (hlAction != null) { + createHyperlink(container, x, y); + currentPageDevice.closeTag(PdfTag.LINK, container); + } } /** @@ -190,7 +221,8 @@ protected void drawTextAt(ITextArea text, int x, int y, int width, int height, T } } - private void createHyperlink(IArea area, int x, int y) { + private PdfAnnotation createHyperlink(IArea area, int x, int y) { + PdfAnnotation annotation = null; IHyperlinkAction hlAction = area.getAction(); if (null != hlAction) { try { @@ -212,24 +244,61 @@ private void createHyperlink(IArea area, int x, int y) { } else { link = hlAction.getHyperlink(); } - switch (type) { case IHyperlinkAction.ACTION_BOOKMARK: - currentPage.createHyperlink(link, bookmark, targetWindow, type, x, y, width, height); + annotation = currentPage.createHyperlink(link, bookmark, targetWindow, type, x, y, width, height); break; case IHyperlinkAction.ACTION_HYPERLINK: - currentPage.createHyperlink(link, null, targetWindow, type, x, y, width, height); + annotation = currentPage.createHyperlink(link, null, targetWindow, type, x, y, width, height); break; case IHyperlinkAction.ACTION_DRILLTHROUGH: - currentPage.createHyperlink(link, null, targetWindow, type, x, y, width, height); + annotation = currentPage.createHyperlink(link, null, targetWindow, type, x, y, width, height); break; } } catch (Exception e) { logger.log(Level.WARNING, e.getMessage(), e); } + if (currentPageDevice.isTagged()) { + PdfArray children; + PdfObject childObject = currentPageDevice.structureCurrentNode.get(PdfName.K); + // The PdfName K means "kids" in this context. + if (childObject == null) { + children = new PdfArray(); + currentPageDevice.structureCurrentNode.put(PdfName.K, children); + } else { + children = new PdfArray(); + children.add(childObject); + currentPageDevice.structureCurrentNode.put(PdfName.K, children); + } + PdfDictionary objr = new PdfDictionary(PdfName.OBJR); + PdfIndirectReference annotationRef = annotation.getIndirectReference(); + objr.put(PdfName.OBJ, annotationRef); + objr.put(PdfName.PG, currentPageDevice.writer.getCurrentPage()); + children.add(objr); + // The link should contain a /Contents key, because it is required by PDF/UA-1. + // However, according to the PDF/UA Best Practice Guide, many or most current + // generation AT do not process this key and relaxation of the /Contents key + // requirement is anticipated in PDF/UA-2. + // If the area has a tooltip, we use that for the /Contents, otherwise we do + // not generate the /Contents entry. + String tooltip = hlAction.getTooltip(); + if (tooltip != null) { + annotation.put(PdfName.CONTENTS, new PdfString(tooltip)); + } + PdfIndirectReference linkref = currentPageDevice.structureCurrentNode.getReference(); + int key = currentPageDevice.structureRoot.addExistingObject(linkref); + annotation.put(PdfName.STRUCTPARENT, new PdfNumber(key)); + if (currentPageDevice.isPdfAFormat()) { + // See PDF specification Table 165 - Annotation flags + // and PDF/A-3 specification rules 6.3.2-1 and 6.3.2-2 + annotation.put(PdfName.F, new PdfNumber(4)); + } + } + } + return annotation; } @SuppressWarnings("unchecked") @@ -276,13 +345,13 @@ protected void visitChildren(IContainerArea container) { if (container.getChildrenCount() > 0) { tagType = container.getTagType(); if (tagType != null) { - currentPageDevice.pushTag(tagType, container); + currentPageDevice.openTag(tagType, container); } } } super.visitChildren(container); if (tagType != null) { - currentPageDevice.popTag(tagType); + currentPageDevice.closeTag(tagType, container); } } @@ -298,7 +367,9 @@ private void createTotalPageTemplate(int x, int y, int width, int height, float public void drawTableBorder(TableArea table) { boolean tagged = currentPageDevice.isTagged(); if (tagged) { - currentPage.beginArtifact(); + PdfDictionary properties = new PdfDictionary(); + properties.put(new PdfName("Type"), new PdfName("Background")); + currentPage.beginArtifact(properties); } super.drawTableBorder(table); if (tagged) { diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PdfNames.java b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PdfNames.java new file mode 100644 index 00000000000..7227716e248 --- /dev/null +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PdfNames.java @@ -0,0 +1,56 @@ +package org.eclipse.birt.report.engine.emitter.pdf; + +import com.lowagie.text.pdf.PdfName; + +/** + * Some standard PdfName constants which are used inside BIRT code, but are + * missing in OpenPDF's PdfName class. + * + * As soon as OpenPDF also defines theses name constants, the corresponding + * names should be removed here and references to them should be replace with + * references to the OpenPDF's PdfName. + * + * A long term goal is to get rid of this class in BIRT once all names are + * defined in OpenPDF. + * + * Some of these names are PDF tag names, some are names of attributes, some are + * used for values of attributes. + * + * If you wonder, why we only have some constants here and create other PdfName + * objects from strings on the fly: It's because we hope to get the best + * performance this way. This class is related to class {@link PdfTag}. Creating + * a {@link com.lowagie.text.pdf.PdfName} involves a little overhead, and + * comparing it to another instance is a bit more complex than comparing two + * strings. We create these constant values here a priori when we need the name + * in source code, whereas we create the PdfName objects on the fly when the + * name is read from a design file. + * + * For a description of the usage of the names, please refer to the PDF + * specification EN/ISO 32000, in particular the sections about tagged PDF and + * accessibility. + * + * @since 4.19 + * + */ +@SuppressWarnings("javadoc") +public final class PdfNames { + + public static final PdfName ALT = new PdfName("Alt"); + public static final PdfName ARTIFACT = new PdfName("Artifact"); + public static final PdfName BACKGROUND = PdfName.BACKGROUND; + public static final PdfName BLOCK = new PdfName("Block"); + public static final PdfName COLSPAN = new PdfName("ColSpan"); + public static final PdfName COLUMN = new PdfName("Column"); + public static final PdfName FOOTER = new PdfName("Footer"); + public static final PdfName HEADER = new PdfName("Header"); + public static final PdfName HEADERS = new PdfName("Headers"); + public static final PdfName LAYOUT = new PdfName("Layout"); + public static final PdfName PAGINATION = new PdfName("Pagination"); + public static final PdfName PLACEMENT = new PdfName("Placement"); + public static final PdfName ROW = new PdfName("Row"); + public static final PdfName ROWSPAN = new PdfName("RowSpan"); + public static final PdfName SCOPE = new PdfName("Scope"); + public static final PdfName SUBTYPE = PdfName.SUBTYPE; + public static final PdfName TYPE = PdfName.TYPE; + public static final PdfName TR = new PdfName(PdfTag.TR); +} diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PdfTag.java b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PdfTag.java new file mode 100644 index 00000000000..cbc3b6f3e67 --- /dev/null +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/PdfTag.java @@ -0,0 +1,42 @@ +package org.eclipse.birt.report.engine.emitter.pdf; + +/** + * String constants for PDF tag names which are used by BIRT. These are tag + * names used for tagged PDF and PDF/UA, with a few exceptions which are + * meaningful only inside BIRT. + * + * @since 4.19 + */ +public final class PdfTag { + + /** + * This is not a PDF tag name, but used as a place-holder. + * + * The actual PDF tag name will be set depending on the context. + */ + public static final String AUTO = "auto"; + + /** PDF tag used for the page header. */ + public static final String PAGE_HEADER = "pageHeader"; + + /** PDF tag used for the page footer. */ + public static final String PAGE_FOOTER = "pageFooter"; + + /** PDF tag used for images and charts. */ + public static final String FIGURE = "Figure"; + + /** PDF tag used for hyperlinks. */ + public static final String LINK = "Link"; + + /** PDF tag used for tables (but not for grids!). */ + public static final String TABLE = "Table"; + + /** PDF tag used for table rows. */ + public static final String TR = "TR"; + + /** PDF tag used for table header cells. */ + public static final String TH = "TH"; + + /** PDF tag used for table data cells. */ + public static final String TD = "TD"; +} diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/README.md b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/README.md index 4503c0e5f0f..c9ae51d41b3 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/README.md +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/README.md @@ -27,7 +27,7 @@ The following list get an overview of all supported user properties, the content Content define the PDF conformance of the created PDF document, e.g. PDF.A1A Location report Data type string - Values PDF.Standard, PDF.X1A2001, PDF.X32002, PDF.A1A, PDF.A1B + Values PDF.Standard, PDF.X1A2001, PDF.X32002, PDF.A1A, PDF.A1B, PDF.A2A, PDF.A2B, PDF.A3A, PDF.A3B, PDF.A3U, PDF.A4F Default PDF.Standard Reference see PdfEmitter.IccColorType, PdfEmitter.IccProfileFile, PdfEmitter.PDFA.AddDocumentTitle, PdfEmitter.PDFA.FallbackFont, PdfEmitter.IncludeCidSet Since 4.16 @@ -39,8 +39,8 @@ The following list get an overview of all supported user properties, the content Content define the PDF/UA conformance of the created PDF document, e.g. PDF.UA-1 Location report Data type string - Values PDF.Standard, PDF.UA-1 - Default PDF.Standard + Values none, PDF.UA-1, PDF.UA-2 + Default none Since 4.18 Designer 4.18 diff --git a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/TOCHandler.java b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/TOCHandler.java index 4fea1450f66..7e531c291b6 100644 --- a/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/TOCHandler.java +++ b/engine/org.eclipse.birt.report.engine.emitter.pdf/src/org/eclipse/birt/report/engine/emitter/pdf/TOCHandler.java @@ -86,8 +86,8 @@ public void createTOC() { /** * create a PDF outline for tocNode, using the pol as the parent PDF outline. * - * @param tocNode The tocNode whose kids need to build a PDF outline tree - * @param pol The parent PDF outline for these kids + * @param tocNode The tocNode whose children need to build a PDF outline tree + * @param pol The parent PDF outline for these children * @param bookmarks All bookMarks created during rendering */ protected void createTOC(TOCNode tocNode, PdfOutline pol, Set bookmarks) { diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/executor/ReportItemExecutor.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/executor/ReportItemExecutor.java index 29fafadea46..e35bd7edf06 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/executor/ReportItemExecutor.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/executor/ReportItemExecutor.java @@ -30,14 +30,11 @@ import org.eclipse.birt.report.engine.api.DataID; import org.eclipse.birt.report.engine.api.DataSetID; import org.eclipse.birt.report.engine.api.InstanceID; -import org.eclipse.birt.report.engine.content.IBandContent; import org.eclipse.birt.report.engine.content.IContent; import org.eclipse.birt.report.engine.content.IGroupContent; import org.eclipse.birt.report.engine.content.IHyperlinkAction; import org.eclipse.birt.report.engine.content.IReportContent; -import org.eclipse.birt.report.engine.content.impl.CellContent; import org.eclipse.birt.report.engine.content.impl.Column; -import org.eclipse.birt.report.engine.content.impl.RowContent; import org.eclipse.birt.report.engine.extension.IBaseResultSet; import org.eclipse.birt.report.engine.extension.ICubeResultSet; import org.eclipse.birt.report.engine.extension.IExecutorContext; @@ -619,17 +616,6 @@ protected void initializeContent(ReportElementDesign design, IContent content) { DesignElementHandle h = ((DesignElementHandle) (rid.getHandle())); if (h != null) { content.setTagType(h.getTagType()); - if (content instanceof CellContent) { - CellContent cell = (CellContent) content; - RowContent row = (RowContent)cell.getParent(); - if (row.getBand() != null) { - int bandType = row.getBand().getBandType(); - // FIXME This prevents that the report designer can override the tag type. - if (bandType == IBandContent.BAND_HEADER || bandType == IBandContent.BAND_GROUP_HEADER) { - content.setTagType("TH"); - } - } - } } } } @@ -722,10 +708,11 @@ protected void finishTOCEntry() { protected void startGroupTOCEntry(IGroupContent group) { TOCBuilder tocBuilder = context.getTOCBuilder(); if (tocBuilder != null) { - TOCEntry entry = getParentTOCEntry(); + TOCEntry parentTOCEntry = getParentTOCEntry(); String hiddenFormats = group.getStyle().getVisibleFormat(); long elementId = getElementId(); - tocEntry = tocBuilder.startGroupEntry(entry, group.getTOC(), group.getBookmark(), hiddenFormats, elementId); + tocEntry = tocBuilder.startGroupEntry(parentTOCEntry, group.getTOC(), group.getBookmark(), hiddenFormats, + elementId); String tocId = tocEntry.getNodeId(); if (group.getBookmark() == null) { group.setBookmark(tocId); diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/i18n/Messages.properties b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/i18n/Messages.properties index 0dbb68c3b6e..887965add9d 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/i18n/Messages.properties +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/i18n/Messages.properties @@ -117,7 +117,7 @@ Error.ResultsetExtractError = Result set not found. Error.FailedToInitializeEmitter = Failed to initialize emitter. ########################################################### -PDFCreator = BIRT Report Engine {0}. +PDFCreator = BIRT Report Engine {0} ########################################################### Error.FlashObjectNotSupported = Flash object report items are not supported in this report format. diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/layout/pdf/util/HTML2Content.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/layout/pdf/util/HTML2Content.java index b7804456c2d..f26c1c0d929 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/layout/pdf/util/HTML2Content.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/layout/pdf/util/HTML2Content.java @@ -633,7 +633,7 @@ static void handleElement(Element ele, Map cssStyles, if ("".equals(text.getText())) // add default list type when tag
    attribute is empty. { text.setText("\u2022"); // the disc type - text.setTagType("Lbl"); // TODO HVB check if this is correct + text.setTagType("Lbl"); } } @@ -684,6 +684,7 @@ else if (lTagName.equals(TAG_DD) || lTagName.equals(TAG_DT)) // $NON-NLS-1$ } else if (htmlBlockDisplay.contains(lTagName) || htmlInlineDisplay.contains(lTagName)) { IContainerContent container = content.getReportContent().createContainerContent(); handleStyle(ele, cssStyles, container); + mapHtmlTagToPdfTag(container, lTagName); addChild(content, container); // handleStyle(ele, cssStyles, container); processNodes(ele, cssStyles, container, action, nestCount); @@ -692,6 +693,34 @@ else if (lTagName.equals(TAG_DD) || lTagName.equals(TAG_DT)) // $NON-NLS-1$ } } + // FIXME: The mapping should be configurable. + // FIXME: In particular, it should be possible to adjust the heading levels, + // (i.e to specify a positive or negative offset, such that H1->H2, H2->H3). + static final Map HTML_TAG_TO_PDF_TAG = Map.of( + "DIV", "DIV", + "P", "P", + "H1", "H1", + "H2", "H2", + "H3", "H3", + "H4", "H4" + ); + + /** + * This sets the container's tag type corresponding to the HTML tag, if + * possible. + * + * @param container + * @param lTagName + */ + private static void mapHtmlTagToPdfTag(IContainerContent container, String lTagName) { + if (lTagName == null) + return; + String mapped = HTML_TAG_TO_PDF_TAG.get(lTagName.toUpperCase()); + if (mapped != null) { + container.setTagType(mapped); + } + } + /** * Outputs the A element * diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/PdfTagConstant.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/PdfTagConstant.java new file mode 100644 index 00000000000..0edd32d778c --- /dev/null +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/PdfTagConstant.java @@ -0,0 +1,19 @@ +package org.eclipse.birt.report.engine.nLayout; + +/** + * Some constants are duplicated here to avoid dependencies. + * + * @see org.eclipse.birt.report.engine.emitter.pdf.PdfTag + * + * @since 4.19 + * + */ +public class PdfTagConstant { + public static final String AUTO = "auto"; + public static final String NONSTRUCT = "NonStruct"; + public static final String P = "P"; + public static final String TD = "TD"; + public static final String TH = "TH"; + public static final String TR = "TR"; + +} diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockContainerArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockContainerArea.java index 3a7c147080a..c50c5b50656 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockContainerArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockContainerArea.java @@ -23,9 +23,11 @@ import org.eclipse.birt.core.exception.BirtException; import org.eclipse.birt.report.engine.content.IContent; import org.eclipse.birt.report.engine.content.IStyle; +import org.eclipse.birt.report.engine.content.impl.ForeignContent; import org.eclipse.birt.report.engine.css.engine.StyleConstants; import org.eclipse.birt.report.engine.css.engine.value.css.CSSValueConstants; import org.eclipse.birt.report.engine.nLayout.LayoutContext; +import org.eclipse.birt.report.engine.nLayout.PdfTagConstant; import org.eclipse.birt.report.engine.nLayout.area.IArea; import org.eclipse.birt.report.engine.nLayout.area.IContainerArea; import org.eclipse.birt.report.engine.nLayout.area.style.BoxStyle; @@ -252,16 +254,22 @@ public SplitResult splitLines(int lineCount) throws BirtException { @Override public SplitResult split(int height, boolean force) throws BirtException { + final SplitResult ret; if (force) { - return _split(height, true); + ret = _split(height, true); } else if (isPageBreakInsideAvoid()) { if (isPageBreakBeforeAvoid()) { - return SplitResult.BEFORE_AVOID_WITH_NULL; + ret = SplitResult.BEFORE_AVOID_WITH_NULL; + } else { + ret = SplitResult.SUCCEED_WITH_NULL; } - return SplitResult.SUCCEED_WITH_NULL; } else { - return _split(height, false); + ret = _split(height, false); } + if (ret.getResult() != null) { + setPreviousPart(ret.getResult()); + } + return ret; } protected SplitResult _split(int height, boolean force) throws BirtException { @@ -503,4 +511,15 @@ public void updateChildrenPosition() { } } + @Override + public String getTagType() { + String tagType = super.getTagType(); + if (PdfTagConstant.AUTO.equals(tagType)) { + if (getContent() instanceof ForeignContent) { + tagType = PdfTagConstant.NONSTRUCT; + } + } + return tagType; + } + } diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockTextArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockTextArea.java index c1f4f18c5fd..b7df46d3261 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockTextArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/BlockTextArea.java @@ -24,6 +24,7 @@ import org.eclipse.birt.report.engine.content.ITextContent; import org.eclipse.birt.report.engine.layout.pdf.util.PropertyUtil; import org.eclipse.birt.report.engine.nLayout.LayoutContext; +import org.eclipse.birt.report.engine.nLayout.PdfTagConstant; import org.eclipse.birt.report.engine.nLayout.area.IArea; import org.eclipse.birt.report.engine.nLayout.area.ILayout; import org.w3c.dom.css.CSSValue; @@ -203,4 +204,13 @@ public void setHelpText(String helpText) { this.helpText = helpText; } + @Override + public String getTagType() { + String tagType = super.getTagType(); + if (PdfTagConstant.AUTO.equals(tagType)) { + tagType = PdfTagConstant.P; + } + return tagType; + } + } diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/CellArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/CellArea.java index f5ff394980e..6ca4f85f0da 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/CellArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/CellArea.java @@ -17,14 +17,17 @@ import java.awt.Color; import org.eclipse.birt.core.exception.BirtException; +import org.eclipse.birt.report.engine.content.IBandContent; import org.eclipse.birt.report.engine.content.ICellContent; import org.eclipse.birt.report.engine.content.IContent; import org.eclipse.birt.report.engine.content.IStyle; import org.eclipse.birt.report.engine.content.impl.ReportContent; +import org.eclipse.birt.report.engine.content.impl.RowContent; import org.eclipse.birt.report.engine.css.engine.StyleConstants; import org.eclipse.birt.report.engine.executor.ExecutionContext; import org.eclipse.birt.report.engine.layout.pdf.util.PropertyUtil; import org.eclipse.birt.report.engine.nLayout.LayoutContext; +import org.eclipse.birt.report.engine.nLayout.PdfTagConstant; import org.eclipse.birt.report.engine.nLayout.area.IContainerArea; import org.eclipse.birt.report.engine.nLayout.area.style.BackgroundImageInfo; import org.eclipse.birt.report.engine.nLayout.area.style.BoxStyle; @@ -352,4 +355,27 @@ public CellArea deepClone() { return cell; } + @Override + public String getTagType() { + String tagType = super.getTagType(); + if (PdfTagConstant.AUTO.equals(tagType)) { + tagType = PdfTagConstant.TD; + // In a table row, either TH or TD depending on the row. + // In a grid row, no tag at all is used. + RowArea row = (RowArea) getParent(); + if (row.getTableArea().isGridDesign()) { + tagType = null; + } else { + RowContent rowContent = (RowContent)row.getContent(); + if (rowContent.getBand() != null) { + int bandType = rowContent.getBand().getBandType(); + if (bandType == IBandContent.BAND_HEADER || bandType == IBandContent.BAND_GROUP_HEADER) { + tagType = PdfTagConstant.TH; + } + } + } + } + return tagType; + } + } diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/ContainerArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/ContainerArea.java index fae1812323f..8c29c0ed540 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/ContainerArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/ContainerArea.java @@ -47,6 +47,7 @@ import org.w3c.dom.css.CSSValue; import com.lowagie.text.Image; +import com.lowagie.text.pdf.PdfStructureElement; /** * @@ -97,7 +98,151 @@ public abstract class ContainerArea extends AbstractArea implements IContainerAr protected transient boolean isInInlineStacking = false; + /** + * It seems as if this variable is no longer used. I cannot find any place where + * it is actually read, except in a cloning constructor. + */ protected transient boolean first = true; + + /** + * This is needed for split tracking in PDF/UA. For example, if a table is + * longer than one page, it must be a single table in the tag structure + * nevertheless, and the table element on the next page must be marked as an + * artifact. + * + * @since 4.19 + * + */ + public static class SplitTrackingData { + public SplitTrackingData() { + }; + + public ContainerArea firstPart = null; + public short partNumber = 1; + public short lastPartNumber = 1; // This is only defined for the first part (when partNumber is 1). + public boolean artifact = false; + } + + /** + * Split tracking data is only present if a split actually occurs and we + * generate tagged PDF output, to save memory. + */ + protected transient SplitTrackingData splitTracking = null; + + /** + * Mark this area is a paging artifact. + */ + public void setArtifact() { + if (splitTracking == null) { + splitTracking = new SplitTrackingData(); + splitTracking.artifact = true; + } + } + + /** + * Is this a paging artifact or not? + * + * @return true if is a paging artifact. + */ + public boolean isArtifact() { + if (splitTracking == null) { + return false; + } + return splitTracking.artifact; + } + + /** + * Track the split. Link to the first part, increment the last part number and + * set the partNumber. + * + * Note: previousPart was just created. + * + * Some parts of the tracking data are stored in the firstPart, while others are + * stored in the current area. + * + */ + protected void setPreviousPart(ContainerArea previousPart) { + if (splitTracking == null) { + splitTracking = new SplitTrackingData(); + } + previousPart.splitTracking = new SplitTrackingData(); + if (splitTracking.partNumber == 1) { + splitTracking.firstPart = previousPart; + } else { + previousPart.splitTracking.firstPart = splitTracking.firstPart; + } + previousPart.splitTracking.partNumber = splitTracking.firstPart.splitTracking.lastPartNumber; + splitTracking.firstPart.splitTracking.lastPartNumber++; + splitTracking.partNumber = splitTracking.firstPart.splitTracking.lastPartNumber; + } + + /** + * @return true if this Area is not split at all or if it is the first part of + * the split areas sequence. + */ + public boolean isFirstPart() { + if (splitTracking == null) { + return true; + } + return splitTracking.partNumber == 1; + } + + /** + * This links to the first part of the split sequence. + * + * @return First area of the split sequence, or null if this is already the + * first area (or not split at all). + */ + public ContainerArea getFirstPart() { + if (splitTracking == null) { + return null; + } + return splitTracking.firstPart; + } + + /** + * Get the number of the last part of the split sequence. This is equal to split + * sequence length. + * + * @return a natural number. + */ + public short getLastPartNumber() { + if (splitTracking == null) { + return 1; + } + return splitTracking.lastPartNumber; + } + + PdfStructureElement structureElement; + + /** + * The PDF structure element (that is, a node inside the tag structure tree). + * + * This is only defined for the first part of the split sequence. + * + * @return The structure element, or null if not called for the first part of + * the split sequence. + */ + public PdfStructureElement getStructureElement() { + return structureElement; + } + + /** + * Set the PDF structure element (that is, a node inside the tag structure + * tree). + * + * @param structureElement a PDF structure element, derived from the PDF tag + * type. + * @throws BirtException if not called for the first part of the split sequence. + */ + public void setStructureElement(PdfStructureElement structureElement) throws BirtException { + if (!isFirstPart()) { + throw new BirtException("Can only store a PdfStructureElement for the first part of a split Area"); + + } + this.structureElement = structureElement; + } + protected transient boolean finished = false; protected CSSValue pageBreakAfter = null; @@ -470,20 +615,31 @@ public void setPageBreakInside(CSSValue pageBreakInside) { public abstract void initialize() throws BirtException; /** - * Split container lines + * Split container lines. + * + * This method need better documentation. What is its purpose? * * @param lineCount - * @return Return the splitted results + * @return Return the split result * @throws BirtException */ public abstract SplitResult splitLines(int lineCount) throws BirtException; /** - * Split container lines + * Try to split the container which does not fit into the current page. + * + * If the container is actually split, then the result status is + * SPLIT_SUCCEED_WITH_PART, and the result container is a new container which + * contains the first part of the split sequence, while this container is + * modified in place to remove that first part. + * + * If we think of this containers content as a List (which is isn't), this is + * logically similar to firtPart = this.content.remove(0); * - * @param height - * @param force - * @return Return the splitted results + * @param height The remaining page height. + * @param force If this is true, ignore "page-break-inside: avoid" and + * "page-break-before: avoid" properties. + * @return The split result. * @throws BirtException */ public abstract SplitResult split(int height, boolean force) throws BirtException; diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/InlineContainerArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/InlineContainerArea.java index 69dca5698d1..f7ffbf552ff 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/InlineContainerArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/InlineContainerArea.java @@ -251,8 +251,13 @@ public String getTagType() { Object generateBy = content.getGenerateBy(); if (generateBy == null) { return "Span"; + // Hmm... I would also like to just return null, but that results + // in an InvalidArgumentException: "The structure has kids" when rendered. } - return ((ITagType) generateBy).getTagType(); + if (generateBy instanceof ITagType) { + return ((ITagType) generateBy).getTagType(); + } + return null; } } diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RepeatableArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RepeatableArea.java index d9be596f9ca..a547681a860 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RepeatableArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RepeatableArea.java @@ -142,7 +142,17 @@ public SplitResult split(int height, boolean force) throws BirtException { } } } - return super.split(height, force); + SplitResult ret = super.split(height, force); + if (ret.status == SplitResult.SPLIT_SUCCEED_WITH_PART) { + Iterator i = children.iterator(); + while (i.hasNext()) { + ContainerArea area = (ContainerArea) i.next(); + if (isInRepeatHeader(area) || "Caption".equals(area.getTagType())) { + area.setArtifact(); + } + } + } + return ret; } @Override diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RowArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RowArea.java index 2c5df51162b..002bed19b9f 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RowArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/RowArea.java @@ -23,6 +23,7 @@ import org.eclipse.birt.report.engine.css.engine.value.css.CSSConstants; import org.eclipse.birt.report.engine.css.engine.value.css.CSSValueConstants; import org.eclipse.birt.report.engine.nLayout.LayoutContext; +import org.eclipse.birt.report.engine.nLayout.PdfTagConstant; import org.eclipse.birt.report.engine.nLayout.area.IArea; /** @@ -262,7 +263,11 @@ public void addChildByColumnId(CellArea cell) { @Override public SplitResult split(int height, boolean force) throws BirtException { if (force) { - return _split(height, force); + SplitResult ret = _split(height, force); + if (ret.getResult() != null) { + setPreviousPart(ret.getResult()); + } + return ret; } else if (isPageBreakInsideAvoid()) { if (isPageBreakBeforeAvoid()) { return SplitResult.BEFORE_AVOID_WITH_NULL; @@ -271,7 +276,11 @@ public SplitResult split(int height, boolean force) throws BirtException { needResolveBorder = true; return SplitResult.SUCCEED_WITH_NULL; } - return _split(height, force); + SplitResult ret = _split(height, force); + if (ret.getResult() != null) { + setPreviousPart(ret.getResult()); + } + return ret; } protected void _splitSpanCell(int height, boolean force) throws BirtException { @@ -470,4 +479,16 @@ private void updateCellBackgroundImage() { } } + @Override + public String getTagType() { + String tagType = super.getTagType(); + if (PdfTagConstant.AUTO.equals(tagType)) { + tagType = PdfTagConstant.TR; + if (getTableArea().isGridDesign()) { + tagType = null; + } + } + return tagType; + } + } diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/TableArea.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/TableArea.java index 98ab4d9f0b3..816df2fb4e7 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/TableArea.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/nLayout/area/impl/TableArea.java @@ -230,8 +230,10 @@ protected void addCaption(String caption) throws BirtException { } ReportContent report = (ReportContent) content.getReportContent(); IRowContent row = report.createRowContent(); + row.setTagType("Caption"); row.setParent(content); ICellContent cell = report.createCellContent(); + cell.setTagType(null); cell.setColSpan(getColumnCount()); cell.setColumn(0); StyleDeclaration cstyle = new StyleDeclaration(report.getCSSEngine()); @@ -241,11 +243,13 @@ protected void addCaption(String caption) throws BirtException { cell.setInlineStyle(cstyle); cell.setParent(row); ILabelContent captionLabel = report.createLabelContent(); + captionLabel.setTagType(null); captionLabel.setParent(cell); captionLabel.setText(caption); StyleDeclaration style = new StyleDeclaration(report.getCSSEngine()); style.setProperty(StyleConstants.STYLE_TEXT_ALIGN, CSSValueConstants.CENTER_VALUE); captionLabel.setInlineStyle(style); + captionLabel.setTagType(null); RowArea captionRow = new RowArea(this, context, row); captionRow.isDummy = true; captionRow.setParent(this); @@ -257,7 +261,6 @@ protected void addCaption(String caption) throws BirtException { captionCell.initialize(); captionCell.isDummy = true; captionCell.setRowSpan(1); - captionRow.children.add(captionCell); BlockTextArea captionText = new BlockTextArea(captionCell, context, captionLabel); captionText.isDummy = true; captionText.layout(); @@ -265,7 +268,6 @@ protected void addCaption(String caption) throws BirtException { captionCell.setContentHeight(h); captionRow.setHeight(captionCell.getAllocatedHeight()); captionRow.finished = true; - add(captionRow); if (repeatList == null) { repeatList = new ArrayList(); } @@ -311,7 +313,7 @@ public SplitResult split(int height, boolean force) throws BirtException { InstanceID unresolvedTableIID = unresolvedRow.getTableArea().getContent().getInstanceID(); // this iid can be null, because the table may be generated // from HTML2Content. - // in this case, they are ignored by unresloved row hint. + // in this case, they are ignored by unresolved row hint. // Currently, large HTML text is not supported to be split. if (unresolvedTableIID != null) { pageHintGenerator.addUnresolvedRowHint(unresolvedTableIID.toUniqueString(), diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCBuilder.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCBuilder.java index f02cc5b3e29..3594bf5c7db 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCBuilder.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCBuilder.java @@ -75,6 +75,17 @@ public TOCEntry startEntry(TOCEntry parent, Object tocValue, String bookmark, St return startEntry(parent, tocValue, bookmark, hiddenFormats, false, elementId); } + /** + * This method seems to be only used in tests, not in the engine! + * + * @param parent + * @param tocValue + * @param bookmark + * @param elementId + * @return + * + * @deprecated for removal. Use the variant with hiddenFormats argument instead. + */ public TOCEntry startEntry(TOCEntry parent, Object tocValue, String bookmark, long elementId) { return startEntry(parent, tocValue, bookmark, null, false, elementId); } diff --git a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCEntry.java b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCEntry.java index 19cad9916d0..fff1c4bb2cb 100644 --- a/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCEntry.java +++ b/engine/org.eclipse.birt.report.engine/src/org/eclipse/birt/report/engine/toc/TOCEntry.java @@ -44,4 +44,16 @@ public void setTreeNode(ITreeNode treeNode) { this.treeNode = treeNode; } + /** + * Returns the nesting level. + * + * @return 0 for the root note, 1 + parent level otherwise. + */ + public int getLevel() { + if (parent == null) { + return 0; + } + return 1 + parent.getLevel(); + } + } diff --git a/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/api/elements/table/LayoutTable.java b/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/api/elements/table/LayoutTable.java index fd64797bf27..fe8284fe245 100644 --- a/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/api/elements/table/LayoutTable.java +++ b/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/api/elements/table/LayoutTable.java @@ -108,7 +108,7 @@ public int getColumnCount() { } /** - * Return a cell element with the given poistion. Uses this method to find cells + * Return a cell element with the given position. Uses this method to find cells * in Table Header, Detail and Footer slots. * * @param slotId the slot index, diff --git a/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/elements/rom.def b/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/elements/rom.def index dd2f459a536..3297c1befe8 100644 --- a/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/elements/rom.def +++ b/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/elements/rom.def @@ -695,16 +695,24 @@ + + + + + + + - - + + + @@ -1919,7 +1927,7 @@ PDF.Standard - PDF.Standard + none RGB @@ -2893,7 +2901,7 @@ - P + auto @@ -2997,7 +3005,7 @@ - P + auto @@ -3089,7 +3097,7 @@ true - P + auto @@ -3502,7 +3510,7 @@ NoChange - TR + auto @@ -3558,7 +3566,7 @@ NoChange - TD + auto @@ -3796,7 +3804,7 @@ true - P + auto diff --git a/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/i18n/Messages.properties b/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/i18n/Messages.properties index a1ee07f59cf..9c41e3944d2 100644 --- a/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/i18n/Messages.properties +++ b/model/org.eclipse.birt.report.model/src/org/eclipse/birt/report/model/i18n/Messages.properties @@ -764,11 +764,18 @@ Choices.emitterPdfVersion.1.4=Version 1.4 Choices.emitterPdfVersion.1.5=Version 1.5 Choices.emitterPdfVersion.1.6=Version 1.6 Choices.emitterPdfVersion.1.7=Version 1.7 +Choices.emitterPdfVersion.2.0=Version 2.0 #96 emitterPdfConformance Choices.emitterPdfConformance.X32002=PDF, X32002 -Choices.emitterPdfConformance.A1A=PDF, A1A -Choices.emitterPdfConformance.A1B=PDF, A1B +Choices.emitterPdfConformance.A1A=PDF/A-1a +Choices.emitterPdfConformance.A1B=PDF/A-1b +Choices.emitterPdfConformance.A2A=PDF/A-2a +Choices.emitterPdfConformance.A2B=PDF/A-2b +Choices.emitterPdfConformance.A3A=PDF/A-3a +Choices.emitterPdfConformance.A3B=PDF/A-3b +Choices.emitterPdfConformance.A3U=PDF/A-3u +Choices.emitterPdfConformance.A4F=PDF/A-4f Choices.emitterPdfConformance.Standard=Standard PDF #97 emitterPdfIccColorType @@ -780,7 +787,9 @@ Choices.textDecoration.normal=Normal Choices.textDecoration.none=None #99 emitterPdfUAConformance -Choices.emitterPdfConformance.UA1=PDF/UA-1 +Choices.emitterPdfUAConformance.UA1=PDF/UA-1 +Choices.emitterPdfUAConformance.UA2=PDF/UA-2 +Choices.emitterPdfUAConformance.none=None ########################################################### # Classes