Skip to content

Commit 7d8ade0

Browse files
BlackEgoistiText-CI
authored andcommitted
Change PdfDocument id generation process
- Add separate fields for original and modified ids to PdfDocument to aid conversion to .xdf format. - Generate new modified id every time PdfReader is present without PdfWriter. - Id values in WriterProperties take precedence over id values specified in PdfReader if both PdfReader and PdfWriter is present. - Remove id generation inside PdfReader getters. - Move id generation process to PdfDocument. - Standard encryption now uses originalDocumentId as a parameter instead of generating new id every time. - Change PdfDocumentId and PdfEncryption tests to reflect new behavior.
1 parent dd982a6 commit 7d8ade0

File tree

13 files changed

+326
-53
lines changed

13 files changed

+326
-53
lines changed

io/src/main/java/com/itextpdf/io/LogMessageConstant.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public final class LogMessageConstant {
7878
public static final String DIRECTONLY_OBJECT_CANNOT_BE_INDIRECT = "DirectOnly object cannot be indirect";
7979
public static final String DOCFONT_HAS_ILLEGAL_DIFFERENCES = "Document Font has illegal differences array. Entry {0} references a glyph ID over 255 and will be ignored.";
8080
public static final String DOCUMENT_ALREADY_HAS_FIELD = "The document already has field {0}. Annotations of the fields with this name will be added to the existing one as children. If you want to have separate fields, please, rename them manually before copying.";
81+
public static final String DOCUMENT_IDS_ARE_CORRUPTED = "The document original and/or modified id is corrupted";
8182
public static final String DOCUMENT_SERIALIZATION_EXCEPTION_RAISED = "Unhandled exception while serialization";
8283
public static final String ELEMENT_DOES_NOT_FIT_AREA = "Element does not fit current area. {0}";
8384
public static final String ELEMENT_WAS_FORCE_PLACED_KEEP_WITH_NEXT_WILL_BE_IGNORED = "Element was placed in a forced way. Keep with next property will be ignored";

kernel/findbugs-filter.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,4 +465,9 @@
465465
</Or>
466466
<Bug pattern="WEAK_MESSAGE_DIGEST_MD5"/>
467467
</Match>
468+
<Match>
469+
<Class name="com.itextpdf.kernel.pdf.PdfDocument"/>
470+
<Method name="open"/>
471+
<Bug pattern="PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS"/>
472+
</Match>
468473
</FindBugsFilter>

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

Lines changed: 70 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,14 @@ public class PdfDocument implements IEventDispatcher, Closeable, Serializable {
163163
protected PdfVersion pdfVersion = PdfVersion.PDF_1_7;
164164

165165
/**
166-
* The original second id when the document is read initially.
166+
* The original (first) id when the document is read initially.
167167
*/
168-
private PdfString originalModifiedDocumentId;
168+
private PdfString originalDocumentId;
169+
170+
/**
171+
* The original modified (second) id when the document is read initially.
172+
*/
173+
private PdfString modifiedDocumentId;
169174

170175
/**
171176
* List of indirect objects used in the document.
@@ -633,6 +638,29 @@ public PdfDocumentInfo getDocumentInfo() {
633638
return info;
634639
}
635640

641+
/**
642+
* Gets original document id
643+
*
644+
* In order to set originalDocumentId {@link WriterProperties#setInitialDocumentId} should be used
645+
*
646+
* @return original dccument id
647+
*/
648+
public PdfString getOriginalDocumentId() {
649+
return originalDocumentId;
650+
}
651+
652+
653+
/**
654+
* Gets modified document id
655+
*
656+
* In order to set modifiedDocumentId {@link WriterProperties#setModifiedDocumentId} should be used
657+
*
658+
* @return modified document id
659+
*/
660+
public PdfString getModifiedDocumentId() {
661+
return modifiedDocumentId;
662+
}
663+
636664
/**
637665
* Gets default page size.
638666
*
@@ -803,8 +831,6 @@ public void close() {
803831
}
804832
checkIsoConformance();
805833

806-
PdfObject fileId = getFileId();
807-
808834
PdfObject crypto = null;
809835
Set<PdfIndirectReference> forbiddenToFlush = new HashSet<>();
810836
if (properties.appendMode) {
@@ -918,6 +944,11 @@ public void close() {
918944
trailer.put(PdfName.Root, catalog.getPdfObject());
919945
trailer.put(PdfName.Info, info.getPdfObject());
920946

947+
948+
//By this time original and modified document ids should always be not null due to initializing in
949+
// either writer properties, or in the writer init section on document open or from pdfreader. So we shouldn't worry about it being null next
950+
PdfObject fileId = PdfEncryption.createInfoId(ByteUtils.getIsoBytes(originalDocumentId.getValue()),
951+
ByteUtils.getIsoBytes(modifiedDocumentId.getValue()));
921952
xref.writeXrefTableAndTrailer(this, fileId, crypto);
922953
writer.flush();
923954
for (ICounter counter : getCounters()) {
@@ -951,44 +982,6 @@ public void close() {
951982
closed = true;
952983
}
953984

954-
private PdfObject getFileId() {
955-
boolean documentIsModified = false;
956-
byte[] originalFileId = null;
957-
958-
if (writer.properties.initialDocumentId != null) {
959-
originalFileId = ByteUtils.getIsoBytes(writer.properties.initialDocumentId.getValue());
960-
}
961-
if (originalFileId == null && !properties.appendMode && writer.crypto != null) { // TODO why only not in append mode when encrypted?
962-
originalFileId = writer.crypto.getDocumentId();
963-
}
964-
if (originalFileId == null && getReader() != null) {
965-
originalFileId = getReader().getOriginalFileId();
966-
documentIsModified = true;
967-
}
968-
if (originalFileId == null) {
969-
originalFileId = PdfEncryption.generateNewDocumentId();
970-
}
971-
972-
byte[] secondId = null;
973-
if (writer.properties.modifiedDocumentId != null) {
974-
secondId = ByteUtils.getIsoBytes(writer.properties.modifiedDocumentId.getValue());
975-
}
976-
if (secondId == null && originalModifiedDocumentId != null) {
977-
PdfString newModifiedId = reader.trailer.getAsArray(PdfName.ID).getAsString(1);
978-
979-
if (!originalModifiedDocumentId.equals(newModifiedId)) {
980-
secondId = ByteUtils.getIsoBytes(newModifiedId.getValue());
981-
} else {
982-
secondId = PdfEncryption.generateNewDocumentId();
983-
}
984-
}
985-
if (secondId == null) {
986-
secondId = (documentIsModified) ? PdfEncryption.generateNewDocumentId() : originalFileId;
987-
}
988-
989-
return PdfEncryption.createInfoId(originalFileId, secondId);
990-
}
991-
992985
/**
993986
* Gets close status of the document.
994987
*
@@ -1833,7 +1826,15 @@ protected void open(PdfVersion newPdfVersion) {
18331826
PdfArray id = reader.trailer.getAsArray(PdfName.ID);
18341827

18351828
if (id != null) {
1836-
originalModifiedDocumentId = id.getAsString(1);
1829+
if (id.size() == 2) {
1830+
originalDocumentId = id.getAsString(0);
1831+
modifiedDocumentId = id.getAsString(1);
1832+
}
1833+
1834+
if (originalDocumentId == null || modifiedDocumentId == null) {
1835+
Logger logger = LoggerFactory.getLogger(PdfDocument.class);
1836+
logger.error(LogMessageConstant.DOCUMENT_IDS_ARE_CORRUPTED);
1837+
}
18371838
}
18381839

18391840
catalog = new PdfCatalog((PdfDictionary) trailer.get(PdfName.Root, true));
@@ -1892,6 +1893,32 @@ protected void open(PdfVersion newPdfVersion) {
18921893
trailer.put(PdfName.ID, reader.trailer.get(PdfName.ID));
18931894
}
18941895
}
1896+
1897+
if (writer.properties != null) {
1898+
PdfString readerModifiedId = modifiedDocumentId;
1899+
if (writer.properties.initialDocumentId != null &&
1900+
!(reader != null && reader.decrypt != null && (properties.appendMode || properties.preserveEncryption))) {
1901+
originalDocumentId = writer.properties.initialDocumentId;
1902+
}
1903+
if (writer.properties.modifiedDocumentId != null) {
1904+
modifiedDocumentId = writer.properties.modifiedDocumentId;
1905+
}
1906+
if (originalDocumentId == null && modifiedDocumentId != null) {
1907+
originalDocumentId = modifiedDocumentId;
1908+
}
1909+
if (modifiedDocumentId == null) {
1910+
if (originalDocumentId == null) {
1911+
originalDocumentId = new PdfString(PdfEncryption.generateNewDocumentId());
1912+
}
1913+
modifiedDocumentId = originalDocumentId;
1914+
}
1915+
if(writer.properties.modifiedDocumentId == null && modifiedDocumentId.equals(readerModifiedId)) {
1916+
modifiedDocumentId = new PdfString(PdfEncryption.generateNewDocumentId());
1917+
}
1918+
}
1919+
1920+
assert originalDocumentId != null;
1921+
assert modifiedDocumentId != null;
18951922
}
18961923
if (properties.appendMode) { // Due to constructor reader and writer not null.
18971924
assert reader != null;

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -506,17 +506,34 @@ public byte[] computeUserPassword() {
506506
}
507507

508508
/**
509-
* Gets file ID, either {@link PdfName#ID} key of trailer or a newly generated id.
509+
* Gets original file ID, the first element in {@link PdfName#ID} key of trailer.
510+
* If the size of ID array does not equal 2, an empty array will be returned.
510511
*
511-
* @return byte array represents file ID.
512-
* @see PdfEncryption#generateNewDocumentId()
512+
* @return byte array represents original file ID.
513+
* @see PdfDocument#getOriginalDocumentId(). The ultimate document id should be taken from PdfDocument
513514
*/
514515
public byte[] getOriginalFileId() {
515516
PdfArray id = trailer.getAsArray(PdfName.ID);
516-
if (id != null) {
517+
if (id != null && id.size() == 2) {
517518
return ByteUtils.getIsoBytes(id.getAsString(0).getValue());
518519
} else {
519-
return PdfEncryption.generateNewDocumentId();
520+
return new byte[0];
521+
}
522+
}
523+
524+
/**
525+
* Gets modified file ID, the second element in {@link PdfName#ID} key of trailer.
526+
* If the size of ID array does not equal 2, an empty array will be returned.
527+
*
528+
* @return byte array represents modified file ID.
529+
* @see PdfDocument#getModifiedDocumentId()
530+
*/
531+
public byte[] getModifiedFileId() {
532+
PdfArray id = trailer.getAsArray(PdfName.ID);
533+
if (id != null && id.size() == 2) {
534+
return ByteUtils.getIsoBytes(id.getAsString(1).getValue());
535+
} else {
536+
return new byte[0];
520537
}
521538
}
522539

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ This file is part of the iText (R) project.
4545

4646
import com.itextpdf.io.LogMessageConstant;
4747
import com.itextpdf.io.source.ByteArrayOutputStream;
48+
import com.itextpdf.io.source.ByteUtils;
4849
import com.itextpdf.io.util.FileUtil;
4950
import org.slf4j.Logger;
5051
import org.slf4j.LoggerFactory;
@@ -276,7 +277,7 @@ protected void initCryptoIfSpecified(PdfVersion version) {
276277
EncryptionProperties encryptProps = properties.encryptionProperties;
277278
if (properties.isStandardEncryptionUsed()) {
278279
crypto = new PdfEncryption(encryptProps.userPassword, encryptProps.ownerPassword, encryptProps.standardEncryptPermissions,
279-
encryptProps.encryptionAlgorithm, PdfEncryption.generateNewDocumentId(), version);
280+
encryptProps.encryptionAlgorithm, ByteUtils.getIsoBytes(this.document.getOriginalDocumentId().toString()), version);
280281
} else if (properties.isPublicKeyEncryptionUsed()) {
281282
crypto = new PdfEncryption(encryptProps.publicCertificates,
282283
encryptProps.publicKeyEncryptPermissions, encryptProps.encryptionAlgorithm, version);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ public WriterProperties setModifiedDocumentId(PdfString modifiedDocumentId) {
223223
this.modifiedDocumentId = modifiedDocumentId;
224224
return this;
225225
}
226-
227226
/**
228227
* This activates debug mode with pdfDebug tool.
229228
* It causes additional overhead of duplicating document bytes into memory, so use it careful.

0 commit comments

Comments
 (0)