diff --git a/include/qpdf/QPDFNameTreeObjectHelper.hh b/include/qpdf/QPDFNameTreeObjectHelper.hh index 12950d0..1db1241 100644 --- a/include/qpdf/QPDFNameTreeObjectHelper.hh +++ b/include/qpdf/QPDFNameTreeObjectHelper.hh @@ -50,7 +50,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper QPDFObjectHandle, QPDF&, std::function value_validator, - bool auto_repair = true); + bool auto_repair); + + // Validate the name tree. Returns true if the tree is valid. + // + // If the tree is not valid and auto_repair is true, attempt to repair the tree. + QPDF_DLL + bool validate(bool repair = true); // Create an empty name tree QPDF_DLL diff --git a/include/qpdf/QPDFNumberTreeObjectHelper.hh b/include/qpdf/QPDFNumberTreeObjectHelper.hh index ec06fd3..5d6c669 100644 --- a/include/qpdf/QPDFNumberTreeObjectHelper.hh +++ b/include/qpdf/QPDFNumberTreeObjectHelper.hh @@ -48,7 +48,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper QPDFObjectHandle, QPDF&, std::function value_validator, - bool auto_repair = true); + bool auto_repair); QPDF_DLL ~QPDFNumberTreeObjectHelper() override; @@ -59,6 +59,12 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper typedef long long int numtree_number; + // Validate the name tree. Returns true if the tree is valid. + // + // If the tree is not valid and auto_repair is true, attempt to repair the tree. + QPDF_DLL + bool validate(bool repair = true); + // Return overall minimum and maximum indices QPDF_DLL numtree_number getMin(); diff --git a/libqpdf/NNTree.cc b/libqpdf/NNTree.cc index f22dbc2..d3ff3b6 100644 --- a/libqpdf/NNTree.cc +++ b/libqpdf/NNTree.cc @@ -891,3 +891,33 @@ NNTreeImpl::remove(QPDFObjectHandle const& key, QPDFObjectHandle* value) iter.remove(); return true; } + +bool +NNTreeImpl::validate(bool a_repair) +{ + bool first = true; + QPDFObjectHandle last_key; + try { + for (auto const& [key, value]: *this) { + if (!details.keyValid(key)) { + error(oh, "invalid key in validate"); + } + if (!value_valid(value)) { + error(oh, "invalid value in validate"); + } + if (first) { + first = false; + } else if (last_key && details.compareKeys(last_key, key) != -1) { + error(oh, "keys are not sorted in validate"); + } + last_key = key; + } + } catch (QPDFExc& e) { + if (a_repair) { + warn(oh, std::string("attempting to repair after error: ") + e.what()); + repair(); + } + return false; + } + return true; +} diff --git a/libqpdf/QPDFNameTreeObjectHelper.cc b/libqpdf/QPDFNameTreeObjectHelper.cc index b6344ec..d3bccf3 100644 --- a/libqpdf/QPDFNameTreeObjectHelper.cc +++ b/libqpdf/QPDFNameTreeObjectHelper.cc @@ -215,3 +215,9 @@ QPDFNameTreeObjectHelper::getAsMap() const result.insert(begin(), end()); return result; } + +bool +QPDFNameTreeObjectHelper::validate(bool repair) +{ + return m->impl->validate(repair); +} diff --git a/libqpdf/QPDFNumberTreeObjectHelper.cc b/libqpdf/QPDFNumberTreeObjectHelper.cc index 297c67d..eae8251 100644 --- a/libqpdf/QPDFNumberTreeObjectHelper.cc +++ b/libqpdf/QPDFNumberTreeObjectHelper.cc @@ -251,3 +251,9 @@ QPDFNumberTreeObjectHelper::getAsMap() const result.insert(begin(), end()); return result; } + +bool +QPDFNumberTreeObjectHelper::validate(bool repair) +{ + return m->impl->validate(repair); +} diff --git a/libqpdf/qpdf/NNTree.hh b/libqpdf/qpdf/NNTree.hh index ac266db..fcaf401 100644 --- a/libqpdf/qpdf/NNTree.hh +++ b/libqpdf/qpdf/NNTree.hh @@ -109,6 +109,8 @@ class NNTreeImpl iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value); bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr); + bool validate(bool repair = true); + // Change the split threshold for easier testing. There's no real reason to expose this to // downstream tree helpers, but it has to be public so we can call it from the test suite. void setSplitThreshold(int split_threshold);