Commit fe4853fed484a73bc355af53259d26b48e68215d

Authored by m-holger
1 parent 7061ee1c

Refactor `QPDFParser` to simplify `parse` methods by removing `empty` flag, adju…

…sting return values for uninitialized objects, and cleaning up error handling logic.
libqpdf/QPDFParser.cc
... ... @@ -51,7 +51,6 @@ QPDFObjectHandle
51 51 QPDFParser::parse(InputSource& input, std::string const& object_description, QPDF* context)
52 52 {
53 53 qpdf::Tokenizer tokenizer;
54   - bool empty = false;
55 54 if (auto result = QPDFParser(
56 55 input,
57 56 make_description(input.getName(), object_description),
... ... @@ -60,7 +59,7 @@ QPDFParser::parse(InputSource& input, std::string const& object_description, QPD
60 59 nullptr,
61 60 context,
62 61 false)
63   - .parse(empty, false)) {
  62 + .parse()) {
64 63 return result;
65 64 }
66 65 return {QPDFObject::create<QPDF_Null>()};
... ... @@ -73,23 +72,24 @@ QPDFParser::parse_content(
73 72 qpdf::Tokenizer& tokenizer,
74 73 QPDF* context)
75 74 {
76   - bool empty = false;
77   - if (auto result = QPDFParser(
78   - input,
79   - std::move(sp_description),
80   - "content",
81   - tokenizer,
82   - nullptr,
83   - context,
84   - true,
85   - 0,
86   - 0,
87   - context && context->doc().reconstructed_xref())
88   - .parse(empty, true)) {
  75 + static const std::string content("content"); // GCC12 - make constexpr
  76 + auto p = QPDFParser(
  77 + input,
  78 + std::move(sp_description),
  79 + content,
  80 + tokenizer,
  81 + nullptr,
  82 + context,
  83 + true,
  84 + 0,
  85 + 0,
  86 + context && context->doc().reconstructed_xref());
  87 + auto result = p.parse(true);
  88 + if (result || p.empty_) {
  89 + // In content stream mode, leave object uninitialized to indicate EOF
89 90 return result;
90 91 }
91   - // In content stream mode, leave object uninitialized to indicate EOF
92   - return {empty ? nullptr : QPDFObject::create<QPDF_Null>()};
  92 + return {QPDFObject::create<QPDF_Null>()};
93 93 }
94 94  
95 95 QPDFObjectHandle
... ... @@ -101,21 +101,25 @@ QPDFParser::parse(
101 101 QPDFObjectHandle::StringDecrypter* decrypter,
102 102 QPDF* context)
103 103 {
104   - if (auto result = QPDFParser(
105   - input,
106   - make_description(input.getName(), object_description),
107   - object_description,
108   - *tokenizer.m,
109   - decrypter,
110   - context,
111   - false)
112   - .parse(empty, false)) {
  104 + // ABI: This parse overload is only used by the deprecated QPDFObjectHandle::parse. It is the
  105 + // only user of the 'empty' member. When removing this overload also remove 'empty'.
  106 + auto p = QPDFParser(
  107 + input,
  108 + make_description(input.getName(), object_description),
  109 + object_description,
  110 + *tokenizer.m,
  111 + decrypter,
  112 + context,
  113 + false);
  114 + auto result = p.parse();
  115 + empty = p.empty_;
  116 + if (result) {
113 117 return result;
114 118 }
115 119 return {QPDFObject::create<QPDF_Null>()};
116 120 }
117 121  
118   -std::pair<QPDFObjectHandle, bool>
  122 +QPDFObjectHandle
119 123 QPDFParser::parse(
120 124 InputSource& input,
121 125 std::string const& object_description,
... ... @@ -124,55 +128,44 @@ QPDFParser::parse(
124 128 QPDF& context,
125 129 bool sanity_checks)
126 130 {
127   - bool empty{false};
128   - auto result = QPDFParser(
129   - input,
130   - make_description(input.getName(), object_description),
131   - object_description,
132   - tokenizer,
133   - decrypter,
134   - &context,
135   - true,
136   - 0,
137   - 0,
138   - sanity_checks)
139   - .parse(empty, false);
140   - if (result) {
141   - return {result, empty};
142   - }
143   - return {QPDFObject::create<QPDF_Null>(), empty};
  131 + return QPDFParser(
  132 + input,
  133 + make_description(input.getName(), object_description),
  134 + object_description,
  135 + tokenizer,
  136 + decrypter,
  137 + &context,
  138 + true,
  139 + 0,
  140 + 0,
  141 + sanity_checks)
  142 + .parse();
144 143 }
145 144  
146   -std::pair<QPDFObjectHandle, bool>
  145 +QPDFObjectHandle
147 146 QPDFParser::parse(
148 147 is::OffsetBuffer& input, int stream_id, int obj_id, qpdf::Tokenizer& tokenizer, QPDF& context)
149 148 {
150   - bool empty{false};
151   - auto result = QPDFParser(
152   - input,
153   - std::make_shared<QPDFObject::Description>(
154   - QPDFObject::ObjStreamDescr(stream_id, obj_id)),
155   - "",
156   - tokenizer,
157   - nullptr,
158   - &context,
159   - true,
160   - stream_id,
161   - obj_id)
162   - .parse(empty, false);
163   -
164   - if (result) {
165   - return {result, empty};
166   - }
167   - return {QPDFObject::create<QPDF_Null>(), empty};
  149 + return QPDFParser(
  150 + input,
  151 + std::make_shared<QPDFObject::Description>(
  152 + QPDFObject::ObjStreamDescr(stream_id, obj_id)),
  153 + "",
  154 + tokenizer,
  155 + nullptr,
  156 + &context,
  157 + true,
  158 + stream_id,
  159 + obj_id)
  160 + .parse();
168 161 }
169 162  
170 163 QPDFObjectHandle
171   -QPDFParser::parse(bool& empty, bool content_stream)
  164 +QPDFParser::parse(bool content_stream)
172 165 {
173 166 try {
174   - return parse_first(empty, content_stream);
175   - } catch (Error& e) {
  167 + return parse_first(content_stream);
  168 + } catch (Error&) {
176 169 return {};
177 170 } catch (QPDFExc& e) {
178 171 throw e;
... ... @@ -185,15 +178,14 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
185 178 }
186 179  
187 180 QPDFObjectHandle
188   -QPDFParser::parse_first(bool& empty, bool content_stream)
  181 +QPDFParser::parse_first(bool content_stream)
189 182 {
190 183 // This method must take care not to resolve any objects. Don't check the type of any object
191 184 // without first ensuring that it is a direct object. Otherwise, doing so may have the side
192 185 // effect of reading the object and changing the file pointer. If you do this, it will cause a
193 186 // logic error to be thrown from QPDF::inParse().
194 187  
195   - ParseGuard pg(context);
196   - empty = false;
  188 + QPDF::Doc::ParseGuard pg(context);
197 189 start = input.tell();
198 190 if (!tokenizer.nextToken(input, object_description)) {
199 191 warn(tokenizer.getErrorMessage());
... ... @@ -203,7 +195,7 @@ QPDFParser::parse_first(bool&amp; empty, bool content_stream)
203 195 case QPDFTokenizer::tt_eof:
204 196 if (content_stream) {
205 197 // In content stream mode, leave object uninitialized to indicate EOF
206   - empty = true;
  198 + empty_ = true;
207 199 return {};
208 200 }
209 201 warn("unexpected EOF");
... ... @@ -255,10 +247,15 @@ QPDFParser::parse_first(bool&amp; empty, bool content_stream)
255 247 if (content_stream) {
256 248 return withDescription<QPDF_Operator>(value);
257 249 } else if (value == "endobj") {
258   - // We just saw endobj without having read anything. Treat this as a null and do
259   - // not move the input source's offset.
  250 + // We just saw endobj without having read anything. Nothing in the PDF spec appears
  251 + // to allow empty objects, but they have been encountered in actual PDF files and
  252 + // Adobe Reader appears to ignore them. Treat this as a null and do not move the
  253 + // input source's offset.
  254 + empty_ = true;
260 255 input.seek(input.getLastOffset(), SEEK_SET);
261   - empty = true;
  256 + if (!content_stream) {
  257 + warn("empty object treated as null");
  258 + }
262 259 return {};
263 260 } else {
264 261 warn("unknown token while reading object; treating as string");
... ...
libqpdf/QPDF_objects.cc
... ... @@ -1233,13 +1233,9 @@ QPDFObjectHandle
1233 1233 Objects::readTrailer()
1234 1234 {
1235 1235 qpdf_offset_t offset = m->file->tell();
1236   - auto [object, empty] =
  1236 + auto object =
1237 1237 QPDFParser::parse(*m->file, "trailer", m->tokenizer, nullptr, qpdf, m->reconstructed_xref);
1238   - if (empty) {
1239   - // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1240   - // actual PDF files and Adobe Reader appears to ignore them.
1241   - warn(damagedPDF("trailer", "empty object treated as null"));
1242   - } else if (object.isDictionary() && m->objects.readToken(*m->file).isWord("stream")) {
  1238 + if (object.isDictionary() && m->objects.readToken(*m->file).isWord("stream")) {
1243 1239 warn(damagedPDF("trailer", m->file->tell(), "stream keyword found in trailer"));
1244 1240 }
1245 1241 // Override last_offset so that it points to the beginning of the object we just read
... ... @@ -1255,19 +1251,15 @@ Objects::readObject(std::string const&amp; description, QPDFObjGen og)
1255 1251  
1256 1252 StringDecrypter decrypter{&qpdf, og};
1257 1253 StringDecrypter* decrypter_ptr = m->encp->encrypted ? &decrypter : nullptr;
1258   - auto [object, empty] = QPDFParser::parse(
  1254 + auto object = QPDFParser::parse(
1259 1255 *m->file,
1260 1256 m->last_object_description,
1261 1257 m->tokenizer,
1262 1258 decrypter_ptr,
1263 1259 qpdf,
1264 1260 m->reconstructed_xref || m->in_read_xref_stream);
1265   - ;
1266   - if (empty) {
1267   - // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1268   - // actual PDF files and Adobe Reader appears to ignore them.
1269   - warn(damagedPDF(*m->file, m->file->getLastOffset(), "empty object treated as null"));
1270   - return object;
  1261 + if (!object) {
  1262 + return {};
1271 1263 }
1272 1264 auto token = readToken(*m->file);
1273 1265 if (object.isDictionary() && token.isWord("stream")) {
... ... @@ -1366,24 +1358,6 @@ Objects::validateStreamLineEnd(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_off
1366 1358 }
1367 1359 }
1368 1360  
1369   -QPDFObjectHandle
1370   -Objects::readObjectInStream(is::OffsetBuffer& input, int stream_id, int obj_id)
1371   -{
1372   - auto [object, empty] = QPDFParser::parse(input, stream_id, obj_id, m->tokenizer, qpdf);
1373   - if (empty) {
1374   - // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1375   - // actual PDF files and Adobe Reader appears to ignore them.
1376   - warn(QPDFExc(
1377   - qpdf_e_damaged_pdf,
1378   - m->file->getName() + " object stream " + std::to_string(stream_id),
1379   - +"object " + std::to_string(obj_id) + " 0, offset " +
1380   - std::to_string(input.getLastOffset()),
1381   - 0,
1382   - "empty object treated as null"));
1383   - }
1384   - return object;
1385   -}
1386   -
1387 1361 bool
1388 1362 Objects ::findEndstream()
1389 1363 {
... ... @@ -1536,25 +1510,25 @@ Objects::readObjectAtOffset(
1536 1510 return;
1537 1511 }
1538 1512  
1539   - QPDFObjectHandle oh = readObject(description, og);
  1513 + if (auto oh = readObject(description, og)) {
  1514 + // Determine the end offset of this object before and after white space. We use these
  1515 + // numbers to validate linearization hint tables. Offsets and lengths of objects may imply
  1516 + // the end of an object to be anywhere between these values.
  1517 + qpdf_offset_t end_before_space = m->file->tell();
1540 1518  
1541   - // Determine the end offset of this object before and after white space. We use these
1542   - // numbers to validate linearization hint tables. Offsets and lengths of objects may imply
1543   - // the end of an object to be anywhere between these values.
1544   - qpdf_offset_t end_before_space = m->file->tell();
1545   -
1546   - // skip over spaces
1547   - while (true) {
1548   - char ch;
1549   - if (!m->file->read(&ch, 1)) {
1550   - throw damagedPDF(m->file->tell(), "EOF after endobj");
1551   - }
1552   - if (!isspace(static_cast<unsigned char>(ch))) {
1553   - m->file->seek(-1, SEEK_CUR);
1554   - break;
  1519 + // skip over spaces
  1520 + while (true) {
  1521 + char ch;
  1522 + if (!m->file->read(&ch, 1)) {
  1523 + throw damagedPDF(m->file->tell(), "EOF after endobj");
  1524 + }
  1525 + if (!isspace(static_cast<unsigned char>(ch))) {
  1526 + m->file->seek(-1, SEEK_CUR);
  1527 + break;
  1528 + }
1555 1529 }
  1530 + m->objects.updateCache(og, oh.obj_sp(), end_before_space, m->file->tell());
1556 1531 }
1557   - m->objects.updateCache(og, oh.obj_sp(), end_before_space, m->file->tell());
1558 1532 }
1559 1533  
1560 1534 QPDFObjectHandle
... ... @@ -1564,7 +1538,7 @@ Objects::readObjectAtOffset(
1564 1538 auto og = read_object_start(offset);
1565 1539 auto oh = readObject(description, og);
1566 1540  
1567   - if (!m->objects.isUnresolved(og)) {
  1541 + if (!oh || !m->objects.isUnresolved(og)) {
1568 1542 return oh;
1569 1543 }
1570 1544  
... ... @@ -1804,8 +1778,9 @@ Objects::resolveObjectsInStream(int obj_stream_number)
1804 1778 if (entry != m->xref_table.end() && entry->second.getType() == 2 &&
1805 1779 entry->second.getObjStreamNumber() == obj_stream_number) {
1806 1780 is::OffsetBuffer in("", {b_start + obj_offset, obj_size}, obj_offset);
1807   - auto oh = readObjectInStream(in, obj_stream_number, obj_id);
1808   - updateCache(og, oh.obj_sp(), end_before_space, end_after_space);
  1781 + if (auto oh = QPDFParser::parse(in, obj_stream_number, obj_id, m->tokenizer, qpdf)) {
  1782 + updateCache(og, oh.obj_sp(), end_before_space, end_after_space);
  1783 + }
1809 1784 } else {
1810 1785 QTC::TC("qpdf", "QPDF not caching overridden objstm object");
1811 1786 }
... ...
libqpdf/qpdf/QPDFParser.hh
... ... @@ -41,8 +41,8 @@ class QPDFParser
41 41 QPDFObjectHandle::StringDecrypter* decrypter,
42 42 QPDF* context);
43 43  
44   - // For use by QPDF. Return parsed object and whether it is empty.
45   - static std::pair<QPDFObjectHandle, bool> parse(
  44 + // For use by QPDF.
  45 + static QPDFObjectHandle parse(
46 46 InputSource& input,
47 47 std::string const& object_description,
48 48 qpdf::Tokenizer& tokenizer,
... ... @@ -50,7 +50,7 @@ class QPDFParser
50 50 QPDF& context,
51 51 bool sanity_checks);
52 52  
53   - static std::pair<QPDFObjectHandle, bool> parse(
  53 + static QPDFObjectHandle parse(
54 54 qpdf::is::OffsetBuffer& input,
55 55 int stream_id,
56 56 int obj_id,
... ... @@ -112,8 +112,8 @@ class QPDFParser
112 112 int null_count{0};
113 113 };
114 114  
115   - QPDFObjectHandle parse(bool& empty, bool content_stream);
116   - QPDFObjectHandle parse_first(bool& empty, bool content_stream);
  115 + QPDFObjectHandle parse(bool content_stream = false);
  116 + QPDFObjectHandle parse_first(bool content_stream);
117 117 QPDFObjectHandle parseRemainder(bool content_stream);
118 118 void add(std::shared_ptr<QPDFObject>&& obj);
119 119 void addNull();
... ... @@ -158,6 +158,7 @@ class QPDFParser
158 158 int int_count{0};
159 159 long long int_buffer[2]{0, 0};
160 160 qpdf_offset_t last_offset_buffer[2]{0, 0};
  161 + bool empty_{false};
161 162 };
162 163  
163 164 #endif // QPDFPARSER_HH
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -1039,7 +1039,6 @@ class QPDF::Doc::Objects: Common
1039 1039 QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og);
1040 1040 void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
1041 1041 void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
1042   - QPDFObjectHandle readObjectInStream(qpdf::is::OffsetBuffer& input, int stream_id, int obj_id);
1043 1042 size_t recoverStreamLength(
1044 1043 std::shared_ptr<InputSource> input, QPDFObjGen og, qpdf_offset_t stream_offset);
1045 1044  
... ...
qpdf/qtest/qpdf/issue-100.out
... ... @@ -7,7 +7,6 @@ WARNING: issue-100.pdf (trailer, offset 950): recovered trailer has no /Root ent
7 7 WARNING: issue-100.pdf (trailer, offset 488): stream keyword found in trailer
8 8 WARNING: issue-100.pdf (trailer, offset 418): recovered trailer has no /Root entry
9 9 WARNING: issue-100.pdf (object 1 0, offset 83): unexpected dictionary close token
10   -WARNING: issue-100.pdf (object 1 0, offset 87): expected endobj
11 10 WARNING: issue-100.pdf (object 5 0, offset 268): unknown token while reading object; treating as null
12 11 WARNING: issue-100.pdf (object 5 0, offset 286): unknown token while reading object; treating as null
13 12 WARNING: issue-100.pdf (object 5 0, offset 289): unknown token while reading object; treating as null
... ... @@ -15,9 +14,7 @@ WARNING: issue-100.pdf (object 5 0, offset 294): unknown token while reading obj
15 14 WARNING: issue-100.pdf (object 5 0, offset 297): unknown token while reading object; treating as null
16 15 WARNING: issue-100.pdf (object 5 0, offset 304): unknown token while reading object; treating as null
17 16 WARNING: issue-100.pdf (object 5 0, offset 304): too many errors; giving up on reading object
18   -WARNING: issue-100.pdf (object 5 0, offset 308): expected endobj
19 17 WARNING: issue-100.pdf (object 8 0, offset 107): invalid character ()) in hexstring
20   -WARNING: issue-100.pdf (object 8 0, offset 109): expected endobj
21 18 WARNING: issue-100.pdf (object 9 0, offset 527): unknown token while reading object; treating as string
22 19 WARNING: issue-100.pdf (object 9 0, offset 529): expected endobj
23 20 WARNING: issue-100.pdf (object 10 0, offset 573): expected endobj
... ...
qpdf/qtest/qpdf/issue-101.out
... ... @@ -8,7 +8,6 @@ WARNING: issue-101.pdf (object 11 0, offset 637): unknown token while reading ob
8 8 WARNING: issue-101.pdf (object 11 0, offset 639): unknown token while reading object; treating as null
9 9 WARNING: issue-101.pdf (object 11 0, offset 644): unknown token while reading object; treating as null
10 10 WARNING: issue-101.pdf (object 11 0, offset 644): too many errors; giving up on reading object
11   -WARNING: issue-101.pdf (object 11 0, offset 647): expected endobj
12 11 WARNING: issue-101.pdf (trailer, offset 4433): recovered trailer has no /Root entry
13 12 WARNING: issue-101.pdf (trailer, offset 4183): stream keyword found in trailer
14 13 WARNING: issue-101.pdf (trailer, offset 4113): recovered trailer has no /Root entry
... ... @@ -40,9 +39,7 @@ WARNING: issue-101.pdf (object 7 0, offset 3866): unknown token while reading ob
40 39 WARNING: issue-101.pdf (object 7 0, offset 3873): unknown token while reading object; treating as null
41 40 WARNING: issue-101.pdf (object 7 0, offset 3879): unknown token while reading object; treating as null
42 41 WARNING: issue-101.pdf (object 7 0, offset 3879): too many errors; giving up on reading object
43   -WARNING: issue-101.pdf (object 7 0, offset 3888): expected endobj
44 42 WARNING: issue-101.pdf (object 8 0, offset 4067): invalid character ()) in hexstring
45   -WARNING: issue-101.pdf (object 8 0, offset 4069): expected endobj
46 43 WARNING: issue-101.pdf (object 9 0, offset 2832): unknown token while reading object; treating as string
47 44 WARNING: issue-101.pdf (object 9 0, offset 2834): expected endobj
48 45 qpdf: issue-101.pdf: unable to find trailer dictionary while recovering damaged file
... ...
qpdf/qtest/qpdf/issue-150.out
1 1 WARNING: issue-150.pdf: can't find PDF header
2 2 WARNING: issue-150.pdf (xref stream: object 8 0, offset 56): treating object as null because of error during parsing : overflow/underflow converting 9900000000000000000 to 64-bit integer
3   -WARNING: issue-150.pdf (xref stream: object 8 0, offset 75): expected endobj
4 3 WARNING: issue-150.pdf: file is damaged
5 4 WARNING: issue-150.pdf (offset 4): xref not found
6 5 WARNING: issue-150.pdf: Attempting to reconstruct cross-reference table
  6 +WARNING: issue-150.pdf (object 8 0): object has offset 0
7 7 qpdf: issue-150.pdf: unable to find trailer dictionary while recovering damaged file
... ...
qpdf/qtest/qpdf/issue-1503.out
... ... @@ -6,11 +6,8 @@ WARNING: issue-1503.pdf (object 31 0, offset 813): unknown token while reading o
6 6 WARNING: issue-1503.pdf (object 31 0, offset 851): unknown token while reading object; treating as null
7 7 WARNING: issue-1503.pdf (object 31 0, offset 856): unknown token while reading object; treating as null
8 8 WARNING: issue-1503.pdf (object 31 0, offset 861): unexpected 'endobj' or 'endstream' while reading object; giving up on reading object
9   -WARNING: issue-1503.pdf (object 31 0, offset 871): expected endobj
10 9 WARNING: issue-1503.pdf (object 38 0, offset 1126): unexpected 'endobj' or 'endstream' while reading object; giving up on reading object
11   -WARNING: issue-1503.pdf (object 38 0, offset 1133): expected endobj
12 10 WARNING: issue-1503.pdf (object 40 0, offset 1195): unexpected array close token; giving up on reading object
13   -WARNING: issue-1503.pdf (object 40 0, offset 1198): expected endobj
14 11 WARNING: issue-1503.pdf (object 41 0, offset 1359): stream dictionary lacks /Length key
15 12 WARNING: issue-1503.pdf (object 41 0, offset 1411): attempting to recover stream length
16 13 WARNING: issue-1503.pdf (object 41 0, offset 1411): recovered stream length: 54
... ... @@ -22,9 +19,7 @@ WARNING: issue-1503.pdf (object 44 0, offset 1814): name with stray # will not w
22 19 WARNING: issue-1503.pdf (object 44 0, offset 1821): unknown token while reading object; treating as null
23 20 WARNING: issue-1503.pdf (object 44 0, offset 1826): unknown token while reading object; treating as null
24 21 WARNING: issue-1503.pdf (object 44 0, offset 1826): too many errors; giving up on reading object
25   -WARNING: issue-1503.pdf (object 44 0, offset 1829): expected endobj
26 22 WARNING: issue-1503.pdf (object 46 0, offset 1923): unexpected array close token; giving up on reading object
27   -WARNING: issue-1503.pdf (object 46 0, offset 1926): expected endobj
28 23 WARNING: issue-1503.pdf (object 47 0, offset 2087): stream dictionary lacks /Length key
29 24 WARNING: issue-1503.pdf (object 47 0, offset 2139): attempting to recover stream length
30 25 WARNING: issue-1503.pdf (object 47 0, offset 2139): recovered stream length: 54
... ... @@ -59,8 +54,6 @@ WARNING: issue-1503.pdf (object 151 0, offset 3836): unknown token while reading
59 54 WARNING: issue-1503.pdf (object 151 0, offset 3958): unknown token while reading object; treating as null
60 55 WARNING: issue-1503.pdf (object 152 0, offset 4088): parse error while reading object
61 56 WARNING: issue-1503.pdf (object 152 0, offset 4088): unexpected EOF
62   -WARNING: issue-1503.pdf (object 152 0, offset 4088): expected endobj
63   -WARNING: issue-1503.pdf (object 152 0, offset 4088): EOF after endobj
64 57 WARNING: issue-1503.pdf (object 155 0, offset 162): unknown token while reading object; treating as null
65 58 WARNING: issue-1503.pdf (object 155 0, offset 342): unknown token while reading object; treating as null
66 59 WARNING: issue-1503.pdf (object 155 0, offset 345): unknown token while reading object; treating as null
... ...
qpdf/qtest/qpdf/issue-99.out
... ... @@ -8,15 +8,12 @@ WARNING: issue-99.pdf (object 1 0, offset 815): unknown token while reading obje
8 8 WARNING: issue-99.pdf (object 1 0, offset 835): unknown token while reading object; treating as null
9 9 WARNING: issue-99.pdf (object 1 0, offset 855): unknown token while reading object; treating as null
10 10 WARNING: issue-99.pdf (object 1 0, offset 855): too many errors; giving up on reading object
11   -WARNING: issue-99.pdf (object 1 0, offset 858): expected endobj
12 11 WARNING: issue-99.pdf (object 2 0, offset 64): expected endobj
13 12 WARNING: issue-99.pdf (object 5 0, offset 2452): unknown token while reading object; treating as string
14 13 WARNING: issue-99.pdf (object 6 0, offset 2506): unexpected array close token; giving up on reading object
15   -WARNING: issue-99.pdf (object 6 0, offset 2507): expected endobj
16 14 WARNING: issue-99.pdf (object 10 0, offset 3708): expected dictionary keys but found non-name objects; ignoring
17 15 WARNING: issue-99.pdf (object 11 0, offset 4485): unknown token while reading object; treating as null
18 16 WARNING: issue-99.pdf (object 11 0, offset 4497): unexpected array close token; giving up on reading object
19   -WARNING: issue-99.pdf (object 11 0, offset 4499): expected endobj
20 17 WARNING: issue-99.pdf: unable to find trailer dictionary while recovering damaged file
21 18 WARNING: object 1 0: Pages tree includes non-dictionary object; ignoring
22 19 WARNING: object 1 0: operation for dictionary attempted on object of type null: ignoring key replacement request
... ...
qpdf/qtest/qpdf/parse-object.out
... ... @@ -6,6 +6,4 @@ WARNING: parsed object: treating unexpected brace token as null
6 6 WARNING: parsed object: treating unexpected brace token as null
7 7 WARNING: parsed object: unexpected dictionary close token
8 8 WARNING: bad-parse.qdf (object 7 0, offset 1121): unexpected EOF
9   -WARNING: bad-parse.qdf (object 7 0, offset 1121): expected endobj
10   -WARNING: bad-parse.qdf (object 7 0, offset 1121): EOF after endobj
11 9 test 31 done
... ...