Commit ddd78c1b7f53f09710431d58cd94659271f325cc

Authored by Jay Berkenbilt
1 parent 84cd53f5

Fix QPDFObjectHandle::shallowCopy

It's not really a shallow copy. It just doesn't cross indirect object
boundaries. The old implementation had a bug that would cause multiple
shallow copies of the same object to share memory, which was not the
intention.
include/qpdf/QPDFObjectHandle.hh
... ... @@ -514,10 +514,11 @@ class QPDFObjectHandle
514 514 QPDF_DLL
515 515 QPDF* getOwningQPDF();
516 516  
517   - // Create a shallow copy of an object as a direct object. Since
518   - // this is a shallow copy, for dictionaries and arrays, any keys
519   - // or items that were indirect objects will still be indirect
520   - // objects that point to the same place.
  517 + // Create a shallow of an object as a direct object, but do not
  518 + // traverse across indirect object boundaries. That means that,
  519 + // for dictionaries and arrays, any keys or items that were
  520 + // indirect objects will still be indirect objects that point to
  521 + // the same place.
521 522 QPDF_DLL
522 523 QPDFObjectHandle shallowCopy();
523 524  
... ... @@ -880,7 +881,7 @@ class QPDFObjectHandle
880 881 void objectWarning(std::string const& warning);
881 882 void assertType(char const* type_name, bool istype);
882 883 void dereference();
883   - void makeDirectInternal(std::set<int>& visited);
  884 + void copyObject(std::set<QPDFObjGen>& visited, bool cross_indirect);
884 885 void releaseResolved();
885 886 static void setObjectDescriptionFromInput(
886 887 QPDFObjectHandle, QPDF*, std::string const&,
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -2025,11 +2025,14 @@ QPDFObjectHandle::shallowCopy()
2025 2025 new_obj = *this;
2026 2026 }
2027 2027  
  2028 + std::set<QPDFObjGen> visited;
  2029 + new_obj.copyObject(visited, false);
2028 2030 return new_obj;
2029 2031 }
2030 2032  
2031 2033 void
2032   -QPDFObjectHandle::makeDirectInternal(std::set<int>& visited)
  2034 +QPDFObjectHandle::copyObject(std::set<QPDFObjGen>& visited,
  2035 + bool cross_indirect)
2033 2036 {
2034 2037 assertInitialized();
2035 2038  
... ... @@ -2040,17 +2043,17 @@ QPDFObjectHandle::makeDirectInternal(std::set&lt;int&gt;&amp; visited)
2040 2043 "attempt to make a stream into a direct object");
2041 2044 }
2042 2045  
2043   - int cur_objid = this->m->objid;
2044   - if (cur_objid != 0)
  2046 + QPDFObjGen cur_og(this->m->objid, this->m->generation);
  2047 + if (cur_og.getObj() != 0)
2045 2048 {
2046   - if (visited.count(cur_objid))
  2049 + if (visited.count(cur_og))
2047 2050 {
2048 2051 QTC::TC("qpdf", "QPDFObjectHandle makeDirect loop");
2049 2052 throw std::runtime_error(
2050 2053 "loop detected while converting object from "
2051 2054 "indirect to direct");
2052 2055 }
2053   - visited.insert(cur_objid);
  2056 + visited.insert(cur_og);
2054 2057 }
2055 2058  
2056 2059 if (isReserved())
... ... @@ -2105,7 +2108,10 @@ QPDFObjectHandle::makeDirectInternal(std::set&lt;int&gt;&amp; visited)
2105 2108 for (int i = 0; i < n; ++i)
2106 2109 {
2107 2110 items.push_back(getArrayItem(i));
2108   - items.back().makeDirectInternal(visited);
  2111 + if (cross_indirect || (! items.back().isIndirect()))
  2112 + {
  2113 + items.back().copyObject(visited, cross_indirect);
  2114 + }
2109 2115 }
2110 2116 new_obj = new QPDF_Array(items);
2111 2117 }
... ... @@ -2118,7 +2124,10 @@ QPDFObjectHandle::makeDirectInternal(std::set&lt;int&gt;&amp; visited)
2118 2124 iter != keys.end(); ++iter)
2119 2125 {
2120 2126 items[*iter] = getKey(*iter);
2121   - items[*iter].makeDirectInternal(visited);
  2127 + if (cross_indirect || (! items[*iter].isIndirect()))
  2128 + {
  2129 + items[*iter].copyObject(visited, cross_indirect);
  2130 + }
2122 2131 }
2123 2132 new_obj = new QPDF_Dictionary(items);
2124 2133 }
... ... @@ -2130,17 +2139,17 @@ QPDFObjectHandle::makeDirectInternal(std::set&lt;int&gt;&amp; visited)
2130 2139  
2131 2140 this->m->obj = new_obj;
2132 2141  
2133   - if (cur_objid)
  2142 + if (cur_og.getObj())
