Commit 3f8c4c273649c857f5a607dcbb422729fce3a166
1 parent
b67a3c15
categorize all error messages and include object information if available
git-svn-id: svn+q:///qpdf/trunk@829 71b93d88-0707-0410-a8cf-f5a4172ac649
Showing
95 changed files
with
660 additions
and
266 deletions
ChangeLog
| 1 | +2009-10-19 Jay Berkenbilt <jberkenb@argonst.com> | |
| 2 | + | |
| 3 | + * Include information about the last object read in most error | |
| 4 | + messages. Most of the time, this will provide a good hint as to | |
| 5 | + which object contains the error, but it's possible that the last | |
| 6 | + object read may not necessarily be the one that has the error if | |
| 7 | + the erroneous object was previously read and cached. | |
| 8 | + | |
| 1 | 9 | 2009-10-18 Jay Berkenbilt <ejb@ql.org> |
| 2 | 10 | |
| 3 | 11 | * If forcing version, disable object stream creation and/or | ... | ... |
TODO
| ... | ... | @@ -16,25 +16,10 @@ |
| 16 | 16 | * Add comments for the security functions that map them back to the |
| 17 | 17 | items in Adobe's products. |
| 18 | 18 | |
| 19 | - * Add error codes to QPDFException. Change the error interface so | |
| 20 | - that warnings and errors are pointers that can be queried using | |
| 21 | - more C API functions. We need a way to get a full string as well | |
| 22 | - as an error code, file name, offset, and message. We should go | |
| 23 | - through all error messages to try to include all these fields as | |
| 24 | - appropriate. Make sure invalid password is specifically | |
| 25 | - detectable. I/O errors and so forth should also be | |
| 26 | - distinguishable. Make sure all errors include information about | |
| 27 | - the most recent read location including byte offset and | |
| 28 | - object/generation number. | |
| 29 | - | |
| 30 | - * It might be nice to be able to trap I/O errors separately from | |
| 31 | - other errors; especially be able to separate errors that the user | |
| 32 | - can fix (like permission errors) from errors that they probably | |
| 33 | - can't fix like corrupted PDF files, unsupported filters, or | |
| 34 | - internal errors. However, only QPDF::processFile(), which does the | |
| 35 | - initial read, and QPDFWriter::QPDFWriter(), which does the initial | |
| 36 | - write, are at all likely to generate such errors for a case other | |
| 37 | - than a catastrophic failure. | |
| 19 | + * Change the C error interface so that warnings and errors are | |
| 20 | + pointers that can be queried using more C API functions. We need a | |
| 21 | + way to get a full string as well as an error code, file name, | |
| 22 | + offset, and message. | |
| 38 | 23 | |
| 39 | 24 | * "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic |
| 40 | 25 | (http://delphi.about.com). .. use at your own risk and for whatever | ... | ... |
examples/qtest/bookmarks.test
| ... | ... | @@ -30,7 +30,7 @@ $td->runtest("no bookmarks", |
| 30 | 30 | $td->runtest("bad", |
| 31 | 31 | {$td->COMMAND => "pdf-bookmarks 3.pdf"}, |
| 32 | 32 | {$td->STRING => "pdf-bookmarks processing file 3.pdf: " . |
| 33 | - "3.pdf: offset 0: not a PDF file\n", | |
| 33 | + "3.pdf: not a PDF file\n", | |
| 34 | 34 | $td->EXIT_STATUS => 2}, |
| 35 | 35 | $td->NORMALIZE_NEWLINES); |
| 36 | 36 | ... | ... |
examples/qtest/npages.test
| ... | ... | @@ -16,7 +16,7 @@ $td->runtest("normal", |
| 16 | 16 | |
| 17 | 17 | $td->runtest("error", |
| 18 | 18 | {$td->COMMAND => "pdf-npages bad"}, |
| 19 | - {$td->STRING => "pdf-npages: bad: offset 0: not a PDF file\n", | |
| 19 | + {$td->STRING => "pdf-npages: bad: not a PDF file\n", | |
| 20 | 20 | $td->EXIT_STATUS => 2}, |
| 21 | 21 | $td->NORMALIZE_NEWLINES); |
| 22 | 22 | ... | ... |
include/qpdf/QPDF.hh
| ... | ... | @@ -371,9 +371,11 @@ class DLL_EXPORT QPDF |
| 371 | 371 | int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream); |
| 372 | 372 | void insertXrefEntry(int obj, int f0, int f1, int f2, |
| 373 | 373 | bool overwrite = false); |
| 374 | + void setLastObjectDescription(std::string const& description, | |
| 375 | + int objid, int generation); | |
| 374 | 376 | QPDFObjectHandle readObject( |
| 375 | - InputSource*, int objid, int generation, | |
| 376 | - bool in_object_stream); | |
| 377 | + InputSource*, std::string const& description, | |
| 378 | + int objid, int generation, bool in_object_stream); | |
| 377 | 379 | QPDFObjectHandle readObjectInternal( |
| 378 | 380 | InputSource* input, int objid, int generation, |
| 379 | 381 | bool in_object_stream, |
| ... | ... | @@ -383,7 +385,8 @@ class DLL_EXPORT QPDF |
| 383 | 385 | QPDFTokenizer::Token readToken(InputSource*); |
| 384 | 386 | |
| 385 | 387 | QPDFObjectHandle readObjectAtOffset( |
| 386 | - off_t offset, | |
| 388 | + bool attempt_recovery, | |
| 389 | + off_t offset, std::string const& description, | |
| 387 | 390 | int exp_objid, int exp_generation, |
| 388 | 391 | int& act_objid, int& act_generation); |
| 389 | 392 | PointerHolder<QPDFObject> resolve(int objid, int generation); |
| ... | ... | @@ -734,6 +737,7 @@ class DLL_EXPORT QPDF |
| 734 | 737 | |
| 735 | 738 | QPDFTokenizer tokenizer; |
| 736 | 739 | FileInputSource file; |
| 740 | + std::string last_object_description; | |
| 737 | 741 | bool encrypted; |
| 738 | 742 | bool encryption_initialized; |
| 739 | 743 | bool ignore_xref_streams; | ... | ... |
include/qpdf/QPDFExc.hh
| ... | ... | @@ -9,15 +9,47 @@ |
| 9 | 9 | #define __QPDFEXC_HH__ |
| 10 | 10 | |
| 11 | 11 | #include <qpdf/DLL.h> |
| 12 | +#include <qpdf/Constants.h> | |
| 12 | 13 | #include <stdexcept> |
| 14 | +#include <stddef.h> | |
| 13 | 15 | |
| 14 | 16 | class DLL_EXPORT QPDFExc: public std::runtime_error |
| 15 | 17 | { |
| 16 | 18 | public: |
| 17 | - QPDFExc(std::string const& message); | |
| 18 | - QPDFExc(std::string const& filename, int offset, | |
| 19 | + QPDFExc(qpdf_error_code_e error_code, | |
| 20 | + std::string const& filename, | |
| 21 | + std::string const& object, | |
| 22 | + off_t offset, | |
| 19 | 23 | std::string const& message); |
| 20 | 24 | virtual ~QPDFExc() throw (); |
| 25 | + | |
| 26 | + // To get a complete error string, call what(), provided by | |
| 27 | + // std::exception. The accessors below return the original values | |
| 28 | + // used to create the exception. Only the error code and message | |
| 29 | + // are guaranteed to have non-zero/empty values. | |
| 30 | + | |
| 31 | + // There is no lookup code that maps numeric error codes into | |
| 32 | + // strings. The numeric error code is just another way to get at | |
| 33 | + // the underlying issue, but it is more programmer-friendly than | |
| 34 | + // trying to parse a string that is subject to change. | |
| 35 | + | |
| 36 | + qpdf_error_code_e getErrorCode() const; | |
| 37 | + std::string const& getFilename() const; | |
| 38 | + std::string const& getObject() const; | |
| 39 | + off_t getOffset() const; | |
| 40 | + std::string const& getMessage() const; | |
| 41 | + | |
| 42 | + private: | |
| 43 | + static std::string createWhat(std::string const& filename, | |
| 44 | + std::string const& object, | |
| 45 | + off_t offset, | |
| 46 | + std::string const& message); | |
| 47 | + | |
| 48 | + qpdf_error_code_e error_code; | |
| 49 | + std::string filename; | |
| 50 | + std::string object; | |
| 51 | + off_t offset; | |
| 52 | + std::string message; | |
| 21 | 53 | }; |
| 22 | 54 | |
| 23 | 55 | #endif // __QPDFEXC_HH__ | ... | ... |
libqpdf/QPDF.cc
| ... | ... | @@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length) |
| 140 | 140 | size_t len = fread(buffer, 1, length, this->file); |
| 141 | 141 | if ((len == 0) && ferror(this->file)) |
| 142 | 142 | { |
| 143 | - throw QPDFExc(this->filename, this->last_offset, | |
| 143 | + throw QPDFExc(qpdf_e_system, | |
| 144 | + this->filename, "", | |
| 145 | + this->last_offset, | |
| 144 | 146 | std::string("read ") + |
| 145 | 147 | QUtil::int_to_string(length) + " bytes"); |
| 146 | 148 | } |
| ... | ... | @@ -325,7 +327,8 @@ QPDF::parse() |
| 325 | 327 | else |
| 326 | 328 | { |
| 327 | 329 | QTC::TC("qpdf", "QPDF not a pdf file"); |
| 328 | - throw QPDFExc(this->file.getName(), 0, "not a PDF file"); | |
| 330 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 331 | + "", 0, "not a PDF file"); | |
| 329 | 332 | } |
| 330 | 333 | |
| 331 | 334 | // PDF spec says %%EOF must be found within the last 1024 bytes of |
| ... | ... | @@ -369,7 +372,8 @@ QPDF::parse() |
| 369 | 372 | if (! m2) |
| 370 | 373 | { |
| 371 | 374 | QTC::TC("qpdf", "QPDF can't find startxref"); |
| 372 | - throw QPDFExc(this->file.getName() + ": can't find startxref"); | |
| 375 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, | |
| 376 | + "can't find startxref"); | |
| 373 | 377 | } |
| 374 | 378 | off_t xref_offset = atoi(m2.getMatch(1).c_str()); |
| 375 | 379 | read_xref(xref_offset); |
| ... | ... | @@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc& e) |
| 417 | 421 | static PCRE endobj_re("^endobj\\b"); |
| 418 | 422 | static PCRE trailer_re("^trailer\\b"); |
| 419 | 423 | |
| 420 | - warn(QPDFExc(this->file.getName(), 0, "file is damaged")); | |
| 424 | + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, | |
| 425 | + "file is damaged")); | |
| 421 | 426 | warn(e); |
| 422 | - warn(QPDFExc("Attempting to reconstruct cross-reference table")); | |
| 427 | + warn(QPDFExc(qpdf_e_damaged_pdf, "", "", 0, | |
| 428 | + "Attempting to reconstruct cross-reference table")); | |
| 429 | + | |
| 430 | + // Delete all references to type 1 (uncompressed) objects | |
| 431 | + std::set<ObjGen> to_delete; | |
| 432 | + for (std::map<ObjGen, QPDFXRefEntry>::iterator iter = | |
| 433 | + this->xref_table.begin(); | |
| 434 | + iter != this->xref_table.end(); ++iter) | |
| 435 | + { | |
| 436 | + if (((*iter).second).getType() == 1) | |
| 437 | + { | |
| 438 | + to_delete.insert((*iter).first); | |
| 439 | + } | |
| 440 | + } | |
| 441 | + for (std::set<ObjGen>::iterator iter = to_delete.begin(); | |
| 442 | + iter != to_delete.end(); ++iter) | |
| 443 | + { | |
| 444 | + this->xref_table.erase(*iter); | |
| 445 | + } | |
| 423 | 446 | |
| 424 | 447 | this->file.seek(0, SEEK_END); |
| 425 | 448 | off_t eof = this->file.tell(); |
| ... | ... | @@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc& e) |
| 452 | 475 | // read "trailer" |
| 453 | 476 | this->file.seek(this->file.getLastOffset(), SEEK_SET); |
| 454 | 477 | readToken(&this->file); |
| 455 | - QPDFObjectHandle t = readObject(&this->file, 0, 0, false); | |
| 478 | + QPDFObjectHandle t = | |
| 479 | + readObject(&this->file, "trailer", 0, 0, false); | |
| 456 | 480 | if (! t.isDictionary()) |
| 457 | 481 | { |
| 458 | 482 | // Oh well. It was worth a try. |
| ... | ... | @@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc& e) |
| 473 | 497 | // with bad startxref pointers even when they have object |
| 474 | 498 | // streams. |
| 475 | 499 | |
| 476 | - throw QPDFExc(this->file.getName() + ": unable to find trailer " | |
| 500 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, | |
| 501 | + "unable to find trailer " | |
| 477 | 502 | "dictionary while recovering damaged file"); |
| 478 | 503 | } |
| 479 | 504 | |
| ... | ... | @@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset) |
| 513 | 538 | if (size != max_obj + 1) |
| 514 | 539 | { |
| 515 | 540 | QTC::TC("qpdf", "QPDF xref size mismatch"); |
| 516 | - warn(QPDFExc(this->file.getName() + | |
| 517 | - std::string(": reported number of objects (") + | |
| 541 | + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, | |
| 542 | + std::string("reported number of objects (") + | |
| 518 | 543 | QUtil::int_to_string(size) + |
| 519 | 544 | ") inconsistent with actual number of objects (" + |
| 520 | 545 | QUtil::int_to_string(max_obj + 1) + ")")); |
| ... | ... | @@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset) |
| 542 | 567 | if (! m1) |
| 543 | 568 | { |
| 544 | 569 | QTC::TC("qpdf", "QPDF invalid xref"); |
| 545 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 570 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 571 | + "xref table", this->file.getLastOffset(), | |
| 546 | 572 | "xref syntax invalid"); |
| 547 | 573 | } |
| 548 | 574 | int obj = atoi(m1.getMatch(1).c_str()); |
| ... | ... | @@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset) |
| 563 | 589 | { |
| 564 | 590 | QTC::TC("qpdf", "QPDF invalid xref entry"); |
| 565 | 591 | throw QPDFExc( |
| 566 | - this->file.getName(), this->file.getLastOffset(), | |
| 592 | + qpdf_e_damaged_pdf, this->file.getName(), | |
| 593 | + "xref table", this->file.getLastOffset(), | |
| 567 | 594 | "invalid xref entry (obj=" + |
| 568 | 595 | QUtil::int_to_string(i) + ")"); |
| 569 | 596 | } |
| ... | ... | @@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset) |
| 595 | 622 | } |
| 596 | 623 | |
| 597 | 624 | // Set offset to previous xref table if any |
| 598 | - QPDFObjectHandle cur_trailer = readObject(&this->file, 0, 0, false); | |
| 625 | + QPDFObjectHandle cur_trailer = | |
| 626 | + readObject(&this->file, "trailer", 0, 0, false); | |
| 599 | 627 | if (! cur_trailer.isDictionary()) |
| 600 | 628 | { |
| 601 | 629 | QTC::TC("qpdf", "QPDF missing trailer"); |
| 602 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 630 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 631 | + "", this->file.getLastOffset(), | |
| 603 | 632 | "expected trailer dictionary"); |
| 604 | 633 | } |
| 605 | 634 | |
| ... | ... | @@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset) |
| 610 | 639 | if (! this->trailer.hasKey("/Size")) |
| 611 | 640 | { |
| 612 | 641 | QTC::TC("qpdf", "QPDF trailer lacks size"); |
| 613 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 642 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 643 | + "trailer", this->file.getLastOffset(), | |
| 614 | 644 | "trailer dictionary lacks /Size key"); |
| 615 | 645 | } |
| 616 | 646 | if (! this->trailer.getKey("/Size").isInteger()) |
| 617 | 647 | { |
| 618 | 648 | QTC::TC("qpdf", "QPDF trailer size not integer"); |
| 619 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 649 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 650 | + "trailer", this->file.getLastOffset(), | |
| 620 | 651 | "/Size key in trailer dictionary is not " |
| 621 | 652 | "an integer"); |
| 622 | 653 | } |
| ... | ... | @@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset) |
| 640 | 671 | } |
| 641 | 672 | else |
| 642 | 673 | { |
| 643 | - throw QPDFExc(this->file.getName(), xref_offset, | |
| 674 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 675 | + "xref stream", xref_offset, | |
| 644 | 676 | "invalid /XRefStm"); |
| 645 | 677 | } |
| 646 | 678 | } |
| ... | ... | @@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset) |
| 659 | 691 | if (! cur_trailer.getKey("/Prev").isInteger()) |
| 660 | 692 | { |
| 661 | 693 | QTC::TC("qpdf", "QPDF trailer prev not integer"); |
| 662 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 694 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 695 | + "trailer", this->file.getLastOffset(), | |
| 663 | 696 | "/Prev key in trailer dictionary is not " |
| 664 | 697 | "an integer"); |
| 665 | 698 | } |
| ... | ... | @@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset) |
| 685 | 718 | QPDFObjectHandle xref_obj; |
| 686 | 719 | try |
| 687 | 720 | { |
| 688 | - xref_obj = readObjectAtOffset(xref_offset, 0, 0, xobj, xgen); | |
| 721 | + xref_obj = readObjectAtOffset( | |
| 722 | + false, xref_offset, "xref stream", 0, 0, xobj, xgen); | |
| 689 | 723 | } |
| 690 | 724 | catch (QPDFExc& e) |
| 691 | 725 | { |
| ... | ... | @@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset) |
| 705 | 739 | if (! found) |
| 706 | 740 | { |
| 707 | 741 | QTC::TC("qpdf", "QPDF can't find xref"); |
| 708 | - throw QPDFExc(this->file.getName(), xref_offset, "xref not found"); | |
| 742 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 743 | + "", xref_offset, "xref not found"); | |
| 709 | 744 | } |
| 710 | 745 | |
| 711 | 746 | return xref_offset; |
| ... | ... | @@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) |
| 725 | 760 | dict.getKey("/Size").isInteger() && |
| 726 | 761 | (Index_obj.isArray() || Index_obj.isNull()))) |
| 727 | 762 | { |
| 728 | - throw QPDFExc(this->file.getName(), xref_offset, | |
| 763 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 764 | + "xref stream", xref_offset, | |
| 729 | 765 | "Cross-reference stream does not have" |
| 730 | 766 | " proper /W and /Index keys"); |
| 731 | 767 | } |
| ... | ... | @@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) |
| 735 | 771 | int n_index = Index_obj.getArrayNItems(); |
| 736 | 772 | if ((n_index % 2) || (n_index < 2)) |
| 737 | 773 | { |
| 738 | - throw QPDFExc(this->file.getName(), xref_offset, | |
| 774 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 775 | + "xref stream", xref_offset, | |
| 739 | 776 | "Cross-reference stream's /Index has an" |
| 740 | 777 | " invalid number of values"); |
| 741 | 778 | } |
| ... | ... | @@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) |
| 747 | 784 | } |
| 748 | 785 | else |
| 749 | 786 | { |
| 750 | - throw QPDFExc(this->file.getName(), xref_offset, | |
| 787 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 788 | + "xref stream", xref_offset, | |
| 751 | 789 | "Cross-reference stream's /Index's item " + |
| 752 | 790 | QUtil::int_to_string(i) + |
| 753 | 791 | " is not an integer"); |
| ... | ... | @@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) |
| 785 | 823 | |
| 786 | 824 | if (expected_size != actual_size) |
| 787 | 825 | { |
| 788 | - QPDFExc x(this->file.getName(), xref_offset, | |
| 826 | + QPDFExc x(qpdf_e_damaged_pdf, this->file.getName(), | |
| 827 | + "xref stream", xref_offset, | |
| 789 | 828 | "Cross-reference stream data has the wrong size;" |
| 790 | 829 | " expected = " + QUtil::int_to_string(expected_size) + |
| 791 | 830 | "; actual = " + QUtil::int_to_string(actual_size)); |
| ... | ... | @@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle& xref_obj) |
| 866 | 905 | { |
| 867 | 906 | if (! dict.getKey("/Prev").isInteger()) |
| 868 | 907 | { |
| 869 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 908 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 909 | + "xref stream", this->file.getLastOffset(), | |
| 870 | 910 | "/Prev key in xref stream dictionary is not " |
| 871 | 911 | "an integer"); |
| 872 | 912 | } |
| ... | ... | @@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite) |
| 935 | 975 | break; |
| 936 | 976 | |
| 937 | 977 | default: |
| 938 | - throw QPDFExc(this->file.getName(), 0, | |
| 978 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 979 | + "xref stream", this->file.getLastOffset(), | |
| 939 | 980 | "unknown xref stream entry type " + |
| 940 | 981 | QUtil::int_to_string(f0)); |
| 941 | 982 | break; |
| ... | ... | @@ -972,10 +1013,32 @@ QPDF::showXRefTable() |
| 972 | 1013 | } |
| 973 | 1014 | } |
| 974 | 1015 | |
| 1016 | +void | |
| 1017 | +QPDF::setLastObjectDescription(std::string const& description, | |
| 1018 | + int objid, int generation) | |
| 1019 | +{ | |
| 1020 | + this->last_object_description.clear(); | |
| 1021 | + if (! description.empty()) | |
| 1022 | + { | |
| 1023 | + this->last_object_description += description; | |
| 1024 | + if (objid > 0) | |
| 1025 | + { | |
| 1026 | + this->last_object_description += ": "; | |
| 1027 | + } | |
| 1028 | + } | |
| 1029 | + if (objid > 0) | |
| 1030 | + { | |
| 1031 | + this->last_object_description += "object " + | |
| 1032 | + QUtil::int_to_string(objid) + " " + | |
| 1033 | + QUtil::int_to_string(generation); | |
| 1034 | + } | |
| 1035 | +} | |
| 1036 | + | |
| 975 | 1037 | QPDFObjectHandle |
| 976 | -QPDF::readObject(InputSource* input, int objid, int generation, | |
| 977 | - bool in_object_stream) | |
| 1038 | +QPDF::readObject(InputSource* input, std::string const& description, | |
| 1039 | + int objid, int generation, bool in_object_stream) | |
| 978 | 1040 | { |
| 1041 | + setLastObjectDescription(description, objid, generation); | |
| 979 | 1042 | off_t offset = input->tell(); |
| 980 | 1043 | QPDFObjectHandle object = readObjectInternal( |
| 981 | 1044 | input, objid, generation, in_object_stream, false, false); |
| ... | ... | @@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input, |
| 1017 | 1080 | case QPDFTokenizer::tt_brace_close: |
| 1018 | 1081 | // Don't know what to do with these for now |
| 1019 | 1082 | QTC::TC("qpdf", "QPDF bad brace"); |
| 1020 | - throw QPDFExc(input->getName(), input->getLastOffset(), | |
| 1083 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1084 | + this->last_object_description, | |
| 1085 | + input->getLastOffset(), | |
| 1021 | 1086 | "unexpected brace token"); |
| 1022 | 1087 | break; |
| 1023 | 1088 | |
| ... | ... | @@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input, |
| 1029 | 1094 | else |
| 1030 | 1095 | { |
| 1031 | 1096 | QTC::TC("qpdf", "QPDF bad array close"); |
| 1032 | - throw QPDFExc(input->getName(), input->getLastOffset(), | |
| 1097 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1098 | + this->last_object_description, | |
| 1099 | + input->getLastOffset(), | |
| 1033 | 1100 | "unexpected array close token"); |
| 1034 | 1101 | } |
| 1035 | 1102 | break; |
| ... | ... | @@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input, |
| 1042 | 1109 | else |
| 1043 | 1110 | { |
| 1044 | 1111 | QTC::TC("qpdf", "QPDF bad dictionary close"); |
| 1045 | - throw QPDFExc(input->getName(), input->getLastOffset(), | |
| 1112 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1113 | + this->last_object_description, | |
| 1114 | + input->getLastOffset(), | |
| 1046 | 1115 | "unexpected dictionary close token"); |
| 1047 | 1116 | } |
| 1048 | 1117 | break; |
| ... | ... | @@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input, |
| 1097 | 1166 | } |
| 1098 | 1167 | else |
| 1099 | 1168 | { |
| 1100 | - throw QPDFExc(input->getName(), input->getLastOffset(), | |
| 1169 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1170 | + this->last_object_description, | |
| 1171 | + input->getLastOffset(), | |
| 1101 | 1172 | "unknown token while reading object (" + |
| 1102 | 1173 | value + ")"); |
| 1103 | 1174 | } |
| ... | ... | @@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input, |
| 1116 | 1187 | break; |
| 1117 | 1188 | |
| 1118 | 1189 | default: |
| 1119 | - throw QPDFExc(input->getName(), input->getLastOffset(), | |
| 1190 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1191 | + this->last_object_description, | |
| 1192 | + input->getLastOffset(), | |
| 1120 | 1193 | "unknown token type while reading object"); |
| 1121 | 1194 | break; |
| 1122 | 1195 | } |
| ... | ... | @@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input, |
| 1153 | 1226 | { |
| 1154 | 1227 | QTC::TC("qpdf", "QPDF dictionary odd number of elements"); |
| 1155 | 1228 | throw QPDFExc( |
| 1156 | - input->getName(), input->getLastOffset(), | |
| 1229 | + qpdf_e_damaged_pdf, input->getName(), | |
| 1230 | + this->last_object_description, input->getLastOffset(), | |
| 1157 | 1231 | "dictionary ending here has an odd number of elements"); |
| 1158 | 1232 | } |
| 1159 | 1233 | for (unsigned int i = 0; i < olist.size(); i += 2) |
| ... | ... | @@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input, |
| 1163 | 1237 | if (! key_obj.isName()) |
| 1164 | 1238 | { |
| 1165 | 1239 | throw QPDFExc( |
| 1166 | - input->getName(), offset, | |
| 1240 | + qpdf_e_damaged_pdf, | |
| 1241 | + input->getName(), this->last_object_description, offset, | |
| 1167 | 1242 | std::string("dictionary key not name (") + |
| 1168 | 1243 | key_obj.unparse() + ")"); |
| 1169 | 1244 | } |
| ... | ... | @@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input, |
| 1209 | 1284 | if (dict.count("/Length") == 0) |
| 1210 | 1285 | { |
| 1211 | 1286 | QTC::TC("qpdf", "QPDF stream without length"); |
| 1212 | - throw QPDFExc(input->getName(), offset, | |
| 1287 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1288 | + this->last_object_description, offset, | |
| 1213 | 1289 | "stream dictionary lacks /Length key"); |
| 1214 | 1290 | } |
| 1215 | 1291 | |
| ... | ... | @@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input, |
| 1217 | 1293 | if (! length_obj.isInteger()) |
| 1218 | 1294 | { |
| 1219 | 1295 | QTC::TC("qpdf", "QPDF stream length not integer"); |
| 1220 | - throw QPDFExc(input->getName(), offset, | |
| 1296 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1297 | + this->last_object_description, offset, | |
| 1221 | 1298 | "/Length key in stream dictionary is not " |
| 1222 | 1299 | "an integer"); |
| 1223 | 1300 | } |
| ... | ... | @@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input, |
| 1229 | 1306 | QPDFTokenizer::tt_word, "endstream"))) |
| 1230 | 1307 | { |
| 1231 | 1308 | QTC::TC("qpdf", "QPDF missing endstream"); |
| 1232 | - throw QPDFExc(input->getName(), input->getLastOffset(), | |
| 1309 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1310 | + this->last_object_description, | |
| 1311 | + input->getLastOffset(), | |
| 1233 | 1312 | "expected endstream"); |
| 1234 | 1313 | } |
| 1235 | 1314 | } |
| ... | ... | @@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input, |
| 1267 | 1346 | |
| 1268 | 1347 | // Try to reconstruct stream length by looking for |
| 1269 | 1348 | // endstream(\r\n?|\n)endobj |
| 1270 | - warn(QPDFExc(input->getName(), stream_offset, | |
| 1349 | + warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1350 | + this->last_object_description, stream_offset, | |
| 1271 | 1351 | "attempting to recover stream length")); |
| 1272 | 1352 | |
| 1273 | 1353 | input->seek(0, SEEK_END); |
| ... | ... | @@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input, |
| 1336 | 1416 | |
| 1337 | 1417 | if (length == 0) |
| 1338 | 1418 | { |
| 1339 | - throw QPDFExc(input->getName(), stream_offset, | |
| 1419 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1420 | + this->last_object_description, stream_offset, | |
| 1340 | 1421 | "unable to recover stream data"); |
| 1341 | 1422 | } |
| 1342 | 1423 | |
| ... | ... | @@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input) |
| 1356 | 1437 | char ch; |
| 1357 | 1438 | if (input->read(&ch, 1) == 0) |
| 1358 | 1439 | { |
| 1359 | - throw QPDFExc(input->getName(), offset, "EOF while reading token"); | |
| 1440 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1441 | + this->last_object_description, offset, | |
| 1442 | + "EOF while reading token"); | |
| 1360 | 1443 | } |
| 1361 | 1444 | else |
| 1362 | 1445 | { |
| ... | ... | @@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input) |
| 1376 | 1459 | |
| 1377 | 1460 | if (token.getType() == QPDFTokenizer::tt_bad) |
| 1378 | 1461 | { |
| 1379 | - throw QPDFExc(input->getName(), offset, token.getErrorMessage()); | |
| 1462 | + throw QPDFExc(qpdf_e_damaged_pdf, input->getName(), | |
| 1463 | + this->last_object_description, offset, | |
| 1464 | + token.getErrorMessage()); | |
| 1380 | 1465 | } |
| 1381 | 1466 | |
| 1382 | 1467 | input->setLastOffset(offset); |
| ... | ... | @@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input) |
| 1385 | 1470 | } |
| 1386 | 1471 | |
| 1387 | 1472 | QPDFObjectHandle |
| 1388 | -QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, | |
| 1473 | +QPDF::readObjectAtOffset(bool try_recovery, | |
| 1474 | + off_t offset, std::string const& description, | |
| 1475 | + int exp_objid, int exp_generation, | |
| 1389 | 1476 | int& objid, int& generation) |
| 1390 | 1477 | { |
| 1478 | + setLastObjectDescription(description, exp_objid, exp_generation); | |
| 1391 | 1479 | this->file.seek(offset, SEEK_SET); |
| 1392 | 1480 | |
| 1393 | 1481 | QPDFTokenizer::Token tobjid = readToken(&this->file); |
| ... | ... | @@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, |
| 1407 | 1495 | if (! (objidok && genok && objok)) |
| 1408 | 1496 | { |
| 1409 | 1497 | QTC::TC("qpdf", "QPDF expected n n obj"); |
| 1410 | - throw QPDFExc(this->file.getName(), offset, "expected n n obj"); | |
| 1498 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1499 | + this->last_object_description, offset, | |
| 1500 | + "expected n n obj"); | |
| 1411 | 1501 | } |
| 1412 | 1502 | objid = atoi(tobjid.getValue().c_str()); |
| 1413 | 1503 | generation = atoi(tgen.getValue().c_str()); |
| ... | ... | @@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, |
| 1416 | 1506 | (! ((objid == exp_objid) && (generation == exp_generation)))) |
| 1417 | 1507 | { |
| 1418 | 1508 | QTC::TC("qpdf", "QPDF err wrong objid/generation"); |
| 1419 | - throw QPDFExc(this->file.getName(), offset, | |
| 1509 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1510 | + this->last_object_description, offset, | |
| 1420 | 1511 | std::string("expected ") + |
| 1421 | 1512 | QUtil::int_to_string(exp_objid) + " " + |
| 1422 | 1513 | QUtil::int_to_string(exp_generation) + " obj"); |
| ... | ... | @@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, |
| 1424 | 1515 | } |
| 1425 | 1516 | catch (QPDFExc& e) |
| 1426 | 1517 | { |
| 1427 | - if (exp_objid && this->attempt_recovery) | |
| 1518 | + if (exp_objid && try_recovery && this->attempt_recovery) | |
| 1428 | 1519 | { |
| 1429 | 1520 | // Try again after reconstructing xref table |
| 1430 | 1521 | reconstruct_xref(e); |
| ... | ... | @@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, |
| 1433 | 1524 | (this->xref_table[og].getType() == 1)) |
| 1434 | 1525 | { |
| 1435 | 1526 | off_t new_offset = this->xref_table[og].getOffset(); |
| 1436 | - // Call readObjectAtOffset with 0 for exp_objid to | |
| 1437 | - // avoid an infinite loop. | |
| 1438 | - QPDFObjectHandle result = | |
| 1439 | - readObjectAtOffset(new_offset, 0, 0, objid, generation); | |
| 1527 | + QPDFObjectHandle result = readObjectAtOffset( | |
| 1528 | + false, new_offset, description, | |
| 1529 | + exp_objid, exp_generation, objid, generation); | |
| 1440 | 1530 | QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); |
| 1441 | 1531 | return result; |
| 1442 | 1532 | } |
| 1533 | + else | |
| 1534 | + { | |
| 1535 | + QTC::TC("qpdf", "QPDF object gone after xref reconstruction"); | |
| 1536 | + warn(QPDFExc( | |
| 1537 | + qpdf_e_damaged_pdf, this->file.getName(), | |
| 1538 | + "", 0, | |
| 1539 | + std::string( | |
| 1540 | + "object " + | |
| 1541 | + QUtil::int_to_string(exp_objid) + | |
| 1542 | + " " + | |
| 1543 | + QUtil::int_to_string(exp_generation) + | |
| 1544 | + " not found in file after regenerating" | |
| 1545 | + " cross reference table"))); | |
| 1546 | + return QPDFObjectHandle::newNull(); | |
| 1547 | + } | |
| 1443 | 1548 | } |
| 1444 | 1549 | else |
| 1445 | 1550 | { |
| ... | ... | @@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, |
| 1448 | 1553 | } |
| 1449 | 1554 | |
| 1450 | 1555 | QPDFObjectHandle oh = readObject( |
| 1451 | - &this->file, objid, generation, false); | |
| 1556 | + &this->file, description, objid, generation, false); | |
| 1452 | 1557 | |
| 1453 | 1558 | if (! (readToken(&this->file) == |
| 1454 | 1559 | QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) |
| 1455 | 1560 | { |
| 1456 | 1561 | QTC::TC("qpdf", "QPDF err expected endobj"); |
| 1457 | - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 1562 | + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1563 | + this->last_object_description, this->file.getLastOffset(), | |
| 1458 | 1564 | "expected endobj")); |
| 1459 | 1565 | } |
| 1460 | 1566 | |
| ... | ... | @@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, |
| 1487 | 1593 | } |
| 1488 | 1594 | else |
| 1489 | 1595 | { |
| 1490 | - throw QPDFExc(this->file.getName(), offset, | |
| 1596 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1597 | + this->last_object_description, offset, | |
| 1491 | 1598 | "EOF after endobj"); |
| 1492 | 1599 | } |
| 1493 | 1600 | } |
| ... | ... | @@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation) |
| 1526 | 1633 | int aobjid; |
| 1527 | 1634 | int ageneration; |
| 1528 | 1635 | QPDFObjectHandle oh = |
| 1529 | - readObjectAtOffset(offset, objid, generation, | |
| 1636 | + readObjectAtOffset(true, offset, "", objid, generation, | |
| 1530 | 1637 | aobjid, ageneration); |
| 1531 | 1638 | } |
| 1532 | 1639 | break; |
| ... | ... | @@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation) |
| 1536 | 1643 | break; |
| 1537 | 1644 | |
| 1538 | 1645 | default: |
| 1539 | - throw QPDFExc(this->file.getName(), 0, | |
| 1646 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0, | |
| 1540 | 1647 | "object " + |
| 1541 | 1648 | QUtil::int_to_string(objid) + "/" + |
| 1542 | 1649 | QUtil::int_to_string(generation) + |
| ... | ... | @@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) |
| 1554 | 1661 | QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0); |
| 1555 | 1662 | if (! obj_stream.isStream()) |
| 1556 | 1663 | { |
| 1557 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 1664 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1665 | + this->last_object_description, | |
| 1666 | + this->file.getLastOffset(), | |
| 1558 | 1667 | "supposed object stream " + |
| 1559 | 1668 | QUtil::int_to_string(obj_stream_number) + |
| 1560 | 1669 | " is not a stream"); |
| ... | ... | @@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number) |
| 1570 | 1679 | if (! (dict.getKey("/Type").isName() && |
| 1571 | 1680 | dict.getKey("/Type").getName() == "/ObjStm")) |
| 1572 | 1681 | { |
| 1573 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 1682 | + QTC::TC("qpdf", "QPDF ERR object stream with wrong type"); | |
| 1683 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1684 | + this->last_object_description, | |
| 1685 | + this->file.getLastOffset(), | |
| 1574 | 1686 | "supposed object stream " + |
| 1575 | 1687 | QUtil::int_to_string(obj_stream_number) + |
| 1576 | 1688 | " has wrong type"); |
| ... | ... | @@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) |
| 1579 | 1691 | if (! (dict.getKey("/N").isInteger() && |
| 1580 | 1692 | dict.getKey("/First").isInteger())) |
| 1581 | 1693 | { |
| 1582 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 1694 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1695 | + this->last_object_description, | |
| 1696 | + this->file.getLastOffset(), | |
| 1583 | 1697 | "object stream " + |
| 1584 | 1698 | QUtil::int_to_string(obj_stream_number) + |
| 1585 | 1699 | " has incorrect keys"); |
| ... | ... | @@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number) |
| 1602 | 1716 | if (! ((tnum.getType() == QPDFTokenizer::tt_integer) && |
| 1603 | 1717 | (toffset.getType() == QPDFTokenizer::tt_integer))) |
| 1604 | 1718 | { |
| 1605 | - throw QPDFExc(input.getName(), input.getLastOffset(), | |
| 1719 | + throw QPDFExc(qpdf_e_damaged_pdf, input.getName(), | |
| 1720 | + this->last_object_description, input.getLastOffset(), | |
| 1606 | 1721 | "expected integer in object stream header"); |
| 1607 | 1722 | } |
| 1608 | 1723 | |
| ... | ... | @@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) |
| 1617 | 1732 | int obj = (*iter).first; |
| 1618 | 1733 | int offset = (*iter).second; |
| 1619 | 1734 | input.seek(offset, SEEK_SET); |
| 1620 | - QPDFObjectHandle oh = readObject(&input, obj, 0, true); | |
| 1735 | + QPDFObjectHandle oh = readObject(&input, "", obj, 0, true); | |
| 1621 | 1736 | |
| 1622 | 1737 | // Store in cache |
| 1623 | 1738 | ObjGen og(obj, 0); |
| ... | ... | @@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation, |
| 1830 | 1945 | size_t len = this->file.read(buf, to_read); |
| 1831 | 1946 | if (len == 0) |
| 1832 | 1947 | { |
| 1833 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 1948 | + throw QPDFExc(qpdf_e_damaged_pdf, | |
| 1949 | + this->file.getName(), | |
| 1950 | + this->last_object_description, | |
| 1951 | + this->file.getLastOffset(), | |
| 1834 | 1952 | "unexpected EOF reading stream data"); |
| 1835 | 1953 | } |
| 1836 | 1954 | length -= len; |
| 1837 | 1955 | pipeline->write((unsigned char*)buf, len); |
| 1838 | 1956 | } |
| 1839 | 1957 | } |
| 1958 | + catch (QPDFExc& e) | |
| 1959 | + { | |
| 1960 | + warn(e); | |
| 1961 | + } | |
| 1840 | 1962 | catch (std::runtime_error& e) |
| 1841 | 1963 | { |
| 1842 | 1964 | QTC::TC("qpdf", "QPDF decoding error warning"); |
| 1843 | - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 1965 | + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 1966 | + "", this->file.getLastOffset(), | |
| 1844 | 1967 | "error decoding stream data for object " + |
| 1845 | 1968 | QUtil::int_to_string(objid) + " " + |
| 1846 | 1969 | QUtil::int_to_string(generation) + ": " + e.what())); |
| ... | ... | @@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, |
| 1896 | 2019 | } |
| 1897 | 2020 | else |
| 1898 | 2021 | { |
| 1899 | - throw QPDFExc(this->file.getName() + ": invalid Type in page tree"); | |
| 2022 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 2023 | + this->last_object_description, | |
| 2024 | + this->file.getLastOffset(), | |
| 2025 | + + ": invalid Type in page tree"); | |
| 1900 | 2026 | } |
| 1901 | 2027 | } | ... | ... |
libqpdf/QPDFExc.cc
| 1 | 1 | #include <qpdf/QPDFExc.hh> |
| 2 | 2 | #include <qpdf/QUtil.hh> |
| 3 | 3 | |
| 4 | -QPDFExc::QPDFExc(std::string const& message) : | |
| 5 | - std::runtime_error(message) | |
| 4 | +QPDFExc::QPDFExc(qpdf_error_code_e error_code, | |
| 5 | + std::string const& filename, | |
| 6 | + std::string const& object, | |
| 7 | + off_t offset, | |
| 8 | + std::string const& message) : | |
| 9 | + std::runtime_error(createWhat(filename, object, offset, message)), | |
| 10 | + error_code(error_code), | |
| 11 | + filename(filename), | |
| 12 | + object(object), | |
| 13 | + offset(offset), | |
| 14 | + message(message) | |
| 6 | 15 | { |
| 7 | 16 | } |
| 8 | 17 | |
| 9 | -QPDFExc::QPDFExc(std::string const& filename, int offset, | |
| 10 | - std::string const& message) : | |
| 11 | - std::runtime_error(filename + ": offset " + QUtil::int_to_string(offset) + | |
| 12 | - ": " + message) | |
| 18 | +QPDFExc::~QPDFExc() throw () | |
| 13 | 19 | { |
| 14 | 20 | } |
| 15 | 21 | |
| 16 | -QPDFExc::~QPDFExc() throw () | |
| 22 | +std::string | |
| 23 | +QPDFExc::createWhat(std::string const& filename, | |
| 24 | + std::string const& object, | |
| 25 | + off_t offset, | |
| 26 | + std::string const& message) | |
| 17 | 27 | { |
| 28 | + std::string result; | |
| 29 | + if (! filename.empty()) | |
| 30 | + { | |
| 31 | + result += filename; | |
| 32 | + } | |
| 33 | + if (! (object.empty() && offset == 0)) | |
| 34 | + { | |
| 35 | + result += " ("; | |
| 36 | + if (! object.empty()) | |
| 37 | + { | |
| 38 | + result += object; | |
| 39 | + if (offset > 0) | |
| 40 | + { | |
| 41 | + result += ", "; | |
| 42 | + } | |
| 43 | + } | |
| 44 | + if (offset > 0) | |
| 45 | + { | |
| 46 | + result += "file position " + QUtil::int_to_string(offset); | |
| 47 | + } | |
| 48 | + result += ")"; | |
| 49 | + } | |
| 50 | + if (! result.empty()) | |
| 51 | + { | |
| 52 | + result += ": "; | |
| 53 | + } | |
| 54 | + result += message; | |
| 55 | + return result; | |
| 18 | 56 | } | ... | ... |
libqpdf/QPDFXRefEntry.cc
| ... | ... | @@ -16,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) : |
| 16 | 16 | { |
| 17 | 17 | if ((type < 1) || (type > 2)) |
| 18 | 18 | { |
| 19 | - throw QPDFExc("invalid xref type " + QUtil::int_to_string(type)); | |
| 19 | + throw std::logic_error( | |
| 20 | + "invalid xref type " + QUtil::int_to_string(type)); | |
| 20 | 21 | } |
| 21 | 22 | } |
| 22 | 23 | |
| ... | ... | @@ -31,7 +32,7 @@ QPDFXRefEntry::getOffset() const |
| 31 | 32 | { |
| 32 | 33 | if (this->type != 1) |
| 33 | 34 | { |
| 34 | - throw QPDFExc( | |
| 35 | + throw std::logic_error( | |
| 35 | 36 | "getOffset called for xref entry of type != 1"); |
| 36 | 37 | } |
| 37 | 38 | return this->field1; |
| ... | ... | @@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const |
| 42 | 43 | { |
| 43 | 44 | if (this->type != 2) |
| 44 | 45 | { |
| 45 | - throw QPDFExc( | |
| 46 | + throw std::logic_error( | |
| 46 | 47 | "getObjStreamNumber called for xref entry of type != 2"); |
| 47 | 48 | } |
| 48 | 49 | return this->field1; |
| ... | ... | @@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const |
| 53 | 54 | { |
| 54 | 55 | if (this->type != 2) |
| 55 | 56 | { |
| 56 | - throw QPDFExc( | |
| 57 | + throw std::logic_error( | |
| 57 | 58 | "getObjStreamIndex called for xref entry of type != 2"); |
| 58 | 59 | } |
| 59 | 60 | return this->field2; | ... | ... |
libqpdf/QPDF_Stream.cc
| ... | ... | @@ -59,7 +59,7 @@ QPDF_Stream::getStreamData() |
| 59 | 59 | Pl_Buffer buf("stream data buffer"); |
| 60 | 60 | if (! pipeStreamData(&buf, true, false, false)) |
| 61 | 61 | { |
| 62 | - throw QPDFExc("getStreamData called on unfilterable stream"); | |
| 62 | + throw std::logic_error("getStreamData called on unfilterable stream"); | |
| 63 | 63 | } |
| 64 | 64 | return buf.getBuffer(); |
| 65 | 65 | } |
| ... | ... | @@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector<std::string>& filters, |
| 208 | 208 | if (! filters_okay) |
| 209 | 209 | { |
| 210 | 210 | QTC::TC("qpdf", "QPDF_Stream invalid filter"); |
| 211 | - throw QPDFExc(qpdf->getFilename(), this->offset, | |
| 212 | - "invalid filter object type for this stream"); | |
| 211 | + throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(), | |
| 212 | + "", this->offset, | |
| 213 | + "stream filter type is not name or array"); | |
| 213 | 214 | } |
| 214 | 215 | |
| 215 | 216 | // `filters' now contains a list of filters to be applied in | ... | ... |
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -337,14 +337,16 @@ QPDF::initializeEncryption() |
| 337 | 337 | (id_obj.getArrayNItems() == 2) && |
| 338 | 338 | id_obj.getArrayItem(0).isString())) |
| 339 | 339 | { |
| 340 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 340 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 341 | + "trailer", this->file.getLastOffset(), | |
| 341 | 342 | "invalid /ID in trailer dictionary"); |
| 342 | 343 | } |
| 343 | 344 | |
| 344 | 345 | std::string id1 = id_obj.getArrayItem(0).getStringValue(); |
| 345 | 346 | if (id1.length() != id_bytes) |
| 346 | 347 | { |
| 347 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 348 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 349 | + "trailer", this->file.getLastOffset(), | |
| 348 | 350 | "first /ID string in trailer dictionary has " |
| 349 | 351 | "incorrect length"); |
| 350 | 352 | } |
| ... | ... | @@ -352,19 +354,23 @@ QPDF::initializeEncryption() |
| 352 | 354 | QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); |
| 353 | 355 | if (! encryption_dict.isDictionary()) |
| 354 | 356 | { |
| 355 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 357 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 358 | + this->last_object_description, | |
| 359 | + this->file.getLastOffset(), | |
| 356 | 360 | "/Encrypt in trailer dictionary is not a dictionary"); |
| 357 | 361 | } |
| 358 | 362 | |
| 359 | 363 | if (! (encryption_dict.getKey("/Filter").isName() && |
| 360 | 364 | (encryption_dict.getKey("/Filter").getName() == "/Standard"))) |
| 361 | 365 | { |
| 362 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 366 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 367 | + "encryption dictionary", this->file.getLastOffset(), | |
| 363 | 368 | "unsupported encryption filter"); |
| 364 | 369 | } |
| 365 | 370 | if (! encryption_dict.getKey("/SubFilter").isNull()) |
| 366 | 371 | { |
| 367 | - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 372 | + warn(QPDFExc(qpdf_e_unsupported, this->file.getName(), | |
| 373 | + "encryption dictionary", this->file.getLastOffset(), | |
| 368 | 374 | "file uses encryption SubFilters," |
| 369 | 375 | " which qpdf does not support")); |
| 370 | 376 | } |
| ... | ... | @@ -375,7 +381,8 @@ QPDF::initializeEncryption() |
| 375 | 381 | encryption_dict.getKey("/U").isString() && |
| 376 | 382 | encryption_dict.getKey("/P").isInteger())) |
| 377 | 383 | { |
| 378 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 384 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 385 | + "encryption dictionary", this->file.getLastOffset(), | |
| 379 | 386 | "some encryption dictionary parameters are missing " |
| 380 | 387 | "or the wrong type"); |
| 381 | 388 | } |
| ... | ... | @@ -389,7 +396,8 @@ QPDF::initializeEncryption() |
| 389 | 396 | if (! (((R == 2) || (R == 3) || (R == 4)) && |
| 390 | 397 | ((V == 1) || (V == 2) || (V == 4)))) |
| 391 | 398 | { |
| 392 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 399 | + throw QPDFExc(qpdf_e_unsupported, this->file.getName(), | |
| 400 | + "encryption dictionary", this->file.getLastOffset(), | |
| 393 | 401 | "Unsupported /R or /V in encryption dictionary"); |
| 394 | 402 | } |
| 395 | 403 | |
| ... | ... | @@ -397,7 +405,8 @@ QPDF::initializeEncryption() |
| 397 | 405 | |
| 398 | 406 | if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) |
| 399 | 407 | { |
| 400 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 408 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 409 | + "encryption dictionary", this->file.getLastOffset(), | |
| 401 | 410 | "incorrect length for /O and/or /P in " |
| 402 | 411 | "encryption dictionary"); |
| 403 | 412 | } |
| ... | ... | @@ -408,7 +417,8 @@ QPDF::initializeEncryption() |
| 408 | 417 | Length = encryption_dict.getKey("/Length").getIntValue(); |
| 409 | 418 | if ((Length % 8) || (Length < 40) || (Length > 128)) |
| 410 | 419 | { |
| 411 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 420 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 421 | + "encryption dictionary", this->file.getLastOffset(), | |
| 412 | 422 | "invalid /Length value in encryption dictionary"); |
| 413 | 423 | } |
| 414 | 424 | } |
| ... | ... | @@ -471,7 +481,8 @@ QPDF::initializeEncryption() |
| 471 | 481 | } |
| 472 | 482 | if (this->cf_file != this->cf_stream) |
| 473 | 483 | { |
| 474 | - throw QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 484 | + throw QPDFExc(qpdf_e_unsupported, this->file.getName(), | |
| 485 | + "encryption dictionary", this->file.getLastOffset(), | |
| 475 | 486 | "This document has embedded files that are" |
| 476 | 487 | " encrypted differently from the rest of the file." |
| 477 | 488 | " qpdf does not presently support this due to" |
| ... | ... | @@ -492,7 +503,8 @@ QPDF::initializeEncryption() |
| 492 | 503 | } |
| 493 | 504 | else |
| 494 | 505 | { |
| 495 | - throw QPDFExc(this->file.getName() + ": invalid password"); | |
| 506 | + throw QPDFExc(qpdf_e_password, this->file.getName(), | |
| 507 | + "", 0, "invalid password"); | |
| 496 | 508 | } |
| 497 | 509 | |
| 498 | 510 | this->encryption_key = compute_encryption_key(this->user_password, data); |
| ... | ... | @@ -542,7 +554,9 @@ QPDF::decryptString(std::string& str, int objid, int generation) |
| 542 | 554 | break; |
| 543 | 555 | |
| 544 | 556 | default: |
| 545 | - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 557 | + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 558 | + this->last_object_description, | |
| 559 | + this->file.getLastOffset(), | |
| 546 | 560 | "unknown encryption filter for strings" |
| 547 | 561 | " (check /StrF in /Encrypt dictionary);" |
| 548 | 562 | " strings may be decrypted improperly")); |
| ... | ... | @@ -554,28 +568,47 @@ QPDF::decryptString(std::string& str, int objid, int generation) |
| 554 | 568 | } |
| 555 | 569 | |
| 556 | 570 | std::string key = getKeyForObject(objid, generation, use_aes); |
| 557 | - if (use_aes) | |
| 571 | + try | |
| 558 | 572 | { |
| 559 | - QTC::TC("qpdf", "QPDF_encryption aes decode string"); | |
| 560 | - assert(key.length() == Pl_AES_PDF::key_size); | |
| 561 | - Pl_Buffer bufpl("decrypted string"); | |
| 562 | - Pl_AES_PDF pl("aes decrypt string", &bufpl, false, | |
| 563 | - (unsigned char const*)key.c_str()); | |
| 564 | - pl.write((unsigned char*)str.c_str(), str.length()); | |
| 565 | - pl.finish(); | |
| 566 | - Buffer* buf = bufpl.getBuffer(); | |
| 567 | - str = std::string((char*)buf->getBuffer(), (size_t)buf->getSize()); | |
| 568 | - delete buf; | |
| 573 | + if (use_aes) | |
| 574 | + { | |
| 575 | + QTC::TC("qpdf", "QPDF_encryption aes decode string"); | |
| 576 | + assert(key.length() == Pl_AES_PDF::key_size); | |
| 577 | + Pl_Buffer bufpl("decrypted string"); | |
| 578 | + Pl_AES_PDF pl("aes decrypt string", &bufpl, false, | |
| 579 | + (unsigned char const*)key.c_str()); | |
| 580 | + pl.write((unsigned char*)str.c_str(), str.length()); | |
| 581 | + pl.finish(); | |
| 582 | + PointerHolder<Buffer> buf = bufpl.getBuffer(); | |
| 583 | + str = std::string((char*)buf.getPointer()->getBuffer(), | |
| 584 | + (size_t)buf.getPointer()->getSize()); | |
| 585 | + } | |
| 586 | + else | |
| 587 | + { | |
| 588 | + QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); | |
| 589 | + unsigned int vlen = str.length(); | |
| 590 | + // Using PointerHolder will cause a new char[] to be deleted | |
| 591 | + // with delete instead of delete [], but it's okay since the | |
| 592 | + // array is of a fundamental type, so there is no destructor | |
| 593 | + // to be called. Using PointerHolder guarantees that tmp will | |
| 594 | + // be freed even if rc4.process throws an exception. | |
| 595 | + PointerHolder<char> tmp = QUtil::copy_string(str); | |
| 596 | + RC4 rc4((unsigned char const*)key.c_str(), key.length()); | |
| 597 | + rc4.process((unsigned char*)tmp.getPointer(), vlen); | |
| 598 | + str = std::string(tmp.getPointer(), vlen); | |
| 599 | + } | |
| 569 | 600 | } |
| 570 | - else | |
| 601 | + catch (QPDFExc& e) | |
| 602 | + { | |
| 603 | + throw; | |
| 604 | + } | |
| 605 | + catch (std::runtime_error& e) | |
| 571 | 606 | { |
| 572 | - QTC::TC("qpdf", "QPDF_encryption rc4 decode string"); | |
| 573 | - unsigned int vlen = str.length(); | |
| 574 | - char* tmp = QUtil::copy_string(str); | |
| 575 | - RC4 rc4((unsigned char const*)key.c_str(), key.length()); | |
| 576 | - rc4.process((unsigned char*)tmp, vlen); | |
| 577 | - str = std::string(tmp, vlen); | |
| 578 | - delete [] tmp; | |
| 607 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 608 | + this->last_object_description, this->file.getLastOffset(), | |
| 609 | + "error decrypting string for object " + | |
| 610 | + QUtil::int_to_string(objid) + " " + | |
| 611 | + QUtil::int_to_string(generation) + ": " + e.what()); | |
| 579 | 612 | } |
| 580 | 613 | } |
| 581 | 614 | |
| ... | ... | @@ -645,7 +678,9 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, |
| 645 | 678 | |
| 646 | 679 | default: |
| 647 | 680 | // filter local to this stream. |
| 648 | - warn(QPDFExc(this->file.getName(), this->file.getLastOffset(), | |
| 681 | + warn(QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 682 | + this->last_object_description, | |
| 683 | + this->file.getLastOffset(), | |
| 649 | 684 | "unknown encryption filter for streams" |
| 650 | 685 | " (check " + method_source + ");" |
| 651 | 686 | " streams may be decrypted improperly")); | ... | ... |
libqpdf/QPDF_linearization.cc
| ... | ... | @@ -175,7 +175,8 @@ QPDF::readLinearizationData() |
| 175 | 175 | |
| 176 | 176 | if (! isLinearized()) |
| 177 | 177 | { |
| 178 | - throw QPDFExc(this->file.getName() + " is not linearized"); | |
| 178 | + throw std::logic_error("called readLinearizationData for file" | |
| 179 | + " that is not linearized"); | |
| 179 | 180 | } |
| 180 | 181 | |
| 181 | 182 | // /L is read and stored in linp by isLinearized() |
| ... | ... | @@ -193,7 +194,10 @@ QPDF::readLinearizationData() |
| 193 | 194 | T.isInteger() && |
| 194 | 195 | (P.isInteger() || P.isNull()))) |
| 195 | 196 | { |
| 196 | - throw QPDFExc("some keys in linearization dictionary are of " | |
| 197 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 198 | + "linearization dictionary", | |
| 199 | + this->file.getLastOffset(), | |
| 200 | + "some keys in linearization dictionary are of " | |
| 197 | 201 | "the wrong type"); |
| 198 | 202 | } |
| 199 | 203 | |
| ... | ... | @@ -201,7 +205,10 @@ QPDF::readLinearizationData() |
| 201 | 205 | unsigned int n_H_items = H.getArrayNItems(); |
| 202 | 206 | if (! ((n_H_items == 2) || (n_H_items == 4))) |
| 203 | 207 | { |
| 204 | - throw QPDFExc("H has the wrong number of items"); | |
| 208 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 209 | + "linearization dictionary", | |
| 210 | + this->file.getLastOffset(), | |
| 211 | + "H has the wrong number of items"); | |
| 205 | 212 | } |
| 206 | 213 | |
| 207 | 214 | std::vector<int> H_items; |
| ... | ... | @@ -214,7 +221,10 @@ QPDF::readLinearizationData() |
| 214 | 221 | } |
| 215 | 222 | else |
| 216 | 223 | { |
| 217 | - throw QPDFExc("some H items are of the wrong type"); | |
| 224 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 225 | + "linearization dictionary", | |
| 226 | + this->file.getLastOffset(), | |
| 227 | + "some H items are of the wrong type"); | |
| 218 | 228 | } |
| 219 | 229 | } |
| 220 | 230 | |
| ... | ... | @@ -301,13 +311,17 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length) |
| 301 | 311 | { |
| 302 | 312 | int obj; |
| 303 | 313 | int gen; |
| 304 | - QPDFObjectHandle H = readObjectAtOffset(offset, 0, 0, obj, gen); | |
| 314 | + QPDFObjectHandle H = readObjectAtOffset( | |
| 315 | + false, offset, "linearization hint stream", 0, 0, obj, gen); | |
| 305 | 316 | ObjCache& oc = this->obj_cache[ObjGen(obj, gen)]; |
| 306 | 317 | off_t min_end_offset = oc.end_before_space; |
| 307 | 318 | off_t max_end_offset = oc.end_after_space; |
| 308 | 319 | if (! H.isStream()) |
| 309 | 320 | { |
| 310 | - throw QPDFExc("hint table is not a stream"); | |
| 321 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 322 | + "linearization dictionary", | |
| 323 | + this->file.getLastOffset(), | |
| 324 | + "hint table is not a stream"); | |
| 311 | 325 | } |
| 312 | 326 | |
| 313 | 327 | QPDFObjectHandle Hdict = H.getDict(); |
| ... | ... | @@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length) |
| 340 | 354 | std::cout << "expected = " << computed_end |
| 341 | 355 | << "; actual = " << min_end_offset << ".." |
| 342 | 356 | << max_end_offset << std::endl; |
| 343 | - throw QPDFExc("hint table length mismatch"); | |
| 357 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 358 | + "linearization dictionary", | |
| 359 | + this->file.getLastOffset(), | |
| 360 | + "hint table length mismatch"); | |
| 344 | 361 | } |
| 345 | 362 | H.pipeStreamData(&pl, true, false, false); |
| 346 | 363 | return Hdict; |
| ... | ... | @@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const& og) |
| 651 | 668 | break; |
| 652 | 669 | |
| 653 | 670 | default: |
| 654 | - throw QPDFExc( | |
| 655 | - this->file.getName(), 0, | |
| 671 | + throw std::logic_error( | |
| 656 | 672 | "getLinearizationOffset called for xref entry not of type 1 or 2"); |
| 657 | 673 | break; |
| 658 | 674 | } | ... | ... |
libqpdf/QPDF_optimization.cc
| ... | ... | @@ -233,9 +233,12 @@ QPDF::optimizePagesTree( |
| 233 | 233 | { |
| 234 | 234 | if (! allow_changes) |
| 235 | 235 | { |
| 236 | - throw QPDFExc(this->file.getName() + | |
| 237 | - ": optimize detected an " | |
| 238 | - "inheritable resource"); | |
| 236 | + throw QPDFExc(qpdf_e_internal, this->file.getName(), | |
| 237 | + this->last_object_description, | |
| 238 | + this->file.getLastOffset(), | |
| 239 | + "optimize detected an " | |
| 240 | + "inheritable resource when called " | |
| 241 | + "in no-change mode"); | |
| 239 | 242 | } |
| 240 | 243 | |
| 241 | 244 | // This is an inheritable resource |
| ... | ... | @@ -338,7 +341,10 @@ QPDF::optimizePagesTree( |
| 338 | 341 | } |
| 339 | 342 | else |
| 340 | 343 | { |
| 341 | - throw QPDFExc(this->file.getName() + ": invalid Type in page tree"); | |
| 344 | + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), | |
| 345 | + this->last_object_description, | |
| 346 | + this->file.getLastOffset(), | |
| 347 | + "invalid Type in page tree"); | |
| 342 | 348 | } |
| 343 | 349 | } |
| 344 | 350 | ... | ... |
qpdf/qpdf.cc
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | #include <string.h> |
| 3 | 3 | #include <stdlib.h> |
| 4 | 4 | #include <fcntl.h> |
| 5 | +#include <stdio.h> | |
| 5 | 6 | |
| 6 | 7 | #include <qpdf/QUtil.hh> |
| 7 | 8 | #include <qpdf/QTC.hh> |
| ... | ... | @@ -533,6 +534,7 @@ parse_encrypt_options( |
| 533 | 534 | int main(int argc, char* argv[]) |
| 534 | 535 | { |
| 535 | 536 | whoami = QUtil::getWhoami(argv[0]); |
| 537 | + setlinebuf(stdout); | |
| 536 | 538 | |
| 537 | 539 | // For libtool's sake.... |
| 538 | 540 | if (strncmp(whoami, "lt-", 3) == 0) |
| ... | ... | @@ -892,7 +894,15 @@ int main(int argc, char* argv[]) |
| 892 | 894 | } |
| 893 | 895 | if (show_linearization) |
| 894 | 896 | { |
| 895 | - pdf.showLinearizationData(); | |
| 897 | + if (pdf.isLinearized()) | |
| 898 | + { | |
| 899 | + pdf.showLinearizationData(); | |
| 900 | + } | |
| 901 | + else | |
| 902 | + { | |
| 903 | + std::cout << infilename << " is not linearized" | |
| 904 | + << std::endl; | |
| 905 | + } | |
| 896 | 906 | } |
| 897 | 907 | if (show_xref) |
| 898 | 908 | { | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -171,3 +171,5 @@ QPDFWriter forced version disabled encryption 0 |
| 171 | 171 | qpdf-c called qpdf_set_r4_encryption_parameters 0 |
| 172 | 172 | qpdf-c called qpdf_set_static_aes_IV 0 |
| 173 | 173 | QPDF_encryption stream crypt filter 0 |
| 174 | +QPDF ERR object stream with wrong type 0 | |
| 175 | +QPDF object gone after xref reconstruction 0 | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -191,6 +191,8 @@ my @badfiles = ("not a PDF file", # 1 |
| 191 | 191 | "unknown stream /Filter", # 31 |
| 192 | 192 | "obj/gen mismatch", # 32 |
| 193 | 193 | "invalid stream /Filter and xref", # 33 |
| 194 | + "obj/gen in wrong place", # 34 | |
| 195 | + "object stream of wrong type", # 35 | |
| 194 | 196 | ); |
| 195 | 197 | |
| 196 | 198 | $n_tests += @badfiles + 5; |
| ... | ... | @@ -249,7 +251,7 @@ $n_tests += @badfiles + 8; |
| 249 | 251 | # though in some cases it may. Acrobat Reader would not be able to |
| 250 | 252 | # recover any of these files any better. |
| 251 | 253 | my %recover_failures = (); |
| 252 | -for (1, 7, 13..21, 24..27, 29..30, 33) | |
| 254 | +for (1, 7, 13..21, 24, 29..30, 33, 35) | |
| 253 | 255 | { |
| 254 | 256 | $recover_failures{$_} = 1; |
| 255 | 257 | } | ... | ... |
qpdf/qtest/qpdf/append-page-content-damaged-c-check.out
| 1 | -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged | |
| 1 | +WARNING: append-page-content-damaged.pdf: file is damaged | |
| 2 | 2 | WARNING: append-page-content-damaged.pdf: can't find startxref |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | version: 1.3 |
| 5 | 5 | linearized: 0 |
| 6 | 6 | encrypted: 0 |
| 7 | -warning: append-page-content-damaged.pdf: offset 0: file is damaged | |
| 7 | +warning: append-page-content-damaged.pdf: file is damaged | |
| 8 | 8 | warning: append-page-content-damaged.pdf: can't find startxref |
| 9 | 9 | warning: Attempting to reconstruct cross-reference table | ... | ... |
qpdf/qtest/qpdf/append-page-content-damaged-check.out
| 1 | -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged | |
| 1 | +WARNING: append-page-content-damaged.pdf: file is damaged | |
| 2 | 2 | WARNING: append-page-content-damaged.pdf: can't find startxref |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | checking append-page-content-damaged.pdf | ... | ... |
qpdf/qtest/qpdf/append-page-content-damaged.out
| 1 | -WARNING: append-page-content-damaged.pdf: offset 0: file is damaged | |
| 1 | +WARNING: append-page-content-damaged.pdf: file is damaged | |
| 2 | 2 | WARNING: append-page-content-damaged.pdf: can't find startxref |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | qpdf: operation succeeded with warnings; resulting file may have some problems | ... | ... |
qpdf/qtest/qpdf/bad1-recover.out
qpdf/qtest/qpdf/bad1.out
qpdf/qtest/qpdf/bad10-recover.out
| 1 | -WARNING: bad10.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer | |
| 1 | +WARNING: bad10.pdf: file is damaged | |
| 2 | +WARNING: bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad10.out
qpdf/qtest/qpdf/bad11-recover.out
| 1 | -WARNING: bad11.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer | |
| 1 | +WARNING: bad11.pdf: file is damaged | |
| 2 | +WARNING: bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad11.out
qpdf/qtest/qpdf/bad13-recover.out
| 1 | -WARNING: bad13.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad13.pdf: offset 753: unexpected brace token | |
| 1 | +WARNING: bad13.pdf: file is damaged | |
| 2 | +WARNING: bad13.pdf (trailer, file position 753): unexpected brace token | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad13.pdf: offset 753: unexpected brace token | |
| 4 | +bad13.pdf (trailer, file position 753): unexpected brace token | ... | ... |
qpdf/qtest/qpdf/bad13.out
qpdf/qtest/qpdf/bad14-recover.out
| 1 | -WARNING: bad14.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad14.pdf: offset 753: unexpected brace token | |
| 1 | +WARNING: bad14.pdf: file is damaged | |
| 2 | +WARNING: bad14.pdf (trailer, file position 753): unexpected brace token | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad14.pdf: offset 753: unexpected brace token | |
| 4 | +bad14.pdf (trailer, file position 753): unexpected brace token | ... | ... |
qpdf/qtest/qpdf/bad14.out
qpdf/qtest/qpdf/bad15-recover.out
| 1 | -WARNING: bad15.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad15.pdf: offset 753: unexpected array close token | |
| 1 | +WARNING: bad15.pdf: file is damaged | |
| 2 | +WARNING: bad15.pdf (trailer, file position 753): unexpected array close token | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad15.pdf: offset 753: unexpected array close token | |
| 4 | +bad15.pdf (trailer, file position 753): unexpected array close token | ... | ... |
qpdf/qtest/qpdf/bad15.out
qpdf/qtest/qpdf/bad16-recover.out
| 1 | -WARNING: bad16.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad16.pdf: offset 753: unexpected dictionary close token | |
| 1 | +WARNING: bad16.pdf: file is damaged | |
| 2 | +WARNING: bad16.pdf (trailer, file position 753): unexpected dictionary close token | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad16.pdf: offset 753: unexpected dictionary close token | |
| 4 | +bad16.pdf (trailer, file position 753): unexpected dictionary close token | ... | ... |
qpdf/qtest/qpdf/bad16.out
qpdf/qtest/qpdf/bad17-recover.out
| 1 | -WARNING: bad17.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad17.pdf: offset 753: dictionary ending here has an odd number of elements | |
| 1 | +WARNING: bad17.pdf: file is damaged | |
| 2 | +WARNING: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad17.pdf: offset 753: dictionary ending here has an odd number of elements | |
| 4 | +bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements | ... | ... |
qpdf/qtest/qpdf/bad17.out
qpdf/qtest/qpdf/bad18-recover.out
| 1 | -WARNING: bad18.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad18.pdf: offset 753: unexpected ) | |
| 1 | +WARNING: bad18.pdf: file is damaged | |
| 2 | +WARNING: bad18.pdf (trailer, file position 753): unexpected ) | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad18.pdf: offset 753: unexpected ) | |
| 4 | +bad18.pdf (trailer, file position 753): unexpected ) | ... | ... |
qpdf/qtest/qpdf/bad18.out
qpdf/qtest/qpdf/bad19-recover.out
| 1 | -WARNING: bad19.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad19.pdf: offset 753: unexpected > | |
| 1 | +WARNING: bad19.pdf: file is damaged | |
| 2 | +WARNING: bad19.pdf (trailer, file position 753): unexpected > | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad19.pdf: offset 753: unexpected > | |
| 4 | +bad19.pdf (trailer, file position 753): unexpected > | ... | ... |
qpdf/qtest/qpdf/bad19.out
qpdf/qtest/qpdf/bad2-recover.out
qpdf/qtest/qpdf/bad20-recover.out
| 1 | -WARNING: bad20.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad20.pdf: offset 753: invalid character (q) in hexstring | |
| 1 | +WARNING: bad20.pdf: file is damaged | |
| 2 | +WARNING: bad20.pdf (trailer, file position 753): invalid character (q) in hexstring | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad20.pdf: offset 753: invalid character (q) in hexstring | |
| 4 | +bad20.pdf (trailer, file position 753): invalid character (q) in hexstring | ... | ... |
qpdf/qtest/qpdf/bad20.out
qpdf/qtest/qpdf/bad21-recover.out
| 1 | -WARNING: bad21.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad21.pdf: offset 742: invalid name token | |
| 1 | +WARNING: bad21.pdf: file is damaged | |
| 2 | +WARNING: bad21.pdf (trailer, file position 742): invalid name token | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad21.pdf: offset 742: invalid name token | |
| 4 | +bad21.pdf (trailer, file position 742): invalid name token | ... | ... |
qpdf/qtest/qpdf/bad21.out
qpdf/qtest/qpdf/bad22-recover.out
qpdf/qtest/qpdf/bad22.out
qpdf/qtest/qpdf/bad23-recover.out
qpdf/qtest/qpdf/bad23.out
qpdf/qtest/qpdf/bad24-recover.out
| 1 | -WARNING: bad24.pdf: offset 341: attempting to recover stream length | |
| 2 | -bad24.pdf: offset 341: unable to recover stream data | |
| 1 | +WARNING: bad24.pdf (object 4 0, file position 341): attempting to recover stream length | |
| 2 | +bad24.pdf (object 4 0, file position 341): unable to recover stream data | ... | ... |
qpdf/qtest/qpdf/bad24.out
qpdf/qtest/qpdf/bad25-recover.out
| 1 | -WARNING: bad25.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad25.pdf: offset 307: expected n n obj | |
| 1 | +WARNING: bad25.pdf: file is damaged | |
| 2 | +WARNING: bad25.pdf (object 4 0, file position 307): expected n n obj | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad25.pdf: offset 307: expected n n obj | |
| 4 | +WARNING: bad25.pdf: object 4 0 not found in file after regenerating cross reference table | |
| 5 | +/QTest is implicit | |
| 6 | +/QTest is indirect | |
| 7 | +/QTest is null | |
| 8 | +unparse: 4 0 R | |
| 9 | +unparseResolved: null | |
| 10 | +test 1 done | ... | ... |
qpdf/qtest/qpdf/bad25.out
qpdf/qtest/qpdf/bad26-recover.out
| 1 | -WARNING: bad26.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad26.pdf: offset 307: expected n n obj | |
| 1 | +WARNING: bad26.pdf: file is damaged | |
| 2 | +WARNING: bad26.pdf (object 4 0, file position 307): expected n n obj | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad26.pdf: offset 307: expected n n obj | |
| 4 | +WARNING: bad26.pdf: object 4 0 not found in file after regenerating cross reference table | |
| 5 | +/QTest is implicit | |
| 6 | +/QTest is indirect | |
| 7 | +/QTest is null | |
| 8 | +unparse: 4 0 R | |
| 9 | +unparseResolved: null | |
| 10 | +test 1 done | ... | ... |
qpdf/qtest/qpdf/bad26.out
qpdf/qtest/qpdf/bad27-recover.out
| 1 | -WARNING: bad27.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad27.pdf: offset 307: expected n n obj | |
| 1 | +WARNING: bad27.pdf: file is damaged | |
| 2 | +WARNING: bad27.pdf (object 4 0, file position 307): expected n n obj | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad27.pdf: offset 307: expected n n obj | |
| 4 | +WARNING: bad27.pdf: object 4 0 not found in file after regenerating cross reference table | |
| 5 | +/QTest is implicit | |
| 6 | +/QTest is indirect | |
| 7 | +/QTest is null | |
| 8 | +unparse: 4 0 R | |
| 9 | +unparseResolved: null | |
| 10 | +test 1 done | ... | ... |
qpdf/qtest/qpdf/bad27.out
qpdf/qtest/qpdf/bad28-recover.out
qpdf/qtest/qpdf/bad28.out
qpdf/qtest/qpdf/bad29-recover.out
| 1 | -WARNING: bad29.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad29.pdf: offset 742: null character not allowed in name token | |
| 1 | +WARNING: bad29.pdf: file is damaged | |
| 2 | +WARNING: bad29.pdf (trailer, file position 742): null character not allowed in name token | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -bad29.pdf: offset 742: null character not allowed in name token | |
| 4 | +bad29.pdf (trailer, file position 742): null character not allowed in name token | ... | ... |
qpdf/qtest/qpdf/bad29.out
qpdf/qtest/qpdf/bad3-recover.out
| 1 | -WARNING: bad3.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad3.pdf: offset 542: xref not found | |
| 1 | +WARNING: bad3.pdf: file is damaged | |
| 2 | +WARNING: bad3.pdf (file position 542): xref not found | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad3.out
qpdf/qtest/qpdf/bad30-recover.out
No preview for this file type
qpdf/qtest/qpdf/bad30.out
No preview for this file type
qpdf/qtest/qpdf/bad32-recover.out
| 1 | -WARNING: bad32.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad32.pdf: offset 307: expected 4 0 obj | |
| 1 | +WARNING: bad32.pdf: file is damaged | |
| 2 | +WARNING: bad32.pdf (object 4 0, file position 307): expected 4 0 obj | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | +WARNING: bad32.pdf: object 4 0 not found in file after regenerating cross reference table | |
| 4 | 5 | /QTest is implicit |
| 5 | 6 | /QTest is indirect |
| 6 | 7 | /QTest is null | ... | ... |
qpdf/qtest/qpdf/bad32.out
qpdf/qtest/qpdf/bad33-recover.out
No preview for this file type
qpdf/qtest/qpdf/bad33.out
qpdf/qtest/qpdf/bad34-recover.out
0 โ 100644
| 1 | +WARNING: bad34.pdf: file is damaged | |
| 2 | +WARNING: bad34.pdf (object 4 0, file position 322): expected n n obj | |
| 3 | +WARNING: Attempting to reconstruct cross-reference table | |
| 4 | +/QTest is indirect | |
| 5 | +/QTest is a stream. Dictionary: << /Length 44 /Quack 9 0 R >> | |
| 6 | +Raw stream data: | |
| 7 | +BT | |
| 8 | + /F1 24 Tf | |
| 9 | + 72 720 Td | |
| 10 | + (Potato) Tj | |
| 11 | +ET | |
| 12 | + | |
| 13 | +Uncompressed stream data: | |
| 14 | +BT | |
| 15 | + /F1 24 Tf | |
| 16 | + 72 720 Td | |
| 17 | + (Potato) Tj | |
| 18 | +ET | |
| 19 | + | |
| 20 | +End of stream data | |
| 21 | +unparse: 4 0 R | |
| 22 | +unparseResolved: 4 0 R | |
| 23 | +test 1 done | ... | ... |
qpdf/qtest/qpdf/bad34.out
0 โ 100644
| 1 | +bad34.pdf (object 4 0, file position 322): expected n n obj | ... | ... |
qpdf/qtest/qpdf/bad34.pdf
0 โ 100644
| 1 | +%PDF-1.3 | |
| 2 | +1 0 obj | |
| 3 | +<< | |
| 4 | + /Type /Catalog | |
| 5 | + /Pages 2 0 R | |
| 6 | +>> | |
| 7 | +endobj | |
| 8 | + | |
| 9 | +2 0 obj | |
| 10 | +<< | |
| 11 | + /Type /Pages | |
| 12 | + /Kids [ | |
| 13 | + 3 0 R | |
| 14 | + ] | |
| 15 | + /Count 1 | |
| 16 | +>> | |
| 17 | +endobj | |
| 18 | + | |
| 19 | +4 0 obj | |
| 20 | +<< | |
| 21 | + /Length 44 | |
| 22 | + /Quack 9 0 R | |
| 23 | +>> | |
| 24 | +stream | |
| 25 | +BT | |
| 26 | + /F1 24 Tf | |
| 27 | + 72 720 Td | |
| 28 | + (Potato) Tj | |
| 29 | +ET | |
| 30 | +endstream | |
| 31 | +endobj | |
| 32 | + | |
| 33 | +3 0 obj | |
| 34 | +<< | |
| 35 | + /Type /Page | |
| 36 | + /Parent 2 0 R | |
| 37 | + /MediaBox [0 0 612 792] | |
| 38 | + /Contents 4 0 R | |
| 39 | + /Resources << | |
| 40 | + /ProcSet 5 0 R | |
| 41 | + /Font << | |
| 42 | + /F1 6 0 R | |
| 43 | + >> | |
| 44 | + >> | |
| 45 | +>> | |
| 46 | +endobj | |
| 47 | + | |
| 48 | +5 0 obj | |
| 49 | +[ | |
| 50 | ||
| 51 | + /Text | |
| 52 | +] | |
| 53 | +endobj | |
| 54 | + | |
| 55 | +6 0 obj | |
| 56 | +<< | |
| 57 | + /Type /Font | |
| 58 | + /Subtype /Type1 | |
| 59 | + /Name /F1 | |
| 60 | + /BaseFont /Helvetica | |
| 61 | + /Encoding /WinAnsiEncoding | |
| 62 | +>> | |
| 63 | +endobj | |
| 64 | + | |
| 65 | +xref | |
| 66 | +0 7 | |
| 67 | +0000000000 65535 f | |
| 68 | +0000000009 00000 n | |
| 69 | +0000000063 00000 n | |
| 70 | +0000000135 00000 n | |
| 71 | +0000000322 00000 n | |
| 72 | +0000000418 00000 n | |
| 73 | +0000000453 00000 n | |
| 74 | +trailer << | |
| 75 | + /Size 7 | |
| 76 | + /Root 1 0 R | |
| 77 | + /QTest 4 0 R | |
| 78 | +>> | |
| 79 | +startxref | |
| 80 | +571 | |
| 81 | +%%EOF | ... | ... |
qpdf/qtest/qpdf/bad35-recover.out
0 โ 100644
| 1 | +bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type | ... | ... |
qpdf/qtest/qpdf/bad35.out
0 โ 100644
| 1 | +bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type | ... | ... |
qpdf/qtest/qpdf/bad35.pdf
0 โ 100644
No preview for this file type
qpdf/qtest/qpdf/bad4-recover.out
| 1 | -WARNING: bad4.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad4.pdf: offset 547: xref syntax invalid | |
| 1 | +WARNING: bad4.pdf: file is damaged | |
| 2 | +WARNING: bad4.pdf (xref table, file position 547): xref syntax invalid | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad4.out
qpdf/qtest/qpdf/bad5-recover.out
| 1 | -WARNING: bad5.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad5.pdf: offset 591: invalid xref entry (obj=2) | |
| 1 | +WARNING: bad5.pdf: file is damaged | |
| 2 | +WARNING: bad5.pdf (xref table, file position 591): invalid xref entry (obj=2) | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad5.out
qpdf/qtest/qpdf/bad7-recover.out
| 1 | -WARNING: bad7.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad7.pdf: offset 698: expected trailer dictionary | |
| 1 | +WARNING: bad7.pdf: file is damaged | |
| 2 | +WARNING: bad7.pdf (file position 698): expected trailer dictionary | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | bad7.pdf: unable to find trailer dictionary while recovering damaged file | ... | ... |
qpdf/qtest/qpdf/bad7.out
qpdf/qtest/qpdf/bad8-recover.out
| 1 | -WARNING: bad8.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad8.pdf: offset 543: xref not found | |
| 1 | +WARNING: bad8.pdf: file is damaged | |
| 2 | +WARNING: bad8.pdf (file position 543): xref not found | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad8.out
qpdf/qtest/qpdf/bad9-recover.out
| 1 | -WARNING: bad9.pdf: offset 0: file is damaged | |
| 2 | -WARNING: bad9.pdf: offset 712: trailer dictionary lacks /Size key | |
| 1 | +WARNING: bad9.pdf: file is damaged | |
| 2 | +WARNING: bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is implicit |
| 5 | 5 | /QTest is direct | ... | ... |
qpdf/qtest/qpdf/bad9.out
qpdf/qtest/qpdf/c-no-recovery.out
qpdf/qtest/qpdf/c-read-errors.out
qpdf/qtest/qpdf/c-read-warnings-and-errors.out
| 1 | -warning: bad17.pdf: offset 0: file is damaged | |
| 2 | -warning: bad17.pdf: offset 753: dictionary ending here has an odd number of elements | |
| 1 | +warning: bad17.pdf: file is damaged | |
| 2 | +warning: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements | |
| 3 | 3 | warning: Attempting to reconstruct cross-reference table |
| 4 | -error: bad17.pdf: offset 753: dictionary ending here has an odd number of elements | |
| 4 | +error: bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements | ... | ... |
qpdf/qtest/qpdf/c-write-damaged.out
qpdf/qtest/qpdf/c-write-errors.out
qpdf/qtest/qpdf/c-write-warnings-and-errors.out
| 1 | -warning: bad33.pdf: offset 0: file is damaged | |
| 2 | -warning: bad33.pdf: offset 1771: xref not found | |
| 1 | +warning: bad33.pdf: file is damaged | |
| 2 | +warning: bad33.pdf (file position 1771): xref not found | |
| 3 | 3 | warning: Attempting to reconstruct cross-reference table |
| 4 | -error: bad33.pdf: offset 629: invalid filter object type for this stream | |
| 4 | +error: bad33.pdf (file position 629): stream filter type is not name or array | ... | ... |
qpdf/qtest/qpdf/damaged-stream.out
| ... | ... | @@ -2,4 +2,4 @@ checking damaged-stream.pdf |
| 2 | 2 | PDF Version: 1.3 |
| 3 | 3 | File is not encrypted |
| 4 | 4 | File is not linearized |
| 5 | -WARNING: damaged-stream.pdf: offset 426: error decoding stream data for object 5 0: LZWDecoder: bad code received | |
| 5 | +WARNING: damaged-stream.pdf (file position 426): error decoding stream data for object 5 0: LZWDecoder: bad code received | ... | ... |
qpdf/qtest/qpdf/heifer.out
| 1 | -WARNING: heifer.pdf: offset 0: file is damaged | |
| 2 | -WARNING: heifer.pdf: offset 92741: xref not found | |
| 1 | +WARNING: heifer.pdf: file is damaged | |
| 2 | +WARNING: heifer.pdf (file position 92741): xref not found | |
| 3 | 3 | WARNING: Attempting to reconstruct cross-reference table |
| 4 | -WARNING: heifer.pdf: offset 51: attempting to recover stream length | |
| 4 | +WARNING: heifer.pdf (object 2 0, file position 51): attempting to recover stream length | |
| 5 | 5 | qpdf: operation succeeded with warnings; resulting file may have some problems | ... | ... |
qpdf/qtest/qpdf/xref-with-short-size-recover.out
| 1 | -WARNING: xref-with-short-size.pdf: offset 16227: Cross-reference stream data has the wrong size; expected = 52; actual = 56 | |
| 1 | +WARNING: xref-with-short-size.pdf (xref stream, file position 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56 | |
| 2 | 2 | qpdf: operation succeeded with warnings; resulting file may have some problems | ... | ... |
qpdf/qtest/qpdf/xref-with-short-size.out
| 1 | -WARNING: xref-with-short-size.pdf: offset 16227: Cross-reference stream data has the wrong size; expected = 52; actual = 56 | |
| 1 | +WARNING: xref-with-short-size.pdf (xref stream, file position 16227): Cross-reference stream data has the wrong size; expected = 52; actual = 56 | |
| 2 | 2 | 1/0: compressed; stream = 5, index = 1 |
| 3 | 3 | 2/0: compressed; stream = 5, index = 0 |
| 4 | 4 | 3/0: uncompressed; offset = 15 | ... | ... |
qpdf/test_driver.cc
| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | #include <qpdf/Pl_Buffer.hh> |
| 10 | 10 | #include <qpdf/QPDFWriter.hh> |
| 11 | 11 | #include <iostream> |
| 12 | +#include <stdio.h> | |
| 12 | 13 | #include <string.h> |
| 13 | 14 | #include <stdlib.h> |
| 14 | 15 | #include <map> |
| ... | ... | @@ -319,6 +320,7 @@ void runtest(int n, char const* filename) |
| 319 | 320 | |
| 320 | 321 | int main(int argc, char* argv[]) |
| 321 | 322 | { |
| 323 | + setlinebuf(stdout); | |
| 322 | 324 | if ((whoami = strrchr(argv[0], '/')) == NULL) |
| 323 | 325 | { |
| 324 | 326 | whoami = argv[0]; | ... | ... |