/src/qpdf/libqpdf/QPDF_Dictionary.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <qpdf/QPDFObjectHandle_private.hh> |
2 | | |
3 | | #include <qpdf/QPDFObject_private.hh> |
4 | | #include <qpdf/QTC.hh> |
5 | | |
6 | | using namespace std::literals; |
7 | | using namespace qpdf; |
8 | | |
9 | | QPDF_Dictionary* |
10 | | BaseDictionary::dict() const |
11 | 0 | { |
12 | 0 | if (auto d = as<QPDF_Dictionary>()) { |
13 | 0 | return d; |
14 | 0 | } |
15 | 0 | throw std::runtime_error("Expected a dictionary but found a non-dictionary object"); |
16 | 0 | return nullptr; // unreachable |
17 | 0 | } |
18 | | |
19 | | bool |
20 | | BaseDictionary::hasKey(std::string const& key) const |
21 | 0 | { |
22 | 0 | auto d = dict(); |
23 | 0 | return d->items.contains(key) && !d->items[key].null(); |
24 | 0 | } |
25 | | |
26 | | QPDFObjectHandle |
27 | | BaseDictionary::getKey(std::string const& key) const |
28 | 0 | { |
29 | 0 | auto d = dict(); |
30 | | |
31 | | // PDF spec says fetching a non-existent key from a dictionary returns the null object. |
32 | 0 | auto item = d->items.find(key); |
33 | 0 | if (item != d->items.end()) { |
34 | | // May be a null object |
35 | 0 | return item->second; |
36 | 0 | } |
37 | 0 | static auto constexpr msg = " -> dictionary key $VD"sv; |
38 | 0 | return QPDF_Null::create(obj, msg, key); |
39 | 0 | } |
40 | | |
41 | | std::set<std::string> |
42 | | BaseDictionary::getKeys() |
43 | 0 | { |
44 | 0 | std::set<std::string> result; |
45 | 0 | for (auto& iter: dict()->items) { |
46 | 0 | if (!iter.second.isNull()) { |
47 | 0 | result.insert(iter.first); |
48 | 0 | } |
49 | 0 | } |
50 | 0 | return result; |
51 | 0 | } |
52 | | |
53 | | std::map<std::string, QPDFObjectHandle> const& |
54 | | BaseDictionary::getAsMap() const |
55 | 0 | { |
56 | 0 | return dict()->items; |
57 | 0 | } |
58 | | |
59 | | void |
60 | | BaseDictionary::removeKey(std::string const& key) |
61 | 0 | { |
62 | | // no-op if key does not exist |
63 | 0 | dict()->items.erase(key); |
64 | 0 | } |
65 | | |
66 | | void |
67 | | BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value) |
68 | 0 | { |
69 | 0 | auto d = dict(); |
70 | 0 | if (value.isNull() && !value.isIndirect()) { |
71 | | // The PDF spec doesn't distinguish between keys with null values and missing keys. |
72 | | // Allow indirect nulls which are equivalent to a dangling reference, which is |
73 | | // permitted by the spec. |
74 | 0 | d->items.erase(key); |
75 | 0 | } else { |
76 | | // add or replace value |
77 | 0 | d->items[key] = value; |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | void |
82 | | QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const |
83 | 0 | { |
84 | 0 | auto qpdf = getOwningQPDF(); |
85 | 0 | auto item_qpdf = item.getOwningQPDF(); |
86 | 0 | if (qpdf && item_qpdf && qpdf != item_qpdf) { |
87 | 0 | QTC::TC("qpdf", "QPDFObjectHandle check ownership"); |
88 | 0 | throw std::logic_error( |
89 | 0 | "Attempting to add an object from a different QPDF. Use " |
90 | 0 | "QPDF::copyForeignObject to add objects from another file."); |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | bool |
95 | | QPDFObjectHandle::hasKey(std::string const& key) const |
96 | 0 | { |
97 | 0 | auto dict = as_dictionary(strict); |
98 | 0 | if (dict) { |
99 | 0 | return dict.hasKey(key); |
100 | 0 | } else { |
101 | 0 | typeWarning("dictionary", "returning false for a key containment request"); |
102 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); |
103 | 0 | return false; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | QPDFObjectHandle |
108 | | QPDFObjectHandle::getKey(std::string const& key) const |
109 | 0 | { |
110 | 0 | if (auto dict = as_dictionary(strict)) { |
111 | 0 | return dict.getKey(key); |
112 | 0 | } |
113 | 0 | typeWarning("dictionary", "returning null for attempted key retrieval"); |
114 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey"); |
115 | 0 | static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv; |
116 | 0 | return QPDF_Null::create(obj, msg, ""); |
117 | 0 | } |
118 | | |
119 | | QPDFObjectHandle |
120 | | QPDFObjectHandle::getKeyIfDict(std::string const& key) const |
121 | 0 | { |
122 | 0 | return isNull() ? newNull() : getKey(key); |
123 | 0 | } |
124 | | |
125 | | std::set<std::string> |
126 | | QPDFObjectHandle::getKeys() const |
127 | 0 | { |
128 | 0 | if (auto dict = as_dictionary(strict)) { |
129 | 0 | return dict.getKeys(); |
130 | 0 | } |
131 | 0 | typeWarning("dictionary", "treating as empty"); |
132 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); |
133 | 0 | return {}; |
134 | 0 | } |
135 | | |
136 | | std::map<std::string, QPDFObjectHandle> |
137 | | QPDFObjectHandle::getDictAsMap() const |
138 | 0 | { |
139 | 0 | if (auto dict = as_dictionary(strict)) { |
140 | 0 | return dict.getAsMap(); |
141 | 0 | } |
142 | 0 | typeWarning("dictionary", "treating as empty"); |
143 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap"); |
144 | 0 | return {}; |
145 | 0 | } |
146 | | |
147 | | void |
148 | | QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value) |
149 | 0 | { |
150 | 0 | if (auto dict = as_dictionary(strict)) { |
151 | 0 | checkOwnership(value); |
152 | 0 | dict.replaceKey(key, value); |
153 | 0 | return; |
154 | 0 | } |
155 | 0 | typeWarning("dictionary", "ignoring key replacement request"); |
156 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey"); |
157 | 0 | } |
158 | | |
159 | | QPDFObjectHandle |
160 | | QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value) |
161 | 0 | { |
162 | 0 | replaceKey(key, value); |
163 | 0 | return value; |
164 | 0 | } |
165 | | |
166 | | QPDFObjectHandle |
167 | | QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value) |
168 | 0 | { |
169 | 0 | QPDFObjectHandle old = removeKeyAndGetOld(key); |
170 | 0 | replaceKey(key, value); |
171 | 0 | return old; |
172 | 0 | } |
173 | | |
174 | | void |
175 | | QPDFObjectHandle::removeKey(std::string const& key) |
176 | 0 | { |
177 | 0 | if (auto dict = as_dictionary(strict)) { |
178 | 0 | dict.removeKey(key); |
179 | 0 | return; |
180 | 0 | } |
181 | 0 | typeWarning("dictionary", "ignoring key removal request"); |
182 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey"); |
183 | 0 | } |
184 | | |
185 | | QPDFObjectHandle |
186 | | QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) |
187 | 0 | { |
188 | 0 | auto result = QPDFObjectHandle::newNull(); |
189 | 0 | if (auto dict = as_dictionary(strict)) { |
190 | 0 | result = dict.getKey(key); |
191 | 0 | } |
192 | 0 | removeKey(key); |
193 | 0 | return result; |
194 | 0 | } |