Commit 3f8c4c273649c857f5a607dcbb422729fce3a166

Authored by Jay Berkenbilt
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 2009-10-18 Jay Berkenbilt <ejb@ql.org> 9 2009-10-18 Jay Berkenbilt <ejb@ql.org>
2 10
3 * If forcing version, disable object stream creation and/or 11 * If forcing version, disable object stream creation and/or
@@ -16,25 +16,10 @@ @@ -16,25 +16,10 @@
16 * Add comments for the security functions that map them back to the 16 * Add comments for the security functions that map them back to the
17 items in Adobe's products. 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 * "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic 24 * "Delphi wrapper unit 'qpdf.pas' created by Zarko Gajic
40 (http://delphi.about.com). .. use at your own risk and for whatever 25 (http://delphi.about.com). .. use at your own risk and for whatever
examples/qtest/bookmarks.test
@@ -30,7 +30,7 @@ $td-&gt;runtest(&quot;no bookmarks&quot;, @@ -30,7 +30,7 @@ $td-&gt;runtest(&quot;no bookmarks&quot;,
30 $td->runtest("bad", 30 $td->runtest("bad",
31 {$td->COMMAND => "pdf-bookmarks 3.pdf"}, 31 {$td->COMMAND => "pdf-bookmarks 3.pdf"},
32 {$td->STRING => "pdf-bookmarks processing file 3.pdf: " . 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 $td->EXIT_STATUS => 2}, 34 $td->EXIT_STATUS => 2},
35 $td->NORMALIZE_NEWLINES); 35 $td->NORMALIZE_NEWLINES);
36 36
examples/qtest/npages.test
@@ -16,7 +16,7 @@ $td-&gt;runtest(&quot;normal&quot;, @@ -16,7 +16,7 @@ $td-&gt;runtest(&quot;normal&quot;,
16 16
17 $td->runtest("error", 17 $td->runtest("error",
18 {$td->COMMAND => "pdf-npages bad"}, 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 $td->EXIT_STATUS => 2}, 20 $td->EXIT_STATUS => 2},
21 $td->NORMALIZE_NEWLINES); 21 $td->NORMALIZE_NEWLINES);
22 22
include/qpdf/QPDF.hh
@@ -371,9 +371,11 @@ class DLL_EXPORT QPDF @@ -371,9 +371,11 @@ class DLL_EXPORT QPDF
371 int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream); 371 int processXRefStream(off_t offset, QPDFObjectHandle& xref_stream);
372 void insertXrefEntry(int obj, int f0, int f1, int f2, 372 void insertXrefEntry(int obj, int f0, int f1, int f2,
373 bool overwrite = false); 373 bool overwrite = false);
  374 + void setLastObjectDescription(std::string const& description,
  375 + int objid, int generation);
374 QPDFObjectHandle readObject( 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 QPDFObjectHandle readObjectInternal( 379 QPDFObjectHandle readObjectInternal(
378 InputSource* input, int objid, int generation, 380 InputSource* input, int objid, int generation,
379 bool in_object_stream, 381 bool in_object_stream,
@@ -383,7 +385,8 @@ class DLL_EXPORT QPDF @@ -383,7 +385,8 @@ class DLL_EXPORT QPDF
383 QPDFTokenizer::Token readToken(InputSource*); 385 QPDFTokenizer::Token readToken(InputSource*);
384 386
385 QPDFObjectHandle readObjectAtOffset( 387 QPDFObjectHandle readObjectAtOffset(
386 - off_t offset, 388 + bool attempt_recovery,
  389 + off_t offset, std::string const& description,
387 int exp_objid, int exp_generation, 390 int exp_objid, int exp_generation,
388 int& act_objid, int& act_generation); 391 int& act_objid, int& act_generation);
389 PointerHolder<QPDFObject> resolve(int objid, int generation); 392 PointerHolder<QPDFObject> resolve(int objid, int generation);
@@ -734,6 +737,7 @@ class DLL_EXPORT QPDF @@ -734,6 +737,7 @@ class DLL_EXPORT QPDF
734 737
735 QPDFTokenizer tokenizer; 738 QPDFTokenizer tokenizer;
736 FileInputSource file; 739 FileInputSource file;
  740 + std::string last_object_description;
737 bool encrypted; 741 bool encrypted;
738 bool encryption_initialized; 742 bool encryption_initialized;
739 bool ignore_xref_streams; 743 bool ignore_xref_streams;
include/qpdf/QPDFExc.hh
@@ -9,15 +9,47 @@ @@ -9,15 +9,47 @@
9 #define __QPDFEXC_HH__ 9 #define __QPDFEXC_HH__
10 10
11 #include <qpdf/DLL.h> 11 #include <qpdf/DLL.h>
  12 +#include <qpdf/Constants.h>
12 #include <stdexcept> 13 #include <stdexcept>
  14 +#include <stddef.h>
13 15
14 class DLL_EXPORT QPDFExc: public std::runtime_error 16 class DLL_EXPORT QPDFExc: public std::runtime_error
15 { 17 {
16 public: 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 std::string const& message); 23 std::string const& message);
20 virtual ~QPDFExc() throw (); 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 #endif // __QPDFEXC_HH__ 55 #endif // __QPDFEXC_HH__
libqpdf/QPDF.cc
@@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length) @@ -140,7 +140,9 @@ QPDF::FileInputSource::read(char* buffer, int length)
140 size_t len = fread(buffer, 1, length, this->file); 140 size_t len = fread(buffer, 1, length, this->file);
141 if ((len == 0) && ferror(this->file)) 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 std::string("read ") + 146 std::string("read ") +
145 QUtil::int_to_string(length) + " bytes"); 147 QUtil::int_to_string(length) + " bytes");
146 } 148 }
@@ -325,7 +327,8 @@ QPDF::parse() @@ -325,7 +327,8 @@ QPDF::parse()
325 else 327 else
326 { 328 {
327 QTC::TC("qpdf", "QPDF not a pdf file"); 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 // PDF spec says %%EOF must be found within the last 1024 bytes of 334 // PDF spec says %%EOF must be found within the last 1024 bytes of
@@ -369,7 +372,8 @@ QPDF::parse() @@ -369,7 +372,8 @@ QPDF::parse()
369 if (! m2) 372 if (! m2)
370 { 373 {
371 QTC::TC("qpdf", "QPDF can't find startxref"); 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 off_t xref_offset = atoi(m2.getMatch(1).c_str()); 378 off_t xref_offset = atoi(m2.getMatch(1).c_str());
375 read_xref(xref_offset); 379 read_xref(xref_offset);
@@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -417,9 +421,28 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
417 static PCRE endobj_re("^endobj\\b"); 421 static PCRE endobj_re("^endobj\\b");
418 static PCRE trailer_re("^trailer\\b"); 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 warn(e); 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 this->file.seek(0, SEEK_END); 447 this->file.seek(0, SEEK_END);
425 off_t eof = this->file.tell(); 448 off_t eof = this->file.tell();
@@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -452,7 +475,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
452 // read "trailer" 475 // read "trailer"
453 this->file.seek(this->file.getLastOffset(), SEEK_SET); 476 this->file.seek(this->file.getLastOffset(), SEEK_SET);
454 readToken(&this->file); 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 if (! t.isDictionary()) 480 if (! t.isDictionary())
457 { 481 {
458 // Oh well. It was worth a try. 482 // Oh well. It was worth a try.
@@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e) @@ -473,7 +497,8 @@ QPDF::reconstruct_xref(QPDFExc&amp; e)
473 // with bad startxref pointers even when they have object 497 // with bad startxref pointers even when they have object
474 // streams. 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 "dictionary while recovering damaged file"); 502 "dictionary while recovering damaged file");
478 } 503 }
479 504
@@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset) @@ -513,8 +538,8 @@ QPDF::read_xref(off_t xref_offset)
513 if (size != max_obj + 1) 538 if (size != max_obj + 1)
514 { 539 {
515 QTC::TC("qpdf", "QPDF xref size mismatch"); 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 QUtil::int_to_string(size) + 543 QUtil::int_to_string(size) +
519 ") inconsistent with actual number of objects (" + 544 ") inconsistent with actual number of objects (" +
520 QUtil::int_to_string(max_obj + 1) + ")")); 545 QUtil::int_to_string(max_obj + 1) + ")"));
@@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset) @@ -542,7 +567,8 @@ QPDF::read_xrefTable(off_t xref_offset)
542 if (! m1) 567 if (! m1)
543 { 568 {
544 QTC::TC("qpdf", "QPDF invalid xref"); 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 "xref syntax invalid"); 572 "xref syntax invalid");
547 } 573 }
548 int obj = atoi(m1.getMatch(1).c_str()); 574 int obj = atoi(m1.getMatch(1).c_str());
@@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset) @@ -563,7 +589,8 @@ QPDF::read_xrefTable(off_t xref_offset)
563 { 589 {
564 QTC::TC("qpdf", "QPDF invalid xref entry"); 590 QTC::TC("qpdf", "QPDF invalid xref entry");
565 throw QPDFExc( 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 "invalid xref entry (obj=" + 594 "invalid xref entry (obj=" +
568 QUtil::int_to_string(i) + ")"); 595 QUtil::int_to_string(i) + ")");
569 } 596 }
@@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset) @@ -595,11 +622,13 @@ QPDF::read_xrefTable(off_t xref_offset)
595 } 622 }
596 623
597 // Set offset to previous xref table if any 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 if (! cur_trailer.isDictionary()) 627 if (! cur_trailer.isDictionary())
600 { 628 {
601 QTC::TC("qpdf", "QPDF missing trailer"); 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 "expected trailer dictionary"); 632 "expected trailer dictionary");
604 } 633 }
605 634
@@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset) @@ -610,13 +639,15 @@ QPDF::read_xrefTable(off_t xref_offset)
610 if (! this->trailer.hasKey("/Size")) 639 if (! this->trailer.hasKey("/Size"))
611 { 640 {
612 QTC::TC("qpdf", "QPDF trailer lacks size"); 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 "trailer dictionary lacks /Size key"); 644 "trailer dictionary lacks /Size key");
615 } 645 }
616 if (! this->trailer.getKey("/Size").isInteger()) 646 if (! this->trailer.getKey("/Size").isInteger())
617 { 647 {
618 QTC::TC("qpdf", "QPDF trailer size not integer"); 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 "/Size key in trailer dictionary is not " 651 "/Size key in trailer dictionary is not "
621 "an integer"); 652 "an integer");
622 } 653 }
@@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset) @@ -640,7 +671,8 @@ QPDF::read_xrefTable(off_t xref_offset)
640 } 671 }
641 else 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 "invalid /XRefStm"); 676 "invalid /XRefStm");
645 } 677 }
646 } 678 }
@@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset) @@ -659,7 +691,8 @@ QPDF::read_xrefTable(off_t xref_offset)
659 if (! cur_trailer.getKey("/Prev").isInteger()) 691 if (! cur_trailer.getKey("/Prev").isInteger())
660 { 692 {
661 QTC::TC("qpdf", "QPDF trailer prev not integer"); 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 "/Prev key in trailer dictionary is not " 696 "/Prev key in trailer dictionary is not "
664 "an integer"); 697 "an integer");
665 } 698 }
@@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset) @@ -685,7 +718,8 @@ QPDF::read_xrefStream(off_t xref_offset)
685 QPDFObjectHandle xref_obj; 718 QPDFObjectHandle xref_obj;
686 try 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 catch (QPDFExc& e) 724 catch (QPDFExc& e)
691 { 725 {
@@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset) @@ -705,7 +739,8 @@ QPDF::read_xrefStream(off_t xref_offset)
705 if (! found) 739 if (! found)
706 { 740 {
707 QTC::TC("qpdf", "QPDF can't find xref"); 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 return xref_offset; 746 return xref_offset;
@@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -725,7 +760,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
725 dict.getKey("/Size").isInteger() && 760 dict.getKey("/Size").isInteger() &&
726 (Index_obj.isArray() || Index_obj.isNull()))) 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 "Cross-reference stream does not have" 765 "Cross-reference stream does not have"
730 " proper /W and /Index keys"); 766 " proper /W and /Index keys");
731 } 767 }
@@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -735,7 +771,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
735 int n_index = Index_obj.getArrayNItems(); 771 int n_index = Index_obj.getArrayNItems();
736 if ((n_index % 2) || (n_index < 2)) 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 "Cross-reference stream's /Index has an" 776 "Cross-reference stream's /Index has an"
740 " invalid number of values"); 777 " invalid number of values");
741 } 778 }
@@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -747,7 +784,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
747 } 784 }
748 else 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 "Cross-reference stream's /Index's item " + 789 "Cross-reference stream's /Index's item " +
752 QUtil::int_to_string(i) + 790 QUtil::int_to_string(i) +
753 " is not an integer"); 791 " is not an integer");
@@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -785,7 +823,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
785 823
786 if (expected_size != actual_size) 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 "Cross-reference stream data has the wrong size;" 828 "Cross-reference stream data has the wrong size;"
790 " expected = " + QUtil::int_to_string(expected_size) + 829 " expected = " + QUtil::int_to_string(expected_size) +
791 "; actual = " + QUtil::int_to_string(actual_size)); 830 "; actual = " + QUtil::int_to_string(actual_size));
@@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -866,7 +905,8 @@ QPDF::processXRefStream(off_t xref_offset, QPDFObjectHandle&amp; xref_obj)
866 { 905 {
867 if (! dict.getKey("/Prev").isInteger()) 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 "/Prev key in xref stream dictionary is not " 910 "/Prev key in xref stream dictionary is not "
871 "an integer"); 911 "an integer");
872 } 912 }
@@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite) @@ -935,7 +975,8 @@ QPDF::insertXrefEntry(int obj, int f0, int f1, int f2, bool overwrite)
935 break; 975 break;
936 976
937 default: 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 "unknown xref stream entry type " + 980 "unknown xref stream entry type " +
940 QUtil::int_to_string(f0)); 981 QUtil::int_to_string(f0));
941 break; 982 break;
@@ -972,10 +1013,32 @@ QPDF::showXRefTable() @@ -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 QPDFObjectHandle 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 off_t offset = input->tell(); 1042 off_t offset = input->tell();
980 QPDFObjectHandle object = readObjectInternal( 1043 QPDFObjectHandle object = readObjectInternal(
981 input, objid, generation, in_object_stream, false, false); 1044 input, objid, generation, in_object_stream, false, false);
@@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input, @@ -1017,7 +1080,9 @@ QPDF::readObjectInternal(InputSource* input,
1017 case QPDFTokenizer::tt_brace_close: 1080 case QPDFTokenizer::tt_brace_close:
1018 // Don't know what to do with these for now 1081 // Don't know what to do with these for now
1019 QTC::TC("qpdf", "QPDF bad brace"); 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 "unexpected brace token"); 1086 "unexpected brace token");
1022 break; 1087 break;
1023 1088
@@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input, @@ -1029,7 +1094,9 @@ QPDF::readObjectInternal(InputSource* input,
1029 else 1094 else
1030 { 1095 {
1031 QTC::TC("qpdf", "QPDF bad array close"); 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 "unexpected array close token"); 1100 "unexpected array close token");
1034 } 1101 }
1035 break; 1102 break;
@@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input, @@ -1042,7 +1109,9 @@ QPDF::readObjectInternal(InputSource* input,
1042 else 1109 else
1043 { 1110 {
1044 QTC::TC("qpdf", "QPDF bad dictionary close"); 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 "unexpected dictionary close token"); 1115 "unexpected dictionary close token");
1047 } 1116 }
1048 break; 1117 break;
@@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input, @@ -1097,7 +1166,9 @@ QPDF::readObjectInternal(InputSource* input,
1097 } 1166 }
1098 else 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 "unknown token while reading object (" + 1172 "unknown token while reading object (" +
1102 value + ")"); 1173 value + ")");
1103 } 1174 }
@@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input, @@ -1116,7 +1187,9 @@ QPDF::readObjectInternal(InputSource* input,
1116 break; 1187 break;
1117 1188
1118 default: 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 "unknown token type while reading object"); 1193 "unknown token type while reading object");
1121 break; 1194 break;
1122 } 1195 }
@@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input, @@ -1153,7 +1226,8 @@ QPDF::readObjectInternal(InputSource* input,
1153 { 1226 {
1154 QTC::TC("qpdf", "QPDF dictionary odd number of elements"); 1227 QTC::TC("qpdf", "QPDF dictionary odd number of elements");
1155 throw QPDFExc( 1228 throw QPDFExc(
1156 - input->getName(), input->getLastOffset(), 1229 + qpdf_e_damaged_pdf, input->getName(),
  1230 + this->last_object_description, input->getLastOffset(),
1157 "dictionary ending here has an odd number of elements"); 1231 "dictionary ending here has an odd number of elements");
1158 } 1232 }
1159 for (unsigned int i = 0; i < olist.size(); i += 2) 1233 for (unsigned int i = 0; i < olist.size(); i += 2)
@@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input, @@ -1163,7 +1237,8 @@ QPDF::readObjectInternal(InputSource* input,
1163 if (! key_obj.isName()) 1237 if (! key_obj.isName())
1164 { 1238 {
1165 throw QPDFExc( 1239 throw QPDFExc(
1166 - input->getName(), offset, 1240 + qpdf_e_damaged_pdf,
  1241 + input->getName(), this->last_object_description, offset,
1167 std::string("dictionary key not name (") + 1242 std::string("dictionary key not name (") +
1168 key_obj.unparse() + ")"); 1243 key_obj.unparse() + ")");
1169 } 1244 }
@@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input, @@ -1209,7 +1284,8 @@ QPDF::readObjectInternal(InputSource* input,
1209 if (dict.count("/Length") == 0) 1284 if (dict.count("/Length") == 0)
1210 { 1285 {
1211 QTC::TC("qpdf", "QPDF stream without length"); 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 "stream dictionary lacks /Length key"); 1289 "stream dictionary lacks /Length key");
1214 } 1290 }
1215 1291
@@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input, @@ -1217,7 +1293,8 @@ QPDF::readObjectInternal(InputSource* input,
1217 if (! length_obj.isInteger()) 1293 if (! length_obj.isInteger())
1218 { 1294 {
1219 QTC::TC("qpdf", "QPDF stream length not integer"); 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 "/Length key in stream dictionary is not " 1298 "/Length key in stream dictionary is not "
1222 "an integer"); 1299 "an integer");
1223 } 1300 }
@@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input, @@ -1229,7 +1306,9 @@ QPDF::readObjectInternal(InputSource* input,
1229 QPDFTokenizer::tt_word, "endstream"))) 1306 QPDFTokenizer::tt_word, "endstream")))
1230 { 1307 {
1231 QTC::TC("qpdf", "QPDF missing endstream"); 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 "expected endstream"); 1312 "expected endstream");
1234 } 1313 }
1235 } 1314 }
@@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input, @@ -1267,7 +1346,8 @@ QPDF::recoverStreamLength(InputSource* input,
1267 1346
1268 // Try to reconstruct stream length by looking for 1347 // Try to reconstruct stream length by looking for
1269 // endstream(\r\n?|\n)endobj 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 "attempting to recover stream length")); 1351 "attempting to recover stream length"));
1272 1352
1273 input->seek(0, SEEK_END); 1353 input->seek(0, SEEK_END);
@@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input, @@ -1336,7 +1416,8 @@ QPDF::recoverStreamLength(InputSource* input,
1336 1416
1337 if (length == 0) 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 "unable to recover stream data"); 1421 "unable to recover stream data");
1341 } 1422 }
1342 1423
@@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input) @@ -1356,7 +1437,9 @@ QPDF::readToken(InputSource* input)
1356 char ch; 1437 char ch;
1357 if (input->read(&ch, 1) == 0) 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 else 1444 else
1362 { 1445 {
@@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input) @@ -1376,7 +1459,9 @@ QPDF::readToken(InputSource* input)
1376 1459
1377 if (token.getType() == QPDFTokenizer::tt_bad) 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 input->setLastOffset(offset); 1467 input->setLastOffset(offset);
@@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input) @@ -1385,9 +1470,12 @@ QPDF::readToken(InputSource* input)
1385 } 1470 }
1386 1471
1387 QPDFObjectHandle 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 int& objid, int& generation) 1476 int& objid, int& generation)
1390 { 1477 {
  1478 + setLastObjectDescription(description, exp_objid, exp_generation);
1391 this->file.seek(offset, SEEK_SET); 1479 this->file.seek(offset, SEEK_SET);
1392 1480
1393 QPDFTokenizer::Token tobjid = readToken(&this->file); 1481 QPDFTokenizer::Token tobjid = readToken(&this->file);
@@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, @@ -1407,7 +1495,9 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1407 if (! (objidok && genok && objok)) 1495 if (! (objidok && genok && objok))
1408 { 1496 {
1409 QTC::TC("qpdf", "QPDF expected n n obj"); 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 objid = atoi(tobjid.getValue().c_str()); 1502 objid = atoi(tobjid.getValue().c_str());
1413 generation = atoi(tgen.getValue().c_str()); 1503 generation = atoi(tgen.getValue().c_str());
@@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, @@ -1416,7 +1506,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1416 (! ((objid == exp_objid) && (generation == exp_generation)))) 1506 (! ((objid == exp_objid) && (generation == exp_generation))))
1417 { 1507 {
1418 QTC::TC("qpdf", "QPDF err wrong objid/generation"); 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 std::string("expected ") + 1511 std::string("expected ") +
1421 QUtil::int_to_string(exp_objid) + " " + 1512 QUtil::int_to_string(exp_objid) + " " +
1422 QUtil::int_to_string(exp_generation) + " obj"); 1513 QUtil::int_to_string(exp_generation) + " obj");
@@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, @@ -1424,7 +1515,7 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1424 } 1515 }
1425 catch (QPDFExc& e) 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 // Try again after reconstructing xref table 1520 // Try again after reconstructing xref table
1430 reconstruct_xref(e); 1521 reconstruct_xref(e);
@@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, @@ -1433,13 +1524,27 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1433 (this->xref_table[og].getType() == 1)) 1524 (this->xref_table[og].getType() == 1))
1434 { 1525 {
1435 off_t new_offset = this->xref_table[og].getOffset(); 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 QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset"); 1530 QTC::TC("qpdf", "QPDF recovered in readObjectAtOffset");
1441 return result; 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 else 1549 else
1445 { 1550 {
@@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, @@ -1448,13 +1553,14 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1448 } 1553 }
1449 1554
1450 QPDFObjectHandle oh = readObject( 1555 QPDFObjectHandle oh = readObject(
1451 - &this->file, objid, generation, false); 1556 + &this->file, description, objid, generation, false);
1452 1557
1453 if (! (readToken(&this->file) == 1558 if (! (readToken(&this->file) ==
1454 QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj"))) 1559 QPDFTokenizer::Token(QPDFTokenizer::tt_word, "endobj")))
1455 { 1560 {
1456 QTC::TC("qpdf", "QPDF err expected endobj"); 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 "expected endobj")); 1564 "expected endobj"));
1459 } 1565 }
1460 1566
@@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation, @@ -1487,7 +1593,8 @@ QPDF::readObjectAtOffset(off_t offset, int exp_objid, int exp_generation,
1487 } 1593 }
1488 else 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 "EOF after endobj"); 1598 "EOF after endobj");
1492 } 1599 }
1493 } 1600 }
@@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation) @@ -1526,7 +1633,7 @@ QPDF::resolve(int objid, int generation)
1526 int aobjid; 1633 int aobjid;
1527 int ageneration; 1634 int ageneration;
1528 QPDFObjectHandle oh = 1635 QPDFObjectHandle oh =
1529 - readObjectAtOffset(offset, objid, generation, 1636 + readObjectAtOffset(true, offset, "", objid, generation,
1530 aobjid, ageneration); 1637 aobjid, ageneration);
1531 } 1638 }
1532 break; 1639 break;
@@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation) @@ -1536,7 +1643,7 @@ QPDF::resolve(int objid, int generation)
1536 break; 1643 break;
1537 1644
1538 default: 1645 default:
1539 - throw QPDFExc(this->file.getName(), 0, 1646 + throw QPDFExc(qpdf_e_damaged_pdf, this->file.getName(), "", 0,
1540 "object " + 1647 "object " +
1541 QUtil::int_to_string(objid) + "/" + 1648 QUtil::int_to_string(objid) + "/" +
1542 QUtil::int_to_string(generation) + 1649 QUtil::int_to_string(generation) +
@@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1554,7 +1661,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1554 QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0); 1661 QPDFObjectHandle obj_stream = getObjectByID(obj_stream_number, 0);
1555 if (! obj_stream.isStream()) 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 "supposed object stream " + 1667 "supposed object stream " +
1559 QUtil::int_to_string(obj_stream_number) + 1668 QUtil::int_to_string(obj_stream_number) +
1560 " is not a stream"); 1669 " is not a stream");
@@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1570,7 +1679,10 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1570 if (! (dict.getKey("/Type").isName() && 1679 if (! (dict.getKey("/Type").isName() &&
1571 dict.getKey("/Type").getName() == "/ObjStm")) 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 "supposed object stream " + 1686 "supposed object stream " +
1575 QUtil::int_to_string(obj_stream_number) + 1687 QUtil::int_to_string(obj_stream_number) +
1576 " has wrong type"); 1688 " has wrong type");
@@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1579,7 +1691,9 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1579 if (! (dict.getKey("/N").isInteger() && 1691 if (! (dict.getKey("/N").isInteger() &&
1580 dict.getKey("/First").isInteger())) 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 "object stream " + 1697 "object stream " +
1584 QUtil::int_to_string(obj_stream_number) + 1698 QUtil::int_to_string(obj_stream_number) +
1585 " has incorrect keys"); 1699 " has incorrect keys");
@@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1602,7 +1716,8 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1602 if (! ((tnum.getType() == QPDFTokenizer::tt_integer) && 1716 if (! ((tnum.getType() == QPDFTokenizer::tt_integer) &&
1603 (toffset.getType() == QPDFTokenizer::tt_integer))) 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 "expected integer in object stream header"); 1721 "expected integer in object stream header");
1607 } 1722 }
1608 1723
@@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) @@ -1617,7 +1732,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number)
1617 int obj = (*iter).first; 1732 int obj = (*iter).first;
1618 int offset = (*iter).second; 1733 int offset = (*iter).second;
1619 input.seek(offset, SEEK_SET); 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 // Store in cache 1737 // Store in cache
1623 ObjGen og(obj, 0); 1738 ObjGen og(obj, 0);
@@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation, @@ -1830,17 +1945,25 @@ QPDF::pipeStreamData(int objid, int generation,
1830 size_t len = this->file.read(buf, to_read); 1945 size_t len = this->file.read(buf, to_read);
1831 if (len == 0) 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 "unexpected EOF reading stream data"); 1952 "unexpected EOF reading stream data");
1835 } 1953 }
1836 length -= len; 1954 length -= len;
1837 pipeline->write((unsigned char*)buf, len); 1955 pipeline->write((unsigned char*)buf, len);
1838 } 1956 }
1839 } 1957 }
  1958 + catch (QPDFExc& e)
  1959 + {
  1960 + warn(e);
  1961 + }
