Commit d1ebe30ff63a2f79da041e2d0c4718523db1beda

Authored by Jay Berkenbilt
1 parent 9689f4cd

Add QPDFObjectHandle::shallowCopy()

ChangeLog
1 1 2012-06-21 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * QPDFObjectHandle: add shallowCopy() method
  4 +
3 5 * QPDF: add new APIs for adding and removing pages. This includes
4 6 addPage(), addPageAt(), and removePage(). Also a method
5 7 updateAllPagesCache() is now available to force update of the
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -190,6 +190,13 @@ class QPDFObjectHandle
190 190 QPDF_DLL
191 191 bool isOrHasName(std::string const&);
192 192  
  193 + // Create a shallow copy of an object as a direct object. Since
  194 + // this is a shallow copy, for dictionaries and arrays, any keys
  195 + // or items that were indirect objects will still be indirect
  196 + // objects that point to the same place.
  197 + QPDF_DLL
  198 + QPDFObjectHandle shallowCopy();
  199 +
193 200 // Mutator methods. Use with caution.
194 201  
195 202 // Recursively copy this object, making it direct. Throws an
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -663,6 +663,38 @@ QPDFObjectHandle::newStream(QPDF* qpdf, PointerHolder&lt;Buffer&gt; data)
663 663 return result;
664 664 }
665 665  
  666 +QPDFObjectHandle
  667 +QPDFObjectHandle::shallowCopy()
  668 +{
  669 + assertInitialized();
  670 +
  671 + if (isStream())
  672 + {
  673 + QTC::TC("qpdf", "QPDFObjectHandle ERR shallow copy stream");
  674 + throw std::runtime_error(
  675 + "attempt to make a shallow copy of a stream");
  676 + }
  677 +
  678 + QPDFObjectHandle new_obj;
  679 + if (isArray())
  680 + {
  681 + QTC::TC("qpdf", "QPDFObjectHandle shallow copy array");
  682 + new_obj = newArray(getArrayAsVector());
  683 + }
  684 + else if (isDictionary())
  685 + {
  686 + QTC::TC("qpdf", "QPDFObjectHandle shallow copy dictionary");
  687 + new_obj = newDictionary(getDictAsMap());
  688 + }
  689 + else
  690 + {
  691 + QTC::TC("qpdf", "QPDFObjectHandle shallow copy scalar");
  692 + new_obj = *this;
  693 + }
  694 +
  695 + return new_obj;
  696 +}
  697 +
