Blob Blame History Raw
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index e30e467..c20aa75 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -522,6 +522,18 @@ class QPDF
     };
     friend class Resolver;
 
+    // Warner class allows QPDFObjectHandle to create warnings
+    class Warner
+    {
+       friend class QPDFObjectHandle;
+      private:
+        static void warn(QPDF* qpdf, QPDFExc const& e)
+        {
+            qpdf->warn(e);
+        }
+    };
+    friend class Warner;
+
     // Pipe class is restricted to QPDF_Stream
     class Pipe
     {
diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh
index 8140005..e5cabd0 100644
--- a/include/qpdf/QPDFObjectHandle.hh
+++ b/include/qpdf/QPDFObjectHandle.hh
@@ -28,6 +28,7 @@ class QPDF;
 class QPDF_Dictionary;
 class QPDF_Array;
 class QPDFTokenizer;
+class QPDFExc;
 
 class QPDFObjectHandle
 {
@@ -623,6 +624,9 @@ class QPDFObjectHandle
     static void parseContentStream_internal(
         QPDFObjectHandle stream, ParserCallbacks* callbacks);
 
+    // Other methods
+    static void warn(QPDF*, QPDFExc const&);
+
     bool initialized;
 
     QPDF* qpdf;			// 0 for direct object
diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh
index cbdc065..9acea97 100644
--- a/include/qpdf/QUtil.hh
+++ b/include/qpdf/QUtil.hh
@@ -125,6 +125,9 @@ namespace QUtil
 
     QPDF_DLL
     void initializeWithRandomBytes(unsigned char* data, size_t len);
+
+    QPDF_DLL
+    bool is_space(char);
 };
 
 #endif // __QUTIL_HH__
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index cae7a7c..245f301 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -1084,6 +1084,11 @@ QPDF::readObject(PointerHolder<InputSource> input,
                 else
                 {
                     QTC::TC("qpdf", "QPDF stream without newline");
+                    if (! QUtil::is_space(ch))
+                    {
+                        QTC::TC("qpdf", "QPDF stream with non-space");
+                        input->unreadCh(ch);
+                    }
                     warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(),
                                  this->last_object_description,
                                  input->tell(),
@@ -1274,9 +1280,9 @@ QPDF::recoverStreamLength(PointerHolder<InputSource> input,
 
     if (length == 0)
     {
-	throw QPDFExc(qpdf_e_damaged_pdf, input->getName(),
+	warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(),
 		      this->last_object_description, stream_offset,
-		      "unable to recover stream data");
+		      "unable to recover stream data; treating stream as empty"));
     }
 
     QTC::TC("qpdf", "QPDF recovered stream length");
@@ -1459,31 +1465,41 @@ QPDF::resolve(int objid, int generation)
 	}
 
 	QPDFXRefEntry const& entry = this->xref_table[og];
-	switch (entry.getType())
-	{
-	  case 1:
-	    {
-		qpdf_offset_t offset = entry.getOffset();
-		// Object stored in cache by readObjectAtOffset
-		int aobjid;
-		int ageneration;
-		QPDFObjectHandle oh =
-		    readObjectAtOffset(true, offset, "", objid, generation,
-				       aobjid, ageneration);
-	    }
-	    break;
+        try
+        {
+            switch (entry.getType())
+            {
+              case 1:
+                {
+                    qpdf_offset_t offset = entry.getOffset();
+                    // Object stored in cache by readObjectAtOffset
+                    int aobjid;
+                    int ageneration;
+                    QPDFObjectHandle oh =
+                        readObjectAtOffset(true, offset, "", objid, generation,
+                                           aobjid, ageneration);
+                }
+                break;
 
-	  case 2:
-	    resolveObjectsInStream(entry.getObjStreamNumber());
-	    break;
+              case 2:
+                resolveObjectsInStream(entry.getObjStreamNumber());
+                break;
 
-	  default:
-	    throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), "", 0,
-			  "object " +
-			  QUtil::int_to_string(objid) + "/" +
-			  QUtil::int_to_string(generation) +
-			  " has unexpected xref entry type");
-	}
+              default:
+                throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), "", 0,
+                              "object " +
+                              QUtil::int_to_string(objid) + "/" +
+                              QUtil::int_to_string(generation) +
+                              " has unexpected xref entry type");
+            }
+        }
+        catch (QPDFExc& e)
+        {
+            warn(e);
+            QPDFObjectHandle oh = QPDFObjectHandle::newNull();
+            this->obj_cache[og] =
+                ObjCache(QPDFObjectHandle::ObjAccessor::getObject(oh), -1, -1);
+        }
     }
 
     return this->obj_cache[og].object;
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index eec4fae..55f8099 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -1533,3 +1533,20 @@ QPDFObjectHandle::dereference()
         }
     }
 }
