Coverage Report

Created: 2025-10-12 07:06

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
55.5k
{
12
55.5k
    if (auto d = as<QPDF_Dictionary>()) {
13
55.5k
        return d;
14
55.5k
    }
15
0
    throw std::runtime_error("Expected a dictionary but found a non-dictionary object");
16
0
    return nullptr; // unreachable
17
55.5k
}
18
19
QPDFObjectHandle const&
20
BaseHandle::operator[](std::string const& key) const
21
834k
{
22
834k
    if (auto d = as<QPDF_Dictionary>()) {
23
834k
        auto it = d->items.find(key);
24
834k
        if (it != d->items.end()) {
25
443k
            return it->second;
26
443k
        }
27
834k
    }
28
390k
    static const QPDFObjectHandle null_obj;
29
390k
    return null_obj;
30
834k
}
31
32
bool
33
BaseHandle::contains(std::string const& key) const
34
100k
{
35
100k
    return !(*this)[key].null();
36
100k
}
37
38
std::set<std::string>
39
BaseDictionary::getKeys()
40
8.08k
{
41
8.08k
    std::set<std::string> result;
42
35.4k
    for (auto& iter: dict()->items) {
43
35.4k
        if (!iter.second.null()) {
44
30.6k
            result.insert(iter.first);
45
30.6k
        }
46
35.4k
    }
47
8.08k
    return result;
48
8.08k
}
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
262k
{
59
    // no-op if key does not exist
60
262k
    if (auto d = as<QPDF_Dictionary>()) {
61
262k
        return d->items.erase(key);
62
262k
    }
63
0
    return 0;
64
262k
}
65
66
void
67
BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
68
47.4k
{
69
47.4k
    auto d = dict();
70
47.4k
    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
47.4k
    } else {
76
        // add or replace value
77
47.4k
        d->items[key] = value;
78
47.4k
    }
79
47.4k
}
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
12.6M
    BaseDictionary(obj)
88
12.6M
{
89
12.6M
}
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
47.4k
{
100
47.4k
    auto qpdf = getOwningQPDF();
101
47.4k
    auto item_qpdf = item.getOwningQPDF();
102
47.4k
    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
47.4k
}
108
109
bool
110
QPDFObjectHandle::hasKey(std::string const& key) const
111
103k
{
112
103k
    if (Dictionary dict = *this) {
113
100k
        return dict.contains(key);
114
100k
    } else {
115
3.14k
        typeWarning("dictionary", "returning false for a key containment request");
116
3.14k
        QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
117
3.14k
        return false;
118
3.14k
    }
119
103k
}
120
121
QPDFObjectHandle
122
QPDFObjectHandle::getKey(std::string const& key) const
123
425k
{
124
425k
    if (auto result = (*this)[key]) {
125
252k
        return result;
126
252k
    }
127
172k
    if (isDictionary()) {
128
172k
        static auto constexpr msg = " -> dictionary key $VD"sv;
129
172k
        return QPDF_Null::create(obj, msg, key);
130
172k
    }
131
2
    typeWarning("dictionary", "returning null for attempted key retrieval");
132
2
    static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
133
2
    return QPDF_Null::create(obj, msg, "");
134
172k
}
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
8.29k
{
145
8.29k
    if (auto dict = as_dictionary(strict)) {
146
8.08k
        return dict.getKeys();
147
8.08k
    }
148
211
    typeWarning("dictionary", "treating as empty");
149
211
    QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
150
211
    return {};
151
8.29k
}
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
50.2k
{
167
50.2k
    if (auto dict = as_dictionary(strict)) {
168
47.4k
        checkOwnership(value);
169
47.4k
        dict.replaceKey(key, value);
170
47.4k
        return;
171
47.4k
    }
172
2.82k
    typeWarning("dictionary", "ignoring key replacement request");
173
2.82k
    QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
174
2.82k
}
175
176
QPDFObjectHandle
177
QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)
178
89
{
179
89
    replaceKey(key, value);
180
89
    return value;
181
89
}
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
76.6k
{
194
76.6k
    if (erase(key) || isDictionary()) {
195
76.6k
        return;
196
76.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
}