Commit 5f0708418a0a9cbacc033f52efc11c759120fd09

Authored by Jay Berkenbilt
1 parent 4a1cce0a

Add iterators to name/number tree helpers

include/qpdf/QPDFNameTreeObjectHelper.hh
... ... @@ -26,6 +26,7 @@
26 26 #include <qpdf/QPDFObjGen.hh>
27 27 #include <map>
28 28 #include <memory>
  29 +#include <iterator>
29 30  
30 31 #include <qpdf/DLL.h>
31 32  
... ... @@ -35,6 +36,8 @@
35 36 // normalized for lookup purposes.
36 37  
37 38 class NNTreeImpl;
  39 +class NNTreeIterator;
  40 +class NNTreeDetails;
38 41  
39 42 class QPDFNameTreeObjectHelper: public QPDFObjectHelper
40 43 {
... ... @@ -54,6 +57,66 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper
54 57 QPDF_DLL
55 58 bool findObject(std::string const& utf8, QPDFObjectHandle& oh);
56 59  
  60 + class iterator: public std::iterator<
  61 + std::bidirectional_iterator_tag,
  62 + std::pair<std::string, QPDFObjectHandle>,
  63 + void,
  64 + std::pair<std::string, QPDFObjectHandle>*,
  65 + std::pair<std::string, QPDFObjectHandle>>
  66 + {
  67 + friend class QPDFNameTreeObjectHelper;
  68 + public:
  69 + QPDF_DLL
  70 + bool valid() const;
  71 + QPDF_DLL
  72 + iterator& operator++();
  73 + QPDF_DLL
  74 + iterator operator++(int)
  75 + {
  76 + iterator t = *this;
  77 + ++(*this);
  78 + return t;
  79 + }
  80 + QPDF_DLL
  81 + iterator& operator--();
  82 + QPDF_DLL
  83 + iterator operator--(int)
  84 + {
  85 + iterator t = *this;
  86 + --(*this);
  87 + return t;
  88 + }
  89 + QPDF_DLL
  90 + reference operator*();
  91 + QPDF_DLL
  92 + bool operator==(iterator const& other) const;
  93 + QPDF_DLL
  94 + bool operator!=(iterator const& other) const
  95 + {
  96 + return ! operator==(other);
  97 + }
  98 +
  99 + private:
  100 + iterator(std::shared_ptr<NNTreeIterator> const&);
  101 + std::shared_ptr<NNTreeIterator> impl;
  102 + };
  103 +
  104 + // The iterator looks like map iterator, so i.first is a string
  105 + // and i.second is a QPDFObjectHandle.
  106 + QPDF_DLL
  107 + iterator begin() const;
  108 + QPDF_DLL
  109 + iterator end() const;
  110 + // Return a bidirectional iterator that points to the last item.
  111 + QPDF_DLL
  112 + iterator last() const;
  113 +
  114 + // Find the entry with the given key. If return_prev_if_not_found
  115 + // is true and the item is not found, return the next lower item.
  116 + QPDF_DLL
  117 + iterator find(std::string const& key,
  118 + bool return_prev_if_not_found = false);
  119 +
57 120 // Return the contents of the name tree as a map. Note that name
58 121 // trees may be very large, so this may use a lot of RAM. It is
59 122 // more efficient to use QPDFNameTreeObjectHelper's iterator.
... ...
include/qpdf/QPDFNumberTreeObjectHelper.hh
... ... @@ -33,6 +33,8 @@
33 33 // PDF spec (ISO 32000) for a description of number trees.
34 34  
35 35 class NNTreeImpl;
  36 +class NNTreeIterator;
  37 +class NNTreeDetails;
36 38  
37 39 class QPDFNumberTreeObjectHelper: public QPDFObjectHelper
38 40 {
... ... @@ -73,6 +75,65 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper
73 75 bool findObjectAtOrBelow(numtree_number idx, QPDFObjectHandle& oh,
74 76 numtree_number& offset);
75 77  
  78 + class iterator: public std::iterator<
  79 + std::bidirectional_iterator_tag,
  80 + std::pair<numtree_number, QPDFObjectHandle>,
  81 + void,
  82 + std::pair<numtree_number, QPDFObjectHandle>*,
  83 + std::pair<numtree_number, QPDFObjectHandle>>
  84 + {
  85 + friend class QPDFNumberTreeObjectHelper;
  86 + public:
  87 + QPDF_DLL
  88 + bool valid() const;
  89 + QPDF_DLL
  90 + iterator& operator++();
  91 + QPDF_DLL
  92 + iterator operator++(int)
  93 + {
  94 + iterator t = *this;
  95 + ++(*this);
  96 + return t;
  97 + }
  98 + QPDF_DLL
  99 + iterator& operator--();
  100 + QPDF_DLL
  101 + iterator operator--(int)
  102 + {
  103 + iterator t = *this;
  104 + --(*this);
  105 + return t;
  106 + }
  107 + QPDF_DLL
  108 + reference operator*();
  109 + QPDF_DLL
  110 + bool operator==(iterator const& other) const;
  111 + QPDF_DLL
  112 + bool operator!=(iterator const& other) const
  113 + {
  114 + return ! operator==(other);
  115 + }
  116 +
  117 + private:
  118 + iterator(std::shared_ptr<NNTreeIterator> const&);
  119 + std::shared_ptr<NNTreeIterator> impl;
  120 + };
  121 +
  122 + // The iterator looks like map iterator, so i.first is a string
  123 + // and i.second is a QPDFObjectHandle.
  124 + QPDF_DLL
  125 + iterator begin() const;
  126 + QPDF_DLL
  127 + iterator end() const;
  128 + // Return a bidirectional iterator that points to the last item.
  129 + QPDF_DLL
  130 + iterator last() const;
  131 +
  132 + // Find the entry with the given key. If return_prev_if_not_found
  133 + // is true and the item is not found, return the next lower item.
  134 + QPDF_DLL
  135 + iterator find(numtree_number key, bool return_prev_if_not_found = false);
  136 +
76 137 // Return the contents of the number tree as a map. Note that
77 138 // number trees may be very large, so this may use a lot of RAM.
78 139 // It is more efficient to use QPDFNumberTreeObjectHelper's
... ...
libqpdf/QPDFNameTreeObjectHelper.cc
... ... @@ -48,19 +48,85 @@ QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper()
48 48 {
49 49 }
50 50  
  51 +QPDFNameTreeObjectHelper::iterator::iterator(
  52 + std::shared_ptr<NNTreeIterator> const& i) :
  53 + impl(i)
  54 +{
  55 +}
  56 +
  57 +bool
  58 +QPDFNameTreeObjectHelper::iterator::valid() const
  59 +{
  60 + return impl->valid();
  61 +}
  62 +
  63 +QPDFNameTreeObjectHelper::iterator&
  64 +QPDFNameTreeObjectHelper::iterator::operator++()
  65 +{
  66 + ++(*impl);
  67 + return *this;
  68 +}
  69 +
  70 +QPDFNameTreeObjectHelper::iterator&
  71 +QPDFNameTreeObjectHelper::iterator::operator--()
  72 +{
  73 + --(*impl);
  74 + return *this;
  75 +}
  76 +
  77 +QPDFNameTreeObjectHelper::iterator::reference
  78 +QPDFNameTreeObjectHelper::iterator::operator*()
  79 +{
  80 + auto p = **impl;
  81 + return std::make_pair(p.first.getUTF8Value(), p.second);
  82 +}
  83 +
  84 +bool
  85 +QPDFNameTreeObjectHelper::iterator::operator==(iterator const& other) const
  86 +{
  87 + return *(impl) == *(other.impl);
  88 +}
  89 +
  90 +QPDFNameTreeObjectHelper::iterator
  91 +QPDFNameTreeObjectHelper::begin() const
  92 +{
  93 + return iterator(std::make_shared<NNTreeIterator>(this->m->impl->begin()));
  94 +}
  95 +
  96 +QPDFNameTreeObjectHelper::iterator
  97 +QPDFNameTreeObjectHelper::end() const
  98 +{
  99 + return iterator(std::make_shared<NNTreeIterator>(this->m->impl->end()));
  100 +}
  101 +
  102 +QPDFNameTreeObjectHelper::iterator
  103 +QPDFNameTreeObjectHelper::last() const
  104 +{
  105 + return iterator(std::make_shared<NNTreeIterator>(this->m->impl->last()));
  106 +}
  107 +
  108 +QPDFNameTreeObjectHelper::iterator
  109 +QPDFNameTreeObjectHelper::find(std::string const& key,
  110 + bool return_prev_if_not_found)
  111 +{
  112 + auto i = this->m->impl->find(QPDFObjectHandle::newUnicodeString(key),
  113 + return_prev_if_not_found);
  114 + return iterator(std::make_shared<NNTreeIterator>(i));
  115 +}
  116 +
51 117 bool
52 118 QPDFNameTreeObjectHelper::hasName(std::string const& name)
53 119 {
54   - auto i = this->m->impl->find(QPDFObjectHandle::newUnicodeString(name));
55   - return (i != this->m->impl->end());
  120 + auto i = find(name);
  121 + return (i != end());
56 122 }
57 123  
58 124 bool
59 125 QPDFNameTreeObjectHelper::findObject(
60 126 std::string const& name, QPDFObjectHandle& oh)
61 127 {
62   - auto i = this->m->impl->find(QPDFObjectHandle::newUnicodeString(name));
63   - if (i == this->m->impl->end())
  128 + auto i = find(name);
  129 + if (i == end())
64 130 {
65 131 return false;
66 132 }
... ... @@ -72,11 +138,6 @@ std::map&lt;std::string, QPDFObjectHandle&gt;
72 138 QPDFNameTreeObjectHelper::getAsMap() const
73 139 {
74 140 std::map<std::string, QPDFObjectHandle> result;
75   - for (auto i: *(this->m->impl))
76   - {
77   - result.insert(
78   - std::make_pair(i.first.getUTF8Value(),
79   - i.second));
80   - }
  141 + result.insert(begin(), end());
81 142 return result;
82 143 }
... ...
libqpdf/QPDFNumberTreeObjectHelper.cc
... ... @@ -44,41 +44,107 @@ QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(QPDFObjectHandle oh) :
44 44 {
45 45 }
46 46  
  47 +QPDFNumberTreeObjectHelper::iterator::iterator(
  48 + std::shared_ptr<NNTreeIterator> const& i) :
  49 + impl(i)
  50 +{
  51 +}
  52 +
  53 +bool
  54 +QPDFNumberTreeObjectHelper::iterator::valid() const
  55 +{
  56 + return impl->valid();
  57 +}
  58 +
  59 +QPDFNumberTreeObjectHelper::iterator&
  60 +QPDFNumberTreeObjectHelper::iterator::operator++()
  61 +{
  62 + ++(*impl);
  63 + return *this;
  64 +}
  65 +
  66 +QPDFNumberTreeObjectHelper::iterator&
  67 +QPDFNumberTreeObjectHelper::iterator::operator--()
  68 +{
  69 + --(*impl);
  70 + return *this;
  71 +}
  72 +
  73 +QPDFNumberTreeObjectHelper::iterator::reference
  74 +QPDFNumberTreeObjectHelper::iterator::operator*()
  75 +{
  76 + auto p = **impl;
  77 + return std::make_pair(p.first.getIntValue(), p.second);
  78 +}
  79 +
  80 +bool
  81 +QPDFNumberTreeObjectHelper::iterator::operator==(iterator const& other) const
  82 +{
  83 + return *(impl) == *(other.impl);
  84 +}
  85 +
  86 +QPDFNumberTreeObjectHelper::iterator
  87 +QPDFNumberTreeObjectHelper::begin() const
  88 +{
  89 + return iterator(std::make_shared<NNTreeIterator>(this->m->impl->begin()));
  90 +}
  91 +
  92 +QPDFNumberTreeObjectHelper::iterator
  93 +QPDFNumberTreeObjectHelper::end() const
  94 +{
  95 + return iterator(std::make_shared<NNTreeIterator>(this->m->impl->end()));
  96 +}
  97 +
  98 +QPDFNumberTreeObjectHelper::iterator
  99 +QPDFNumberTreeObjectHelper::last() const
  100 +{
  101 + return iterator(std::make_shared<NNTreeIterator>(this->m->impl->last()));
  102 +}
  103 +
  104 +QPDFNumberTreeObjectHelper::iterator
  105 +QPDFNumberTreeObjectHelper::find(numtree_number key,
  106 + bool return_prev_if_not_found)
  107 +{
  108 + auto i = this->m->impl->find(QPDFObjectHandle::newInteger(key),
  109 + return_prev_if_not_found);
  110 + return iterator(std::make_shared<NNTreeIterator>(i));
  111 +}
  112 +
47 113 QPDFNumberTreeObjectHelper::numtree_number
48 114 QPDFNumberTreeObjectHelper::getMin()
49 115 {
50   - auto i = this->m->impl->begin();
51   - if (i == this->m->impl->end())
  116 + auto i = begin();
  117 + if (i == end())
52 118 {
53 119 return 0;
54 120 }
55   - return (*i).first.getIntValue();
  121 + return (*i).first;
56 122 }
57 123  
58 124 QPDFNumberTreeObjectHelper::numtree_number
59 125 QPDFNumberTreeObjectHelper::getMax()
60 126 {
61   - auto i = this->m->impl->last();
62   - if (i == this->m->impl->end())
  127 + auto i = last();
  128 + if (i == end())
63 129 {
64 130 return 0;
65 131 }
66   - return (*i).first.getIntValue();
  132 + return (*i).first;
67 133 }
68 134  
69 135 bool
70 136 QPDFNumberTreeObjectHelper::hasIndex(numtree_number idx)
71 137 {
72   - auto i = this->m->impl->find(QPDFObjectHandle::newInteger(idx));
73   - return (i != this->m->impl->end());
  138 + auto i = find(idx);
  139 + return (i != this->end());
74 140 }
75 141  
76 142 bool
77 143 QPDFNumberTreeObjectHelper::findObject(
78 144 numtree_number idx, QPDFObjectHandle& oh)
79 145 {
80   - auto i = this->m->impl->find(QPDFObjectHandle::newInteger(idx));
81   - if (i == this->m->impl->end())
  146 + auto i = find(idx);
  147 + if (i == end())
82 148 {
83 149 return false;
84 150 }
... ... @@ -91,13 +157,13 @@ QPDFNumberTreeObjectHelper::findObjectAtOrBelow(
91 157 numtree_number idx, QPDFObjectHandle& oh,
92 158 numtree_number& offset)
93 159 {
94   - auto i = this->m->impl->find(QPDFObjectHandle::newInteger(idx), true);
95   - if (i == this->m->impl->end())
  160 + auto i = find(idx, true);
  161 + if (i == end())
96 162 {
97 163 return false;
98 164 }
99 165 oh = (*i).second;
100   - offset = idx - (*i).first.getIntValue();
  166 + offset = idx - (*i).first;
101 167 return true;
102 168 }
103 169  
... ... @@ -105,11 +171,6 @@ std::map&lt;QPDFNumberTreeObjectHelper::numtree_number, QPDFObjectHandle&gt;
105 171 QPDFNumberTreeObjectHelper::getAsMap() const
106 172 {
107 173 std::map<numtree_number, QPDFObjectHandle> result;
108   - for (auto i: *(this->m->impl))
109   - {
110   - result.insert(
111   - std::make_pair(i.first.getIntValue(),
112   - i.second));
113   - }
  174 + result.insert(begin(), end());
114 175 return result;
115 176 }
... ...
libtests/nntree.cc
1 1 #include <qpdf/QPDFNumberTreeObjectHelper.hh>
  2 +#include <qpdf/QPDFNameTreeObjectHelper.hh>
2 3 #include <qpdf/QPDF.hh>
3 4 #include <qpdf/QUtil.hh>
4 5 #include <iostream>
5 6  
  7 +static bool any_failures = false;
  8 +
6 9 bool report(QPDFObjectHandle oh, long long item, long long exp_item)
7 10 {
8 11 QPDFNumberTreeObjectHelper nh(oh);
... ... @@ -56,7 +59,7 @@ bool report(QPDFObjectHandle oh, long long item, long long exp_item)
56 59 return failed;
57 60 }
58 61  
59   -int main()
  62 +void test_bsearch()
60 63 {
61 64 QPDF q;
62 65 q.emptyPDF();
... ... @@ -78,8 +81,7 @@ int main()
78 81 return node;
79 82 };
80 83  
81   - bool any_failures = false;
82   - auto r = [&any_failures](QPDFObjectHandle& oh, int item, int exp) {
  84 + auto r = [](QPDFObjectHandle& oh, int item, int exp) {
83 85 if (report(oh, item, exp))
84 86 {
85 87 any_failures = true;
... ... @@ -119,6 +121,133 @@ int main()
119 121  
120 122 if (! any_failures)
121 123 {
122   - std::cout << "all tests passed" << std::endl;
  124 + std::cout << "bsearch tests passed" << std::endl;
  125 + }
  126 +}
  127 +
  128 +QPDFObjectHandle new_node(QPDF& q, std::string const& key)
  129 +{
  130 + auto dict = QPDFObjectHandle::newDictionary();
  131 + dict.replaceKey(key, QPDFObjectHandle::newArray());
  132 + return q.makeIndirectObject(dict);
  133 +}
  134 +
  135 +static void check_find(QPDFNameTreeObjectHelper& nh,
  136 + std::string const& key, bool prev_if_not_found)
  137 +{
  138 + auto i = nh.find(key, prev_if_not_found);
  139 + std::cout << "find " << key << " (" << prev_if_not_found << "): ";
  140 + if (i == nh.end())
  141 + {
  142 + std::cout << "not found";
  143 + }
  144 + else
  145 + {
  146 + std::cout << (*i).first << " -> " << (*i).second.unparse();
  147 + }
  148 + std::cout << std::endl;
  149 +}
  150 +
  151 +void test_depth()
  152 +{
  153 + int constexpr NITEMS = 3;
  154 + QPDF q;
  155 + q.emptyPDF();
  156 + auto root = q.getRoot();
  157 + auto n0 = new_node(q, "/Kids");
  158 + root.replaceKey("/NT", n0);
  159 + auto k0 = root.getKey("/NT").getKey("/Kids");
  160 + for (int i1 = 0; i1 < NITEMS; ++i1)
  161 + {
  162 + auto n1 = new_node(q, "/Kids");
  163 + k0.appendItem(n1);
  164 + auto k1 = n1.getKey("/Kids");
  165 + for (int i2 = 0; i2 < NITEMS; ++i2)
  166 + {
  167 + auto n2 = new_node(q, "/Kids");
  168 + k1.appendItem(n2);
  169 + auto k2 = n2.getKey("/Kids");
  170 + for (int i3 = 0; i3 < NITEMS; ++i3)
  171 + {
  172 + auto n3 = new_node(q, "/Names");
  173 + k2.appendItem(n3);
  174 + auto items = n3.getKey("/Names");
  175 + std::string first;
  176 + std::string last;
  177 + for (int i4 = 0; i4 < NITEMS; ++i4)
  178 + {
  179 + int val = (((((i1
  180 + * NITEMS) + i2)
  181 + * NITEMS) + i3)
  182 + * NITEMS) + i4;
  183 + std::string str = QUtil::int_to_string(10 * val, 6);
  184 + items.appendItem(
  185 + QPDFObjectHandle::newString(str));
  186 + items.appendItem(
  187 + QPDFObjectHandle::newString("val " + str));
  188 + if (i4 == 0)
  189 + {
  190 + first = str;
  191 + }
  192 + else if (i4 == NITEMS - 1)
  193 + {
  194 + last = str;
  195 + }
  196 + }
  197 + auto limits = QPDFObjectHandle::newArray();
  198 + n3.replaceKey("/Limits", limits);
  199 + limits.appendItem(QPDFObjectHandle::newString(first));
  200 + limits.appendItem(QPDFObjectHandle::newString(last));
  201 + }
  202 + auto limits = QPDFObjectHandle::newArray();
  203 + n2.replaceKey("/Limits", limits);
  204 + limits.appendItem(k2.getArrayItem(0)
  205 + .getKey("/Limits")
  206 + .getArrayItem(0));
  207 + limits.appendItem(k2.getArrayItem(NITEMS - 1)
  208 + .getKey("/Limits")
  209 + .getArrayItem(1));
  210 + }
  211 + auto limits = QPDFObjectHandle::newArray();
  212 + n1.replaceKey("/Limits", limits);
  213 + limits.appendItem(k1.getArrayItem(0)
  214 + .getKey("/Limits")
  215 + .getArrayItem(0));
  216 + limits.appendItem(k1.getArrayItem(NITEMS - 1)
  217 + .getKey("/Limits")
  218 + .getArrayItem(1));
123 219 }
  220 +
  221 + QPDFNameTreeObjectHelper nh(n0);
  222 + std::cout << "--- forward ---" << std::endl;
  223 + for (auto i: nh)
  224 + {
  225 + std::cout << i.first << " -> "
  226 + << i.second.unparse() << std::endl;
  227 + }
  228 + std::cout << "--- backward ---" << std::endl;
  229 + for (auto i = nh.last(); i.valid(); --i)
  230 + {
  231 + std::cout << (*i).first << " -> "
  232 + << (*i).second.unparse() << std::endl;
  233 + }
  234 +
  235 + // Find
  236 + check_find(nh, "000300", false);
  237 + check_find(nh, "000305", true);
  238 + check_find(nh, "000305", false);
  239 + check_find(nh, "00000", false);
  240 + check_find(nh, "00000", true);
  241 + check_find(nh, "000800", false);
  242 + check_find(nh, "000805", false);
  243 + check_find(nh, "000805", true);
  244 +}
  245 +
  246 +int main()
  247 +{
  248 + test_bsearch();
  249 + test_depth();
  250 +
  251 + return 0;
124 252 }
  253 +
... ...
libtests/qtest/nntree.test
... ... @@ -3,14 +3,15 @@ require 5.008;
3 3 use warnings;
4 4 use strict;
5 5  
  6 +chdir("nntree") or die "chdir testdir failed: $!\n";
  7 +
6 8 require TestDriver;
7 9  
8 10 my $td = new TestDriver('nntree');
9 11  
10 12 $td->runtest("nntree",
11 13 {$td->COMMAND => "nntree"},
12   - {$td->STRING => "all tests passed\n",
13   - $td->EXIT_STATUS => 0},
  14 + {$td->FILE => "nntree.out", $td->EXIT_STATUS => 0},
14 15 $td->NORMALIZE_NEWLINES);
15 16  
16 17 $td->report(1);
... ...
libtests/qtest/nntree/nntree.out 0 → 100644
  1 +bsearch tests passed
  2 +--- forward ---
  3 +000000 -> (val 000000)
  4 +000010 -> (val 000010)
  5 +000020 -> (val 000020)
  6 +000030 -> (val 000030)
  7 +000040 -> (val 000040)
  8 +000050 -> (val 000050)
  9 +000060 -> (val 000060)
  10 +000070 -> (val 000070)
  11 +000080 -> (val 000080)
  12 +000090 -> (val 000090)
  13 +000100 -> (val 000100)
  14 +000110 -> (val 000110)
  15 +000120 -> (val 000120)
  16 +000130 -> (val 000130)
  17 +000140 -> (val 000140)
  18 +000150 -> (val 000150)
  19 +000160 -> (val 000160)
  20 +000170 -> (val 000170)
  21 +000180 -> (val 000180)
  22 +000190 -> (val 000190)
  23 +000200 -> (val 000200)
  24 +000210 -> (val 000210)
  25 +000220 -> (val 000220)
  26 +000230 -> (val 000230)
  27 +000240 -> (val 000240)
  28 +000250 -> (val 000250)
  29 +000260 -> (val 000260)
  30 +000270 -> (val 000270)
  31 +000280 -> (val 000280)
  32 +000290 -> (val 000290)
  33 +000300 -> (val 000300)
  34 +000310 -> (val 000310)
  35 +000320 -> (val 000320)
  36 +000330 -> (val 000330)
  37 +000340 -> (val 000340)
  38 +000350 -> (val 000350)
  39 +000360 -> (val 000360)
  40 +000370 -> (val 000370)
  41 +000380 -> (val 000380)
  42 +000390 -> (val 000390)
  43 +000400 -> (val 000400)
  44 +000410 -> (val 000410)
  45 +000420 -> (val 000420)
  46 +000430 -> (val 000430)
  47 +000440 -> (val 000440)
  48 +000450 -> (val 000450)
  49 +000460 -> (val 000460)
  50 +000470 -> (val 000470)
  51 +000480 -> (val 000480)
  52 +000490 -> (val 000490)
  53 +000500 -> (val 000500)
  54 +000510 -> (val 000510)
  55 +000520 -> (val 000520)
  56 +000530 -> (val 000530)
  57 +000540 -> (val 000540)
  58 +000550 -> (val 000550)
  59 +000560 -> (val 000560)
  60 +000570 -> (val 000570)
  61 +000580 -> (val 000580)
  62 +000590 -> (val 000590)
  63 +000600 -> (val 000600)
  64 +000610 -> (val 000610)
  65 +000620 -> (val 000620)
  66 +000630 -> (val 000630)
  67 +000640 -> (val 000640)
  68 +000650 -> (val 000650)
  69 +000660 -> (val 000660)
  70 +000670 -> (val 000670)
  71 +000680 -> (val 000680)
  72 +000690 -> (val 000690)
  73 +000700 -> (val 000700)
  74 +000710 -> (val 000710)
  75 +000720 -> (val 000720)
  76 +000730 -> (val 000730)
  77 +000740 -> (val 000740)
  78 +000750 -> (val 000750)
  79 +000760 -> (val 000760)
  80 +000770 -> (val 000770)
  81 +000780 -> (val 000780)
  82 +000790 -> (val 000790)
  83 +000800 -> (val 000800)
  84 +--- backward ---
  85 +000800 -> (val 000800)
  86 +000790 -> (val 000790)
  87 +000780 -> (val 000780)
  88 +000770 -> (val 000770)
  89 +000760 -> (val 000760)
  90 +000750 -> (val 000750)
  91 +000740 -> (val 000740)
  92 +000730 -> (val 000730)
  93 +000720 -> (val 000720)
  94 +000710 -> (val 000710)
  95 +000700 -> (val 000700)
  96 +000690 -> (val 000690)
  97 +000680 -> (val 000680)
  98 +000670 -> (val 000670)
  99 +000660 -> (val 000660)
  100 +000650 -> (val 000650)
  101 +000640 -> (val 000640)
  102 +000630 -> (val 000630)
  103 +000620 -> (val 000620)
  104 +000610 -> (val 000610)
  105 +000600 -> (val 000600)
  106 +000590 -> (val 000590)
  107 +000580 -> (val 000580)
  108 +000570 -> (val 000570)
  109 +000560 -> (val 000560)
  110 +000550 -> (val 000550)
  111 +000540 -> (val 000540)
  112 +000530 -> (val 000530)
  113 +000520 -> (val 000520)
  114 +000510 -> (val 000510)
  115 +000500 -> (val 000500)
  116 +000490 -> (val 000490)
  117 +000480 -> (val 000480)
  118 +000470 -> (val 000470)
  119 +000460 -> (val 000460)
  120 +000450 -> (val 000450)
  121 +000440 -> (val 000440)
  122 +000430 -> (val 000430)
  123 +000420 -> (val 000420)
  124 +000410 -> (val 000410)
  125 +000400 -> (val 000400)
  126 +000390 -> (val 000390)
  127 +000380 -> (val 000380)
  128 +000370 -> (val 000370)
  129 +000360 -> (val 000360)
  130 +000350 -> (val 000350)
  131 +000340 -> (val 000340)
  132 +000330 -> (val 000330)
  133 +000320 -> (val 000320)
  134 +000310 -> (val 000310)
  135 +000300 -> (val 000300)
  136 +000290 -> (val 000290)
  137 +000280 -> (val 000280)
  138 +000270 -> (val 000270)
  139 +000260 -> (val 000260)
  140 +000250 -> (val 000250)
  141 +000240 -> (val 000240)
  142 +000230 -> (val 000230)
  143 +000220 -> (val 000220)
  144 +000210 -> (val 000210)
  145 +000200 -> (val 000200)
  146 +000190 -> (val 000190)
  147 +000180 -> (val 000180)
  148 +000170 -> (val 000170)
  149 +000160 -> (val 000160)
  150 +000150 -> (val 000150)
  151 +000140 -> (val 000140)
  152 +000130 -> (val 000130)
  153 +000120 -> (val 000120)
  154 +000110 -> (val 000110)
  155 +000100 -> (val 000100)
  156 +000090 -> (val 000090)
  157 +000080 -> (val 000080)
  158 +000070 -> (val 000070)
  159 +000060 -> (val 000060)
  160 +000050 -> (val 000050)
  161 +000040 -> (val 000040)
  162 +000030 -> (val 000030)
  163 +000020 -> (val 000020)
  164 +000010 -> (val 000010)
  165 +000000 -> (val 000000)
  166 +find 000300 (0): 000300 -> (val 000300)
  167 +find 000305 (1): 000300 -> (val 000300)
  168 +find 000305 (0): not found
  169 +find 00000 (0): not found
  170 +find 00000 (1): not found
  171 +find 000800 (0): 000800 -> (val 000800)
  172 +find 000805 (0): not found
  173 +find 000805 (1): 000800 -> (val 000800)
... ...
qpdf/qtest/qpdf/name-tree.out
... ... @@ -7,4 +7,13 @@
7 7 20 twenty -> twenty.
8 8 22 twenty-two -> twenty-two!
9 9 29 twenty-nine -> twenty-nine!
  10 +01 one -> one!
  11 +06 σιχ -> six!
  12 +07 sev•n -> seven!
  13 +11 elephant -> elephant?
  14 +12 twelve -> twelve!
  15 +15 fifteen -> fifteen!
  16 +20 twenty -> twenty.
  17 +22 twenty-two -> twenty-two!
  18 +29 twenty-nine -> twenty-nine!
10 19 test 48 done
... ...
qpdf/qtest/qpdf/number-tree.out
... ... @@ -12,4 +12,18 @@
12 12 22 twenty-two
13 13 23 twenty-three
14 14 29 twenty-nine
  15 +1 one
  16 +2 two
  17 +3 three
  18 +5 five
  19 +6 six
  20 +9 nine
  21 +11 elephant
  22 +12 twelve
  23 +15 fifteen
  24 +19 nineteen
  25 +20 twenty
  26 +22 twenty-two
  27 +23 twenty-three
  28 +29 twenty-nine
15 29 test 46 done
... ...
qpdf/test_driver.cc
... ... @@ -1749,13 +1749,17 @@ void runtest(int n, char const* filename1, char const* arg2)
1749 1749 // number-tree.pdf
1750 1750 QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");
1751 1751 QPDFNumberTreeObjectHelper ntoh(qtest);
  1752 + for (auto iter: ntoh)
  1753 + {
  1754 + std::cout << iter.first << " "
  1755 + << iter.second.getStringValue()
  1756 + << std::endl;
  1757 + }
1752 1758 QPDFNumberTreeObjectHelper::idx_map ntoh_map = ntoh.getAsMap();
1753   - for (QPDFNumberTreeObjectHelper::idx_map::iterator iter =
1754   - ntoh_map.begin();
1755   - iter != ntoh_map.end(); ++iter)
  1759 + for (auto& iter: ntoh_map)
1756 1760 {
1757   - std::cout << (*iter).first << " "
1758   - << (*iter).second.getStringValue()
  1761 + std::cout << iter.first << " "
  1762 + << iter.second.getStringValue()
1759 1763 << std::endl;
1760 1764 }
1761 1765 assert(1 == ntoh.getMin());
... ... @@ -1793,13 +1797,17 @@ void runtest(int n, char const* filename1, char const* arg2)
1793 1797 // name-tree.pdf
1794 1798 QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest");
1795 1799 QPDFNameTreeObjectHelper ntoh(qtest);
  1800 + for (auto iter: ntoh)
  1801 + {
  1802 + std::cout << iter.first << " -> "
  1803 + << iter.second.getStringValue()
  1804 + << std::endl;
  1805 + }
1796 1806 std::map<std::string, QPDFObjectHandle> ntoh_map = ntoh.getAsMap();
1797   - for (std::map<std::string, QPDFObjectHandle>::iterator iter =
1798   - ntoh_map.begin();
1799   - iter != ntoh_map.end(); ++iter)
  1807 + for (auto& iter: ntoh_map)
1800 1808 {
1801   - std::cout << (*iter).first << " -> "
1802   - << (*iter).second.getStringValue()
  1809 + std::cout << iter.first << " -> "
  1810 + << iter.second.getStringValue()
1803 1811 << std::endl;
1804 1812 }
1805 1813 assert(ntoh.hasName("11 elephant"));
... ... @@ -1809,6 +1817,9 @@ void runtest(int n, char const* filename1, char const* arg2)
1809 1817 assert(! ntoh.findObject("potato", oh));
1810 1818 assert(ntoh.findObject("07 sev\xe2\x80\xa2n", oh));
1811 1819 assert("seven!" == oh.getStringValue());
  1820 + auto last = ntoh.last();
  1821 + assert((*last).first == "29 twenty-nine");
  1822 + assert((*last).second.getUTF8Value() == "twenty-nine!");
1812 1823 }
1813 1824 else if (n == 49)
1814 1825 {
... ...