Coverage Report

Created: 2025-07-01 06:14

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