+
+void
+QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e)
+{
+    // If parsing on behalf of a QPDF object and want to give a
+    // warning, we can warn through the object. If parsing for some
+    // other reason, such as an explicit creation of an object from a
+    // string, then just throw the exception.
+    if (qpdf)
+    {
+        QPDF::Warner::warn(qpdf, e);
+    }
+    else
+    {
+        throw e;
+    }
+}
diff --git a/libqpdf/QPDFTokenizer.cc b/libqpdf/QPDFTokenizer.cc
index 91b1682..8ed6afc 100644
--- a/libqpdf/QPDFTokenizer.cc
+++ b/libqpdf/QPDFTokenizer.cc
@@ -7,6 +7,7 @@
 #include <qpdf/PCRE.hh>
 #include <qpdf/QTC.hh>
 #include <qpdf/QPDFExc.hh>
+#include <qpdf/QUtil.hh>
 
 #include <stdexcept>
 #include <string.h>
@@ -16,10 +17,6 @@ static bool is_hex_digit(char ch)
 {
     return (strchr("0123456789abcdefABCDEF", ch) != 0);
 }
-static bool is_space(char ch)
-{
-    return (strchr(" \f\n\r\t\v", ch) != 0);
-}
 
 QPDFTokenizer::QPDFTokenizer() :
     pound_special_in_name(true),
