Coverage Report

Created: 2025-10-10 06:17

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
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
}