diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index ebd833db..c831c2ca 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -414,6 +414,7 @@ class QPDFWriter void initializeSpecialStreams(); void preserveObjectStreams(); void generateObjectStreams(); + std::string getOriginalID1(); void generateID(); void interpretR3EncryptionParameters( std::set& bits_to_clear, diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index d34b095c..05446400 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -1246,8 +1246,28 @@ QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream, writeString(" /ID ["); if (linearization_pass == 1) { - writeString("<00000000000000000000000000000000>" - "<00000000000000000000000000000000>"); + std::string original_id1 = getOriginalID1(); + if (original_id1.empty()) + { + writeString("<00000000000000000000000000000000>"); + } + else + { + // Write a string of zeroes equal in length to the + // representation of the original ID. While writing the + // original ID would have the same number of bytes, it + // would cause a change to the deterministic ID generated + // by older versions of the software that hard-coded the + // length of the ID to 16 bytes. + writeString("<"); + size_t len = QPDF_String(original_id1).unparse(true).length() - 2; + for (size_t i = 0; i < len; ++i) + { + writeString("0"); + } + writeString(">"); + } + writeString("<00000000000000000000000000000000>"); } else { @@ -1952,6 +1972,20 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index) } } +std::string +QPDFWriter::getOriginalID1() +{ + QPDFObjectHandle trailer = this->m->pdf.getTrailer(); + if (trailer.hasKey("/ID")) + { + return trailer.getKey("/ID").getArrayItem(0).getStringValue(); + } + else + { + return ""; + } +} + void QPDFWriter::generateID() { @@ -2042,12 +2076,9 @@ QPDFWriter::generateID() // generated ID for both. this->m->id2 = result; - if (trailer.hasKey("/ID")) - { - // Note: keep /ID from old file even if --static-id was given. - this->m->id1 = trailer.getKey("/ID").getArrayItem(0).getStringValue(); - } - else + // Note: keep /ID from old file even if --static-id was given. + this->m->id1 = getOriginalID1(); + if (this->m->id1.empty()) { this->m->id1 = this->m->id2; } diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index ae070735..b4d926e2 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -240,7 +240,7 @@ foreach my $d (@bug_tests) show_ntests(); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 87; +$n_tests += 93; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -303,7 +303,8 @@ $td->runtest("check output", {$td->FILE => "unreferenced-indirect-scalar.out"}); # Encrypt files whose /ID strings are other than 32 bytes long (bug -# 2991412). +# 2991412). Also linearize these files, which was reported in a +# separate bug by email. foreach my $file (qw(short-id long-id)) { $td->runtest("encrypt $file.pdf", @@ -318,6 +319,23 @@ foreach my $file (qw(short-id long-id)) {$td->FILE => "$file-check.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); + + $td->runtest("linearize $file.pdf", + {$td->COMMAND => + "qpdf --deterministic-id --linearize $file.pdf a.pdf"}, + {$td->STRING => "", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + + $td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "$file-linearized.pdf"}); + + $td->runtest("check $file.pdf", + {$td->COMMAND => "qpdf --check a.pdf"}, + {$td->FILE => "$file-linearized-check.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); } # Handle file with invalid xref table and object 0 as a regular object diff --git a/qpdf/qtest/qpdf/long-id-linearized-check.out b/qpdf/qtest/qpdf/long-id-linearized-check.out new file mode 100644 index 00000000..af887be9 --- /dev/null +++ b/qpdf/qtest/qpdf/long-id-linearized-check.out @@ -0,0 +1,6 @@ +checking a.pdf +PDF Version: 1.3 +File is not encrypted +File is linearized +No syntax or stream encoding errors found; the file may still contain +errors that qpdf cannot detect diff --git a/qpdf/qtest/qpdf/long-id-linearized.pdf b/qpdf/qtest/qpdf/long-id-linearized.pdf new file mode 100644 index 00000000..2993fd30 Binary files /dev/null and b/qpdf/qtest/qpdf/long-id-linearized.pdf differ diff --git a/qpdf/qtest/qpdf/short-id-linearized-check.out b/qpdf/qtest/qpdf/short-id-linearized-check.out new file mode 100644 index 00000000..af887be9 --- /dev/null +++ b/qpdf/qtest/qpdf/short-id-linearized-check.out @@ -0,0 +1,6 @@ +checking a.pdf +PDF Version: 1.3 +File is not encrypted +File is linearized +No syntax or stream encoding errors found; the file may still contain +errors that qpdf cannot detect diff --git a/qpdf/qtest/qpdf/short-id-linearized.pdf b/qpdf/qtest/qpdf/short-id-linearized.pdf new file mode 100644 index 00000000..ec8829d7 Binary files /dev/null and b/qpdf/qtest/qpdf/short-id-linearized.pdf differ