/src/qpdf/libqpdf/QPDF_Dictionary.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <qpdf/QPDF_Dictionary.hh> |
2 | | |
3 | | #include <qpdf/JSON_writer.hh> |
4 | | #include <qpdf/QPDFObject_private.hh> |
5 | | #include <qpdf/QPDF_Name.hh> |
6 | | #include <qpdf/QPDF_Null.hh> |
7 | | #include <qpdf/QUtil.hh> |
8 | | |
9 | | using namespace std::literals; |
10 | | |
11 | | QPDF_Dictionary::QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items) : |
12 | | QPDFValue(::ot_dictionary), |
13 | | items(items) |
14 | 31.3k | { |
15 | 31.3k | } |
16 | | |
17 | | QPDF_Dictionary::QPDF_Dictionary(std::map<std::string, QPDFObjectHandle>&& items) : |
18 | | QPDFValue(::ot_dictionary), |
19 | | items(items) |
20 | 0 | { |
21 | 0 | } |
22 | | |
23 | | std::shared_ptr<QPDFObject> |
24 | | QPDF_Dictionary::create(std::map<std::string, QPDFObjectHandle> const& items) |
25 | 24.6k | { |
26 | 24.6k | return do_create(new QPDF_Dictionary(items)); |
27 | 24.6k | } |
28 | | |
29 | | std::shared_ptr<QPDFObject> |
30 | | QPDF_Dictionary::create(std::map<std::string, QPDFObjectHandle>&& items) |
31 | 6.73k | { |
32 | 6.73k | return do_create(new QPDF_Dictionary(items)); |
33 | 6.73k | } |
34 | | |
35 | | std::shared_ptr<QPDFObject> |
36 | | QPDF_Dictionary::copy(bool shallow) |
37 | 0 | { |
38 | 0 | if (shallow) { |
39 | 0 | return create(items); |
40 | 0 | } else { |
41 | 0 | std::map<std::string, QPDFObjectHandle> new_items; |
42 | 0 | for (auto const& item: this->items) { |
43 | 0 | auto value = item.second; |
44 | 0 | new_items[item.first] = value.isIndirect() ? value : value.shallowCopy(); |
45 | 0 | } |
46 | 0 | return create(new_items); |
47 | 0 | } |
48 | 0 | } |
49 | | |
50 | | void |
51 | | QPDF_Dictionary::disconnect() |
52 | 21.4k | { |
53 | 82.3k | for (auto& iter: this->items) { |
54 | 82.3k | QPDFObjectHandle::DisconnectAccess::disconnect(iter.second); |
55 | 82.3k | } |
56 | 21.4k | } |
57 | | |
58 | | std::string |
59 | | QPDF_Dictionary::unparse() |
60 | 0 | { |
61 | 0 | std::string result = "<< "; |
62 | 0 | for (auto& iter: this->items) { |
63 | 0 | if (!iter.second.isNull()) { |
64 | 0 | result += QPDF_Name::normalizeName(iter.first) + " " + iter.second.unparse() + " "; |
65 | 0 | } |
66 | 0 | } |
67 | 0 | result += ">>"; |
68 | 0 | return result; |
69 | 0 | } |
70 | | |
71 | | void |
72 | | QPDF_Dictionary::writeJSON(int json_version, JSON::Writer& p) |
73 | 0 | { |
74 | 0 | p.writeStart('{'); |
75 | 0 | for (auto& iter: this->items) { |
76 | 0 | if (!iter.second.isNull()) { |
77 | 0 | p.writeNext(); |
78 | 0 | if (json_version == 1) { |
79 | 0 | p << "\"" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first)) |
80 | 0 | << "\": "; |
81 | 0 | } else if (auto res = QPDF_Name::analyzeJSONEncoding(iter.first); res.first) { |
82 | 0 | if (res.second) { |
83 | 0 | p << "\"" << iter.first << "\": "; |
84 | 0 | } else { |
85 | 0 | p << "\"" << JSON::Writer::encode_string(iter.first) << "\": "; |
86 | 0 | } |
87 | 0 | } else { |
88 | 0 | p << "\"n:" << JSON::Writer::encode_string(QPDF_Name::normalizeName(iter.first)) |
89 | 0 | << "\": "; |
90 | 0 | } |
91 | 0 | iter.second.writeJSON(json_version, p); |
92 | 0 | } |
93 | 0 | } |
94 | 0 | p.writeEnd('}'); |
95 | 0 | } |
96 | | |
97 | | bool |
98 | | QPDF_Dictionary::hasKey(std::string const& key) |
99 | 26.9k | { |
100 | 26.9k | return ((this->items.count(key) > 0) && (!this->items[key].isNull())); |
101 | 26.9k | } |
102 | | |
103 | | QPDFObjectHandle |
104 | | QPDF_Dictionary::getKey(std::string const& key) |
105 | 13.4k | { |
106 | | // PDF spec says fetching a non-existent key from a dictionary returns the null object. |
107 | 13.4k | auto item = this->items.find(key); |
108 | 13.4k | if (item != this->items.end()) { |
109 | | // May be a null object |
110 | 13.4k | return item->second; |
111 | 13.4k | } else { |
112 | 0 | static auto constexpr msg = " -> dictionary key $VD"sv; |
113 | 0 | return QPDF_Null::create(shared_from_this(), msg, key); |
114 | 0 | } |
115 | 13.4k | } |
116 | | |
117 | | std::set<std::string> |
118 | | QPDF_Dictionary::getKeys() |
119 | 0 | { |
120 | 0 | std::set<std::string> result; |
121 | 0 | for (auto& iter: this->items) { |
122 | 0 | if (!iter.second.isNull()) { |
123 | 0 | result.insert(iter.first); |
124 | 0 | } |
125 | 0 | } |
126 | 0 | return result; |
127 | 0 | } |
128 | | |
129 | | std::map<std::string, QPDFObjectHandle> const& |
130 | | QPDF_Dictionary::getAsMap() const |
131 | 0 | { |
132 | 0 | return this->items; |
133 | 0 | } |
134 | | |
135 | | void |
136 | | QPDF_Dictionary::replaceKey(std::string const& key, QPDFObjectHandle value) |
137 | 103k | { |
138 | 103k | if (value.isNull() && !value.isIndirect()) { |
139 | | // The PDF spec doesn't distinguish between keys with null values and missing keys. Allow |
140 | | // indirect nulls which are equivalent to a dangling reference, which is permitted by the |
141 | | // spec. |
142 | 12.0k | removeKey(key); |
143 | 91.1k | } else { |
144 | | // add or replace value |
145 | 91.1k | this->items[key] = value; |
146 | 91.1k | } |
147 | 103k | } |
148 | | |
149 | | void |
150 | | QPDF_Dictionary::removeKey(std::string const& key) |
151 | 15.2k | { |
152 | | // no-op if key does not exist |
153 | 15.2k | this->items.erase(key); |
154 | 15.2k | } |