Commit 137ee9a14450029f50e64905890ed7972a9d8c9f
1 parent
7e1237af
Refactor `enqueueObject` to `enqueue`, update related method signatures and logi…
…c across `QPDFWriter` for consistency and improved readability.
Showing
4 changed files
with
64 additions
and
45 deletions
libqpdf/QPDFWriter.cc
| @@ -395,7 +395,7 @@ class QPDFWriter::Members: QPDF::Doc::Writer | @@ -395,7 +395,7 @@ class QPDFWriter::Members: QPDF::Doc::Writer | ||
| 395 | void preserveObjectStreams(); | 395 | void preserveObjectStreams(); |
| 396 | void generateObjectStreams(); | 396 | void generateObjectStreams(); |
| 397 | void initializeSpecialStreams(); | 397 | void initializeSpecialStreams(); |
| 398 | - void enqueueObject(QPDFObjectHandle object); | 398 | + void enqueue(QPDFObjectHandle const& object); |
| 399 | void enqueueObjectsStandard(); | 399 | void enqueueObjectsStandard(); |
| 400 | void enqueueObjectsPCLm(); | 400 | void enqueueObjectsPCLm(); |
| 401 | void enqueuePart(std::vector<QPDFObjectHandle>& part); | 401 | void enqueuePart(std::vector<QPDFObjectHandle>& part); |
| @@ -1366,18 +1366,19 @@ QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) | @@ -1366,18 +1366,19 @@ QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og) | ||
| 1366 | } | 1366 | } |
| 1367 | 1367 | ||
| 1368 | void | 1368 | void |
| 1369 | -QPDFWriter::Members::enqueueObject(QPDFObjectHandle object) | 1369 | +QPDFWriter::Members::enqueue(QPDFObjectHandle const& object) |
| 1370 | { | 1370 | { |
| 1371 | - if (object.isIndirect()) { | ||
| 1372 | - // This owner check can only be done for indirect objects. It is possible for a direct | ||
| 1373 | - // object to have an owning QPDF that is from another file if a direct QPDFObjectHandle from | ||
| 1374 | - // one file was insert into another file without copying. Doing that is safe even if the | ||
| 1375 | - // original QPDF gets destroyed, which just disconnects the QPDFObjectHandle from its owner. | ||
| 1376 | - if (object.getOwningQPDF() != &qpdf) { | ||
| 1377 | - throw std::logic_error( | ||
| 1378 | - "QPDFObjectHandle from different QPDF found while writing. Use " | ||
| 1379 | - "QPDF::copyForeignObject to add objects from another file."); | ||
| 1380 | - } | 1371 | + if (object.indirect()) { |
| 1372 | + util::assertion( | ||
| 1373 | + // This owner check can only be done for indirect objects. It is possible for a direct | ||
| 1374 | + // object to have an owning QPDF that is from another file if a direct QPDFObjectHandle | ||
| 1375 | + // from one file was insert into another file without copying. Doing that is safe even | ||
| 1376 | + // if the original QPDF gets destroyed, which just disconnects the QPDFObjectHandle from | ||
| 1377 | + // its owner. | ||
| 1378 | + object.qpdf() == &qpdf, | ||
| 1379 | + "QPDFObjectHandle from different QPDF found while writing. " | ||
| 1380 | + "Use QPDF::copyForeignObject to add objects from another file." // | ||
| 1381 | + ); | ||
| 1381 | 1382 | ||
| 1382 | if (qdf_mode && object.isStreamOfType("/XRef")) { | 1383 | if (qdf_mode && object.isStreamOfType("/XRef")) { |
| 1383 | // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so | 1384 | // As a special case, do not output any extraneous XRef streams in QDF mode. Doing so |
| @@ -1397,7 +1398,7 @@ QPDFWriter::Members::enqueueObject(QPDFObjectHandle object) | @@ -1397,7 +1398,7 @@ QPDFWriter::Members::enqueueObject(QPDFObjectHandle object) | ||
| 1397 | // stream. Object streams always have generation 0. | 1398 | // stream. Object streams always have generation 0. |
| 1398 | // Detect loops by storing invalid object ID -1, which will get overwritten later. | 1399 | // Detect loops by storing invalid object ID -1, which will get overwritten later. |
| 1399 | o.renumber = -1; | 1400 | o.renumber = -1; |
| 1400 | - enqueueObject(qpdf.getObject(o.object_stream, 0)); | 1401 | + enqueue(qpdf.getObject(o.object_stream, 0)); |
| 1401 | } else { | 1402 | } else { |
| 1402 | object_queue.emplace_back(object); | 1403 | object_queue.emplace_back(object); |
| 1403 | o.renumber = next_objid++; | 1404 | o.renumber = next_objid++; |
| @@ -1413,25 +1414,25 @@ QPDFWriter::Members::enqueueObject(QPDFObjectHandle object) | @@ -1413,25 +1414,25 @@ QPDFWriter::Members::enqueueObject(QPDFObjectHandle object) | ||
| 1413 | ++next_objid; | 1414 | ++next_objid; |
| 1414 | } | 1415 | } |
| 1415 | } | 1416 | } |
| 1416 | - } else if (o.renumber == -1) { | ||
| 1417 | - // This can happen if a specially constructed file indicates that an object stream is | ||
| 1418 | - // inside itself. | ||
| 1419 | } | 1417 | } |
| 1420 | return; | 1418 | return; |
| 1421 | - } else if (!linearized) { | ||
| 1422 | - if (object.isArray()) { | ||
| 1423 | - for (auto& item: object.as_array()) { | ||
| 1424 | - enqueueObject(item); | ||
| 1425 | - } | ||
| 1426 | - } else if (auto d = object.as_dictionary()) { | ||
| 1427 | - for (auto const& item: d) { | ||
| 1428 | - if (!item.second.null()) { | ||
| 1429 | - enqueueObject(item.second); | ||
| 1430 | - } | ||
| 1431 | - } | 1419 | + } |
| 1420 | + | ||
| 1421 | + if (linearized) { | ||
| 1422 | + return; | ||
| 1423 | + } | ||
| 1424 | + | ||
| 1425 | + if (Array array = object) { | ||
| 1426 | + for (auto& item: array) { | ||
| 1427 | + enqueue(item); | ||
| 1428 | + } | ||
| 1429 | + return; | ||
| 1430 | + } | ||
| 1431 | + | ||
| 1432 | + for (auto const& item: Dictionary(object)) { | ||
| 1433 | + if (!item.second.null()) { | ||
| 1434 | + enqueue(item.second); | ||
| 1432 | } | 1435 | } |
| 1433 | - } else { | ||
| 1434 | - // ignore | ||
| 1435 | } | 1436 | } |
| 1436 | } | 1437 | } |
| 1437 | 1438 | ||
| @@ -1439,9 +1440,9 @@ void | @@ -1439,9 +1440,9 @@ void | ||
| 1439 | QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags) | 1440 | QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags) |
| 1440 | { | 1441 | { |
| 1441 | if (!linearized) { | 1442 | if (!linearized) { |
| 1442 | - enqueueObject(child); | 1443 | + enqueue(child); |
| 1443 | } | 1444 | } |
| 1444 | - if (child.isIndirect()) { | 1445 | + if (child.indirect()) { |
| 1445 | write(obj[child].renumber).write(" 0 R"); | 1446 | write(obj[child].renumber).write(" 0 R"); |
| 1446 | } else { | 1447 | } else { |
| 1447 | unparseObject(child, level, flags); | 1448 | unparseObject(child, level, flags); |
| @@ -2490,7 +2491,7 @@ void | @@ -2490,7 +2491,7 @@ void | ||
| 2490 | QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part) | 2491 | QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part) |
| 2491 | { | 2492 | { |
| 2492 | for (auto const& oh: part) { | 2493 | for (auto const& oh: part) { |
| 2493 | - enqueueObject(oh); | 2494 | + enqueue(oh); |
| 2494 | } | 2495 | } |
| 2495 | } | 2496 | } |
| 2496 | 2497 | ||
| @@ -2504,7 +2505,7 @@ QPDFWriter::Members::writeEncryptionDictionary() | @@ -2504,7 +2505,7 @@ QPDFWriter::Members::writeEncryptionDictionary() | ||
| 2504 | write("<<"); | 2505 | write("<<"); |
| 2505 | if (V >= 4) { | 2506 | if (V >= 4) { |
| 2506 | write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM "); | 2507 | write(" /CF << /StdCF << /AuthEvent /DocOpen /CFM "); |
| 2507 | - write(encrypt_use_aes ? ((V < 5) ? "/AESV2" : "/AESV3") : "/V2"); | 2508 | + write(encrypt_use_aes ? (V < 5 ? "/AESV2" : "/AESV3") : "/V2"); |
| 2508 | // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of | 2509 | // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of |
| 2509 | // MacOS won't open encrypted files without it. | 2510 | // MacOS won't open encrypted files without it. |
| 2510 | write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>"); | 2511 | write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>"); |
| @@ -3108,19 +3109,19 @@ QPDFWriter::Members::enqueueObjectsStandard() | @@ -3108,19 +3109,19 @@ QPDFWriter::Members::enqueueObjectsStandard() | ||
| 3108 | { | 3109 | { |
| 3109 | if (preserve_unreferenced_objects) { | 3110 | if (preserve_unreferenced_objects) { |
| 3110 | for (auto const& oh: qpdf.getAllObjects()) { | 3111 | for (auto const& oh: qpdf.getAllObjects()) { |
| 3111 | - enqueueObject(oh); | 3112 | + enqueue(oh); |
| 3112 | } | 3113 | } |
| 3113 | } | 3114 | } |
| 3114 | 3115 | ||
| 3115 | // Put root first on queue. | 3116 | // Put root first on queue. |
| 3116 | auto trailer = trimmed_trailer(); | 3117 | auto trailer = trimmed_trailer(); |
| 3117 | - enqueueObject(trailer["/Root"]); | 3118 | + enqueue(trailer["/Root"]); |
| 3118 | 3119 | ||
| 3119 | // Next place any other objects referenced from the trailer dictionary into the queue, handling | 3120 | // Next place any other objects referenced from the trailer dictionary into the queue, handling |
| 3120 | // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op. | 3121 | // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op. |
| 3121 | for (auto& item: trailer) { | 3122 | for (auto& item: trailer) { |
| 3122 | if (!item.second.null()) { | 3123 | if (!item.second.null()) { |
| 3123 | - enqueueObject(item.second); | 3124 | + enqueue(item.second); |
| 3124 | } | 3125 | } |
| 3125 | } | 3126 | } |
| 3126 | } | 3127 | } |
| @@ -3134,22 +3135,19 @@ QPDFWriter::Members::enqueueObjectsPCLm() | @@ -3134,22 +3135,19 @@ QPDFWriter::Members::enqueueObjectsPCLm() | ||
| 3134 | 3135 | ||
| 3135 | // enqueue all pages first | 3136 | // enqueue all pages first |
| 3136 | for (auto& page: pages) { | 3137 | for (auto& page: pages) { |
| 3137 | - // enqueue page | ||
| 3138 | - enqueueObject(page); | ||
| 3139 | - | ||
| 3140 | - // enqueue page contents stream | ||
| 3141 | - enqueueObject(page["/Contents"]); | 3138 | + enqueue(page); |
| 3139 | + enqueue(page["/Contents"]); | ||
| 3142 | 3140 | ||
| 3143 | // enqueue all the strips for each page | 3141 | // enqueue all the strips for each page |
| 3144 | for (auto& image: Dictionary(page["/Resources"]["/XObject"])) { | 3142 | for (auto& image: Dictionary(page["/Resources"]["/XObject"])) { |
| 3145 | if (!image.second.null()) { | 3143 | if (!image.second.null()) { |
| 3146 | - enqueueObject(image.second); | ||
| 3147 | - enqueueObject(QPDFObjectHandle::newStream(&qpdf, image_transform_content)); | 3144 | + enqueue(image.second); |
| 3145 | + enqueue(qpdf.newStream(image_transform_content)); | ||
| 3148 | } | 3146 | } |
| 3149 | } | 3147 | } |
| 3150 | } | 3148 | } |
| 3151 | 3149 | ||
| 3152 | - enqueueObject(trimmed_trailer()["/Root"]); | 3150 | + enqueue(trimmed_trailer()["/Root"]); |
| 3153 | } | 3151 | } |
| 3154 | 3152 | ||
| 3155 | void | 3153 | void |
qpdf/qtest/qpdf/bad-direct-root.out
0 → 100644
| 1 | +WARNING: bad-direct-root.pdf: can't find PDF header | ||
| 2 | +WARNING: bad-direct-root.pdf: file is damaged | ||
| 3 | +WARNING: bad-direct-root.pdf: can't find startxref | ||
| 4 | +WARNING: bad-direct-root.pdf: Attempting to reconstruct cross-reference table | ||
| 5 | +WARNING: bad-direct-root.pdf (trailer, offset 249): unknown token while reading object; treating as null | ||
| 6 | +WARNING: bad-direct-root.pdf (trailer, offset 261): unknown token while reading object; treating as null | ||
| 7 | +WARNING: bad-direct-root.pdf (trailer, offset 186): expected dictionary keys but found non-name objects; ignoring | ||
| 8 | +WARNING: bad-direct-root.pdf (object 1 0, offset 65): expected endobj | ||
| 9 | +WARNING: bad-direct-root.pdf (object 2 0, offset 114): unknown token while reading object; treating as null | ||
| 10 | +WARNING: bad-direct-root.pdf (object 2 0, offset 122): invalid character (/) in hexstring | ||
| 11 | +WARNING: bad-direct-root.pdf (object 2 0, offset 125): unknown token while reading object; treating as null | ||
| 12 | +WARNING: bad-direct-root.pdf (object 2 0, offset 114): expected dictionary keys but found non-name objects; ignoring | ||
| 13 | +WARNING: bad-direct-root.pdf (object 2 0, offset 141): expected endobj | ||
| 14 | +WARNING: bad-direct-root.pdf, object 2 0 at offset 85: kid 0 (from 0) MediaBox is undefined; setting to letter / ANSI A | ||
| 15 | +WARNING: bad-direct-root.pdf, object 2 0 at offset 85: /Type key should be /Page but is not; overriding | ||
| 16 | +qpdf: error encountered after writing part 4 of linearized data |
qpdf/qtest/qpdf/bad-direct-root.pdf
0 → 100644
No preview for this file type
qpdf/qtest/specific-file.test
| @@ -14,7 +14,7 @@ cleanup(); | @@ -14,7 +14,7 @@ cleanup(); | ||
| 14 | 14 | ||
| 15 | my $td = new TestDriver('specific-file'); | 15 | my $td = new TestDriver('specific-file'); |
| 16 | 16 | ||
| 17 | -my $n_tests = 11; | 17 | +my $n_tests = 12; |
| 18 | 18 | ||
| 19 | # Special PDF files that caused problems at some point | 19 | # Special PDF files that caused problems at some point |
| 20 | 20 | ||
| @@ -63,6 +63,11 @@ $td->runtest("Acroform /DR with indirect subkey", | @@ -63,6 +63,11 @@ $td->runtest("Acroform /DR with indirect subkey", | ||
| 63 | $td->runtest("check output", | 63 | $td->runtest("check output", |
| 64 | {$td->FILE => "a.pdf"}, | 64 | {$td->FILE => "a.pdf"}, |
| 65 | {$td->FILE => "dr-with-indirect-item-out.pdf"}); | 65 | {$td->FILE => "dr-with-indirect-item-out.pdf"}); |
| 66 | +$td->runtest("Bad direct Root", | ||
| 67 | + {$td->COMMAND => | ||
| 68 | + "qpdf --static-id bad-direct-root.pdf --linearize a.pdf"}, | ||
| 69 | + {$td->FILE => "bad-direct-root.out", $td->EXIT_STATUS => 2}, | ||
| 70 | + $td->NORMALIZE_NEWLINES); | ||
| 66 | 71 | ||
| 67 | cleanup(); | 72 | cleanup(); |
| 68 | $td->report($n_tests); | 73 | $td->report($n_tests); |