Commit 16fd6e64f947e17144e05325e51d792df33eaa61
1 parent
837dcf8f
Add QPDFWriter::getFinalVersion (fixes #266)
Showing
5 changed files
with
73 additions
and
12 deletions
ChangeLog
| 1 | 2019-01-04 Jay Berkenbilt <ejb@ql.org> | 1 | 2019-01-04 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * Add new method QPDFWriter::getFinalVersion, which returns the | ||
| 4 | + PDF version that will ultimately be written to the final file. See | ||
| 5 | + comments in QPDFWriter.hh for some restrictions on its use. Fixes | ||
| 6 | + #266. | ||
| 7 | + | ||
| 3 | * When unexpected errors are found while checking linearization | 8 | * When unexpected errors are found while checking linearization |
| 4 | data, print an error message instead of calling assert, which | 9 | data, print an error message instead of calling assert, which |
| 5 | cause the program to crash. Fixes #209, #231. | 10 | cause the program to crash. Fixes #209, #231. |
include/qpdf/QPDFWriter.hh
| @@ -404,6 +404,18 @@ class QPDFWriter | @@ -404,6 +404,18 @@ class QPDFWriter | ||
| 404 | QPDF_DLL | 404 | QPDF_DLL |
| 405 | void registerProgressReporter(PointerHolder<ProgressReporter>); | 405 | void registerProgressReporter(PointerHolder<ProgressReporter>); |
| 406 | 406 | ||
| 407 | + // Return the PDF version that will be written into the header. | ||
| 408 | + // Calling this method does all the preparation for writing, so it | ||
| 409 | + // is an error to call any methods that may cause a change to the | ||
| 410 | + // version. Adding new objects to the original file after calling | ||
| 411 | + // this may also cause problems. It is safe to update existing | ||
| 412 | + // objects or stream contents after calling this method, e.g., to | ||
| 413 | + // include the final version number in metadata. | ||
| 414 | + QPDF_DLL | ||
| 415 | + std::string getFinalVersion(); | ||
| 416 | + | ||
| 417 | + // Write the final file. There is no expectation of being able to | ||
| 418 | + // call write() more than once. | ||
| 407 | QPDF_DLL | 419 | QPDF_DLL |
| 408 | void write(); | 420 | void write(); |
| 409 | 421 | ||
| @@ -473,6 +485,7 @@ class QPDFWriter | @@ -473,6 +485,7 @@ class QPDFWriter | ||
| 473 | void writeLinearized(); | 485 | void writeLinearized(); |
| 474 | void enqueuePart(std::vector<QPDFObjectHandle>& part); | 486 | void enqueuePart(std::vector<QPDFObjectHandle>& part); |
| 475 | void writeEncryptionDictionary(); | 487 | void writeEncryptionDictionary(); |
| 488 | + void doWriteSetup(); | ||
| 476 | void writeHeader(); | 489 | void writeHeader(); |
| 477 | void writeHintStream(int hint_id); | 490 | void writeHintStream(int hint_id); |
| 478 | qpdf_offset_t writeXRefTable( | 491 | qpdf_offset_t writeXRefTable( |
| @@ -598,6 +611,7 @@ class QPDFWriter | @@ -598,6 +611,7 @@ class QPDFWriter | ||
| 598 | bool deterministic_id; | 611 | bool deterministic_id; |
| 599 | Pl_MD5* md5_pipeline; | 612 | Pl_MD5* md5_pipeline; |
| 600 | std::string deterministic_id_data; | 613 | std::string deterministic_id_data; |
| 614 | + bool did_write_setup; | ||
| 601 | 615 | ||
| 602 | // For linearization only | 616 | // For linearization only |
| 603 | std::string lin_pass1_filename; | 617 | std::string lin_pass1_filename; |
libqpdf/QPDFWriter.cc
| @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) : | @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) : | ||
| 63 | max_ostream_index(0), | 63 | max_ostream_index(0), |
| 64 | deterministic_id(false), | 64 | deterministic_id(false), |
| 65 | md5_pipeline(0), | 65 | md5_pipeline(0), |
| 66 | + did_write_setup(false), | ||
| 66 | events_expected(0), | 67 | events_expected(0), |
| 67 | events_seen(0), | 68 | events_seen(0), |
| 68 | next_progress_report(0) | 69 | next_progress_report(0) |
| @@ -2358,8 +2359,14 @@ QPDFWriter::prepareFileForWrite() | @@ -2358,8 +2359,14 @@ QPDFWriter::prepareFileForWrite() | ||
| 2358 | } | 2359 | } |
| 2359 | 2360 | ||
| 2360 | void | 2361 | void |
| 2361 | -QPDFWriter::write() | 2362 | +QPDFWriter::doWriteSetup() |
| 2362 | { | 2363 | { |
| 2364 | + if (this->m->did_write_setup) | ||
| 2365 | + { | ||
| 2366 | + return; | ||
| 2367 | + } | ||
| 2368 | + this->m->did_write_setup = true; | ||
| 2369 | + | ||
| 2363 | // Do preliminary setup | 2370 | // Do preliminary setup |
| 2364 | 2371 | ||
| 2365 | if (this->m->linearized) | 2372 | if (this->m->linearized) |
| @@ -2507,6 +2514,23 @@ QPDFWriter::write() | @@ -2507,6 +2514,23 @@ QPDFWriter::write() | ||
| 2507 | setMinimumPDFVersion("1.5"); | 2514 | setMinimumPDFVersion("1.5"); |
| 2508 | } | 2515 | } |
| 2509 | 2516 | ||
| 2517 | + setMinimumPDFVersion(this->m->pdf.getPDFVersion(), | ||
| 2518 | + this->m->pdf.getExtensionLevel()); | ||
| 2519 | + this->m->final_pdf_version = this->m->min_pdf_version; | ||
| 2520 | + this->m->final_extension_level = this->m->min_extension_level; | ||
| 2521 | + if (! this->m->forced_pdf_version.empty()) | ||
| 2522 | + { | ||
| 2523 | + QTC::TC("qpdf", "QPDFWriter using forced PDF version"); | ||
| 2524 | + this->m->final_pdf_version = this->m->forced_pdf_version; | ||
| 2525 | + this->m->final_extension_level = this->m->forced_extension_level; | ||
| 2526 | + } | ||
| 2527 | +} | ||
| 2528 | + | ||
| 2529 | +void | ||
| 2530 | +QPDFWriter::write() | ||
| 2531 | +{ | ||
| 2532 | + doWriteSetup(); | ||
| 2533 | + | ||
| 2510 | // Set up progress reporting. We spent about equal amounts of time | 2534 | // Set up progress reporting. We spent about equal amounts of time |
| 2511 | // preparing and writing one pass. To get a rough estimate of | 2535 | // preparing and writing one pass. To get a rough estimate of |
| 2512 | // progress, we track handling of indirect objects. For linearized | 2536 | // progress, we track handling of indirect objects. For linearized |
| @@ -2569,20 +2593,16 @@ QPDFWriter::writeEncryptionDictionary() | @@ -2569,20 +2593,16 @@ QPDFWriter::writeEncryptionDictionary() | ||
| 2569 | closeObject(this->m->encryption_dict_objid); | 2593 | closeObject(this->m->encryption_dict_objid); |
| 2570 | } | 2594 | } |
| 2571 | 2595 | ||
| 2596 | +std::string | ||
| 2597 | +QPDFWriter::getFinalVersion() | ||
| 2598 | +{ | ||
| 2599 | + doWriteSetup(); | ||
| 2600 | + return this->m->final_pdf_version; | ||
| 2601 | +} | ||
| 2602 | + | ||
| 2572 | void | 2603 | void |
| 2573 | QPDFWriter::writeHeader() | 2604 | QPDFWriter::writeHeader() |
| 2574 | { | 2605 | { |
| 2575 | - setMinimumPDFVersion(this->m->pdf.getPDFVersion(), | ||
| 2576 | - this->m->pdf.getExtensionLevel()); | ||
| 2577 | - this->m->final_pdf_version = this->m->min_pdf_version; | ||
| 2578 | - this->m->final_extension_level = this->m->min_extension_level; | ||
| 2579 | - if (! this->m->forced_pdf_version.empty()) | ||
| 2580 | - { | ||
| 2581 | - QTC::TC("qpdf", "QPDFWriter using forced PDF version"); | ||
| 2582 | - this->m->final_pdf_version = this->m->forced_pdf_version; | ||
| 2583 | - this->m->final_extension_level = this->m->forced_extension_level; | ||
| 2584 | - } | ||
| 2585 | - | ||
| 2586 | writeString("%PDF-"); | 2606 | writeString("%PDF-"); |
| 2587 | writeString(this->m->final_pdf_version); | 2607 | writeString(this->m->final_pdf_version); |
| 2588 | if (this->m->pclm) | 2608 | if (this->m->pclm) |
qpdf/qtest/qpdf.test
| @@ -175,6 +175,16 @@ $td->runtest("\@file exists and file doesn't", | @@ -175,6 +175,16 @@ $td->runtest("\@file exists and file doesn't", | ||
| 175 | 175 | ||
| 176 | show_ntests(); | 176 | show_ntests(); |
| 177 | # ---------- | 177 | # ---------- |
| 178 | +$td->notify("--- Final Version ---"); | ||
| 179 | +$n_tests += 1; | ||
| 180 | + | ||
| 181 | +$td->runtest("check final version", | ||
| 182 | + {$td->COMMAND => "test_driver 54 minimal.pdf"}, | ||
| 183 | + {$td->STRING => "test 54 done\n", $td->EXIT_STATUS => 0}, | ||
| 184 | + $td->NORMALIZE_NEWLINES); | ||
| 185 | + | ||
| 186 | +show_ntests(); | ||
| 187 | +# ---------- | ||
| 178 | $td->notify("--- Dangling Refs ---"); | 188 | $td->notify("--- Dangling Refs ---"); |
| 179 | my @dangling = (qw(minimal dangling-refs)); | 189 | my @dangling = (qw(minimal dangling-refs)); |
| 180 | $n_tests += 2 * scalar(@dangling); | 190 | $n_tests += 2 * scalar(@dangling); |
qpdf/test_driver.cc
| @@ -1865,6 +1865,18 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1865,6 +1865,18 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1865 | w.setStaticID(true); | 1865 | w.setStaticID(true); |
| 1866 | w.write(); | 1866 | w.write(); |
| 1867 | } | 1867 | } |
| 1868 | + else if (n == 54) | ||
| 1869 | + { | ||
| 1870 | + // Test getFinalVersion. This must be invoked with a file | ||
| 1871 | + // whose final version is not 1.5. | ||
| 1872 | + QPDFWriter w(pdf, "a.pdf"); | ||
| 1873 | + assert(pdf.getPDFVersion() != "1.5"); | ||
| 1874 | + w.setObjectStreamMode(qpdf_o_generate); | ||
| 1875 | + if (w.getFinalVersion() != "1.5") | ||
| 1876 | + { | ||
| 1877 | + std::cout << "oops: " << w.getFinalVersion() << std::endl; | ||
| 1878 | + } | ||
| 1879 | + } | ||
| 1868 | else | 1880 | else |
| 1869 | { | 1881 | { |
| 1870 | throw std::runtime_error(std::string("invalid test ") + | 1882 | throw std::runtime_error(std::string("invalid test ") + |