/src/qpdf/libqpdf/QPDFParser.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <qpdf/QPDFParser.hh> |
2 | | |
3 | | #include <qpdf/QPDF.hh> |
4 | | #include <qpdf/QPDFObjGen.hh> |
5 | | #include <qpdf/QPDFObjectHandle.hh> |
6 | | #include <qpdf/QPDFObject_private.hh> |
7 | | #include <qpdf/QPDF_Array.hh> |
8 | | #include <qpdf/QPDF_Bool.hh> |
9 | | #include <qpdf/QPDF_Dictionary.hh> |
10 | | #include <qpdf/QPDF_InlineImage.hh> |
11 | | #include <qpdf/QPDF_Integer.hh> |
12 | | #include <qpdf/QPDF_Name.hh> |
13 | | #include <qpdf/QPDF_Null.hh> |
14 | | #include <qpdf/QPDF_Operator.hh> |
15 | | #include <qpdf/QPDF_Real.hh> |
16 | | #include <qpdf/QPDF_Reserved.hh> |
17 | | #include <qpdf/QPDF_Stream.hh> |
18 | | #include <qpdf/QPDF_String.hh> |
19 | | #include <qpdf/QTC.hh> |
20 | | #include <qpdf/QUtil.hh> |
21 | | |
22 | | #include <memory> |
23 | | |
24 | | using ObjectPtr = std::shared_ptr<QPDFObject>; |
25 | | |
26 | | QPDFObjectHandle |
27 | | QPDFParser::parse(bool& empty, bool content_stream) |
28 | 172k | { |
29 | | // This method must take care not to resolve any objects. Don't check the type of any object |
30 | | // without first ensuring that it is a direct object. Otherwise, doing so may have the side |
31 | | // effect of reading the object and changing the file pointer. If you do this, it will cause a |
32 | | // logic error to be thrown from QPDF::inParse(). |
33 | | |
34 | 172k | QPDF::ParseGuard pg(context); |
35 | 172k | empty = false; |
36 | 172k | start = input->tell(); |
37 | | |
38 | 172k | if (!tokenizer.nextToken(*input, object_description)) { |
39 | 1.20k | warn(tokenizer.getErrorMessage()); |
40 | 1.20k | } |
41 | | |
42 | 172k | switch (tokenizer.getType()) { |
43 | 1.25k | case QPDFTokenizer::tt_eof: |
44 | 1.25k | if (content_stream) { |
45 | | // In content stream mode, leave object uninitialized to indicate EOF |
46 | 0 | return {}; |
47 | 0 | } |
48 | 1.25k | QTC::TC("qpdf", "QPDFParser eof in parse"); |
49 | 1.25k | warn("unexpected EOF"); |
50 | 1.25k | return {QPDF_Null::create()}; |
51 | | |
52 | 1.20k | case QPDFTokenizer::tt_bad: |
53 | 1.20k | QTC::TC("qpdf", "QPDFParser bad token in parse"); |
54 | 1.20k | return {QPDF_Null::create()}; |
55 | | |
56 | 6 | case QPDFTokenizer::tt_brace_open: |
57 | 9 | case QPDFTokenizer::tt_brace_close: |
58 | 9 | QTC::TC("qpdf", "QPDFParser bad brace"); |
59 | 9 | warn("treating unexpected brace token as null"); |
60 | 9 | return {QPDF_Null::create()}; |
61 | | |
62 | 92 | case QPDFTokenizer::tt_array_close: |
63 | 92 | QTC::TC("qpdf", "QPDFParser bad array close"); |
64 | 92 | warn("treating unexpected array close token as null"); |
65 | 92 | return {QPDF_Null::create()}; |
66 | | |
67 | 232 | case QPDFTokenizer::tt_dict_close: |
68 | 232 | QTC::TC("qpdf", "QPDFParser bad dictionary close"); |
69 | 232 | warn("unexpected dictionary close token"); |
70 | 232 | return {QPDF_Null::create()}; |
71 | | |
72 | 2.19k | case QPDFTokenizer::tt_array_open: |
73 | 122k | case QPDFTokenizer::tt_dict_open: |
74 | 122k | stack.clear(); |
75 | 122k | stack.emplace_back( |
76 | 122k | input, |
77 | 122k | (tokenizer.getType() == QPDFTokenizer::tt_array_open) ? st_array : st_dictionary_key); |
78 | 122k | frame = &stack.back(); |
79 | 122k | return parseRemainder(content_stream); |
80 | | |
81 | 3 | case QPDFTokenizer::tt_bool: |
82 | 3 | return withDescription<QPDF_Bool>(tokenizer.getValue() == "true"); |
83 | | |
84 | 259 | case QPDFTokenizer::tt_null: |
85 | 259 | return {QPDF_Null::create()}; |
86 | | |
87 | 38.6k | case QPDFTokenizer::tt_integer: |
88 | 38.6k | return withDescription<QPDF_Integer>(QUtil::string_to_ll(tokenizer.getValue().c_str())); |
89 | | |
90 | 258 | case QPDFTokenizer::tt_real: |
91 | 258 | return withDescription<QPDF_Real>(tokenizer.getValue()); |
92 | | |
93 | 3.23k | case QPDFTokenizer::tt_name: |
94 | 3.23k | return withDescription<QPDF_Name>(tokenizer.getValue()); |
95 | | |
96 | 4.50k | case QPDFTokenizer::tt_word: |
97 | 4.50k | { |
98 | 4.50k | auto const& value = tokenizer.getValue(); |
99 | 4.50k | if (content_stream) { |
100 | 0 | return withDescription<QPDF_Operator>(value); |
101 | 4.50k | } else if (value == "endobj") { |
102 | | // We just saw endobj without having read anything. Treat this as a null and do |
103 | | // not move the input source's offset. |
104 | 71 | input->seek(input->getLastOffset(), SEEK_SET); |
105 | 71 | empty = true; |
106 | 71 | return {QPDF_Null::create()}; |
107 | 4.43k | } else { |
108 | 4.43k | QTC::TC("qpdf", "QPDFParser treat word as string"); |
109 | 4.43k | warn("unknown token while reading object; treating as string"); |
110 | 4.43k | return withDescription<QPDF_String>(value); |
111 | 4.43k | } |
112 | 4.50k | } |
113 | | |
114 | 181 | case QPDFTokenizer::tt_string: |
115 | 181 | if (decrypter) { |
116 | 0 | std::string s{tokenizer.getValue()}; |
117 | 0 | decrypter->decryptString(s); |
118 | 0 | return withDescription<QPDF_String>(s); |
119 | 181 | } else { |
120 | 181 | return withDescription<QPDF_String>(tokenizer.getValue()); |
121 | 181 | } |
122 | | |
123 | 0 | default: |
124 | 0 | warn("treating unknown token type as null while reading object"); |
125 | 0 | return {QPDF_Null::create()}; |
126 | 172k | } |
127 | 172k | } |
128 | | |
129 | | QPDFObjectHandle |
130 | | QPDFParser::parseRemainder(bool content_stream) |
131 | 122k | { |
132 | | // This method must take care not to resolve any objects. Don't check the type of any object |
133 | | // without first ensuring that it is a direct object. Otherwise, doing so may have the side |
134 | | // effect of reading the object and changing the file pointer. If you do this, it will cause a |
135 | | // logic error to be thrown from QPDF::inParse(). |
136 | | |
137 | 122k | bad_count = 0; |
138 | 122k | bool b_contents = false; |
139 | | |
140 | 4.45M | while (true) { |
141 | 4.45M | if (!tokenizer.nextToken(*input, object_description)) { |
142 | 11.3k | warn(tokenizer.getErrorMessage()); |
143 | 11.3k | } |
144 | 4.45M | ++good_count; // optimistically |
145 | | |
146 | 4.45M | if (int_count != 0) { |
147 | | // Special handling of indirect references. Treat integer tokens as part of an indirect |
148 | | // reference until proven otherwise. |
149 | 1.23M | if (tokenizer.getType() == QPDFTokenizer::tt_integer) { |
150 | 622k | if (++int_count > 2) { |
151 | | // Process the oldest buffered integer. |
152 | 159k | addInt(int_count); |
153 | 159k | } |
154 | 622k | last_offset_buffer[int_count % 2] = input->getLastOffset(); |
155 | 622k | int_buffer[int_count % 2] = QUtil::string_to_ll(tokenizer.getValue().c_str()); |
156 | 622k | continue; |
157 | | |
158 | 622k | } else if ( |
159 | 614k | int_count >= 2 && tokenizer.getType() == QPDFTokenizer::tt_word && |
160 | 614k | tokenizer.getValue() == "R") { |
161 | 383k | if (context == nullptr) { |
162 | 0 | QTC::TC("qpdf", "QPDFParser indirect without context"); |
163 | 0 | throw std::logic_error("QPDFParser::parse called without context on an object " |
164 | 0 | "with indirect references"); |
165 | 0 | } |
166 | 383k | auto id = QIntC::to_int(int_buffer[(int_count - 1) % 2]); |
167 | 383k | auto gen = QIntC::to_int(int_buffer[(int_count) % 2]); |
168 | 383k | if (!(id < 1 || gen < 0 || gen >= 65535)) { |
169 | 381k | add(QPDF::ParseGuard::getObject(context, id, gen, parse_pdf)); |
170 | 381k | } else { |
171 | 2.08k | QTC::TC("qpdf", "QPDFParser invalid objgen"); |
172 | 2.08k | addNull(); |
173 | 2.08k | } |
174 | 383k | int_count = 0; |
175 | 383k | continue; |
176 | | |
177 | 383k | } else if (int_count > 0) { |
178 | | // Process the buffered integers before processing the current token. |
179 | 230k | if (int_count > 1) { |
180 | 78.4k | addInt(int_count - 1); |
181 | 78.4k | } |
182 | 230k | addInt(int_count); |
183 | 230k | int_count = 0; |
184 | 230k | } |
185 | 1.23M | } |
186 | | |
187 | 3.44M | switch (tokenizer.getType()) { |
188 | 1.36k | case QPDFTokenizer::tt_eof: |
189 | 1.36k | warn("parse error while reading object"); |
190 | 1.36k | if (content_stream) { |
191 | | // In content stream mode, leave object uninitialized to indicate EOF |
192 | 0 | return {}; |
193 | 0 | } |
194 | 1.36k | QTC::TC("qpdf", "QPDFParser eof in parseRemainder"); |
195 | 1.36k | warn("unexpected EOF"); |
196 | 1.36k | return {QPDF_Null::create()}; |
197 | | |
198 | 10.9k | case QPDFTokenizer::tt_bad: |
199 | 10.9k | QTC::TC("qpdf", "QPDFParser bad token in parseRemainder"); |
200 | 10.9k | if (tooManyBadTokens()) { |
201 | 674 | return {QPDF_Null::create()}; |
202 | 674 | } |
203 | 10.2k | addNull(); |
204 | 10.2k | continue; |
205 | | |
206 | 710 | case QPDFTokenizer::tt_brace_open: |
207 | 1.08k | case QPDFTokenizer::tt_brace_close: |
208 | 1.08k | QTC::TC("qpdf", "QPDFParser bad brace in parseRemainder"); |
209 | 1.08k | warn("treating unexpected brace token as null"); |
210 | 1.08k | if (tooManyBadTokens()) { |
211 | 124 | return {QPDF_Null::create()}; |
212 | 124 | } |
213 | 956 | addNull(); |
214 | 956 | continue; |
215 | | |
216 | 109k | case QPDFTokenizer::tt_array_close: |
217 | 109k | if (frame->state == st_array) { |
218 | 106k | auto object = QPDF_Array::create(std::move(frame->olist), frame->null_count > 100); |
219 | 106k | setDescription(object, frame->offset - 1); |
220 | | // The `offset` points to the next of "[". Set the rewind offset to point to the |
221 | | // beginning of "[". This has been explicitly tested with whitespace surrounding the |
222 | | // array start delimiter. getLastOffset points to the array end token and therefore |
223 | | // can't be used here. |
224 | 106k | if (stack.size() <= 1) { |
225 | 1.88k | return object; |
226 | 1.88k | } |
227 | 104k | stack.pop_back(); |
228 | 104k | frame = &stack.back(); |
229 | 104k | add(std::move(object)); |
230 | 104k | } else { |
231 | 3.20k | QTC::TC("qpdf", "QPDFParser bad array close in parseRemainder"); |
232 | 3.20k | warn("treating unexpected array close token as null"); |
233 | 3.20k | if (tooManyBadTokens()) { |
234 | 164 | return {QPDF_Null::create()}; |
235 | 164 | } |
236 | 3.03k | addNull(); |
237 | 3.03k | } |
238 | 107k | continue; |
239 | | |
240 | 214k | case QPDFTokenizer::tt_dict_close: |
241 | 214k | if (frame->state <= st_dictionary_value) { |
242 | | // Attempt to recover more or less gracefully from invalid dictionaries. |
243 | 211k | auto& dict = frame->dict; |
244 | | |
245 | 211k | if (frame->state == st_dictionary_value) { |
246 | 1.20k | QTC::TC("qpdf", "QPDFParser no val for last key"); |
247 | 1.20k | warn( |
248 | 1.20k | frame->offset, |
249 | 1.20k | "dictionary ended prematurely; using null as value for last key"); |
250 | 1.20k | dict[frame->key] = QPDF_Null::create(); |
251 | 1.20k | } |
252 | | |
253 | 211k | if (!frame->olist.empty()) { |
254 | 12.3k | fixMissingKeys(); |
255 | 12.3k | } |
256 | | |
257 | 211k | if (!frame->contents_string.empty() && dict.count("/Type") && |
258 | 211k | dict["/Type"].isNameAndEquals("/Sig") && dict.count("/ByteRange") && |
259 | 211k | dict.count("/Contents") && dict["/Contents"].isString()) { |
260 | 0 | dict["/Contents"] = QPDFObjectHandle::newString(frame->contents_string); |
261 | 0 | dict["/Contents"].setParsedOffset(frame->contents_offset); |
262 | 0 | } |
263 | 211k | auto object = QPDF_Dictionary::create(std::move(dict)); |
264 | 211k | setDescription(object, frame->offset - 2); |
265 | | // The `offset` points to the next of "<<". Set the rewind offset to point to the |
266 | | // beginning of "<<". This has been explicitly tested with whitespace surrounding |
267 | | // the dictionary start delimiter. getLastOffset points to the dictionary end token |
268 | | // and therefore can't be used here. |
269 | 211k | if (stack.size() <= 1) { |
270 | 113k | return object; |
271 | 113k | } |
272 | 98.3k | stack.pop_back(); |
273 | 98.3k | frame = &stack.back(); |
274 | 98.3k | add(std::move(object)); |
275 | 98.3k | } else { |
276 | 2.30k | QTC::TC("qpdf", "QPDFParser bad dictionary close in parseRemainder"); |
277 | 2.30k | warn("unexpected dictionary close token"); |
278 | 2.30k | if (tooManyBadTokens()) { |
279 | 44 | return {QPDF_Null::create()}; |
280 | 44 | } |
281 | 2.26k | addNull(); |
282 | 2.26k | } |
283 | 100k | continue; |
284 | | |
285 | 131k | case QPDFTokenizer::tt_array_open: |
286 | 243k | case QPDFTokenizer::tt_dict_open: |
287 | 243k | if (stack.size() > 499) { |
288 | 46 | QTC::TC("qpdf", "QPDFParser too deep"); |
289 | 46 | warn("ignoring excessively deeply nested data structure"); |
290 | 46 | return {QPDF_Null::create()}; |
291 | 243k | } else { |
292 | 243k | b_contents = false; |
293 | 243k | stack.emplace_back( |
294 | 243k | input, |
295 | 243k | (tokenizer.getType() == QPDFTokenizer::tt_array_open) ? st_array |
296 | 243k | : st_dictionary_key); |
297 | 243k | frame = &stack.back(); |
298 | 243k | continue; |
299 | 243k | } |
300 | | |
301 | 2.10k | case QPDFTokenizer::tt_bool: |
302 | 2.10k | addScalar<QPDF_Bool>(tokenizer.getValue() == "true"); |
303 | 2.10k | continue; |
304 | | |
305 | 344k | case QPDFTokenizer::tt_null: |
306 | 344k | addNull(); |
307 | 344k | continue; |
308 | | |
309 | 614k | case QPDFTokenizer::tt_integer: |
310 | 614k | if (!content_stream) { |
311 | | // Buffer token in case it is part of an indirect reference. |
312 | 614k | last_offset_buffer[1] = input->getLastOffset(); |
313 | 614k | int_buffer[1] = QUtil::string_to_ll(tokenizer.getValue().c_str()); |
314 | 614k | int_count = 1; |
315 | 614k | } else { |
316 | 0 | addScalar<QPDF_Integer>(QUtil::string_to_ll(tokenizer.getValue().c_str())); |
317 | 0 | } |
318 | 614k | continue; |
319 | | |
320 | 31.4k | case QPDFTokenizer::tt_real: |
321 | 31.4k | addScalar<QPDF_Real>(tokenizer.getValue()); |
322 | 31.4k | continue; |
323 | | |
324 | 1.75M | case QPDFTokenizer::tt_name: |
325 | 1.75M | if (frame->state == st_dictionary_key) { |
326 | 788k | frame->key = tokenizer.getValue(); |
327 | 788k | frame->state = st_dictionary_value; |
328 | 788k | b_contents = decrypter && frame->key == "/Contents"; |
329 | 788k | continue; |
330 | 967k | } else { |
331 | 967k | addScalar<QPDF_Name>(tokenizer.getValue()); |
332 | 967k | } |
333 | 967k | continue; |
334 | | |
335 | 967k | case QPDFTokenizer::tt_word: |
336 | 84.6k | if (content_stream) { |
337 | 0 | addScalar<QPDF_Operator>(tokenizer.getValue()); |
338 | 84.6k | } else { |
339 | 84.6k | QTC::TC("qpdf", "QPDFParser treat word as string in parseRemainder"); |
340 | 84.6k | warn("unknown token while reading object; treating as string"); |
341 | 84.6k | if (tooManyBadTokens()) { |
342 | 4.28k | return {QPDF_Null::create()}; |
343 | 4.28k | } |
344 | 80.3k | addScalar<QPDF_String>(tokenizer.getValue()); |
345 | 80.3k | } |
346 | 80.3k | continue; |
347 | | |
348 | 80.3k | case QPDFTokenizer::tt_string: |
349 | 35.6k | { |
350 | 35.6k | auto const& val = tokenizer.getValue(); |
351 | 35.6k | if (decrypter) { |
352 | 0 | if (b_contents) { |
353 | 0 | frame->contents_string = val; |
354 | 0 | frame->contents_offset = input->getLastOffset(); |
355 | 0 | b_contents = false; |
356 | 0 | } |
357 | 0 | std::string s{val}; |
358 | 0 | decrypter->decryptString(s); |
359 | 0 | addScalar<QPDF_String>(s); |
360 | 35.6k | } else { |
361 | 35.6k | addScalar<QPDF_String>(val); |
362 | 35.6k | } |
363 | 35.6k | } |
364 | 35.6k | continue; |
365 | | |
366 | 0 | default: |
367 | 0 | warn("treating unknown token type as null while reading object"); |
368 | 0 | if (tooManyBadTokens()) { |
369 | 0 | return {QPDF_Null::create()}; |
370 | 0 | } |
371 | 0 | addNull(); |
372 | 3.44M | } |
373 | 3.44M | } |
374 | 122k | } |
375 | | |
376 | | void |
377 | | QPDFParser::add(std::shared_ptr<QPDFObject>&& obj) |
378 | 2.17M | { |
379 | 2.17M | if (frame->state != st_dictionary_value) { |
380 | | // If state is st_dictionary_key then there is a missing key. Push onto olist for |
381 | | // processing once the tt_dict_close token has been found. |
382 | 1.39M | frame->olist.emplace_back(std::move(obj)); |
383 | 1.39M | } else { |
384 | 778k | if (auto res = frame->dict.insert_or_assign(frame->key, std::move(obj)); !res.second) { |
385 | 3.65k | warnDuplicateKey(); |
386 | 3.65k | } |
387 | 778k | frame->state = st_dictionary_key; |
388 | 778k | } |
389 | 2.17M | } |
390 | | |
391 | | void |
392 | | QPDFParser::addNull() |
393 | 363k | { |
394 | 363k | const static ObjectPtr null_obj = QPDF_Null::create(); |
395 | | |
396 | 363k | if (frame->state != st_dictionary_value) { |
397 | | // If state is st_dictionary_key then there is a missing key. Push onto olist for |
398 | | // processing once the tt_dict_close token has been found. |
399 | 359k | frame->olist.emplace_back(null_obj); |
400 | 359k | } else { |
401 | 3.63k | if (auto res = frame->dict.insert_or_assign(frame->key, null_obj); !res.second) { |
402 | 49 | warnDuplicateKey(); |
403 | 49 | } |
404 | 3.63k | frame->state = st_dictionary_key; |
405 | 3.63k | } |
406 | 363k | ++frame->null_count; |
407 | 363k | } |
408 | | |
409 | | void |
410 | | QPDFParser::addInt(int count) |
411 | 469k | { |
412 | 469k | auto obj = QPDF_Integer::create(int_buffer[count % 2]); |
413 | 469k | obj->setDescription(context, description, last_offset_buffer[count % 2]); |
414 | 469k | add(std::move(obj)); |
415 | 469k | } |
416 | | |
417 | | template <typename T, typename... Args> |
418 | | void |
419 | | QPDFParser::addScalar(Args&&... args) |
420 | 1.11M | { |
421 | 1.11M | auto obj = T::create(args...); |
422 | 1.11M | obj->setDescription(context, description, input->getLastOffset()); |
423 | 1.11M | add(std::move(obj)); |
424 | 1.11M | } void QPDFParser::addScalar<QPDF_Bool, bool>(bool&&) Line | Count | Source | 420 | 2.10k | { | 421 | 2.10k | auto obj = T::create(args...); | 422 | 2.10k | obj->setDescription(context, description, input->getLastOffset()); | 423 | 2.10k | add(std::move(obj)); | 424 | 2.10k | } |
Unexecuted instantiation: void QPDFParser::addScalar<QPDF_Integer, long long>(long long&&) void QPDFParser::addScalar<QPDF_Real, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 420 | 31.4k | { | 421 | 31.4k | auto obj = T::create(args...); | 422 | 31.4k | obj->setDescription(context, description, input->getLastOffset()); | 423 | 31.4k | add(std::move(obj)); | 424 | 31.4k | } |
void QPDFParser::addScalar<QPDF_Name, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 420 | 967k | { | 421 | 967k | auto obj = T::create(args...); | 422 | 967k | obj->setDescription(context, description, input->getLastOffset()); | 423 | 967k | add(std::move(obj)); | 424 | 967k | } |
Unexecuted instantiation: void QPDFParser::addScalar<QPDF_Operator, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) void QPDFParser::addScalar<QPDF_String, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 420 | 115k | { | 421 | 115k | auto obj = T::create(args...); | 422 | 115k | obj->setDescription(context, description, input->getLastOffset()); | 423 | 115k | add(std::move(obj)); | 424 | 115k | } |
Unexecuted instantiation: void QPDFParser::addScalar<QPDF_String, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) |
425 | | |
426 | | template <typename T, typename... Args> |
427 | | QPDFObjectHandle |
428 | | QPDFParser::withDescription(Args&&... args) |
429 | 46.7k | { |
430 | 46.7k | auto obj = T::create(args...); |
431 | 46.7k | obj->setDescription(context, description, start); |
432 | 46.7k | return {obj}; |
433 | 46.7k | } QPDFObjectHandle QPDFParser::withDescription<QPDF_Bool, bool>(bool&&) Line | Count | Source | 429 | 3 | { | 430 | 3 | auto obj = T::create(args...); | 431 | 3 | obj->setDescription(context, description, start); | 432 | 3 | return {obj}; | 433 | 3 | } |
QPDFObjectHandle QPDFParser::withDescription<QPDF_Integer, long long>(long long&&) Line | Count | Source | 429 | 38.6k | { | 430 | 38.6k | auto obj = T::create(args...); | 431 | 38.6k | obj->setDescription(context, description, start); | 432 | 38.6k | return {obj}; | 433 | 38.6k | } |
QPDFObjectHandle QPDFParser::withDescription<QPDF_Real, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 429 | 258 | { | 430 | 258 | auto obj = T::create(args...); | 431 | 258 | obj->setDescription(context, description, start); | 432 | 258 | return {obj}; | 433 | 258 | } |
QPDFObjectHandle QPDFParser::withDescription<QPDF_Name, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 429 | 3.23k | { | 430 | 3.23k | auto obj = T::create(args...); | 431 | 3.23k | obj->setDescription(context, description, start); | 432 | 3.23k | return {obj}; | 433 | 3.23k | } |
Unexecuted instantiation: QPDFObjectHandle QPDFParser::withDescription<QPDF_Operator, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) QPDFObjectHandle QPDFParser::withDescription<QPDF_String, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 429 | 4.60k | { | 430 | 4.60k | auto obj = T::create(args...); | 431 | 4.60k | obj->setDescription(context, description, start); | 432 | 4.60k | return {obj}; | 433 | 4.60k | } |
Unexecuted instantiation: QPDFObjectHandle QPDFParser::withDescription<QPDF_String, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) |
434 | | |
435 | | void |
436 | | QPDFParser::setDescription(ObjectPtr& obj, qpdf_offset_t parsed_offset) |
437 | 318k | { |
438 | 318k | if (obj) { |
439 | 318k | obj->setDescription(context, description, parsed_offset); |
440 | 318k | } |
441 | 318k | } |
442 | | |
443 | | void |
444 | | QPDFParser::fixMissingKeys() |
445 | 12.3k | { |
446 | 12.3k | std::set<std::string> names; |
447 | 69.0k | for (auto& obj: frame->olist) { |
448 | 69.0k | if (obj->getTypeCode() == ::ot_name) { |
449 | 9 | names.insert(obj->getStringValue()); |
450 | 9 | } |
451 | 69.0k | } |
452 | 12.3k | int next_fake_key = 1; |
453 | 50.0k | for (auto const& item: frame->olist) { |
454 | 50.0k | while (true) { |
455 | 50.0k | const std::string key = "/QPDFFake" + std::to_string(next_fake_key++); |
456 | 50.0k | const bool found_fake = frame->dict.count(key) == 0 && names.count(key) == 0; |
457 | 50.0k | QTC::TC("qpdf", "QPDFParser found fake", (found_fake ? 0 : 1)); |
458 | 50.0k | if (found_fake) { |
459 | 50.0k | warn( |
460 | 50.0k | frame->offset, |
461 | 50.0k | "expected dictionary key but found non-name object; inserting key " + key); |
462 | 50.0k | frame->dict[key] = item; |
463 | 50.0k | break; |
464 | 50.0k | } |
465 | 50.0k | } |
466 | 50.0k | } |
467 | 12.3k | } |
468 | | |
469 | | bool |
470 | | QPDFParser::tooManyBadTokens() |
471 | 101k | { |
472 | 101k | if (good_count <= 4) { |
473 | 63.8k | if (++bad_count > 5) { |
474 | 5.29k | warn("too many errors; giving up on reading object"); |
475 | 5.29k | return true; |
476 | 5.29k | } |
477 | 63.8k | } else { |
478 | 37.9k | bad_count = 1; |
479 | 37.9k | } |
480 | 96.5k | good_count = 0; |
481 | 96.5k | return false; |
482 | 101k | } |
483 | | |
484 | | void |
485 | | QPDFParser::warn(QPDFExc const& e) const |
486 | 172k | { |
487 | | // If parsing on behalf of a QPDF object and want to give a warning, we can warn through the |
488 | | // object. If parsing for some other reason, such as an explicit creation of an object from a |
489 | | // string, then just throw the exception. |
490 | 172k | if (context) { |
491 | 172k | context->warn(e); |
492 | 172k | } else { |
493 | 0 | throw e; |
494 | 0 | } |
495 | 172k | } |
496 | | |
497 | | void |
498 | | QPDFParser::warnDuplicateKey() |
499 | 3.70k | { |
500 | 3.70k | QTC::TC("qpdf", "QPDFParser duplicate dict key"); |
501 | 3.70k | warn( |
502 | 3.70k | frame->offset, |
503 | 3.70k | "dictionary has duplicated key " + frame->key + "; last occurrence overrides earlier ones"); |
504 | 3.70k | } |
505 | | |
506 | | void |
507 | | QPDFParser::warn(qpdf_offset_t offset, std::string const& msg) const |
508 | 172k | { |
509 | 172k | warn(QPDFExc(qpdf_e_damaged_pdf, input->getName(), object_description, offset, msg)); |
510 | 172k | } |
511 | | |
512 | | void |
513 | | QPDFParser::warn(std::string const& msg) const |
514 | 117k | { |
515 | 117k | warn(input->getLastOffset(), msg); |
516 | 117k | } |