Commit 08ba21cf4935fec39d3454714c03d36ff6a6b836
1 parent
4be2f360
Fix some bugs around null values in dictionaries
Make it so that a key with a null value is always treated as not being present. This was inconsistent before.
Showing
5 changed files
with
52 additions
and
25 deletions
libqpdf/QPDF_Dictionary.cc
| ... | ... | @@ -24,12 +24,11 @@ std::string |
| 24 | 24 | QPDF_Dictionary::unparse() |
| 25 | 25 | { |
| 26 | 26 | std::string result = "<< "; |
| 27 | - for (std::map<std::string, QPDFObjectHandle>::iterator iter = | |
| 28 | - this->items.begin(); | |
| 29 | - iter != this->items.end(); | |
| 30 | - ++iter) { | |
| 31 | - result += QPDF_Name::normalizeName((*iter).first) + " " + | |
| 32 | - (*iter).second.unparse() + " "; | |
| 27 | + for (auto& iter : this->items) { | |
| 28 | + if (!iter.second.isNull()) { | |
| 29 | + result += QPDF_Name::normalizeName(iter.first) + " " + | |
| 30 | + iter.second.unparse() + " "; | |
| 31 | + } | |
| 33 | 32 | } |
| 34 | 33 | result += ">>"; |
| 35 | 34 | return result; |
| ... | ... | @@ -39,12 +38,11 @@ JSON |
| 39 | 38 | QPDF_Dictionary::getJSON() |
| 40 | 39 | { |
| 41 | 40 | JSON j = JSON::makeDictionary(); |
| 42 | - for (std::map<std::string, QPDFObjectHandle>::iterator iter = | |
| 43 | - this->items.begin(); | |
| 44 | - iter != this->items.end(); | |
| 45 | - ++iter) { | |
| 46 | - j.addDictionaryMember( | |
| 47 | - QPDF_Name::normalizeName((*iter).first), (*iter).second.getJSON()); | |
| 41 | + for (auto& iter : this->items) { | |
| 42 | + if (!iter.second.isNull()) { | |
| 43 | + j.addDictionaryMember( | |
| 44 | + QPDF_Name::normalizeName(iter.first), iter.second.getJSON()); | |
| 45 | + } | |
| 48 | 46 | } |
| 49 | 47 | return j; |
| 50 | 48 | } |
| ... | ... | @@ -78,9 +76,10 @@ QPDF_Dictionary::getKey(std::string const& key) |
| 78 | 76 | { |
| 79 | 77 | // PDF spec says fetching a non-existent key from a dictionary |
| 80 | 78 | // returns the null object. |
| 81 | - if (this->items.count(key)) { | |
| 79 | + auto item = this->items.find(key); | |
| 80 | + if (item != this->items.end()) { | |
| 82 | 81 | // May be a null object |
| 83 | - return (*(this->items.find(key))).second; | |
| 82 | + return item->second; | |
| 84 | 83 | } else { |
| 85 | 84 | QPDFObjectHandle null = QPDFObjectHandle::newNull(); |
| 86 | 85 | QPDF* qpdf = 0; |
| ... | ... | @@ -97,12 +96,9 @@ std::set<std::string> |
| 97 | 96 | QPDF_Dictionary::getKeys() |
| 98 | 97 | { |
| 99 | 98 | std::set<std::string> result; |
| 100 | - for (std::map<std::string, QPDFObjectHandle>::const_iterator iter = | |
| 101 | - this->items.begin(); | |
| 102 | - iter != this->items.end(); | |
| 103 | - ++iter) { | |
| 104 | - if (hasKey((*iter).first)) { | |
| 105 | - result.insert((*iter).first); | |
| 99 | + for (auto& iter : this->items) { | |
| 100 | + if (!iter.second.isNull()) { | |
| 101 | + result.insert(iter.first); | |
| 106 | 102 | } |
| 107 | 103 | } |
| 108 | 104 | return result; | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -1421,6 +1421,17 @@ foreach (my $i = 1; $i <= 3; ++$i) |
| 1421 | 1421 | |
| 1422 | 1422 | show_ntests(); |
| 1423 | 1423 | # ---------- |
| 1424 | +$td->notify("--- Dictionary keys ---"); | |
| 1425 | +$n_tests += 1; | |
| 1426 | + | |
| 1427 | +$td->runtest("dictionary keys", | |
| 1428 | + {$td->COMMAND => "test_driver 87 - -"}, | |
| 1429 | + {$td->STRING => "test 87 done\n", | |
| 1430 | + $td->EXIT_STATUS => 0}, | |
| 1431 | + $td->NORMALIZE_NEWLINES); | |
| 1432 | + | |
| 1433 | +show_ntests(); | |
| 1434 | +# ---------- | |
| 1424 | 1435 | $td->notify("--- Stream data ---"); |
| 1425 | 1436 | $n_tests += 2; |
| 1426 | 1437 | ... | ... |
qpdf/qtest/qpdf/bad34-recover.out
| ... | ... | @@ -2,7 +2,7 @@ WARNING: bad34.pdf: file is damaged |
| 2 | 2 | WARNING: bad34.pdf (object 4 0, offset 322): expected n n obj |
| 3 | 3 | WARNING: bad34.pdf: Attempting to reconstruct cross-reference table |
| 4 | 4 | /QTest is indirect and has type stream (10) |
| 5 | -/QTest is a stream. Dictionary: << /Length 44 /Quack 9 0 R >> | |
| 5 | +/QTest is a stream. Dictionary: << /Length 44 >> | |
| 6 | 6 | Raw stream data: |
| 7 | 7 | BT |
| 8 | 8 | /F1 24 Tf | ... | ... |
qpdf/qtest/qpdf/good11.out
qpdf/test_driver.cc
| ... | ... | @@ -3158,6 +3158,26 @@ test_86(QPDF& pdf, char const* arg2) |
| 3158 | 3158 | assert(h.getUTF8Value() == utf8_val); |
| 3159 | 3159 | } |
| 3160 | 3160 | |
| 3161 | +static void | |
| 3162 | +test_87(QPDF& pdf, char const* arg2) | |
| 3163 | +{ | |
| 3164 | + // Explicitly demonstrate null dictionary values being the same as | |
| 3165 | + // missing keys. | |
| 3166 | + auto dict = "<< /A 1 /B null >>"_qpdf; | |
| 3167 | + assert(dict.unparse() == "<< /A 1 >>"); | |
| 3168 | + assert(dict.getKeys() == std::set<std::string>({"/A"})); | |
| 3169 | + dict.replaceKey("/A", QPDFObjectHandle::newNull()); | |
| 3170 | + assert(dict.unparse() == "<< >>"); | |
| 3171 | + assert(dict.getKeys() == std::set<std::string>()); | |
| 3172 | + dict = QPDFObjectHandle::newDictionary({ | |
| 3173 | + {"/A", "2"_qpdf}, | |
| 3174 | + {"/B", QPDFObjectHandle::newNull()}, | |
| 3175 | + }); | |
| 3176 | + assert(dict.unparse() == "<< /A 2 >>"); | |
| 3177 | + assert(dict.getKeys() == std::set<std::string>({"/A"})); | |
| 3178 | + assert(dict.getJSON().unparse() == "{\n \"/A\": 2\n}"); | |
| 3179 | +} | |
| 3180 | + | |
| 3161 | 3181 | void |
| 3162 | 3182 | runtest(int n, char const* filename1, char const* arg2) |
| 3163 | 3183 | { |
| ... | ... | @@ -3165,7 +3185,7 @@ runtest(int n, char const* filename1, char const* arg2) |
| 3165 | 3185 | // the test suite to see how the test is invoked to find the file |
| 3166 | 3186 | // that the test is supposed to operate on. |
| 3167 | 3187 | |
| 3168 | - std::set<int> ignore_filename = {61, 81, 83, 84, 85, 86}; | |
| 3188 | + std::set<int> ignore_filename = {61, 81, 83, 84, 85, 86, 87}; | |
| 3169 | 3189 | |
| 3170 | 3190 | if (n == 0) { |
| 3171 | 3191 | // Throw in some random test cases that don't fit anywhere |
| ... | ... | @@ -3259,7 +3279,7 @@ runtest(int n, char const* filename1, char const* arg2) |
| 3259 | 3279 | {72, test_72}, {73, test_73}, {74, test_74}, {75, test_75}, |
| 3260 | 3280 | {76, test_76}, {77, test_77}, {78, test_78}, {79, test_79}, |
| 3261 | 3281 | {80, test_80}, {81, test_81}, {82, test_82}, {83, test_83}, |
| 3262 | - {84, test_84}, {85, test_85}, {86, test_86}, | |
| 3282 | + {84, test_84}, {85, test_85}, {86, test_86}, {87, test_87}, | |
| 3263 | 3283 | }; |
| 3264 | 3284 | |
| 3265 | 3285 | auto fn = test_functions.find(n); | ... | ... |