Coverage Report

Created: 2025-11-11 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
86.9k
{
12
86.9k
    if (auto d = as<QPDF_Dictionary>()) {
13
86.9k
        return d;
14
86.9k
    }
15
0
    throw std::runtime_error("Expected a dictionary but found a non-dictionary object");
16
0
    return nullptr; // unreachable
17
86.9k
}
18
19
QPDFObjectHandle const&
20
BaseHandle::operator[](std::string const& key) const
21
1.47M
{
22
1.47M
    if (auto d = as<QPDF_Dictionary>()) {
23
1.44M
        auto it = d->items.find(key);
24
1.44M
        if (it != d->items.end()) {
25
739k
            return it->second;
26
739k
        }
27
1.44M
    }
28
731k
    static const QPDFObjectHandle null_obj;
29
731k
    return null_obj;
30
1.47M
}
31
32
bool
33
BaseHandle::contains(std::string const& key) const
34
115k
{
35
115k
    return !(*this)[key].null();
36
115k
}
37
38
std::set<std::string>
39
BaseDictionary::getKeys()
40
14.3k
{
41
14.3k
    std::set<std::string> result;
42
64.9k
    for (auto& iter: dict()->items) {
43
64.9k
        if (!iter.second.null()) {
44
55.5k
            result.insert(iter.first);
45
55.5k
        }
46
64.9k
    }
47
14.3k
    return result;
48
14.3k
}
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
395k
{
59
    // no-op if key does not exist
60
395k
    if (auto d = as<QPDF_Dictionary>()) {
61
395k
        return d->items.erase(key);
62
395k
    }
63
0
    return 0;
64
395k
}
65
66
void
67
BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
68
72.5k
{
69
72.5k
    auto d = dict();
70
72.5k
    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
0
        d->items.erase(key);
75
72.5k
    } else {
76
        // add or replace value
77
72.5k
        d->items[key] = value;
78
72.5k
    }
79
72.5k
}
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
645k
    BaseDictionary(obj)
88
645k
{
89
645k
}
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
72.5k
{
100
72.5k
    auto qpdf = getOwningQPDF();
101
72.5k
    auto item_qpdf = item.getOwningQPDF();
102
72.5k
    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
72.5k
}
108
109
bool
110
QPDFObjectHandle::hasKey(std::string const& key) const
111
97.3k
{
112
97.3k
    if (Dictionary dict = *this) {
113
97.1k
        return dict.contains(key);
114
97.1k
    } else {
115
154
        typeWarning("dictionary", "returning false for a key containment request");
116
154
        QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
117
154
        return false;
118
154
    }
119
97.3k
}
120
121
QPDFObjectHandle
122
QPDFObjectHandle::getKey(std::string const& key) const
123
434k
{
124
434k
    if (auto result = (*this)[key]) {
125
208k
        return result;
126
208k
    }
127
226k
    if (isDictionary()) {
128
226k
        static auto constexpr msg = " -> dictionary key $VD"sv;
129
226k
        return QPDF_Null::create(obj, msg, key);
130
226k
    }
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
226k
}
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
14.4k
{
145
14.4k
    if (auto dict = as_dictionary(strict)) {
146
14.3k
        return dict.getKeys();
147
14.3k
    }
148
66
    typeWarning("dictionary", "treating as empty");
149
66
    QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
150
66
    return {};
151
14.4k
}
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
76.4k
{
167
76.4k
    if (auto dict = as_dictionary(strict)) {
168
72.5k
        checkOwnership(value);
169
72.5k
        dict.replaceKey(key, value);
170
72.5k
        return;
171
72.5k
    }
172
3.88k
    typeWarning("dictionary", "ignoring key replacement request");
173
3.88k
    QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
174
3.88k
}
175
176
QPDFObjectHandle
177
QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)
178
337
{
179
337
    replaceKey(key, value);
180
337
    return value;
181
337
}
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
119k
{
194
119k
    if (erase(key) || isDictionary()) {
195
119k
        return;
196
119k
    }
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
}