From d1ebe30ff63a2f79da041e2d0c4718523db1beda Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Thu, 21 Jun 2012 16:14:34 -0400 Subject: [PATCH] Add QPDFObjectHandle::shallowCopy() --- ChangeLog | 2 + include/qpdf/QPDFObjectHandle.hh | 7 +++ libqpdf/QPDFObjectHandle.cc | 32 +++++++++++ qpdf/qpdf.testcov | 4 ++ qpdf/qtest/qpdf.test | 14 ++++- qpdf/qtest/qpdf/shallow_array-out.pdf | 40 ++++++++++++++ qpdf/qtest/qpdf/shallow_array.pdf | 80 +++++++++++++++++++++++++++ qpdf/qtest/qpdf/shallow_stream.out | 1 + qpdf/test_driver.cc | 38 ++++++++++--- 9 files changed, 208 insertions(+), 10 deletions(-) create mode 100644 qpdf/qtest/qpdf/shallow_array-out.pdf create mode 100644 qpdf/qtest/qpdf/shallow_array.pdf create mode 100644 qpdf/qtest/qpdf/shallow_stream.out diff --git a/ChangeLog b/ChangeLog index 3b164e56..d67a82e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2012-06-21 Jay Berkenbilt + * QPDFObjectHandle: add shallowCopy() method + * QPDF: add new APIs for adding and removing pages. This includes addPage(), addPageAt(), and removePage(). Also a method updateAllPagesCache() is now available to force update of the diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 3a4bff13..e431d8a7 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -190,6 +190,13 @@ class QPDFObjectHandle QPDF_DLL bool isOrHasName(std::string const&); + // Create a shallow copy of an object as a direct object. Since + // this is a shallow copy, for dictionaries and arrays, any keys + // or items that were indirect objects will still be indirect + // objects that point to the same place. + QPDF_DLL + QPDFObjectHandle shallowCopy(); + // Mutator methods. Use with caution. // Recursively copy this object, making it direct. Throws an diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 53d9414c..4e28c405 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -663,6 +663,38 @@ QPDFObjectHandle::newStream(QPDF* qpdf, PointerHolder data) return result; } +QPDFObjectHandle +QPDFObjectHandle::shallowCopy() +{ + assertInitialized(); + + if (isStream()) + { + QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream"); + throw std::runtime_error( + "attempt to make a shallow copy of a stream"); + } + + QPDFObjectHandle new_obj; + if (isArray()) + { + QTC::TC("qpdf", "QPDFObjectHandle shallow copy array"); + new_obj = newArray(getArrayAsVector()); + } + else if (isDictionary()) + { + QTC::TC("qpdf", "QPDFObjectHandle shallow copy dictionary"); + new_obj = newDictionary(getDictAsMap()); + } + else + { + QTC::TC("qpdf", "QPDFObjectHandle shallow copy scalar"); + new_obj = *this; + } + + return new_obj; +} + void QPDFObjectHandle::makeDirectInternal(std::set& visited) { diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index c7207bc8..5c40037e 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -209,3 +209,7 @@ QPDF insert page 2 QPDF updateAllPagesCache 0 QPDF insert non-indirect page 0 QPDF insert indirect page 0 +QPDFObjectHandle ERR shallow copy stream 0 +QPDFObjectHandle shallow copy array 0 +QPDFObjectHandle shallow copy dictionary 0 +QPDFObjectHandle shallow copy scalar 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 053fb3b2..a1a76ce3 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -144,7 +144,7 @@ $td->runtest("duplicate page", $td->NORMALIZE_NEWLINES); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 37; +$n_tests += 40; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -338,6 +338,18 @@ $td->runtest("check output", {$td->FILE => "c-info-out.pdf"}); unlink "a.pdf" or die; +$td->runtest("shallow copy an array", + {$td->COMMAND => "test_driver 20 shallow_array.pdf"}, + {$td->STRING => "test 20 done\n", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "shallow_array-out.pdf"}); +$td->runtest("shallow copy a stream", + {$td->COMMAND => "test_driver 21 shallow_array.pdf"}, + {$td->FILE => "shallow_stream.out", $td->EXIT_STATUS => 2}, + $td->NORMALIZE_NEWLINES); + show_ntests(); # ---------- $td->notify("--- Error Condition Tests ---"); diff --git a/qpdf/qtest/qpdf/shallow_array-out.pdf b/qpdf/qtest/qpdf/shallow_array-out.pdf new file mode 100644 index 00000000..31d86ecc --- /dev/null +++ b/qpdf/qtest/qpdf/shallow_array-out.pdf @@ -0,0 +1,40 @@ +%PDF-1.3 +%¿÷¢þ +1 0 obj +<< /Pages 2 0 R /Type /Catalog >> +endobj +2 0 obj +<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >> +endobj +3 0 obj +<< /Contents 4 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 5 0 R >> /ProcSet 6 0 R >> /Type /Page >> +endobj +4 0 obj +<< /Length 44 >> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj +5 0 obj +<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >> +endobj +6 0 obj +[ /PDF /Text ] +endobj +xref +0 7 +0000000000 65535 f +0000000015 00000 n +0000000064 00000 n +0000000123 00000 n +0000000266 00000 n +0000000359 00000 n +0000000466 00000 n +trailer << /QTest [ /A 1 0 R /B ] /QTest2 [ /A 1 0 R /B 7 ] /Root 1 0 R /Size 7 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> +startxref +496 +%%EOF diff --git a/qpdf/qtest/qpdf/shallow_array.pdf b/qpdf/qtest/qpdf/shallow_array.pdf new file mode 100644 index 00000000..63c39b09 --- /dev/null +++ b/qpdf/qtest/qpdf/shallow_array.pdf @@ -0,0 +1,80 @@ +%PDF-1.3 +1 0 obj +<< + /Type /Catalog + /Pages 2 0 R +>> +endobj + +2 0 obj +<< + /Type /Pages + /Kids [ + 3 0 R + ] + /Count 1 +>> +endobj + +3 0 obj +<< + /Type /Page + /Parent 2 0 R + /MediaBox [0 0 612 792] + /Contents 4 0 R + /Resources << + /ProcSet 5 0 R + /Font << + /F1 6 0 R + >> + >> +>> +endobj + +4 0 obj +<< + /Length 44 +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +[ + /PDF + /Text +] +endobj + +6 0 obj +<< + /Type /Font + /Subtype /Type1 + /Name /F1 + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding +>> +endobj + +xref +0 7 +0000000000 65535 f +0000000009 00000 n +0000000063 00000 n +0000000135 00000 n +0000000307 00000 n +0000000403 00000 n +0000000438 00000 n +trailer << + /Size 7 + /Root 1 0 R + /QTest [ /A 1 0 R /B ] +>> +startxref +556 +%%EOF diff --git a/qpdf/qtest/qpdf/shallow_stream.out b/qpdf/qtest/qpdf/shallow_stream.out new file mode 100644 index 00000000..fec1aa83 --- /dev/null +++ b/qpdf/qtest/qpdf/shallow_stream.out @@ -0,0 +1 @@ +attempt to make a shallow copy of a stream diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index b2ceca5c..d2869317 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -691,17 +691,15 @@ void runtest(int n, char const* filename) // dictionary and modify it. Using the results of // getDictAsMap to create a new dictionary effectively creates // a shallow copy. - std::map page_dict_keys = - QPDFObjectHandle(pages[0]).getDictAsMap(); + QPDFObjectHandle page_tempate = pages[0]; std::vector new_pages; for (std::vector::iterator iter = contents.begin(); iter != contents.end(); ++iter) { // We will retain indirect object references to other // indirect objects other than page content. - page_dict_keys["/Contents"] = *iter; - QPDFObjectHandle page = - QPDFObjectHandle::newDictionary(page_dict_keys); + QPDFObjectHandle page = page_tempate.shallowCopy(); + page.replaceKey("/Contents", *iter); if (iter == contents.begin()) { // leave direct @@ -745,12 +743,10 @@ void runtest(int n, char const* filename) std::vector const& all_pages = pdf.getAllPages(); QPDFObjectHandle contents = createPageContents(pdf, "New page 10"); - std::map page_dict_keys = - QPDFObjectHandle(all_pages[0]).getDictAsMap(); - page_dict_keys["/Contents"] = contents; QPDFObjectHandle page = pdf.makeIndirectObject( - QPDFObjectHandle::newDictionary(page_dict_keys)); + QPDFObjectHandle(all_pages[0]).shallowCopy()); + page.replaceKey("/Contents", contents); // Insert the page manually. QPDFObjectHandle root = pdf.getRoot(); @@ -807,6 +803,30 @@ void runtest(int n, char const* filename) pdf.addPage(pages[5], false); std::cout << "you can't see this" << std::endl; } + else if (n == 20) + { + // Shallow copy an array + QPDFObjectHandle trailer = pdf.getTrailer(); + QPDFObjectHandle qtest = trailer.getKey("/QTest"); + QPDFObjectHandle copy = qtest.shallowCopy(); + // Append shallow copy of a scalar + copy.appendItem(trailer.getKey("/Size").shallowCopy()); + trailer.replaceKey("/QTest2", copy); + + QPDFWriter w(pdf, "a.pdf"); + w.setStaticID(true); + w.setStreamDataMode(qpdf_s_preserve); + w.write(); + } + else if (n == 21) + { + // Try to shallow copy a stream + std::vector const& pages = pdf.getAllPages(); + QPDFObjectHandle page = pages[0]; + QPDFObjectHandle contents = page.getKey("/Contents"); + contents.shallowCopy(); + std::cout << "you can't see this" << std::endl; + } else { throw std::runtime_error(std::string("invalid test ") +