1840 catch (std::runtime_error& e) 1962 catch (std::runtime_error& e)
1841 { 1963 {
1842 QTC::TC("qpdf", "QPDF decoding error warning"); 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 "error decoding stream data for object " + 1967 "error decoding stream data for object " +
1845 QUtil::int_to_string(objid) + " " + 1968 QUtil::int_to_string(objid) + " " +
1846 QUtil::int_to_string(generation) + ": " + e.what())); 1969 QUtil::int_to_string(generation) + ": " + e.what()));
@@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages, @@ -1896,6 +2019,9 @@ QPDF::getAllPagesInternal(QPDFObjectHandle cur_pages,
1896 } 2019 }
1897 else 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 #include <qpdf/QPDFExc.hh> 1 #include <qpdf/QPDFExc.hh>
2 #include <qpdf/QUtil.hh> 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,7 +16,8 @@ QPDFXRefEntry::QPDFXRefEntry(int type, int field1, int field2) :
16 { 16 {
17 if ((type < 1) || (type > 2)) 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,7 +32,7 @@ QPDFXRefEntry::getOffset() const
31 { 32 {
32 if (this->type != 1) 33 if (this->type != 1)
33 { 34 {
34 - throw QPDFExc( 35 + throw std::logic_error(
35 "getOffset called for xref entry of type != 1"); 36 "getOffset called for xref entry of type != 1");
36 } 37 }
37 return this->field1; 38 return this->field1;
@@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const @@ -42,7 +43,7 @@ QPDFXRefEntry::getObjStreamNumber() const
42 { 43 {
43 if (this->type != 2) 44 if (this->type != 2)
44 { 45 {
45 - throw QPDFExc( 46 + throw std::logic_error(
46 "getObjStreamNumber called for xref entry of type != 2"); 47 "getObjStreamNumber called for xref entry of type != 2");
47 } 48 }
48 return this->field1; 49 return this->field1;
@@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const @@ -53,7 +54,7 @@ QPDFXRefEntry::getObjStreamIndex() const
53 { 54 {
54 if (this->type != 2) 55 if (this->type != 2)
55 { 56 {
56 - throw QPDFExc( 57 + throw std::logic_error(
57 "getObjStreamIndex called for xref entry of type != 2"); 58 "getObjStreamIndex called for xref entry of type != 2");
58 } 59 }
59 return this->field2; 60 return this->field2;
libqpdf/QPDF_Stream.cc
@@ -59,7 +59,7 @@ QPDF_Stream::getStreamData() @@ -59,7 +59,7 @@ QPDF_Stream::getStreamData()
59 Pl_Buffer buf("stream data buffer"); 59 Pl_Buffer buf("stream data buffer");
60 if (! pipeStreamData(&buf, true, false, false)) 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 return buf.getBuffer(); 64 return buf.getBuffer();
65 } 65 }
@@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters, @@ -208,8 +208,9 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
208 if (! filters_okay) 208 if (! filters_okay)
209 { 209 {
210 QTC::TC("qpdf", "QPDF_Stream invalid filter"); 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 // `filters' now contains a list of filters to be applied in 216 // `filters' now contains a list of filters to be applied in
libqpdf/QPDF_encryption.cc
@@ -337,14 +337,16 @@ QPDF::initializeEncryption() @@ -337,14 +337,16 @@ QPDF::initializeEncryption()
337 (id_obj.getArrayNItems() == 2) && 337 (id_obj.getArrayNItems() == 2) &&
338 id_obj.getArrayItem(0).isString())) 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 "invalid /ID in trailer dictionary"); 342 "invalid /ID in trailer dictionary");
342 } 343 }
343 344
344 std::string id1 = id_obj.getArrayItem(0).getStringValue(); 345 std::string id1 = id_obj.getArrayItem(0).getStringValue();
345 if (id1.length() != id_bytes) 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 "first /ID string in trailer dictionary has " 350 "first /ID string in trailer dictionary has "
349 "incorrect length"); 351 "incorrect length");
350 } 352 }
@@ -352,19 +354,23 @@ QPDF::initializeEncryption() @@ -352,19 +354,23 @@ QPDF::initializeEncryption()
352 QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt"); 354 QPDFObjectHandle encryption_dict = this->trailer.getKey("/Encrypt");
353 if (! encryption_dict.isDictionary()) 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 "/Encrypt in trailer dictionary is not a dictionary"); 360 "/Encrypt in trailer dictionary is not a dictionary");
357 } 361 }
358 362
359 if (! (encryption_dict.getKey("/Filter").isName() && 363 if (! (encryption_dict.getKey("/Filter").isName() &&
360 (encryption_dict.getKey("/Filter").getName() == "/Standard"))) 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 "unsupported encryption filter"); 368 "unsupported encryption filter");
364 } 369 }
365 if (! encryption_dict.getKey("/SubFilter").isNull()) 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 "file uses encryption SubFilters," 374 "file uses encryption SubFilters,"
369 " which qpdf does not support")); 375 " which qpdf does not support"));
370 } 376 }
@@ -375,7 +381,8 @@ QPDF::initializeEncryption() @@ -375,7 +381,8 @@ QPDF::initializeEncryption()
375 encryption_dict.getKey("/U").isString() && 381 encryption_dict.getKey("/U").isString() &&
376 encryption_dict.getKey("/P").isInteger())) 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 "some encryption dictionary parameters are missing " 386 "some encryption dictionary parameters are missing "
380 "or the wrong type"); 387 "or the wrong type");
381 } 388 }
@@ -389,7 +396,8 @@ QPDF::initializeEncryption() @@ -389,7 +396,8 @@ QPDF::initializeEncryption()
389 if (! (((R == 2) || (R == 3) || (R == 4)) && 396 if (! (((R == 2) || (R == 3) || (R == 4)) &&
390 ((V == 1) || (V == 2) || (V == 4)))) 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 "Unsupported /R or /V in encryption dictionary"); 401 "Unsupported /R or /V in encryption dictionary");
394 } 402 }
395 403
@@ -397,7 +405,8 @@ QPDF::initializeEncryption() @@ -397,7 +405,8 @@ QPDF::initializeEncryption()
397 405
398 if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) 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 "incorrect length for /O and/or /P in " 410 "incorrect length for /O and/or /P in "
402 "encryption dictionary"); 411 "encryption dictionary");
403 } 412 }
@@ -408,7 +417,8 @@ QPDF::initializeEncryption() @@ -408,7 +417,8 @@ QPDF::initializeEncryption()
408 Length = encryption_dict.getKey("/Length").getIntValue(); 417 Length = encryption_dict.getKey("/Length").getIntValue();
409 if ((Length % 8) || (Length < 40) || (Length > 128)) 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 "invalid /Length value in encryption dictionary"); 422 "invalid /Length value in encryption dictionary");
413 } 423 }
414 } 424 }
@@ -471,7 +481,8 @@ QPDF::initializeEncryption() @@ -471,7 +481,8 @@ QPDF::initializeEncryption()
471 } 481 }
472 if (this->cf_file != this->cf_stream) 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 "This document has embedded files that are" 486 "This document has embedded files that are"
476 " encrypted differently from the rest of the file." 487 " encrypted differently from the rest of the file."
477 " qpdf does not presently support this due to" 488 " qpdf does not presently support this due to"
@@ -492,7 +503,8 @@ QPDF::initializeEncryption() @@ -492,7 +503,8 @@ QPDF::initializeEncryption()
492 } 503 }
493 else 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 this->encryption_key = compute_encryption_key(this->user_password, data); 510 this->encryption_key = compute_encryption_key(this->user_password, data);
@@ -542,7 +554,9 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -542,7 +554,9 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
542 break; 554 break;
543 555
544 default: 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 "unknown encryption filter for strings" 560 "unknown encryption filter for strings"
547 " (check /StrF in /Encrypt dictionary);" 561 " (check /StrF in /Encrypt dictionary);"
548 " strings may be decrypted improperly")); 562 " strings may be decrypted improperly"));
@@ -554,28 +568,47 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation) @@ -554,28 +568,47 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
554 } 568 }
555 569
556 std::string key = getKeyForObject(objid, generation, use_aes); 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*&amp; pipeline, int objid, int generation, @@ -645,7 +678,9 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
645 678
646 default: 679 default:
647 // filter local to this stream. 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 "unknown encryption filter for streams" 684 "unknown encryption filter for streams"
650 " (check " + method_source + ");" 685 " (check " + method_source + ");"
651 " streams may be decrypted improperly")); 686 " streams may be decrypted improperly"));
libqpdf/QPDF_linearization.cc
@@ -175,7 +175,8 @@ QPDF::readLinearizationData() @@ -175,7 +175,8 @@ QPDF::readLinearizationData()
175 175
176 if (! isLinearized()) 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 // /L is read and stored in linp by isLinearized() 182 // /L is read and stored in linp by isLinearized()
@@ -193,7 +194,10 @@ QPDF::readLinearizationData() @@ -193,7 +194,10 @@ QPDF::readLinearizationData()
193 T.isInteger() && 194 T.isInteger() &&
194 (P.isInteger() || P.isNull()))) 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 "the wrong type"); 201 "the wrong type");
198 } 202 }
199 203
@@ -201,7 +205,10 @@ QPDF::readLinearizationData() @@ -201,7 +205,10 @@ QPDF::readLinearizationData()
201 unsigned int n_H_items = H.getArrayNItems(); 205 unsigned int n_H_items = H.getArrayNItems();
202 if (! ((n_H_items == 2) || (n_H_items == 4))) 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 std::vector<int> H_items; 214 std::vector<int> H_items;
@@ -214,7 +221,10 @@ QPDF::readLinearizationData() @@ -214,7 +221,10 @@ QPDF::readLinearizationData()
214 } 221 }
215 else 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&amp; pl, off_t offset, size_t length) @@ -301,13 +311,17 @@ QPDF::readHintStream(Pipeline&amp; pl, off_t offset, size_t length)
301 { 311 {
302 int obj; 312 int obj;
303 int gen; 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 ObjCache& oc = this->obj_cache[ObjGen(obj, gen)]; 316 ObjCache& oc = this->obj_cache[ObjGen(obj, gen)];
306 off_t min_end_offset = oc.end_before_space; 317 off_t min_end_offset = oc.end_before_space;
307 off_t max_end_offset = oc.end_after_space; 318 off_t max_end_offset = oc.end_after_space;
308 if (! H.isStream()) 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 QPDFObjectHandle Hdict = H.getDict(); 327 QPDFObjectHandle Hdict = H.getDict();
@@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline&amp; pl, off_t offset, size_t length) @@ -340,7 +354,10 @@ QPDF::readHintStream(Pipeline&amp; pl, off_t offset, size_t length)
340 std::cout << "expected = " << computed_end 354 std::cout << "expected = " << computed_end
341 << "; actual = " << min_end_offset << ".." 355 << "; actual = " << min_end_offset << ".."
342 << max_end_offset << std::endl; 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 H.pipeStreamData(&pl, true, false, false); 362 H.pipeStreamData(&pl, true, false, false);
346 return Hdict; 363 return Hdict;
@@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const&amp; og) @@ -651,8 +668,7 @@ QPDF::getLinearizationOffset(ObjGen const&amp; og)
651 break; 668 break;
652 669
653 default: 670 default:
654 - throw QPDFExc(  
655 - this->file.getName(), 0, 671 + throw std::logic_error(
656 "getLinearizationOffset called for xref entry not of type 1 or 2"); 672 "getLinearizationOffset called for xref entry not of type 1 or 2");
657 break; 673 break;
658 } 674 }
libqpdf/QPDF_optimization.cc
@@ -233,9 +233,12 @@ QPDF::optimizePagesTree( @@ -233,9 +233,12 @@ QPDF::optimizePagesTree(
233 { 233 {
234 if (! allow_changes) 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 // This is an inheritable resource 244 // This is an inheritable resource
@@ -338,7 +341,10 @@ QPDF::optimizePagesTree( @@ -338,7 +341,10 @@ QPDF::optimizePagesTree(
338 } 341 }
339 else 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,6 +2,7 @@
2 #include <string.h> 2 #include <string.h>
3 #include <stdlib.h> 3 #include <stdlib.h>
4 #include <fcntl.h> 4 #include <fcntl.h>
  5 +#include <stdio.h>
5 6
6 #include <qpdf/QUtil.hh> 7 #include <qpdf/QUtil.hh>
7 #include <qpdf/QTC.hh> 8 #include <qpdf/QTC.hh>
@@ -533,6 +534,7 @@ parse_encrypt_options( @@ -533,6 +534,7 @@ parse_encrypt_options(
533 int main(int argc, char* argv[]) 534 int main(int argc, char* argv[])
534 { 535 {
535 whoami = QUtil::getWhoami(argv[0]); 536 whoami = QUtil::getWhoami(argv[0]);
  537 + setlinebuf(stdout);
536 538
537 // For libtool's sake.... 539 // For libtool's sake....
538 if (strncmp(whoami, "lt-", 3) == 0) 540 if (strncmp(whoami, "lt-", 3) == 0)
@@ -892,7 +894,15 @@ int main(int argc, char* argv[]) @@ -892,7 +894,15 @@ int main(int argc, char* argv[])
892 } 894 }
893 if (show_linearization) 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 if (show_xref) 907 if (show_xref)
898 { 908 {
qpdf/qpdf.testcov
@@ -171,3 +171,5 @@ QPDFWriter forced version disabled encryption 0 @@ -171,3 +171,5 @@ QPDFWriter forced version disabled encryption 0
171 qpdf-c called qpdf_set_r4_encryption_parameters 0 171 qpdf-c called qpdf_set_r4_encryption_parameters 0
172 qpdf-c called qpdf_set_static_aes_IV 0 172 qpdf-c called qpdf_set_static_aes_IV 0
173 QPDF_encryption stream crypt filter 0 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 = (&quot;not a PDF file&quot;, # 1 @@ -191,6 +191,8 @@ my @badfiles = (&quot;not a PDF file&quot;, # 1
191 "unknown stream /Filter", # 31 191 "unknown stream /Filter", # 31
192 "obj/gen mismatch", # 32 192 "obj/gen mismatch", # 32
193 "invalid stream /Filter and xref", # 33 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 $n_tests += @badfiles + 5; 198 $n_tests += @badfiles + 5;
@@ -249,7 +251,7 @@ $n_tests += @badfiles + 8; @@ -249,7 +251,7 @@ $n_tests += @badfiles + 8;
249 # though in some cases it may. Acrobat Reader would not be able to 251 # though in some cases it may. Acrobat Reader would not be able to
250 # recover any of these files any better. 252 # recover any of these files any better.
251 my %recover_failures = (); 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 $recover_failures{$_} = 1; 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 WARNING: append-page-content-damaged.pdf: can't find startxref 2 WARNING: append-page-content-damaged.pdf: can't find startxref
3 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 version: 1.3 4 version: 1.3
5 linearized: 0 5 linearized: 0
6 encrypted: 0 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 warning: append-page-content-damaged.pdf: can't find startxref 8 warning: append-page-content-damaged.pdf: can't find startxref
9 warning: Attempting to reconstruct cross-reference table 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 WARNING: append-page-content-damaged.pdf: can't find startxref 2 WARNING: append-page-content-damaged.pdf: can't find startxref
3 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 checking append-page-content-damaged.pdf 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 WARNING: append-page-content-damaged.pdf: can't find startxref 2 WARNING: append-page-content-damaged.pdf: can't find startxref
3 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 qpdf: operation succeeded with warnings; resulting file may have some problems 4 qpdf: operation succeeded with warnings; resulting file may have some problems
qpdf/qtest/qpdf/bad1-recover.out
1 -bad1.pdf: offset 0: not a PDF file 1 +bad1.pdf: not a PDF file
qpdf/qtest/qpdf/bad1.out
1 -bad1.pdf: offset 0: not a PDF file 1 +bad1.pdf: not a PDF file
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad10.out
1 -bad10.pdf: offset 712: /Size key in trailer dictionary is not an integer 1 +bad10.pdf (trailer, file position 712): /Size key in trailer dictionary is not an integer
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad11.out
1 -bad11.pdf: offset 905: /Prev key in trailer dictionary is not an integer 1 +bad11.pdf (trailer, file position 905): /Prev key in trailer dictionary is not an integer
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad13.pdf: offset 753: unexpected brace token 1 +bad13.pdf (trailer, file position 753): unexpected brace token
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad14.pdf: offset 753: unexpected brace token 1 +bad14.pdf (trailer, file position 753): unexpected brace token
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad15.pdf: offset 753: unexpected array close token 1 +bad15.pdf (trailer, file position 753): unexpected array close token
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad16.pdf: offset 753: unexpected dictionary close token 1 +bad16.pdf (trailer, file position 753): unexpected dictionary close token
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad17.pdf: offset 753: dictionary ending here has an odd number of elements 1 +bad17.pdf (trailer, file position 753): dictionary ending here has an odd number of elements
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad18.pdf: offset 753: unexpected ) 1 +bad18.pdf (trailer, file position 753): unexpected )
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad19.pdf: offset 753: unexpected > 1 +bad19.pdf (trailer, file position 753): unexpected >
qpdf/qtest/qpdf/bad2-recover.out
1 -WARNING: bad2.pdf: offset 0: file is damaged 1 +WARNING: bad2.pdf: file is damaged
2 WARNING: bad2.pdf: can't find startxref 2 WARNING: bad2.pdf: can't find startxref
3 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad20.pdf: offset 753: invalid character (q) in hexstring 1 +bad20.pdf (trailer, file position 753): invalid character (q) in hexstring
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad21.pdf: offset 742: invalid name token 1 +bad21.pdf (trailer, file position 742): invalid name token
qpdf/qtest/qpdf/bad22-recover.out
1 -WARNING: bad22.pdf: offset 341: attempting to recover stream length 1 +WARNING: bad22.pdf (object 4 0, file position 341): attempting to recover stream length
2 /QTest is indirect 2 /QTest is indirect
3 /QTest is a stream. Dictionary: << /Qength 44 >> 3 /QTest is a stream. Dictionary: << /Qength 44 >>
4 Raw stream data: 4 Raw stream data:
qpdf/qtest/qpdf/bad22.out
1 -bad22.pdf: offset 317: stream dictionary lacks /Length key 1 +bad22.pdf (object 4 0, file position 317): stream dictionary lacks /Length key
qpdf/qtest/qpdf/bad23-recover.out
1 -WARNING: bad23.pdf: offset 341: attempting to recover stream length 1 +WARNING: bad23.pdf (object 4 0, file position 341): attempting to recover stream length
2 /QTest is indirect 2 /QTest is indirect
3 /QTest is a stream. Dictionary: << /Length () >> 3 /QTest is a stream. Dictionary: << /Length () >>
4 Raw stream data: 4 Raw stream data:
qpdf/qtest/qpdf/bad23.out
1 -bad23.pdf: offset 317: /Length key in stream dictionary is not an integer 1 +bad23.pdf (object 4 0, file position 317): /Length key in stream dictionary is not an integer
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
1 -bad24.pdf: offset 385: expected endstream 1 +bad24.pdf (object 4 0, file position 385): expected endstream
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad25.pdf: offset 307: expected n n obj 1 +bad25.pdf (object 4 0, file position 307): expected n n obj
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad26.pdf: offset 307: expected n n obj 1 +bad26.pdf (object 4 0, file position 307): expected n n obj
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad27.pdf: offset 307: expected n n obj 1 +bad27.pdf (object 4 0, file position 307): expected n n obj
qpdf/qtest/qpdf/bad28-recover.out
1 -WARNING: bad28.pdf: offset 395: expected endobj 1 +WARNING: bad28.pdf (object 4 0, file position 395): expected endobj
2 /QTest is indirect 2 /QTest is indirect
3 /QTest is a stream. Dictionary: << /Length 44 >> 3 /QTest is a stream. Dictionary: << /Length 44 >>
4 Raw stream data: 4 Raw stream data:
qpdf/qtest/qpdf/bad28.out
1 -WARNING: bad28.pdf: offset 395: expected endobj 1 +WARNING: bad28.pdf (object 4 0, file position 395): expected endobj
2 /QTest is indirect 2 /QTest is indirect
3 /QTest is a stream. Dictionary: << /Length 44 >> 3 /QTest is a stream. Dictionary: << /Length 44 >>
4 Raw stream data: 4 Raw stream data:
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 WARNING: Attempting to reconstruct cross-reference table 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
1 -bad29.pdf: offset 742: null character not allowed in name token 1 +bad29.pdf (trailer, file position 742): null character not allowed in name token
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad3.out
1 -bad3.pdf: offset 542: xref not found 1 +bad3.pdf (file position 542): xref not found
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 WARNING: Attempting to reconstruct cross-reference table 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 /QTest is implicit 5 /QTest is implicit
5 /QTest is indirect 6 /QTest is indirect
6 /QTest is null 7 /QTest is null
qpdf/qtest/qpdf/bad32.out
1 -bad32.pdf: offset 307: expected 4 0 obj 1 +bad32.pdf (object 4 0, file position 307): expected 4 0 obj
qpdf/qtest/qpdf/bad33-recover.out
No preview for this file type
qpdf/qtest/qpdf/bad33.out
1 -bad33.pdf: offset 1771: xref not found 1 +bad33.pdf (file position 1771): xref not found
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 + /PDF
  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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad4.out
1 -bad4.pdf: offset 547: xref syntax invalid 1 +bad4.pdf (xref table, file position 547): xref syntax invalid
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad5.out
1 -bad5.pdf: offset 591: invalid xref entry (obj=2) 1 +bad5.pdf (xref table, file position 591): invalid xref entry (obj=2)
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 bad7.pdf: unable to find trailer dictionary while recovering damaged file 4 bad7.pdf: unable to find trailer dictionary while recovering damaged file
qpdf/qtest/qpdf/bad7.out
1 -bad7.pdf: offset 698: expected trailer dictionary 1 +bad7.pdf (file position 698): expected trailer dictionary
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad8.out
1 -bad8.pdf: offset 543: xref not found 1 +bad8.pdf (file position 543): xref not found
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 WARNING: Attempting to reconstruct cross-reference table 3 WARNING: Attempting to reconstruct cross-reference table
4 /QTest is implicit 4 /QTest is implicit
5 /QTest is direct 5 /QTest is direct
qpdf/qtest/qpdf/bad9.out
1 -bad9.pdf: offset 712: trailer dictionary lacks /Size key 1 +bad9.pdf (trailer, file position 712): trailer dictionary lacks /Size key
qpdf/qtest/qpdf/c-no-recovery.out
1 -error: bad33.pdf: offset 1771: xref not found 1 +error: bad33.pdf (file position 1771): xref not found
qpdf/qtest/qpdf/c-read-errors.out
1 -error: bad1.pdf: offset 0: not a PDF file 1 +error: bad1.pdf: not a PDF file
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 warning: Attempting to reconstruct cross-reference table 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
1 -warning: append-page-content-damaged.pdf: offset 0: file is damaged 1 +warning: append-page-content-damaged.pdf: file is damaged
2 warning: append-page-content-damaged.pdf: can't find startxref 2 warning: append-page-content-damaged.pdf: can't find startxref
3 warning: Attempting to reconstruct cross-reference table 3 warning: Attempting to reconstruct cross-reference table
qpdf/qtest/qpdf/c-write-errors.out
1 -error: bad30.pdf: offset 629: invalid filter object type for this stream 1 +error: bad30.pdf (file position 629): stream filter type is not name or array
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 warning: Attempting to reconstruct cross-reference table 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,4 +2,4 @@ checking damaged-stream.pdf
2 PDF Version: 1.3 2 PDF Version: 1.3
3 File is not encrypted 3 File is not encrypted
4 File is not linearized 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 WARNING: Attempting to reconstruct cross-reference table 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 qpdf: operation succeeded with warnings; resulting file may have some problems 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 qpdf: operation succeeded with warnings; resulting file may have some problems 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 1/0: compressed; stream = 5, index = 1 2 1/0: compressed; stream = 5, index = 1
3 2/0: compressed; stream = 5, index = 0 3 2/0: compressed; stream = 5, index = 0
4 3/0: uncompressed; offset = 15 4 3/0: uncompressed; offset = 15
qpdf/test_driver.cc
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 #include <qpdf/Pl_Buffer.hh> 9 #include <qpdf/Pl_Buffer.hh>
10 #include <qpdf/QPDFWriter.hh> 10 #include <qpdf/QPDFWriter.hh>
11 #include <iostream> 11 #include <iostream>
  12 +#include <stdio.h>
12 #include <string.h> 13 #include <string.h>
13 #include <stdlib.h> 14 #include <stdlib.h>
14 #include <map> 15 #include <map>
@@ -319,6 +320,7 @@ void runtest(int n, char const* filename) @@ -319,6 +320,7 @@ void runtest(int n, char const* filename)
319 320
320 int main(int argc, char* argv[]) 321 int main(int argc, char* argv[])
321 { 322 {
  323 + setlinebuf(stdout);
322 if ((whoami = strrchr(argv[0], '/')) == NULL) 324 if ((whoami = strrchr(argv[0], '/')) == NULL)
323 { 325 {
324 whoami = argv[0]; 326 whoami = argv[0];