Skip to content

Commit 3b22733

Browse files
author
Dmitry Radchuk
committed
Add pdf/a-4 support
DEVSIX-7741
1 parent 1e7fc65 commit 3b22733

File tree

25 files changed

+783
-61
lines changed

25 files changed

+783
-61
lines changed

kernel/src/main/java/com/itextpdf/kernel/pdf/IsoKey.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ public enum IsoKey {
3737
TAG_STRUCTURE_ELEMENT,
3838
FONT_GLYPHS,
3939
XREF_TABLE,
40-
SIGNATURE
40+
SIGNATURE,
41+
CRYPTO
4142
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfAConformanceLevel.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public class PdfAConformanceLevel {
4141
public static final PdfAConformanceLevel PDF_A_3A = new PdfAConformanceLevel("3", "A");
4242
public static final PdfAConformanceLevel PDF_A_3B = new PdfAConformanceLevel("3", "B");
4343
public static final PdfAConformanceLevel PDF_A_3U = new PdfAConformanceLevel("3", "U");
44+
public static final PdfAConformanceLevel PDF_A_4 = new PdfAConformanceLevel("4", null);
45+
public static final String PDF_A_4_REVISION = "2020";
4446

4547
private final String conformance;
4648
private final String part;
@@ -59,7 +61,7 @@ public String getPart() {
5961
}
6062

6163
public static PdfAConformanceLevel getConformanceLevel(String part, String conformance) {
62-
String lowLetter = conformance.toUpperCase();
64+
String lowLetter = conformance == null ? null : conformance.toUpperCase();
6365
boolean aLevel = "A".equals(lowLetter);
6466
boolean bLevel = "B".equals(lowLetter);
6567
boolean uLevel = "U".equals(lowLetter);
@@ -87,6 +89,8 @@ public static PdfAConformanceLevel getConformanceLevel(String part, String confo
8789
if (uLevel)
8890
return PdfAConformanceLevel.PDF_A_3U;
8991
break;
92+
case "4":
93+
return PdfAConformanceLevel.PDF_A_4;
9094
}
9195
return null;
9296
}
@@ -99,12 +103,11 @@ public static PdfAConformanceLevel getConformanceLevel(XMPMeta meta) {
99103
partXmpProperty = meta.getProperty(XMPConst.NS_PDFA_ID, XMPConst.PART);
100104
} catch (XMPException ignored) {
101105
}
102-
if (conformanceXmpProperty == null || partXmpProperty == null) {
106+
if (partXmpProperty == null || (conformanceXmpProperty == null && !"4".equals(partXmpProperty.getValue()))) {
103107
return null;
104108
} else {
105-
String conformance = conformanceXmpProperty.getValue();
106-
String part = partXmpProperty.getValue();
107-
return getConformanceLevel(part, conformance);
109+
return getConformanceLevel(partXmpProperty.getValue(),
110+
conformanceXmpProperty == null ? null : conformanceXmpProperty.getValue());
108111
}
109112
}
110113
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocument.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -904,14 +904,11 @@ public void close() {
904904
}
905905

906906
PdfObject pageRoot = catalog.getPageTree().generateTree();
907+
flushInfoDictionary(properties.appendMode);
907908
if (catalog.getPdfObject().isModified() || pageRoot.isModified()) {
908909
catalog.put(PdfName.Pages, pageRoot);
909910
catalog.getPdfObject().flush(false);
910911
}
911-
912-
if (getDocumentInfo().getPdfObject().isModified()) {
913-
getDocumentInfo().getPdfObject().flush(false);
914-
}
915912
flushFonts();
916913

917914
if (writer.crypto != null) {
@@ -960,8 +957,8 @@ public void close() {
960957
if (structTreeRoot != null) {
961958
tryFlushTagStructure(false);
962959
}
960+
flushInfoDictionary(properties.appendMode);
963961
catalog.getPdfObject().flush(false);
964-
getDocumentInfo().getPdfObject().flush(false);
965962
flushFonts();
966963

967964
if (writer.crypto != null) {
@@ -990,17 +987,17 @@ public void close() {
990987
// To avoid encryption of XrefStream and Encryption dictionary remove crypto.
991988
// NOTE. No need in reverting, because it is the last operation with the document.
992989
writer.crypto = null;
990+
checkIsoConformance(crypto, IsoKey.CRYPTO);
993991

994992
if (!properties.appendMode && crypto != null) {
995993
// no need to flush crypto in append mode, it shall not have changed in this case
996994
crypto.flush(false);
997995
}
998996

999-
// The following two operators prevents the possible inconsistency between root and info
997+
// The following operator prevents the possible inconsistency between root and info
1000998
// entries existing in the trailer object and corresponding fields. This inconsistency
1001999
// may appear when user gets trailer and explicitly sets new root or info dictionaries.
10021000
trailer.put(PdfName.Root, catalog.getPdfObject());
1003-
trailer.put(PdfName.Info, getDocumentInfo().getPdfObject());
10041001

10051002
//By this time original and modified document ids should always be not null due to initializing in
10061003
// either writer properties, or in the writer init section on document open or from pdfreader. So we
@@ -2070,7 +2067,6 @@ protected void open(PdfVersion newPdfVersion) {
20702067
}
20712068

20722069
trailer.put(PdfName.Root, catalog.getPdfObject().getIndirectReference());
2073-
trailer.put(PdfName.Info, getDocumentInfo().getPdfObject().getIndirectReference());
20742070

20752071
if (reader != null) {
20762072
// If the reader's trailer contains an ID entry, let's copy it over to the new trailer
@@ -2183,6 +2179,23 @@ protected void open(PdfVersion newPdfVersion) {
21832179
protected void addCustomMetadataExtensions(XMPMeta xmpMeta) {
21842180
}
21852181

2182+
/**
2183+
* Flush info dictionary if needed.
2184+
*
2185+
* @param appendMode <code>true</code> if the document is edited in append mode.
2186+
*/
2187+
protected void flushInfoDictionary(boolean appendMode) {
2188+
PdfObject infoDictObj = getDocumentInfo().getPdfObject();
2189+
if (!appendMode || infoDictObj.isModified()) {
2190+
infoDictObj.flush(false);
2191+
}
2192+
2193+
// The following operator prevents the possible inconsistency between root and info
2194+
// entries existing in the trailer object and corresponding fields. This inconsistency
2195+
// may appear when user gets trailer and explicitly sets new root or info dictionaries.
2196+
trailer.put(PdfName.Info, infoDictObj);
2197+
}
2198+
21862199
/**
21872200
* Updates XMP metadata.
21882201
* Shall be override.

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfDocumentInfo.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ public PdfDocumentInfo addCreationDate() {
122122
return put(PdfName.CreationDate, new PdfDate().getPdfObject());
123123
}
124124

125+
/**
126+
* Remove creation date from the document info dictionary.
127+
*
128+
* @return this instance.
129+
*/
130+
public PdfDocumentInfo removeCreationDate() {
131+
infoDictionary.remove(PdfName.CreationDate);
132+
return this;
133+
}
134+
125135
public PdfDocumentInfo addModDate() {
126136
return put(PdfName.ModDate, new PdfDate().getPdfObject());
127137
}

kernel/src/main/java/com/itextpdf/kernel/pdf/PdfName.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,15 @@ public class PdfName extends PdfPrimitiveObject implements Comparable<PdfName> {
170170
public static final PdfName ca = createDirectName("ca");
171171
public static final PdfName CalGray = createDirectName("CalGray");
172172
public static final PdfName CalRGB = createDirectName("CalRGB");
173+
public static final PdfName RGB = createDirectName("RGB");
173174
public static final PdfName CapHeight = createDirectName("CapHeight");
174175
public static final PdfName Cap = createDirectName("Cap");
175176
public static final PdfName Caption = createDirectName("Caption");
176177
public static final PdfName Caret = createDirectName("Caret");
177178
public static final PdfName Catalog = createDirectName("Catalog");
178179
public static final PdfName Category = createDirectName("Category");
179180
public static final PdfName CCITTFaxDecode = createDirectName("CCITTFaxDecode");
181+
public static final PdfName CCF = createDirectName("CCF");
180182
public static final PdfName Center = createDirectName("Center");
181183
public static final PdfName CenterWindow = createDirectName("CenterWindow");
182184
public static final PdfName Cert = createDirectName("Cert");
@@ -234,11 +236,14 @@ public class PdfName extends PdfPrimitiveObject implements Comparable<PdfName> {
234236
public static final PdfName Dashed = createDirectName("Dashed");
235237
public static final PdfName Data = createDirectName("Data");
236238
public static final PdfName DCTDecode = createDirectName("DCTDecode");
239+
public static final PdfName DCT = createDirectName("DCT");
237240
public static final PdfName Decimal = createDirectName("Decimal");
238241
public static final PdfName Decode = createDirectName("Decode");
239242
public static final PdfName DecodeParms = createDirectName("DecodeParms");
240243
public static final PdfName Default = createDirectName("Default");
241244
public static final PdfName DefaultCMYK = createDirectName("DefaultCMYK");
245+
246+
public static final PdfName CMYK = createDirectName("CMYK");
242247
public static final PdfName DefaultCryptFilter = createDirectName("DefaultCryptFilter");
243248
public static final PdfName DefaultGray = createDirectName("DefaultGray");
244249
public static final PdfName DefaultRGB = createDirectName("DefaultRGB");
@@ -393,6 +398,8 @@ public class PdfName extends PdfPrimitiveObject implements Comparable<PdfName> {
393398
public static final PdfName GoToDp = createDirectName("GoToDp");
394399
public static final PdfName GoToE = createDirectName("GoToE");
395400
public static final PdfName GoToR = createDirectName("GoToR");
401+
402+
public static final PdfName G = createDirectName("G");
396403
public static final PdfName Graph = createDirectName("Graph");
397404
public static final PdfName Group = createDirectName("Group");
398405
public static final PdfName Groove = createDirectName("Groove");
@@ -627,6 +634,8 @@ public class PdfName extends PdfPrimitiveObject implements Comparable<PdfName> {
627634
public static final PdfName Pdf_Version_1_6 = createDirectName("1.6");
628635
public static final PdfName Pdf_Version_1_7 = createDirectName("1.7");
629636
public static final PdfName Pdf_Version_2_0 = createDirectName("2.0");
637+
638+
public static final PdfName PieceInfo = createDirectName("PieceInfo");
630639
public static final PdfName Pg = createDirectName("Pg");
631640
public static final PdfName PI = createDirectName("PI");
632641
public static final PdfName PickTrayByPDFSize = createDirectName("PickTrayByPDFSize");
@@ -671,6 +680,7 @@ public class PdfName extends PdfPrimitiveObject implements Comparable<PdfName> {
671680
public static final PdfName RC = createDirectName("RC");
672681
public static final PdfName RClosedArrow = createDirectName("RClosedArrow");
673682
public static final PdfName RD = createDirectName("RD");
683+
public static final PdfName RL = createDirectName("RL");
674684
public static final PdfName Reason = createDirectName("Reason");
675685
public static final PdfName Recipients = createDirectName("Recipients");
676686
public static final PdfName Rect = createDirectName("Rect");

kernel/src/main/java/com/itextpdf/kernel/xmp/XMPConst.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ public interface XMPConst
212212
*/
213213
String PART = "part";
214214

215+
/**
216+
* ISO 19005 revision
217+
*/
218+
String REV = "rev";
219+
215220
/**
216221
* Conformance, A, B, or U.
217222
*/

kernel/src/test/java/com/itextpdf/kernel/crypto/PdfEncryptionTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,10 @@ public void copyEncryptedDocument() throws GeneralSecurityException, IOException
322322

323323
PdfDictionary srcInfo = srcDoc.getTrailer().getAsDictionary(PdfName.Info);
324324
PdfDictionary destInfo = destDoc.getTrailer().getAsDictionary(PdfName.Info);
325+
if (destInfo == null) {
326+
destInfo = new PdfDictionary();
327+
destDoc.getTrailer().put(PdfName.Info, destInfo);
328+
}
325329
for (PdfName srcInfoKey : srcInfo.keySet()) {
326330
destInfo.put((PdfName) srcInfoKey.copyTo(destDoc), srcInfo.get(srcInfoKey).copyTo(destDoc));
327331
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.itextpdf.kernel.pdf;
2+
3+
import com.itextpdf.kernel.xmp.XMPConst;
4+
import com.itextpdf.kernel.xmp.XMPException;
5+
import com.itextpdf.kernel.xmp.XMPMeta;
6+
import com.itextpdf.kernel.xmp.impl.XMPMetaImpl;
7+
import com.itextpdf.test.ExtendedITextTest;
8+
import com.itextpdf.test.annotations.type.UnitTest;
9+
import org.junit.Assert;
10+
import org.junit.Test;
11+
import org.junit.experimental.categories.Category;
12+
13+
@Category(UnitTest.class)
14+
public class PdfAConformanceLevelTest extends ExtendedITextTest {
15+
@Test
16+
public void getConformanceTest() {
17+
PdfAConformanceLevel level = PdfAConformanceLevel.getConformanceLevel("4", null);
18+
Assert.assertEquals(PdfAConformanceLevel.PDF_A_4, level);
19+
}
20+
21+
@Test
22+
public void getXmpConformanceNullTest() throws XMPException {
23+
XMPMeta meta = new XMPMetaImpl();
24+
meta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, "4");
25+
PdfAConformanceLevel level = PdfAConformanceLevel.getConformanceLevel(meta);
26+
Assert.assertEquals(PdfAConformanceLevel.PDF_A_4, level);
27+
}
28+
29+
@Test
30+
public void getXmpConformanceBTest() throws XMPException {
31+
XMPMeta meta = new XMPMetaImpl();
32+
meta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, "2");
33+
meta.setProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE, "B");
34+
PdfAConformanceLevel level = PdfAConformanceLevel.getConformanceLevel(meta);
35+
Assert.assertEquals(PdfAConformanceLevel.PDF_A_2B, level);
36+
}
37+
}

kernel/src/test/java/com/itextpdf/kernel/pdf/PdfObjectReleaseTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public void addingReleasedObjectToDocumentTest() throws IOException {
135135
String srcFile = SOURCE_FOLDER + "releaseObjectsInSimpleDoc.pdf";
136136

137137
PdfDocument doc = new PdfDocument(new PdfReader(srcFile),
138-
new PdfWriter(SOURCE_FOLDER + "addingReleasedObjectToDocument.pdf"));
138+
new PdfWriter(DESTINATION_FOLDER + "addingReleasedObjectToDocument.pdf"));
139139
try {
140140
PdfObject releasedObj = doc.getPdfObject(1);
141141
releasedObj.release();

0 commit comments

Comments
 (0)