Commit 137ee9a14450029f50e64905890ed7972a9d8c9f

Authored by m-holger
1 parent 7e1237af

Refactor `enqueueObject` to `enqueue`, update related method signatures and logi…

…c across `QPDFWriter` for consistency and improved readability.
libqpdf/QPDFWriter.cc
... ... @@ -395,7 +395,7 @@ class QPDFWriter::Members: QPDF::Doc::Writer
395 395 void preserveObjectStreams();
396 396 void generateObjectStreams();
397 397 void initializeSpecialStreams();
398   - void enqueueObject(QPDFObjectHandle object);
  398 + void enqueue(QPDFObjectHandle const& object);
399 399 void enqueueObjectsStandard();
400 400 void enqueueObjectsPCLm();
401 401 void enqueuePart(std::vector<QPDFObjectHandle>& part);
... ... @@ -1366,18 +1366,19 @@ QPDFWriter::Members::assignCompressedObjectNumbers(QPDFObjGen og)
1366 1366 }
1367 1367  
1368 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 1383 if (qdf_mode && object.isStreamOfType("/XRef")) {
1383 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 1398 // stream. Object streams always have generation 0.
1398 1399 // Detect loops by storing invalid object ID -1, which will get overwritten later.
1399 1400 o.renumber = -1;
1400   - enqueueObject(qpdf.getObject(o.object_stream, 0));
  1401 + enqueue(qpdf.getObject(o.object_stream, 0));
1401 1402 } else {
1402 1403 object_queue.emplace_back(object);
1403 1404 o.renumber = next_objid++;
... ... @@ -1413,25 +1414,25 @@ QPDFWriter::Members::enqueueObject(QPDFObjectHandle object)
1413 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 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 1440 QPDFWriter::Members::unparseChild(QPDFObjectHandle const& child, size_t level, int flags)
1440 1441 {
1441 1442 if (!linearized) {
1442   - enqueueObject(child);
  1443 + enqueue(child);
1443 1444 }
1444   - if (child.isIndirect()) {
  1445 + if (child.indirect()) {
1445 1446 write(obj[child].renumber).write(" 0 R");
1446 1447 } else {
1447 1448 unparseObject(child, level, flags);
... ... @@ -2490,7 +2491,7 @@ void
2490 2491 QPDFWriter::Members::enqueuePart(std::vector<QPDFObjectHandle>& part)
2491 2492 {
2492 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 2505 write("<<");
2505 2506 if (V >= 4) {
2506 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 2509 // The PDF spec says the /Length key is optional, but the PDF previewer on some versions of
2509 2510 // MacOS won't open encrypted files without it.
2510 2511 write((V < 5) ? " /Length 16 >> >>" : " /Length 32 >> >>");
... ... @@ -3108,19 +3109,19 @@ QPDFWriter::Members::enqueueObjectsStandard()
3108 3109 {
3109 3110 if (preserve_unreferenced_objects) {
3110 3111 for (auto const& oh: qpdf.getAllObjects()) {
3111   - enqueueObject(oh);
  3112 + enqueue(oh);
3112 3113 }
3113 3114 }
3114 3115  
3115 3116 // Put root first on queue.
3116 3117 auto trailer = trimmed_trailer();
3117   - enqueueObject(trailer["/Root"]);
  3118 + enqueue(trailer["/Root"]);
3118 3119  
3119 3120 // Next place any other objects referenced from the trailer dictionary into the queue, handling
3120 3121 // direct objects recursively. Root is already there, so enqueuing it a second time is a no-op.
3121 3122 for (auto& item: trailer) {
3122 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 3135  
3135 3136 // enqueue all pages first
3136 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 3141 // enqueue all the strips for each page
3144 3142 for (auto& image: Dictionary(page["/Resources"]["/XObject"])) {
3145 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 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 14  
15 15 my $td = new TestDriver('specific-file');
16 16  
17   -my $n_tests = 11;
  17 +my $n_tests = 12;
18 18  
19 19 # Special PDF files that caused problems at some point
20 20  
... ... @@ -63,6 +63,11 @@ $td-&gt;runtest(&quot;Acroform /DR with indirect subkey&quot;,
63 63 $td->runtest("check output",
64 64 {$td->FILE => "a.pdf"},
65 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 72 cleanup();
68 73 $td->report($n_tests);
... ...