Coverage Report

Created: 2025-07-01 06:10

/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
0
{
12
0
    if (auto d = as<QPDF_Dictionary>()) {
13
0
        return d;
14
0
    }
15
0
    throw std::runtime_error("Expected a dictionary but found a non-dictionary object");
16
0
    return nullptr; // unreachable
17
0
}
18
19
bool
20
BaseDictionary::hasKey(std::string const& key) const
21
0
{
22
0
    auto d = dict();
23
0
    return d->items.contains(key) && !d->items[key].null();
24
0
}
25
26
QPDFObjectHandle
27
BaseDictionary::getKey(std::string const& key) const
28
0
{
29
0
    auto d = dict();
30
31
    // PDF spec says fetching a non-existent key from a dictionary returns the null object.
32
0
    auto item = d->items.find(key);
33
0
    if (item != d->items.end()) {
34
        // May be a null object
35
0
        return item->second;
36
0
    }
37
0
    static auto constexpr msg = " -> dictionary key $VD"sv;
38
0
    return QPDF_Null::create(obj, msg, key);
39
0
}
40
41
std::set<std::string>
42
BaseDictionary::getKeys()
43
0
{
44
0
    std::set<std::string> result;
45
0
    for (auto& iter: dict()->items) {
46
0
        if (!iter.second.isNull()) {
47
0
            result.insert(iter.first);
48
0
        }
49
0
    }
50
0
    return result;
51
0
}
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
0
{
62
    // no-op if key does not exist
63
0
    dict()->items.erase(key);
64
0
}
65
66
void
67
BaseDictionary::replaceKey(std::string const& key, QPDFObjectHandle value)
68
0
{
69
0
    auto d = dict();
70
0
    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
0
    } else {
76
        // add or replace value
77
0
        d->items[key] = value;
78
0
    }
79
0
}
80
81
void
82
QPDFObjectHandle::checkOwnership(QPDFObjectHandle const& item) const
83
0
{
84
0
    auto qpdf = getOwningQPDF();
85
0
    auto item_qpdf = item.getOwningQPDF();
86
0
    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
0
}
93
94
bool
95
QPDFObjectHandle::hasKey(std::string const& key) const
96
0
{
97
0
    auto dict = as_dictionary(strict);
98
0
    if (dict) {
99
0
        return dict.hasKey(key);
100
0
    } else {
101
0
        typeWarning("dictionary", "returning false for a key containment request");
102
0
        QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey");
103
0
        return false;
104
0
    }
105
0
}
106
107
QPDFObjectHandle
108
QPDFObjectHandle::getKey(std::string const& key) const
109
0
{
110
0
    if (auto dict = as_dictionary(strict)) {
111
0
        return dict.getKey(key);
112
0
    }
113
0
    typeWarning("dictionary", "returning null for attempted key retrieval");
114
0
    QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey");
115
0
    static auto constexpr msg = " -> null returned from getting key $VD from non-Dictionary"sv;
116
0
    return QPDF_Null::create(obj, msg, "");
117
0
}
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
0
{
128
0
    if (auto dict = as_dictionary(strict)) {
129
0
        return dict.getKeys();
130
0
    }
131
0
    typeWarning("dictionary", "treating as empty");
132
0
    QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys");
133
0
    return {};
134
0
}
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
0
{
150
0
    if (auto dict = as_dictionary(strict)) {
151
0
        checkOwnership(value);
152
0
        dict.replaceKey(key, value);
153
0
        return;
154
0
    }
155
0
    typeWarning("dictionary", "ignoring key replacement request");
156
0
    QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey");
157
0
}
158
159
QPDFObjectHandle
160
QPDFObjectHandle::replaceKeyAndGetNew(std::string const& key, QPDFObjectHandle const& value)
161
0
{
162
0
    replaceKey(key, value);
163
0
    return value;
164
0
}
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
0
{
177
0
    if (auto dict = as_dictionary(strict)) {
178
0
        dict.removeKey(key);
179
0
        return;
180
0
    }
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
}