2134 2143 {
2135   - visited.erase(cur_objid);
  2144 + visited.erase(cur_og);
2136 2145 }
2137 2146 }
2138 2147  
2139 2148 void
2140 2149 QPDFObjectHandle::makeDirect()
2141 2150 {
2142   - std::set<int> visited;
2143   - makeDirectInternal(visited);
  2151 + std::set<QPDFObjGen> visited;
  2152 + copyObject(visited, true);
2144 2153 }
2145 2154  
2146 2155 void
... ...
qpdf/qtest/qpdf/good10.qdf
... ... @@ -5,17 +5,22 @@
5 5 %% Original object ID: 1 0
6 6 1 0 obj
7 7 <<
8   - /Pages 2 0 R
  8 + /Pages 3 0 R
9 9 /Type /Catalog
10 10 >>
11 11 endobj
12 12  
13   -%% Original object ID: 2 0
  13 +%% Original object ID: 8 0
14 14 2 0 obj
  15 +null
  16 +endobj
  17 +
  18 +%% Original object ID: 2 0
  19 +3 0 obj
15 20 <<
16 21 /Count 1
17 22 /Kids [
18   - 3 0 R
  23 + 4 0 R
19 24 ]
20 25 /Type /Pages
21 26 >>
... ... @@ -23,21 +28,21 @@ endobj
23 28  
24 29 %% Page 1
25 30 %% Original object ID: 3 0
26   -3 0 obj
  31 +4 0 obj
27 32 <<
28   - /Contents 4 0 R
  33 + /Contents 5 0 R
29 34 /MediaBox [
30 35 0
31 36 0
32 37 612
33 38 792
34 39 ]
35   - /Parent 2 0 R
  40 + /Parent 3 0 R
36 41 /Resources <<
37 42 /Font <<
38   - /F1 6 0 R
  43 + /F1 7 0 R
39 44 >>
40   - /ProcSet 7 0 R
  45 + /ProcSet 8 0 R
41 46 >>
42 47 /Type /Page
43 48 >>
... ... @@ -45,9 +50,9 @@ endobj
45 50  
46 51 %% Contents for page 1
47 52 %% Original object ID: 4 0
48   -4 0 obj
  53 +5 0 obj
49 54 <<
50   - /Length 5 0 R
  55 + /Length 6 0 R
51 56 >>
52 57 stream
53 58 BT
... ... @@ -58,12 +63,12 @@ ET
58 63 endstream
59 64 endobj
60 65  
61   -5 0 obj
  66 +6 0 obj
62 67 44
63 68 endobj
64 69  
65 70 %% Original object ID: 6 0
66   -6 0 obj
  71 +7 0 obj
67 72 <<
68 73 /BaseFont /Helvetica
69 74 /Encoding /WinAnsiEncoding
... ... @@ -74,7 +79,7 @@ endobj
74 79 endobj
75 80  
76 81 %% Original object ID: 5 0
77   -7 0 obj
  82 +8 0 obj
78 83 [
79 84 /PDF
80 85 /Text
... ... @@ -82,29 +87,30 @@ endobj
82 87 endobj
83 88  
84 89 xref
85   -0 8
  90 +0 9
86 91 0000000000 65535 f
87 92 0000000052 00000 n
88 93 0000000133 00000 n
89   -0000000242 00000 n
90   -0000000484 00000 n
91   -0000000583 00000 n
92   -0000000629 00000 n
93   -0000000774 00000 n
  94 +0000000181 00000 n
  95 +0000000290 00000 n
  96 +0000000532 00000 n
  97 +0000000631 00000 n
  98 +0000000677 00000 n
  99 +0000000822 00000 n
94 100 trailer <<
95 101 /QTest [
96 102 1
97 103 (2)
98   - null
  104 + 2 0 R
99 105 0.0
100 106 -0.0
101 107 0.
102 108 -0.
103 109 ]
104 110 /Root 1 0 R
105   - /Size 8
  111 + /Size 9
106 112 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
107 113 >>
108 114 startxref
109   -809
  115 +857
110 116 %%EOF
... ...