/src/qpdf/libqpdf/QPDF_Dictionary.cc
Line | Count | Source |
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 | 117k | { |
12 | 117k | if (auto d = as<QPDF_Dictionary>()) { |
13 | 117k | return d; |
14 | 117k | } |
15 | 0 | throw std::runtime_error("Expected a dictionary but found a non-dictionary object"); |
16 | 0 | return nullptr; // unreachable |
17 | 117k | } |
18 | | |
19 | | QPDFObjectHandle const& |
20 | | BaseHandle::operator[](std::string const& key) const |
21 | 47.4k | { |
22 | 47.4k | if (auto d = as<QPDF_Dictionary>()) { |
23 | 47.4k | auto it = d->items.find(key); |
24 | 47.4k | if (it != d->items.end()) { |
25 | 23.7k | return it->second; |
26 | 23.7k | } |
27 | 47.4k | } |
28 | 23.7k | static const QPDFObjectHandle null_obj; |
29 | 23.7k | return null_obj; |
30 | 47.4k | } |
31 | | |
32 | | bool |
33 | | BaseHandle::contains(std::string const& key) const |
34 | 31.6k | { |
35 | 31.6k | return !(*this)[key].null(); |
36 | 31.6k | } |
37 | | |
38 | | std::set<std::string> |
39 | | BaseDictionary::getKeys() |
40 | 0 | { |
41 | 0 | std::set<std::string> result; |
42 | 0 | for (auto& iter: dict()->items) { |
43 | 0 | if (!iter.second.null()) { |
44 | 0 | result.insert(iter.first); |
45 | 0 | } |
46 | 0 | } |
47 | 0 | return result; |
48 | 0 | } |
49 | | |
50 | | std::map<std::string, QPDFObjectHandle> const& |
51 | | BaseDictionary::getAsMap() const |
52 | 0 | { |
53 | 0 | return dict()->items; |
54 | 0 | } |
55 | | |
56 | | size_t |
57 | | BaseHandle::erase(const std::string& key) |
58 | 11.6k | { |
59 | | // no-op if key does not exist |
60 | 11.6k | if (auto d = as<QPDF_Dictionary>()) { |
61 | 11.6k | return d->items.erase(key); |
62 | 11.6k | } |
63 | 0 | return 0; |
64 | 11.6k | } |
65 | | |
66 | | void |
67 | | BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value) |
68 | 117k | { |
69 | 117k | auto d = dict(); |
70 | 117k | if (value.null() && !value.indirect()) { |
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 | 20.7k | d->items.erase(key); |
75 | 97.0k | } else { |
76 | | // add or replace value |
77 | 97.0k | d->items[key] = value; |
78 | 97.0k | } |
79 | 117k | } |
80 | | |
81 | | Dictionary::Dictionary(std::map<std::string, QPDFObjectHandle>&& dict) : |
82 | 0 | BaseDictionary(std::move(dict)) |
83 | 0 | { |
84 | 0 | } |
85 | | |
86 | | Dictionary::Dictionary(std::shared_ptr<QPDFObject> const& obj) : |
87 | 117k | BaseDictionary(obj) |
88 | 117k | { |
89 | 117k | } |
90 | | |
91 | | Dictionary |
92 | | Dictionary::empty() |
93 | 0 | { |
94 | 0 | return Dictionary(std::map<std::string, QPDFObjectHandle>()); |
95 | 0 | } |
96 | | |
97 | | void |
98 | | QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const |
99 | 117k | { |
100 | 117k | auto qpdf = getOwningQPDF(); |
101 | 117k | auto item_qpdf = item.getOwningQPDF(); |
102 | 117k | if (qpdf && item_qpdf && qpdf != item_qpdf) { |
103 | 0 | throw std::logic_error( |
104 | 0 | "Attempting to add an object from a different QPDF. Use " |
105 | 0 | "QPDF::copyForeignObject to add objects from another file."); |
106 | 0 | } |
107 | 117k | } |
108 | | |
109 | | bool |
110 | | QPDFObjectHandle::hasKey(std::string const& key) const |
111 | 31.6k | { |
112 | 31.6k | if (Dictionary dict = *this) { |
113 | 31.6k | return dict.contains(key); |
114 | 31.6k | } else { |
115 | 0 | typeWarning("dictionary", "returning false for a key containment request"); |
116 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); |
117 | 0 | return false; |
118 | 0 | } |
119 | 31.6k | } |
120 | | |
121 | | QPDFObjectHandle |
122 | | QPDFObjectHandle::getKey(std::string const& key) const |
123 | 15.8k | { |
124 | 15.8k | if (auto result = (*this)[key]) { |
125 | 15.8k | return result; |
126 | 15.8k | } |
127 | 0 | if (isDictionary()) { |
128 | 0 | static auto constexpr msg = " -> dictionary key $VD"sv; |
129 | 0 | return QPDF_Null::create(obj, msg, key); |
130 | 0 | } |
131 | 0 | typeWarning("dictionary", "returning null for attempted key retrieval"); |
132 | 0 | static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv; |
133 | 0 | return QPDF_Null::create(obj, msg, ""); |
134 | 0 | } |
135 | | |
136 | | QPDFObjectHandle |
137 | | QPDFObjectHandle::getKeyIfDict(std::string const& key) const |
138 | 0 | { |
139 | 0 | return isNull() ? newNull() : getKey(key); |
140 | 0 | } |
141 | | |
142 | | std::set<std::string> |
143 | | QPDFObjectHandle::getKeys() const |
144 | 0 | { |
145 | 0 | if (auto dict = as_dictionary(strict)) { |
146 | 0 | return dict.getKeys(); |
147 | 0 | } |
148 | 0 | typeWarning("dictionary", "treating as empty"); |
149 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); |
150 | 0 | return {}; |
151 | 0 | } |
152 | | |
153 | | std::map<std::string, QPDFObjectHandle> |
154 | | QPDFObjectHandle::getDictAsMap() const |
155 | 0 | { |
156 | 0 | if (auto dict = as_dictionary(strict)) { |
157 | 0 | return dict.getAsMap(); |
158 | 0 | } |
159 | 0 | typeWarning("dictionary", "treating as empty"); |
160 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap"); |
161 | 0 | return {}; |
162 | 0 | } |
163 | | |
164 | | void |
165 | | QPDFObjectHandle::replaceKey(std::string const& key, QPDFObjectHandle const& value) |
166 | 117k | { |
167 | 117k | if (auto dict = as_dictionary(strict)) { |
168 | 117k | checkOwnership(value); |
169 | 117k | dict.replaceKey(key, value); |
170 | 117k | return; |
171 | 117k | } |
172 | 0 | typeWarning("dictionary", "ignoring key replacement request"); |
173 | 0 | QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey"); |
174 | 0 | } |
175 | | |
176 | | QPDFObjectHandle |
177 | | QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value) |
178 | 0 | { |
179 | 0 | replaceKey(key, value); |
180 | 0 | return value; |
181 | 0 | } |
182 | | |
183 | | QPDFObjectHandle |
184 | | QPDFObjectHandle::replaceKeyAndGetOld(std::string const& key, QPDFObjectHandle const& value) |
185 | 0 | { |
186 | 0 | QPDFObjectHandle old = removeKeyAndGetOld(key); |
187 | 0 | replaceKey(key, value); |
188 | 0 | return old; |
189 | 0 | } |
190 | | |
191 | | void |
192 | | QPDFObjectHandle::removeKey(std::string const& key) |
193 | 11.6k | { |
194 | 11.6k | if (erase(key) || isDictionary()) { |
195 | 11.6k | return; |
196 | 11.6k | } |
197 | 0 | typeWarning("dictionary", "ignoring key removal request"); |
198 | 0 | } |
199 | | |
200 | | QPDFObjectHandle |
201 | | QPDFObjectHandle::removeKeyAndGetOld(std::string const& key) |
202 | 0 | { |
203 | 0 | auto result = (*this)[key]; |
204 | 0 | erase(key); |
205 | 0 | return result ? result : newNull(); |
206 | 0 | } |