@@ -511,7 +508,7 @@ QPDFTokenizer::readToken(PointerHolder<InputSource> input,
 	}
 	else
 	{
-	    if (is_space(static_cast<unsigned char>(ch)) &&
+            if (QUtil::is_space(static_cast<unsigned char>(ch)) &&
 		(input->getLastOffset() == offset))
 	    {
 		++offset;
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index cf45506..5f9c608 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -490,3 +490,9 @@ QUtil::srandom(unsigned int seed)
     srand(seed);
 #endif
 }
+
+bool
+QUtil::is_space(char ch)
+{
+    return (strchr(" \f\n\r\t\v", ch) != 0);
+}
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index e78a4f4..85ddef3 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -268,3 +268,4 @@ QPDF xref space 2
 qpdf pages range omitted at end 0
 qpdf pages range omitted in middle 0
 qpdf npages 0
+QPDF stream with non-space 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 3b7ede5..354445b 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -435,7 +435,7 @@ $td->runtest("EOF terminating literal tokens",
              $td->NORMALIZE_NEWLINES);
 $td->runtest("EOF reading token",
              {$td->COMMAND => "qpdf --check eof-reading-token.pdf"},
-             {$td->FILE => "eof-reading-token.out", $td->EXIT_STATUS => 2},
+             {$td->FILE => "eof-reading-token.out", $td->EXIT_STATUS => 3},
              $td->NORMALIZE_NEWLINES);
 $td->runtest("extra header text",
              {$td->COMMAND => "test_driver 32 minimal.pdf"},
@@ -739,7 +739,9 @@ $n_tests += @badfiles + 5;
 # neither Acrobat nor other PDF viewers really care.  Tests 12 and 28
 # have error conditions that used to be fatal but are now considered
 # non-fatal.
-my %badtest_overrides = (6 => 0, 12 => 0, 28 => 0, 31 => 0);
+my %badtest_overrides = (6 => 0, 12 => 0, 22 => 0, 23 => 0, 24 => 0,
+                         25 => 0, 26 => 0, 27 => 0, 28 => 0, 31 => 0,
+                         32 => 0, 34 => 0);
 for (my $i = 1; $i <= scalar(@badfiles); ++$i)
 {
     my $status = $badtest_overrides{$i};
@@ -788,7 +790,7 @@ $n_tests += @badfiles + 8;
 # though in some cases it may.  Acrobat Reader would not be able to
 # recover any of these files any better.
 my %recover_failures = ();
-for (1, 7, 13..21, 24, 29..30, 33, 35)
+for (1, 7, 13..21, 29..30, 33, 35)
 {
     $recover_failures{$_} = 1;
 }
diff --git a/qpdf/qtest/qpdf/bad22.out b/qpdf/qtest/qpdf/bad22.out
index ec6d5f8..73d081e 100644
--- a/qpdf/qtest/qpdf/bad22.out
+++ b/qpdf/qtest/qpdf/bad22.out
@@ -1 +1,7 @@
-bad22.pdf (object 4 0, file position 314): stream dictionary lacks /Length key
+WARNING: bad22.pdf (object 4 0, file position 314): stream dictionary lacks /Length key
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad23.out b/qpdf/qtest/qpdf/bad23.out
index b4cf25e..9bc1bdc 100644
--- a/qpdf/qtest/qpdf/bad23.out
+++ b/qpdf/qtest/qpdf/bad23.out
@@ -1 +1,7 @@
-bad23.pdf (object 4 0, file position 314): /Length key in stream dictionary is not an integer
+WARNING: bad23.pdf (object 4 0, file position 314): /Length key in stream dictionary is not an integer
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad24-recover.out b/qpdf/qtest/qpdf/bad24-recover.out
index 0af01e9..d1bbe73 100644
--- a/qpdf/qtest/qpdf/bad24-recover.out
+++ b/qpdf/qtest/qpdf/bad24-recover.out
@@ -1,2 +1,9 @@
 WARNING: bad24.pdf (object 4 0, file position 341): attempting to recover stream length
-bad24.pdf (object 4 0, file position 341): unable to recover stream data
+WARNING: bad24.pdf (object 4 0, file position 341): unable to recover stream data; treating stream as empty
+WARNING: bad24.pdf (object 4 0, file position 778): EOF while reading token
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 1 done
diff --git a/qpdf/qtest/qpdf/bad24.out b/qpdf/qtest/qpdf/bad24.out
index f503214..0a4dc8e 100644
--- a/qpdf/qtest/qpdf/bad24.out
+++ b/qpdf/qtest/qpdf/bad24.out
@@ -1 +1,7 @@
-bad24.pdf (object 4 0, file position 385): expected endstream
+WARNING: bad24.pdf (object 4 0, file position 385): expected endstream
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad25.out b/qpdf/qtest/qpdf/bad25.out
index f336b34..d6b13e4 100644
--- a/qpdf/qtest/qpdf/bad25.out
+++ b/qpdf/qtest/qpdf/bad25.out
@@ -1 +1,7 @@
-bad25.pdf (object 4 0, file position 307): expected n n obj
+WARNING: bad25.pdf (object 4 0, file position 307): expected n n obj
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad26.out b/qpdf/qtest/qpdf/bad26.out
index 30c3b72..48263e5 100644
--- a/qpdf/qtest/qpdf/bad26.out
+++ b/qpdf/qtest/qpdf/bad26.out
@@ -1 +1,7 @@
-bad26.pdf (object 4 0, file position 307): expected n n obj
+WARNING: bad26.pdf (object 4 0, file position 307): expected n n obj
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad27.out b/qpdf/qtest/qpdf/bad27.out
index 2c494e4..4f38cca 100644
--- a/qpdf/qtest/qpdf/bad27.out
+++ b/qpdf/qtest/qpdf/bad27.out
@@ -1 +1,7 @@
-bad27.pdf (object 4 0, file position 307): expected n n obj
+WARNING: bad27.pdf (object 4 0, file position 307): expected n n obj
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad32.out b/qpdf/qtest/qpdf/bad32.out
index 60727cc..9b37770 100644
--- a/qpdf/qtest/qpdf/bad32.out
+++ b/qpdf/qtest/qpdf/bad32.out
@@ -1 +1,7 @@
-bad32.pdf (object 4 0, file position 307): expected 4 0 obj
+WARNING: bad32.pdf (object 4 0, file position 307): expected 4 0 obj
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad34.out b/qpdf/qtest/qpdf/bad34.out
index ee65e14..7ba7916 100644
--- a/qpdf/qtest/qpdf/bad34.out
+++ b/qpdf/qtest/qpdf/bad34.out
@@ -1 +1,7 @@
-bad34.pdf (object 4 0, file position 322): expected n n obj
+WARNING: bad34.pdf (object 4 0, file position 322): expected n n obj
+/QTest is implicit
+/QTest is indirect and has type null (2)
+/QTest is null
+unparse: 4 0 R
+unparseResolved: null
+test 0 done
diff --git a/qpdf/qtest/qpdf/bad35-recover.out b/qpdf/qtest/qpdf/bad35-recover.out
index add1666..db7ff5d 100644
--- a/qpdf/qtest/qpdf/bad35-recover.out
+++ b/qpdf/qtest/qpdf/bad35-recover.out
@@ -1 +1,2 @@
-bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type
+WARNING: bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type
+operation for Dictionary object attempted on object of wrong type
diff --git a/qpdf/qtest/qpdf/bad35.out b/qpdf/qtest/qpdf/bad35.out
index add1666..db7ff5d 100644
--- a/qpdf/qtest/qpdf/bad35.out
+++ b/qpdf/qtest/qpdf/bad35.out
@@ -1 +1,2 @@
-bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type
+WARNING: bad35.pdf (object 1 0, file position 521): supposed object stream 1 has wrong type
+operation for Dictionary object attempted on object of wrong type
diff --git a/qpdf/qtest/qpdf/eof-reading-token.out b/qpdf/qtest/qpdf/eof-reading-token.out
index 58e5b09..fefc516 100644
--- a/qpdf/qtest/qpdf/eof-reading-token.out
+++ b/qpdf/qtest/qpdf/eof-reading-token.out
@@ -2,4 +2,4 @@ checking eof-reading-token.pdf
 PDF Version: 1.3
 File is not encrypted
 File is not linearized
-object stream 12 (file position 5): EOF while reading token
+WARNING: object stream 12 (file position 5): EOF while reading token
diff --git a/qpdf/qtest/qpdf/stream-line-enders.out b/qpdf/qtest/qpdf/stream-line-enders.out
index b7a7513..1932771 100644
--- a/qpdf/qtest/qpdf/stream-line-enders.out
+++ b/qpdf/qtest/qpdf/stream-line-enders.out
@@ -1,3 +1,4 @@
-WARNING: stream-line-enders.pdf (object 5 0, file position 378): stream keyword followed by carriage return only
-WARNING: stream-line-enders.pdf (object 6 0, file position 437): stream keyword not followed by proper line terminator
+WARNING: stream-line-enders.pdf (object 5 0, file position 384): stream keyword followed by carriage return only
+WARNING: stream-line-enders.pdf (object 6 0, file position 443): stream keyword not followed by proper line terminator
+WARNING: stream-line-enders.pdf (object 7 0, file position 503): stream keyword not followed by proper line terminator
 qpdf: operation succeeded with warnings; resulting file may have some problems
diff --git a/qpdf/qtest/qpdf/stream-line-enders.pdf b/qpdf/qtest/qpdf/stream-line-enders.pdf
index e623888..2e05caa 100644
--- a/qpdf/qtest/qpdf/stream-line-enders.pdf
+++ b/qpdf/qtest/qpdf/stream-line-enders.pdf
@@ -7,7 +7,7 @@ endobj
 << /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
 endobj
 3 0 obj
-<< /Contents [ 4 0 R 5 0 R 6 0 R ] /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 7 0 R >> /ProcSet 8 0 R >> /Type /Page >>
+<< /Contents [ 4 0 R 5 0 R 6 0 R 7 0 R ] /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 8 0 R >> /ProcSet 9 0 R >> /Type /Page >>
 endobj
 4 0 obj
 << /Length 14 >>
@@ -22,29 +22,35 @@ stream
72 720 Td
 endstream
 endobj
 6 0 obj
-<< /Length 15 >>
+<< /Length 12 >>
 stream (Potato) Tj
-ET
 endstream
 endobj
 7 0 obj
-<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
+<< /Length 11 >>
+stream%comment
+ET
+endstream
 endobj
 8 0 obj
+<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
+endobj
+9 0 obj
 [ /PDF /Text ]
 endobj
 xref
-0 9
+0 10
 0000000000 65535 f 
 0000000015 00000 n 
 0000000064 00000 n 
 0000000123 00000 n 
-0000000282 00000 n 
-0000000346 00000 n 
-0000000405 00000 n 
-0000000469 00000 n 
-0000000576 00000 n 
-trailer << /Root 1 0 R /Size 9 /ID [<08aa98c73f8a7262d77c8328772c3989><7b1f32865e2165debe277f27ee790092>] >>
+0000000288 00000 n 
+0000000352 00000 n 
+0000000411 00000 n 
+0000000472 00000 n 
+0000000532 00000 n 
+0000000639 00000 n 
+trailer << /Root 1 0 R /Size 10 /ID [<08aa98c73f8a7262d77c8328772c3989><5a35fde138b2e8251b9e079b560c0253>] >>
 startxref
-606
+669
 %%EOF
diff --git a/qpdf/qtest/qpdf/stream-line-enders.qdf b/qpdf/qtest/qpdf/stream-line-enders.qdf
index 3353602..aa14901 100644
--- a/qpdf/qtest/qpdf/stream-line-enders.qdf
+++ b/qpdf/qtest/qpdf/stream-line-enders.qdf
@@ -29,6 +29,7 @@ endobj
     4 0 R
     6 0 R
     8 0 R
+    10 0 R
   ]
   /MediaBox [
     0
@@ -39,9 +40,9 @@ endobj
   /Parent 2 0 R
   /Resources <<
     /Font <<
-      /F1 10 0 R
+      /F1 12 0 R
     >>
-    /ProcSet 11 0 R
+    /ProcSet 13 0 R
   >>
   /Type /Page
 >>
@@ -86,16 +87,31 @@ endobj
 >>
 stream
 (Potato) Tj
-ET
 endstream
 endobj
 
 9 0 obj
-15
+12
 endobj
 
+%% Contents for page 1
 %% Original object ID: 7 0
 10 0 obj
+<<
+  /Length 11 0 R
+>>
+stream
+%comment
+ET
+endstream
+endobj
+
+11 0 obj
+12
+endobj
+
+%% Original object ID: 8 0
+12 0 obj
 <<
   /BaseFont /Helvetica
   /Encoding /WinAnsiEncoding
@@ -105,8 +121,8 @@ endobj
 >>
 endobj
 
-%% Original object ID: 8 0
-11 0 obj
+%% Original object ID: 9 0
+13 0 obj
 [
   /PDF
   /Text
@@ -114,24 +130,26 @@ endobj
 endobj
 
 xref
-0 12
+0 14
 0000000000 65535 f 
 0000000052 00000 n 
 0000000133 00000 n 
 0000000242 00000 n 
-0000000516 00000 n 
-0000000585 00000 n 
-0000000654 00000 n 
-0000000719 00000 n 
-0000000788 00000 n 
-0000000858 00000 n 
-0000000904 00000 n 
-0000001050 00000 n 
+0000000527 00000 n 
+0000000596 00000 n 
+0000000665 00000 n 
+0000000730 00000 n 
+0000000799 00000 n 
+0000000866 00000 n 
+0000000935 00000 n 
+0000001004 00000 n 
+0000001051 00000 n 
+0000001197 00000 n 
 trailer <<
   /Root 1 0 R
-  /Size 12
+  /Size 14
   /ID [<08aa98c73f8a7262d77c8328772c3989><31415926535897932384626433832795>]
 >>
 startxref
-1086
+1233
 %%EOF