From 2bf7359e3559f7a6453eb0c444b93e17a2a5a405 Mon Sep 17 00:00:00 2001 From: m-holger Date: Fri, 7 Jul 2023 20:14:59 +0100 Subject: Add new private method QPDF::readObjectInStream --- include/qpdf/QPDF.hh | 1 + libqpdf/QPDF.cc | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index e15b4443..beb98f0b 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1011,6 +1011,7 @@ class QPDF std::string const& description, QPDFObjGen const& og, bool in_object_stream); + QPDFObjectHandle readObjectInStream(std::shared_ptr, QPDFObjGen og); size_t recoverStreamLength( std::shared_ptr input, QPDFObjGen const& og, qpdf_offset_t stream_offset); QPDFTokenizer::Token readToken(std::shared_ptr, size_t max_len = 0); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 9554027c..cfa22730 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1389,6 +1389,26 @@ QPDF::readObject( return object; } +QPDFObjectHandle +QPDF::readObjectInStream(std::shared_ptr input, QPDFObjGen og) +{ + setLastObjectDescription("", og); + qpdf_offset_t offset = input->tell(); + + bool empty = false; + auto object = QPDFParser(input, m->last_object_description, m->tokenizer, nullptr, this) + .parse(empty, false); + if (empty) { + // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in + // actual PDF files and Adobe Reader appears to ignore them. + warn(damagedPDF(input, input->getLastOffset(), "empty object treated as null")); + } + + // Override last_offset so that it points to the beginning of the object we just read + input->setLastOffset(offset); + return object; +} + bool QPDF::findEndstream() { @@ -1750,7 +1770,7 @@ QPDF::resolveObjectsInStream(int obj_stream_number) if ((entry.getType() == 2) && (entry.getObjStreamNumber() == obj_stream_number)) { int offset = iter.second; input->seek(offset, SEEK_SET); - QPDFObjectHandle oh = readObject(input, "", og, true); + QPDFObjectHandle oh = readObjectInStream(input, og); updateCache(og, oh.getObj(), end_before_space, end_after_space); } else { QTC::TC("qpdf", "QPDF not caching overridden objstm object"); -- cgit v1.2.3-54-g00ecf From bee726609c9b0c3f6bc497de9ab0fc0790993c89 Mon Sep 17 00:00:00 2001 From: m-holger Date: Fri, 7 Jul 2023 20:37:13 +0100 Subject: Remove redundant parameters from QPDF::readObject --- include/qpdf/QPDF.hh | 6 +---- libqpdf/QPDF.cc | 68 ++++++++++++++++++++++------------------------------ 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index beb98f0b..afe9e8ec 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1006,11 +1006,7 @@ class QPDF void insertFreeXrefEntry(QPDFObjGen); void insertReconstructedXrefEntry(int obj, qpdf_offset_t f1, int f2); void setLastObjectDescription(std::string const& description, QPDFObjGen const& og); - QPDFObjectHandle readObject( - std::shared_ptr, - std::string const& description, - QPDFObjGen const& og, - bool in_object_stream); + QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og); QPDFObjectHandle readObjectInStream(std::shared_ptr, QPDFObjGen og); size_t recoverStreamLength( std::shared_ptr input, QPDFObjGen const& og, qpdf_offset_t stream_offset); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index cfa22730..3a486457 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -567,7 +567,7 @@ QPDF::reconstruct_xref(QPDFExc& e) insertReconstructedXrefEntry(obj, token_start, gen); } } else if (!m->trailer.isInitialized() && t1.isWord("trailer")) { - QPDFObjectHandle t = readObject(m->file, "trailer", QPDFObjGen(), false); + QPDFObjectHandle t = readObject("trailer", QPDFObjGen()); if (!t.isDictionary()) { // Oh well. It was worth a try. } else { @@ -855,7 +855,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) } // Set offset to previous xref table if any - QPDFObjectHandle cur_trailer = readObject(m->file, "trailer", QPDFObjGen(), false); + QPDFObjectHandle cur_trailer = readObject("trailer", QPDFObjGen()); if (!cur_trailer.isDictionary()) { QTC::TC("qpdf", "QPDF missing trailer"); throw damagedPDF("", "expected trailer dictionary"); @@ -1268,32 +1268,28 @@ QPDF::setLastObjectDescription(std::string const& description, QPDFObjGen const& } QPDFObjectHandle -QPDF::readObject( - std::shared_ptr input, - std::string const& description, - QPDFObjGen const& og, - bool in_object_stream) +QPDF::readObject(std::string const& description, QPDFObjGen og) { setLastObjectDescription(description, og); - qpdf_offset_t offset = input->tell(); + qpdf_offset_t offset = m->file->tell(); bool empty = false; std::shared_ptr decrypter_ph; StringDecrypter* decrypter = nullptr; - if (m->encp->encrypted && (!in_object_stream)) { + if (m->encp->encrypted) { decrypter_ph = std::make_shared(this, og); decrypter = decrypter_ph.get(); } - auto object = QPDFParser(input, m->last_object_description, m->tokenizer, decrypter, this) + auto object = QPDFParser(m->file, m->last_object_description, m->tokenizer, decrypter, this) .parse(empty, false); if (empty) { // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in // actual PDF files and Adobe Reader appears to ignore them. - warn(damagedPDF(input, input->getLastOffset(), "empty object treated as null")); - } else if (object.isDictionary() && (!in_object_stream)) { + warn(damagedPDF(m->file, m->file->getLastOffset(), "empty object treated as null")); + } else if (object.isDictionary()) { // check for stream - qpdf_offset_t cur_offset = input->tell(); - if (readToken(input).isWord("stream")) { + qpdf_offset_t cur_offset = m->file->tell(); + if (readToken(m->file).isWord("stream")) { // The PDF specification states that the word "stream" should be followed by either a // carriage return and a newline or by a newline alone. It specifically disallowed // following it by a carriage return alone since, in that case, there would be no way to @@ -1305,7 +1301,7 @@ QPDF::readObject( while (!done) { done = true; char ch; - if (input->read(&ch, 1) == 0) { + if (m->file->read(&ch, 1) == 0) { // A premature EOF here will result in some other problem that will get reported // at another time. } else if (ch == '\n') { @@ -1313,7 +1309,7 @@ QPDF::readObject( QTC::TC("qpdf", "QPDF stream with NL only"); } else if (ch == '\r') { // Read another character - if (input->read(&ch, 1) != 0) { + if (m->file->read(&ch, 1) != 0) { if (ch == '\n') { // Ready to read stream data QTC::TC("qpdf", "QPDF stream with CRNL"); @@ -1321,32 +1317,27 @@ QPDF::readObject( // Treat the \r by itself as the whitespace after endstream and start // reading stream data in spite of not having seen a newline. QTC::TC("qpdf", "QPDF stream with CR only"); - input->unreadCh(ch); + m->file->unreadCh(ch); warn(damagedPDF( - input, - input->tell(), - "stream keyword followed by carriage return " - "only")); + m->file->tell(), + "stream keyword followed by carriage return only")); } } } else if (QUtil::is_space(ch)) { warn(damagedPDF( - input, input->tell(), "stream keyword followed by extraneous whitespace")); + m->file->tell(), "stream keyword followed by extraneous whitespace")); done = false; } else { QTC::TC("qpdf", "QPDF stream without newline"); - input->unreadCh(ch); + m->file->unreadCh(ch); warn(damagedPDF( - input, - input->tell(), - "stream keyword not followed by proper line " - "terminator")); + m->file->tell(), "stream keyword not followed by proper line terminator")); } } // Must get offset before accessing any additional objects since resolving a previously // unresolved indirect object will change file position. - qpdf_offset_t stream_offset = input->tell(); + qpdf_offset_t stream_offset = m->file->tell(); size_t length = 0; try { @@ -1355,37 +1346,36 @@ QPDF::readObject( if (!length_obj.isInteger()) { if (length_obj.isNull()) { QTC::TC("qpdf", "QPDF stream without length"); - throw damagedPDF(input, offset, "stream dictionary lacks /Length key"); + throw damagedPDF(offset, "stream dictionary lacks /Length key"); } QTC::TC("qpdf", "QPDF stream length not integer"); - throw damagedPDF( - input, offset, "/Length key in stream dictionary is not an integer"); + throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); } length = toS(length_obj.getUIntValue()); // Seek in two steps to avoid potential integer overflow - input->seek(stream_offset, SEEK_SET); - input->seek(toO(length), SEEK_CUR); - if (!readToken(input).isWord("endstream")) { + m->file->seek(stream_offset, SEEK_SET); + m->file->seek(toO(length), SEEK_CUR); + if (!readToken(m->file).isWord("endstream")) { QTC::TC("qpdf", "QPDF missing endstream"); - throw damagedPDF(input, input->getLastOffset(), "expected endstream"); + throw damagedPDF("expected endstream"); } } catch (QPDFExc& e) { if (m->attempt_recovery) { warn(e); - length = recoverStreamLength(input, og, stream_offset); + length = recoverStreamLength(m->file, og, stream_offset); } else { throw; } } object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length)); } else { - input->seek(cur_offset, SEEK_SET); + m->file->seek(cur_offset, SEEK_SET); } } // Override last_offset so that it points to the beginning of the object we just read - input->setLastOffset(offset); + m->file->setLastOffset(offset); return object; } @@ -1579,7 +1569,7 @@ QPDF::readObjectAtOffset( } } - QPDFObjectHandle oh = readObject(m->file, description, og, false); + QPDFObjectHandle oh = readObject(description, og); if (!readToken(m->file).isWord("endobj")) { QTC::TC("qpdf", "QPDF err expected endobj"); -- cgit v1.2.3-54-g00ecf From 2643ed4dd9bd1a8e9bb3dcd95794273e9cf5de0c Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 8 Jul 2023 00:38:50 +0100 Subject: Add new private method QPDF::readTrailer() --- include/qpdf/QPDF.hh | 1 + libqpdf/QPDF.cc | 23 +++- qpdf/qtest/qpdf/issue-100.out | 14 +- qpdf/qtest/qpdf/issue-101.out | 40 +----- qpdf/qtest/qpdf/issue-335a.out | 299 +---------------------------------------- 5 files changed, 25 insertions(+), 352 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index afe9e8ec..c836b5fd 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1006,6 +1006,7 @@ class QPDF void insertFreeXrefEntry(QPDFObjGen); void insertReconstructedXrefEntry(int obj, qpdf_offset_t f1, int f2); void setLastObjectDescription(std::string const& description, QPDFObjGen const& og); + QPDFObjectHandle readTrailer(); QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og); QPDFObjectHandle readObjectInStream(std::shared_ptr, QPDFObjGen og); size_t recoverStreamLength( diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 3a486457..48f1e186 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -567,7 +567,7 @@ QPDF::reconstruct_xref(QPDFExc& e) insertReconstructedXrefEntry(obj, token_start, gen); } } else if (!m->trailer.isInitialized() && t1.isWord("trailer")) { - QPDFObjectHandle t = readObject("trailer", QPDFObjGen()); + QPDFObjectHandle t = readTrailer(); if (!t.isDictionary()) { // Oh well. It was worth a try. } else { @@ -855,7 +855,7 @@ QPDF::read_xrefTable(qpdf_offset_t xref_offset) } // Set offset to previous xref table if any - QPDFObjectHandle cur_trailer = readObject("trailer", QPDFObjGen()); + QPDFObjectHandle cur_trailer = readTrailer(); if (!cur_trailer.isDictionary()) { QTC::TC("qpdf", "QPDF missing trailer"); throw damagedPDF("", "expected trailer dictionary"); @@ -1267,6 +1267,25 @@ QPDF::setLastObjectDescription(std::string const& description, QPDFObjGen const& } } +QPDFObjectHandle +QPDF::readTrailer() +{ + qpdf_offset_t offset = m->file->tell(); + bool empty = false; + auto object = QPDFParser(m->file, "trailer", m->tokenizer, nullptr, this).parse(empty, false); + if (empty) { + // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in + // actual PDF files and Adobe Reader appears to ignore them. + warn(damagedPDF("trailer", "empty object treated as null")); + } else if (object.isDictionary() && readToken(m->file).isWord("stream")) { + warn(damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer")); + } + // Override last_offset so that it points to the beginning of the object we just read + m->file->setLastOffset(offset); + return object; +} + + QPDFObjectHandle QPDF::readObject(std::string const& description, QPDFObjGen og) { diff --git a/qpdf/qtest/qpdf/issue-100.out b/qpdf/qtest/qpdf/issue-100.out index 81b8a7e0..7373ab63 100644 --- a/qpdf/qtest/qpdf/issue-100.out +++ b/qpdf/qtest/qpdf/issue-100.out @@ -1,17 +1,5 @@ WARNING: issue-100.pdf: file is damaged WARNING: issue-100.pdf (offset 736): xref not found WARNING: issue-100.pdf: Attempting to reconstruct cross-reference table -WARNING: issue-100.pdf (object 5 0, offset 268): unknown token while reading object; treating as string -WARNING: issue-100.pdf (object 5 0, offset 286): unknown token while reading object; treating as string -WARNING: issue-100.pdf (object 5 0, offset 289): unknown token while reading object; treating as string -WARNING: issue-100.pdf (object 5 0, offset 294): unknown token while reading object; treating as string -WARNING: issue-100.pdf (object 5 0, offset 297): unknown token while reading object; treating as string -WARNING: issue-100.pdf (object 5 0, offset 304): unknown token while reading object; treating as string -WARNING: issue-100.pdf (object 5 0, offset 304): too many errors; giving up on reading object -WARNING: issue-100.pdf (object 5 0, offset 308): expected endobj -WARNING: issue-100.pdf (object 5 0, offset 418): stream dictionary lacks /Length key -WARNING: issue-100.pdf (object 5 0, offset 489): attempting to recover stream length -WARNING: issue-100.pdf (object 5 0, offset 489): recovered stream length: 12 -WARNING: issue-100.pdf (trailer, offset 953): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-100.pdf (trailer, offset 953): dictionary ended prematurely; using null as value for last key +WARNING: issue-100.pdf (trailer, offset 488): stream keyword found in trailer qpdf: issue-100.pdf: unable to find /Root dictionary diff --git a/qpdf/qtest/qpdf/issue-101.out b/qpdf/qtest/qpdf/issue-101.out index 8905c19a..cffe8da0 100644 --- a/qpdf/qtest/qpdf/issue-101.out +++ b/qpdf/qtest/qpdf/issue-101.out @@ -1,43 +1,5 @@ WARNING: issue-101.pdf: file is damaged WARNING: issue-101.pdf (offset 3526): xref not found WARNING: issue-101.pdf: Attempting to reconstruct cross-reference table -WARNING: issue-101.pdf (object 5 0, offset 1242): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-101.pdf (object 5 0, offset 1242): dictionary ended prematurely; using null as value for last key -WARNING: issue-101.pdf (object 5 0, offset 1438): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (object 5 0, offset 1509): attempting to recover stream length -WARNING: issue-101.pdf (object 5 0, offset 1509): recovered stream length: 8 -WARNING: issue-101.pdf (trailer, offset 1631): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 1702): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 1702): recovered stream length: 12 -WARNING: issue-101.pdf (trailer, offset 2026): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 2097): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 2097): recovered stream length: 257 -WARNING: issue-101.pdf (trailer, offset 2613): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 2684): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 2684): recovered stream length: 74 -WARNING: issue-101.pdf (trailer, offset 2928): unknown token while reading object; treating as string -WARNING: issue-101.pdf (trailer, offset 2930): unknown token while reading object; treating as string -WARNING: issue-101.pdf (trailer, offset 2928): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-101.pdf (trailer, offset 2928): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-101.pdf (trailer, offset 2928): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-101.pdf (trailer, offset 2925): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 2996): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 2996): recovered stream length: 12 -WARNING: issue-101.pdf (trailer, offset 3339): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 3410): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 3410): recovered stream length: 12 -WARNING: issue-101.pdf (trailer, offset 3560): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 3631): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 3631): recovered stream length: 8 -WARNING: issue-101.pdf (trailer, offset 4113): /Length key in stream dictionary is not an integer -WARNING: issue-101.pdf (trailer, offset 4184): attempting to recover stream length -WARNING: issue-101.pdf (trailer, offset 4184): recovered stream length: 8 -WARNING: issue-101.pdf (object 11 0, offset 591): unknown token while reading object; treating as string -WARNING: issue-101.pdf (object 11 0, offset 625): treating unexpected brace token as null -WARNING: issue-101.pdf (object 11 0, offset 626): unknown token while reading object; treating as string -WARNING: issue-101.pdf (object 11 0, offset 637): unknown token while reading object; treating as string -WARNING: issue-101.pdf (object 11 0, offset 639): unknown token while reading object; treating as string -WARNING: issue-101.pdf (object 11 0, offset 644): unknown token while reading object; treating as string -WARNING: issue-101.pdf (object 11 0, offset 644): too many errors; giving up on reading object -WARNING: issue-101.pdf (object 11 0, offset 647): expected endobj +WARNING: issue-101.pdf (trailer, offset 1508): stream keyword found in trailer qpdf: issue-101.pdf: unable to find /Root dictionary diff --git a/qpdf/qtest/qpdf/issue-335a.out b/qpdf/qtest/qpdf/issue-335a.out index 75bfb235..456bc475 100644 --- a/qpdf/qtest/qpdf/issue-335a.out +++ b/qpdf/qtest/qpdf/issue-335a.out @@ -1015,302 +1015,5 @@ WARNING: issue-335a.pdf (trailer, offset 20606): treating unexpected brace token WARNING: issue-335a.pdf (trailer, offset 20606): too many errors; giving up on reading object WARNING: issue-335a.pdf (trailer, offset 20684): unknown token while reading object; treating as string WARNING: issue-335a.pdf (trailer, offset 20683): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 20748): stream keyword followed by extraneous whitespace -WARNING: issue-335a.pdf (trailer, offset 20679): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 20749): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 20749): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 20756): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 20787): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 20787): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 20812): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 20803): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 20803): dictionary has duplicated key /Length; last occurrence overrides earlier ones -WARNING: issue-335a.pdf (trailer, offset 20843): stream keyword followed by extraneous whitespace -WARNING: issue-335a.pdf (trailer, offset 20800): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 20844): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 20844): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 20851): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 20882): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 20882): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 20914): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 20898): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 20895): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 20929): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 20929): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 20949): unexpected > -WARNING: issue-335a.pdf (trailer, offset 20957): unexpected > -WARNING: issue-335a.pdf (trailer, offset 20958): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 20960): unexpected > -WARNING: issue-335a.pdf (trailer, offset 20961): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 20972): treating unexpected brace token as null -WARNING: issue-335a.pdf (trailer, offset 20973): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 20973): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21042): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21026): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 21023): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 21057): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 21057): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 21077): unexpected > -WARNING: issue-335a.pdf (trailer, offset 21085): unexpected > -WARNING: issue-335a.pdf (trailer, offset 21086): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21088): unexpected > -WARNING: issue-335a.pdf (trailer, offset 21089): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21100): treating unexpected brace token as null -WARNING: issue-335a.pdf (trailer, offset 21101): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21101): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21118): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21158): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21202): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21205): treating unexpected brace token as null -WARNING: issue-335a.pdf (trailer, offset 21207): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21212): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21212): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21132): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21138): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21156): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21157): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21158): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21202): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21202): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21154): treating unexpected brace token as null -WARNING: issue-335a.pdf (trailer, offset 21155): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21156): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21157): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21158): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21202): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21202): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21172): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21199): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21201): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21202): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21205): treating unexpected brace token as null -WARNING: issue-335a.pdf (trailer, offset 21207): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21207): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21228): treating unexpected brace token as null -WARNING: issue-335a.pdf (trailer, offset 21229): unexpected ) -WARNING: issue-335a.pdf (trailer, offset 21230): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21262): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21267): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21277): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21277): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21277): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21287): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21389): unexpected dictionary close token -WARNING: issue-335a.pdf (trailer, offset 21392): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21400): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21430): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21438): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21441): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21444): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21452): invalid character (-) in hexstring -WARNING: issue-335a.pdf (trailer, offset 21819): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21819): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21287): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21389): unexpected dictionary close token -WARNING: issue-335a.pdf (trailer, offset 21392): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21400): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21430): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21438): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21441): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21444): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21452): invalid character (-) in hexstring -WARNING: issue-335a.pdf (trailer, offset 21819): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21819): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21407): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 21438): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 21438): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 21452): invalid character (-) in hexstring -WARNING: issue-335a.pdf (trailer, offset 21837): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21850): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 21892): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21900): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21903): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21906): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21918): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21925): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21925): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21918): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21925): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21937): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21962): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21991): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22000): invalid character (t) in hexstring -WARNING: issue-335a.pdf (trailer, offset 22003): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22028): unexpected > -WARNING: issue-335a.pdf (trailer, offset 22030): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22038): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake4 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake5 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake6 -WARNING: issue-335a.pdf (trailer, offset 21936): dictionary has duplicated key /Length; last occurrence overrides earlier ones -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake7 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake8 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake9 -WARNING: issue-335a.pdf (trailer, offset 22044): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22052): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22064): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22064): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 21937): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21962): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21991): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22000): invalid character (t) in hexstring -WARNING: issue-335a.pdf (trailer, offset 22003): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22028): unexpected > -WARNING: issue-335a.pdf (trailer, offset 22030): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22038): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake4 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake5 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake6 -WARNING: issue-335a.pdf (trailer, offset 21936): dictionary has duplicated key /Length; last occurrence overrides earlier ones -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake7 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake8 -WARNING: issue-335a.pdf (trailer, offset 21936): expected dictionary key but found non-name object; inserting key /QPDFFake9 -WARNING: issue-335a.pdf (trailer, offset 21932): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22052): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22052): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22000): invalid character (t) in hexstring -WARNING: issue-335a.pdf (trailer, offset 22088): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22087): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22083): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22136): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22136): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22178): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22190): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22202): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22218): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22201): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22201): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22230): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22238): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22177): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22177): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22177): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 22177): expected dictionary key but found non-name object; inserting key /QPDFFake4 -WARNING: issue-335a.pdf (trailer, offset 22177): expected dictionary key but found non-name object; inserting key /QPDFFake5 -WARNING: issue-335a.pdf (trailer, offset 22276): stream keyword followed by carriage return only -WARNING: issue-335a.pdf (trailer, offset 22173): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22276): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22276): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22202): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22218): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22201): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22201): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22197): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22238): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22238): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22327): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22336): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22338): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22355): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22360): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22326): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22326): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22326): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 22326): expected dictionary key but found non-name object; inserting key /QPDFFake4 -WARNING: issue-335a.pdf (trailer, offset 22326): expected dictionary key but found non-name object; inserting key /QPDFFake5 -WARNING: issue-335a.pdf (trailer, offset 22322): /Length key in stream dictionary is not an integer -WARNING: issue-335a.pdf (trailer, offset 22373): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22373): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22437): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22436): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22432): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22484): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22484): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22650): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22656): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22675): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22687): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22690): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22702): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22701): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22740): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22748): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22761): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22791): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22794): unexpected > -WARNING: issue-335a.pdf (trailer, offset 22796): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22804): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake4 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake5 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake6 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake7 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake8 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake9 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake10 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake11 -WARNING: issue-335a.pdf (trailer, offset 22810): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22817): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22817): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 22687): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22690): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22702): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22701): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22740): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22748): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22761): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22791): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22794): unexpected > -WARNING: issue-335a.pdf (trailer, offset 22796): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22804): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake4 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake5 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake6 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake7 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake8 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake9 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake10 -WARNING: issue-335a.pdf (trailer, offset 22686): expected dictionary key but found non-name object; inserting key /QPDFFake11 -WARNING: issue-335a.pdf (trailer, offset 22817): stream keyword followed by carriage return only -WARNING: issue-335a.pdf (trailer, offset 22682): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22817): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22817): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22702): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22701): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22697): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (trailer, offset 22748): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22748): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 22845): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22869): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 22844): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (trailer, offset 22844): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (trailer, offset 22844): expected dictionary key but found non-name object; inserting key /QPDFFake3 -WARNING: issue-335a.pdf (trailer, offset 22898): expected endstream -WARNING: issue-335a.pdf (trailer, offset 22882): attempting to recover stream length -WARNING: issue-335a.pdf (trailer, offset 22882): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (trailer, offset 23098): invalid character (t) in hexstring -WARNING: issue-335a.pdf (trailer, offset 23101): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23108): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23130): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23147): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23155): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23155): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 23108): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23130): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23147): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23155): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23196): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23324): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (trailer, offset 23324): too many errors; giving up on reading object -WARNING: issue-335a.pdf (trailer, offset 23411): dictionary ended prematurely; using null as value for last key -WARNING: issue-335a.pdf (object 5 0, offset 23451): invalid character (ÿ) in hexstring -WARNING: issue-335a.pdf (object 5 0, offset 23458): unknown token while reading object; treating as string -WARNING: issue-335a.pdf (object 5 0, offset 23444): expected dictionary key but found non-name object; inserting key /QPDFFake1 -WARNING: issue-335a.pdf (object 5 0, offset 23444): expected dictionary key but found non-name object; inserting key /QPDFFake2 -WARNING: issue-335a.pdf (object 5 0, offset 23440): stream dictionary lacks /Length key -WARNING: issue-335a.pdf (object 5 0, offset 23485): attempting to recover stream length -WARNING: issue-335a.pdf (object 5 0, offset 23485): unable to recover stream data; treating stream as empty -WARNING: issue-335a.pdf (object 5 0, offset 24974): expected endobj -WARNING: issue-335a.pdf (object 5 0, offset 24974): EOF after endobj +WARNING: issue-335a.pdf (trailer, offset 20747): stream keyword found in trailer qpdf: issue-335a.pdf: unable to find /Root dictionary -- cgit v1.2.3-54-g00ecf From c422b918b1402ac9e2052ff426c7e64d4342bc99 Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 8 Jul 2023 11:38:23 +0100 Subject: Add new private method QPDF::readStream --- include/qpdf/QPDF.hh | 1 + libqpdf/QPDF.cc | 157 ++++++++++++++++++++++++++------------------------- 2 files changed, 82 insertions(+), 76 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index c836b5fd..ead09a0a 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1008,6 +1008,7 @@ class QPDF void setLastObjectDescription(std::string const& description, QPDFObjGen const& og); QPDFObjectHandle readTrailer(); QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og); + void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); QPDFObjectHandle readObjectInStream(std::shared_ptr, QPDFObjGen og); size_t recoverStreamLength( std::shared_ptr input, QPDFObjGen const& og, qpdf_offset_t stream_offset); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 48f1e186..9391c987 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1296,7 +1296,7 @@ QPDF::readObject(std::string const& description, QPDFObjGen og) std::shared_ptr decrypter_ph; StringDecrypter* decrypter = nullptr; if (m->encp->encrypted) { - decrypter_ph = std::make_shared(this, og); + decrypter_ph = std::make_unique(this, og); decrypter = decrypter_ph.get(); } auto object = QPDFParser(m->file, m->last_object_description, m->tokenizer, decrypter, this) @@ -1309,93 +1309,98 @@ QPDF::readObject(std::string const& description, QPDFObjGen og) // check for stream qpdf_offset_t cur_offset = m->file->tell(); if (readToken(m->file).isWord("stream")) { - // The PDF specification states that the word "stream" should be followed by either a - // carriage return and a newline or by a newline alone. It specifically disallowed - // following it by a carriage return alone since, in that case, there would be no way to - // tell whether the NL in a CR NL sequence was part of the stream data. However, some - // readers, including Adobe reader, accept a carriage return by itself when followed by - // a non-newline character, so that's what we do here. We have also seen files that have - // extraneous whitespace between the stream keyword and the newline. - bool done = false; - while (!done) { - done = true; - char ch; - if (m->file->read(&ch, 1) == 0) { - // A premature EOF here will result in some other problem that will get reported - // at another time. - } else if (ch == '\n') { - // ready to read stream data - QTC::TC("qpdf", "QPDF stream with NL only"); - } else if (ch == '\r') { - // Read another character - if (m->file->read(&ch, 1) != 0) { - if (ch == '\n') { - // Ready to read stream data - QTC::TC("qpdf", "QPDF stream with CRNL"); - } else { - // Treat the \r by itself as the whitespace after endstream and start - // reading stream data in spite of not having seen a newline. - QTC::TC("qpdf", "QPDF stream with CR only"); - m->file->unreadCh(ch); - warn(damagedPDF( - m->file->tell(), - "stream keyword followed by carriage return only")); - } - } - } else if (QUtil::is_space(ch)) { - warn(damagedPDF( - m->file->tell(), "stream keyword followed by extraneous whitespace")); - done = false; + readStream(object, og, offset); + } else { + m->file->seek(cur_offset, SEEK_SET); + } + } + + // Override last_offset so that it points to the beginning of the object we just read + m->file->setLastOffset(offset); + return object; +} + +// After reading stream dictionary and stream keyword, read rest of stream. +void +QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) +{ + // The PDF specification states that the word "stream" should be followed by either a carriage + // return and a newline or by a newline alone. It specifically disallowed following it by a + // carriage return alone since, in that case, there would be no way to tell whether the NL in a + // CR NL sequence was part of the stream data. However, some readers, including Adobe reader, + // accept a carriage return by itself when followed by a non-newline character, so that's what + // we do here. We have also seen files that have extraneous whitespace between the stream + // keyword and the newline. + bool done = false; + while (!done) { + done = true; + char ch; + if (m->file->read(&ch, 1) == 0) { + // A premature EOF here will result in some other problem that will get reported at + // another time. + } else if (ch == '\n') { + // ready to read stream data + QTC::TC("qpdf", "QPDF stream with NL only"); + } else if (ch == '\r') { + // Read another character + if (m->file->read(&ch, 1) != 0) { + if (ch == '\n') { + // Ready to read stream data + QTC::TC("qpdf", "QPDF stream with CRNL"); } else { - QTC::TC("qpdf", "QPDF stream without newline"); + // Treat the \r by itself as the whitespace after endstream and start reading + // stream data in spite of not having seen a newline. + QTC::TC("qpdf", "QPDF stream with CR only"); m->file->unreadCh(ch); warn(damagedPDF( - m->file->tell(), "stream keyword not followed by proper line terminator")); + m->file->tell(), "stream keyword followed by carriage return only")); } } + } else if (QUtil::is_space(ch)) { + warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); + done = false; + } else { + QTC::TC("qpdf", "QPDF stream without newline"); + m->file->unreadCh(ch); + warn(damagedPDF( + m->file->tell(), "stream keyword not followed by proper line terminator")); + } + } - // Must get offset before accessing any additional objects since resolving a previously - // unresolved indirect object will change file position. - qpdf_offset_t stream_offset = m->file->tell(); - size_t length = 0; - - try { - auto length_obj = object.getKey("/Length"); + // Must get offset before accessing any additional objects since resolving a previously + // unresolved indirect object will change file position. + qpdf_offset_t stream_offset = m->file->tell(); + size_t length = 0; - if (!length_obj.isInteger()) { - if (length_obj.isNull()) { - QTC::TC("qpdf", "QPDF stream without length"); - throw damagedPDF(offset, "stream dictionary lacks /Length key"); - } - QTC::TC("qpdf", "QPDF stream length not integer"); - throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); - } + try { + auto length_obj = object.getKey("/Length"); - length = toS(length_obj.getUIntValue()); - // Seek in two steps to avoid potential integer overflow - m->file->seek(stream_offset, SEEK_SET); - m->file->seek(toO(length), SEEK_CUR); - if (!readToken(m->file).isWord("endstream")) { - QTC::TC("qpdf", "QPDF missing endstream"); - throw damagedPDF("expected endstream"); - } - } catch (QPDFExc& e) { - if (m->attempt_recovery) { - warn(e); - length = recoverStreamLength(m->file, og, stream_offset); - } else { - throw; - } + if (!length_obj.isInteger()) { + if (length_obj.isNull()) { + QTC::TC("qpdf", "QPDF stream without length"); + throw damagedPDF(offset, "stream dictionary lacks /Length key"); } - object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length)); + QTC::TC("qpdf", "QPDF stream length not integer"); + throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); + } + + length = toS(length_obj.getUIntValue()); + // Seek in two steps to avoid potential integer overflow + m->file->seek(stream_offset, SEEK_SET); + m->file->seek(toO(length), SEEK_CUR); + if (!readToken(m->file).isWord("endstream")) { + QTC::TC("qpdf", "QPDF missing endstream"); + throw damagedPDF("expected endstream"); + } + } catch (QPDFExc& e) { + if (m->attempt_recovery) { + warn(e); + length = recoverStreamLength(m->file, og, stream_offset); } else { - m->file->seek(cur_offset, SEEK_SET); + throw; } } - - // Override last_offset so that it points to the beginning of the object we just read - m->file->setLastOffset(offset); - return object; + object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length)); } QPDFObjectHandle -- cgit v1.2.3-54-g00ecf From ef866d68cd77cad9640350715adf3374fd5719c7 Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 8 Jul 2023 11:53:48 +0100 Subject: In QPDF::readStream remove variable done --- libqpdf/QPDF.cc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 9391c987..3fbb4da1 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1331,17 +1331,19 @@ QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) // accept a carriage return by itself when followed by a non-newline character, so that's what // we do here. We have also seen files that have extraneous whitespace between the stream // keyword and the newline. - bool done = false; - while (!done) { - done = true; + while (true) { char ch; if (m->file->read(&ch, 1) == 0) { // A premature EOF here will result in some other problem that will get reported at // another time. - } else if (ch == '\n') { + break; + } + if (ch == '\n') { // ready to read stream data QTC::TC("qpdf", "QPDF stream with NL only"); - } else if (ch == '\r') { + break; + } + if (ch == '\r') { // Read another character if (m->file->read(&ch, 1) != 0) { if (ch == '\n') { @@ -1356,15 +1358,16 @@ QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) m->file->tell(), "stream keyword followed by carriage return only")); } } - } else if (QUtil::is_space(ch)) { - warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); - done = false; - } else { + break; + } + if (!QUtil::is_space(ch)) { QTC::TC("qpdf", "QPDF stream without newline"); m->file->unreadCh(ch); warn(damagedPDF( m->file->tell(), "stream keyword not followed by proper line terminator")); + break; } + warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); } // Must get offset before accessing any additional objects since resolving a previously -- cgit v1.2.3-54-g00ecf From aabd3f6f9b09e844958fd4ee07bde5e8df546fc4 Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 8 Jul 2023 12:17:48 +0100 Subject: Add private method QPDF::validateStreamLineEnd --- include/qpdf/QPDF.hh | 1 + libqpdf/QPDF.cc | 84 ++++++++++++++++++++++++++++------------------------ 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index ead09a0a..61510c17 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1009,6 +1009,7 @@ class QPDF QPDFObjectHandle readTrailer(); QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og); void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); + void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); QPDFObjectHandle readObjectInStream(std::shared_ptr, QPDFObjGen og); size_t recoverStreamLength( std::shared_ptr input, QPDFObjGen const& og, qpdf_offset_t stream_offset); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 3fbb4da1..05929764 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1323,6 +1323,47 @@ QPDF::readObject(std::string const& description, QPDFObjGen og) // After reading stream dictionary and stream keyword, read rest of stream. void QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) +{ + validateStreamLineEnd(object, og, offset); + + // Must get offset before accessing any additional objects since resolving a previously + // unresolved indirect object will change file position. + qpdf_offset_t stream_offset = m->file->tell(); + size_t length = 0; + + try { + auto length_obj = object.getKey("/Length"); + + if (!length_obj.isInteger()) { + if (length_obj.isNull()) { + QTC::TC("qpdf", "QPDF stream without length"); + throw damagedPDF(offset, "stream dictionary lacks /Length key"); + } + QTC::TC("qpdf", "QPDF stream length not integer"); + throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); + } + + length = toS(length_obj.getUIntValue()); + // Seek in two steps to avoid potential integer overflow + m->file->seek(stream_offset, SEEK_SET); + m->file->seek(toO(length), SEEK_CUR); + if (!readToken(m->file).isWord("endstream")) { + QTC::TC("qpdf", "QPDF missing endstream"); + throw damagedPDF("expected endstream"); + } + } catch (QPDFExc& e) { + if (m->attempt_recovery) { + warn(e); + length = recoverStreamLength(m->file, og, stream_offset); + } else { + throw; + } + } + object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length)); +} + +void +QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) { // The PDF specification states that the word "stream" should be followed by either a carriage // return and a newline or by a newline alone. It specifically disallowed following it by a @@ -1336,12 +1377,12 @@ QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) if (m->file->read(&ch, 1) == 0) { // A premature EOF here will result in some other problem that will get reported at // another time. - break; + return; } if (ch == '\n') { // ready to read stream data QTC::TC("qpdf", "QPDF stream with NL only"); - break; + return; } if (ch == '\r') { // Read another character @@ -1358,52 +1399,17 @@ QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) m->file->tell(), "stream keyword followed by carriage return only")); } } - break; + return; } if (!QUtil::is_space(ch)) { QTC::TC("qpdf", "QPDF stream without newline"); m->file->unreadCh(ch); warn(damagedPDF( m->file->tell(), "stream keyword not followed by proper line terminator")); - break; + return; } warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); } - - // Must get offset before accessing any additional objects since resolving a previously - // unresolved indirect object will change file position. - qpdf_offset_t stream_offset = m->file->tell(); - size_t length = 0; - - try { - auto length_obj = object.getKey("/Length"); - - if (!length_obj.isInteger()) { - if (length_obj.isNull()) { - QTC::TC("qpdf", "QPDF stream without length"); - throw damagedPDF(offset, "stream dictionary lacks /Length key"); - } - QTC::TC("qpdf", "QPDF stream length not integer"); - throw damagedPDF(offset, "/Length key in stream dictionary is not an integer"); - } - - length = toS(length_obj.getUIntValue()); - // Seek in two steps to avoid potential integer overflow - m->file->seek(stream_offset, SEEK_SET); - m->file->seek(toO(length), SEEK_CUR); - if (!readToken(m->file).isWord("endstream")) { - QTC::TC("qpdf", "QPDF missing endstream"); - throw damagedPDF("expected endstream"); - } - } catch (QPDFExc& e) { - if (m->attempt_recovery) { - warn(e); - length = recoverStreamLength(m->file, og, stream_offset); - } else { - throw; - } - } - object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length)); } QPDFObjectHandle -- cgit v1.2.3-54-g00ecf From 5754ab8b39646404801931ed065f985efa0b34a7 Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 8 Jul 2023 14:04:10 +0100 Subject: Tune QPDF::readObjectInStream --- include/qpdf/QPDF.hh | 2 +- libqpdf/QPDF.cc | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 61510c17..2ee2bb34 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1010,7 +1010,7 @@ class QPDF QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og); void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); - QPDFObjectHandle readObjectInStream(std::shared_ptr, QPDFObjGen og); + QPDFObjectHandle readObjectInStream(std::shared_ptr& input, int obj); size_t recoverStreamLength( std::shared_ptr input, QPDFObjGen const& og, qpdf_offset_t stream_offset); QPDFTokenizer::Token readToken(std::shared_ptr, size_t max_len = 0); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 05929764..8a6a346e 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1413,10 +1413,11 @@ QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset } QPDFObjectHandle -QPDF::readObjectInStream(std::shared_ptr input, QPDFObjGen og) +QPDF::readObjectInStream(std::shared_ptr& input, int obj) { - setLastObjectDescription("", og); - qpdf_offset_t offset = input->tell(); + m->last_object_description.erase(7); // last_object_description starts with "object " + m->last_object_description += std::to_string(obj); + m->last_object_description += " 0"; bool empty = false; auto object = QPDFParser(input, m->last_object_description, m->tokenizer, nullptr, this) @@ -1426,9 +1427,6 @@ QPDF::readObjectInStream(std::shared_ptr input, QPDFObjGen og) // actual PDF files and Adobe Reader appears to ignore them. warn(damagedPDF(input, input->getLastOffset(), "empty object treated as null")); } - - // Override last_offset so that it points to the beginning of the object we just read - input->setLastOffset(offset); return object; } @@ -1787,13 +1785,15 @@ QPDF::resolveObjectsInStream(int obj_stream_number) // found here in the cache. Remember that some objects stored here might have been overridden // by new objects appended to the file, so it is necessary to recheck the xref table and only // cache what would actually be resolved here. + m->last_object_description.clear(); + m->last_object_description += "object "; for (auto const& iter: offsets) { QPDFObjGen og(iter.first, 0); QPDFXRefEntry const& entry = m->xref_table[og]; if ((entry.getType() == 2) && (entry.getObjStreamNumber() == obj_stream_number)) { int offset = iter.second; input->seek(offset, SEEK_SET); - QPDFObjectHandle oh = readObjectInStream(input, og); + QPDFObjectHandle oh = readObjectInStream(input, iter.first); updateCache(og, oh.getObj(), end_before_space, end_after_space); } else { QTC::TC("qpdf", "QPDF not caching overridden objstm object"); -- cgit v1.2.3-54-g00ecf From ba3953f1bf20d5d0331f253e4da1e3e3ce10e895 Mon Sep 17 00:00:00 2001 From: m-holger Date: Sat, 8 Jul 2023 16:20:01 +0100 Subject: Tune QPDF::readObject --- libqpdf/QPDF.cc | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 8a6a346e..7edb76f9 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1291,32 +1291,27 @@ QPDF::readObject(std::string const& description, QPDFObjGen og) { setLastObjectDescription(description, og); qpdf_offset_t offset = m->file->tell(); - bool empty = false; - std::shared_ptr decrypter_ph; - StringDecrypter* decrypter = nullptr; - if (m->encp->encrypted) { - decrypter_ph = std::make_unique(this, og); - decrypter = decrypter_ph.get(); - } - auto object = QPDFParser(m->file, m->last_object_description, m->tokenizer, decrypter, this) + + StringDecrypter decrypter{this, og}; + StringDecrypter* decrypter_ptr = m->encp->encrypted ? &decrypter : nullptr; + auto object = QPDFParser(m->file, m->last_object_description, m->tokenizer, decrypter_ptr, this) .parse(empty, false); if (empty) { // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in // actual PDF files and Adobe Reader appears to ignore them. warn(damagedPDF(m->file, m->file->getLastOffset(), "empty object treated as null")); - } else if (object.isDictionary()) { - // check for stream - qpdf_offset_t cur_offset = m->file->tell(); - if (readToken(m->file).isWord("stream")) { - readStream(object, og, offset); - } else { - m->file->seek(cur_offset, SEEK_SET); - } + return object; + } + auto token = readToken(m->file); + if (object.isDictionary() && token.isWord("stream")) { + readStream(object, og, offset); + token = readToken(m->file); + } + if (!token.isWord("endobj")) { + QTC::TC("qpdf", "QPDF err expected endobj"); + warn(damagedPDF("expected endobj")); } - - // Override last_offset so that it points to the beginning of the object we just read - m->file->setLastOffset(offset); return object; } @@ -1602,11 +1597,6 @@ QPDF::readObjectAtOffset( QPDFObjectHandle oh = readObject(description, og); - if (!readToken(m->file).isWord("endobj")) { - QTC::TC("qpdf", "QPDF err expected endobj"); - warn(damagedPDF("expected endobj")); - } - if (isUnresolved(og)) { // Store the object in the cache here so it gets cached whether we first know the offset or // whether we first know the object ID and generation (in which we case we would get here -- cgit v1.2.3-54-g00ecf