666 698 void
667 699 QPDFObjectHandle::makeDirectInternal(std::set<int>& visited)
668 700 {
... ...
qpdf/qpdf.testcov
... ... @@ -209,3 +209,7 @@ QPDF insert page 2
209 209 QPDF updateAllPagesCache 0
210 210 QPDF insert non-indirect page 0
211 211 QPDF insert indirect page 0
  212 +QPDFObjectHandle ERR shallow copy stream 0
  213 +QPDFObjectHandle shallow copy array 0
  214 +QPDFObjectHandle shallow copy dictionary 0
  215 +QPDFObjectHandle shallow copy scalar 0
... ...
qpdf/qtest/qpdf.test
... ... @@ -144,7 +144,7 @@ $td-&gt;runtest(&quot;duplicate page&quot;,
144 144 $td->NORMALIZE_NEWLINES);
145 145 # ----------
146 146 $td->notify("--- Miscellaneous Tests ---");
147   -$n_tests += 37;
  147 +$n_tests += 40;
148 148  
149 149 $td->runtest("qpdf version",
150 150 {$td->COMMAND => "qpdf --version"},
... ... @@ -338,6 +338,18 @@ $td-&gt;runtest(&quot;check output&quot;,
338 338 {$td->FILE => "c-info-out.pdf"});
339 339 unlink "a.pdf" or die;
340 340  
  341 +$td->runtest("shallow copy an array",
  342 + {$td->COMMAND => "test_driver 20 shallow_array.pdf"},
  343 + {$td->STRING => "test 20 done\n", $td->EXIT_STATUS => 0},
  344 + $td->NORMALIZE_NEWLINES);
  345 +$td->runtest("check output",
  346 + {$td->FILE => "a.pdf"},
  347 + {$td->FILE => "shallow_array-out.pdf"});
  348 +$td->runtest("shallow copy a stream",
  349 + {$td->COMMAND => "test_driver 21 shallow_array.pdf"},
  350 + {$td->FILE => "shallow_stream.out", $td->EXIT_STATUS => 2},
  351 + $td->NORMALIZE_NEWLINES);
  352 +
341 353 show_ntests();
342 354 # ----------
343 355 $td->notify("--- Error Condition Tests ---");
... ...
qpdf/qtest/qpdf/shallow_array-out.pdf 0 → 100644
  1 +%PDF-1.3
  2 +%¿÷¢þ
  3 +1 0 obj
  4 +<< /Pages 2 0 R /Type /Catalog >>
  5 +endobj
  6 +2 0 obj
  7 +<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
  8 +endobj
  9 +3 0 obj
  10 +<< /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 >>
  11 +endobj
  12 +4 0 obj
  13 +<< /Length 44 >>
  14 +stream
  15 +BT
  16 + /F1 24 Tf
  17 + 72 720 Td
  18 + (Potato) Tj
  19 +ET
  20 +endstream
  21 +endobj
  22 +5 0 obj
  23 +<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
  24 +endobj
  25 +6 0 obj
  26 +[ /PDF /Text ]
  27 +endobj
  28 +xref
  29 +0 7
  30 +0000000000 65535 f
  31 +0000000015 00000 n
  32 +0000000064 00000 n
  33 +0000000123 00000 n
  34 +0000000266 00000 n
  35 +0000000359 00000 n
  36 +0000000466 00000 n
  37 +trailer << /QTest [ /A 1 0 R /B ] /QTest2 [ /A 1 0 R /B 7 ] /Root 1 0 R /Size 7 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >>
  38 +startxref
  39 +496
  40 +%%EOF
... ...
qpdf/qtest/qpdf/shallow_array.pdf 0 → 100644
  1 +%PDF-1.3
  2 +1 0 obj
  3 +<<
  4 + /Type /Catalog
  5 + /Pages 2 0 R
  6 +>>
  7 +endobj
  8 +
  9 +2 0 obj
  10 +<<
  11 + /Type /Pages
  12 + /Kids [
  13 + 3 0 R
  14 + ]
  15 + /Count 1
  16 +>>
  17 +endobj
  18 +
  19 +3 0 obj
  20 +<<
  21 + /Type /Page
  22 + /Parent 2 0 R
  23 + /MediaBox [0 0 612 792]
  24 + /Contents 4 0 R
  25 + /Resources <<
  26 + /ProcSet 5 0 R
  27 + /Font <<
  28 + /F1 6 0 R
  29 + >>
  30 + >>
  31 +>>
  32 +endobj
  33 +
  34 +4 0 obj
  35 +<<
  36 + /Length 44
  37 +>>
  38 +stream
  39 +BT
  40 + /F1 24 Tf
  41 + 72 720 Td
  42 + (Potato) Tj
  43 +ET
  44 +endstream
  45 +endobj
  46 +
  47 +5 0 obj
  48 +[
  49 + /PDF
  50 + /Text
  51 +]
  52 +endobj
  53 +
  54 +6 0 obj
  55 +<<
  56 + /Type /Font
  57 + /Subtype /Type1
  58 + /Name /F1
  59 + /BaseFont /Helvetica
  60 + /Encoding /WinAnsiEncoding
  61 +>>
  62 +endobj
  63 +
  64 +xref
  65 +0 7
  66 +0000000000 65535 f
  67 +0000000009 00000 n
  68 +0000000063 00000 n
  69 +0000000135 00000 n
  70 +0000000307 00000 n
  71 +0000000403 00000 n
  72 +0000000438 00000 n
  73 +trailer <<
  74 + /Size 7
  75 + /Root 1 0 R
  76 + /QTest [ /A 1 0 R /B ]
  77 +>>
  78 +startxref
  79 +556
  80 +%%EOF
... ...
qpdf/qtest/qpdf/shallow_stream.out 0 → 100644
  1 +attempt to make a shallow copy of a stream
... ...
qpdf/test_driver.cc
... ... @@ -691,17 +691,15 @@ void runtest(int n, char const* filename)
691 691 // dictionary and modify it. Using the results of
692 692 // getDictAsMap to create a new dictionary effectively creates
693 693 // a shallow copy.
694   - std::map<std::string, QPDFObjectHandle> page_dict_keys =
695   - QPDFObjectHandle(pages[0]).getDictAsMap();
  694 + QPDFObjectHandle page_tempate = pages[0];
696 695 std::vector<QPDFObjectHandle> new_pages;
697 696 for (std::vector<QPDFObjectHandle>::iterator iter = contents.begin();
698 697 iter != contents.end(); ++iter)
699 698 {
700 699 // We will retain indirect object references to other
701 700 // indirect objects other than page content.
702   - page_dict_keys["/Contents"] = *iter;
703   - QPDFObjectHandle page =
704   - QPDFObjectHandle::newDictionary(page_dict_keys);
  701 + QPDFObjectHandle page = page_tempate.shallowCopy();
  702 + page.replaceKey("/Contents", *iter);
705 703 if (iter == contents.begin())
706 704 {
707 705 // leave direct
... ... @@ -745,12 +743,10 @@ void runtest(int n, char const* filename)
745 743 std::vector<QPDFObjectHandle> const& all_pages = pdf.getAllPages();
746 744  
747 745 QPDFObjectHandle contents = createPageContents(pdf, "New page 10");
748   - std::map<std::string, QPDFObjectHandle> page_dict_keys =
749   - QPDFObjectHandle(all_pages[0]).getDictAsMap();
750   - page_dict_keys["/Contents"] = contents;
751 746 QPDFObjectHandle page =
752 747 pdf.makeIndirectObject(
753   - QPDFObjectHandle::newDictionary(page_dict_keys));
  748 + QPDFObjectHandle(all_pages[0]).shallowCopy());
  749 + page.replaceKey("/Contents", contents);
754 750  
755 751 // Insert the page manually.
756 752 QPDFObjectHandle root = pdf.getRoot();
... ... @@ -807,6 +803,30 @@ void runtest(int n, char const* filename)
807 803 pdf.addPage(pages[5], false);
808 804 std::cout << "you can't see this" << std::endl;
809 805 }
  806 + else if (n == 20)
  807 + {
  808 + // Shallow copy an array
  809 + QPDFObjectHandle trailer = pdf.getTrailer();
  810 + QPDFObjectHandle qtest = trailer.getKey("/QTest");
  811 + QPDFObjectHandle copy = qtest.shallowCopy();
  812 + // Append shallow copy of a scalar
  813 + copy.appendItem(trailer.getKey("/Size").shallowCopy());
  814 + trailer.replaceKey("/QTest2", copy);
  815 +
  816 + QPDFWriter w(pdf, "a.pdf");
  817 + w.setStaticID(true);
  818 + w.setStreamDataMode(qpdf_s_preserve);
  819 + w.write();
  820 + }
  821 + else if (n == 21)
  822 + {
  823 + // Try to shallow copy a stream
  824 + std::vector<QPDFObjectHandle> const& pages = pdf.getAllPages();
  825 + QPDFObjectHandle page = pages[0];
  826 + QPDFObjectHandle contents = page.getKey("/Contents");
  827 + contents.shallowCopy();
  828 + std::cout << "you can't see this" << std::endl;
  829 + }
810 830 else
811 831 {
812 832 throw std::runtime_error(std::string("invalid test ") +
... ...