Commit 16fd6e64f947e17144e05325e51d792df33eaa61
1 parent
837dcf8f
Add QPDFWriter::getFinalVersion (fixes #266)
Showing
5 changed files
with
73 additions
and
12 deletions
ChangeLog
| 1 | 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 | 8 | * When unexpected errors are found while checking linearization |
| 4 | 9 | data, print an error message instead of calling assert, which |
| 5 | 10 | cause the program to crash. Fixes #209, #231. | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -404,6 +404,18 @@ class QPDFWriter |
| 404 | 404 | QPDF_DLL |
| 405 | 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 | 419 | QPDF_DLL |
| 408 | 420 | void write(); |
| 409 | 421 | |
| ... | ... | @@ -473,6 +485,7 @@ class QPDFWriter |
| 473 | 485 | void writeLinearized(); |
| 474 | 486 | void enqueuePart(std::vector<QPDFObjectHandle>& part); |
| 475 | 487 | void writeEncryptionDictionary(); |
| 488 | + void doWriteSetup(); | |
| 476 | 489 | void writeHeader(); |
| 477 | 490 | void writeHintStream(int hint_id); |
| 478 | 491 | qpdf_offset_t writeXRefTable( |
| ... | ... | @@ -598,6 +611,7 @@ class QPDFWriter |
| 598 | 611 | bool deterministic_id; |
| 599 | 612 | Pl_MD5* md5_pipeline; |
| 600 | 613 | std::string deterministic_id_data; |
| 614 | + bool did_write_setup; | |
| 601 | 615 | |
| 602 | 616 | // For linearization only |
| 603 | 617 | std::string lin_pass1_filename; | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) : |
| 63 | 63 | max_ostream_index(0), |
| 64 | 64 | deterministic_id(false), |
| 65 | 65 | md5_pipeline(0), |
| 66 | + did_write_setup(false), | |
| 66 | 67 | events_expected(0), |
| 67 | 68 | events_seen(0), |
| 68 | 69 | next_progress_report(0) |
| ... | ... | @@ -2358,8 +2359,14 @@ QPDFWriter::prepareFileForWrite() |
| 2358 | 2359 | } |
| 2359 | 2360 | |
| 2360 | 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 | 2370 | // Do preliminary setup |
| 2364 | 2371 | |
| 2365 | 2372 | if (this->m->linearized) |
| ... | ... | @@ -2507,6 +2514,23 @@ QPDFWriter::write() |
| 2507 | 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 | 2534 | // Set up progress reporting. We spent about equal amounts of time |
| 2511 | 2535 | // preparing and writing one pass. To get a rough estimate of |
| 2512 | 2536 | // progress, we track handling of indirect objects. For linearized |
| ... | ... | @@ -2569,20 +2593,16 @@ QPDFWriter::writeEncryptionDictionary() |
| 2569 | 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 | 2603 | void |
| 2573 | 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 | 2606 | writeString("%PDF-"); |
| 2587 | 2607 | writeString(this->m->final_pdf_version); |
| 2588 | 2608 | if (this->m->pclm) | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -175,6 +175,16 @@ $td->runtest("\@file exists and file doesn't", |
| 175 | 175 | |
| 176 | 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 | 188 | $td->notify("--- Dangling Refs ---"); |
| 179 | 189 | my @dangling = (qw(minimal dangling-refs)); |
| 180 | 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 | 1865 | w.setStaticID(true); |
| 1866 | 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 | 1880 | else |
| 1869 | 1881 | { |
| 1870 | 1882 | throw std::runtime_error(std::string("invalid test ") + | ... | ... |