Commit 5f0708418a0a9cbacc033f52efc11c759120fd09
1 parent
4a1cce0a
Add iterators to name/number tree helpers
Showing
10 changed files
with
628 additions
and
45 deletions
include/qpdf/QPDFNameTreeObjectHelper.hh
| @@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
| 26 | #include <qpdf/QPDFObjGen.hh> | 26 | #include <qpdf/QPDFObjGen.hh> |
| 27 | #include <map> | 27 | #include <map> |
| 28 | #include <memory> | 28 | #include <memory> |
| 29 | +#include <iterator> | ||
| 29 | 30 | ||
| 30 | #include <qpdf/DLL.h> | 31 | #include <qpdf/DLL.h> |
| 31 | 32 | ||
| @@ -35,6 +36,8 @@ | @@ -35,6 +36,8 @@ | ||
| 35 | // normalized for lookup purposes. | 36 | // normalized for lookup purposes. |
| 36 | 37 | ||
| 37 | class NNTreeImpl; | 38 | class NNTreeImpl; |
| 39 | +class NNTreeIterator; | ||
| 40 | +class NNTreeDetails; | ||
| 38 | 41 | ||
| 39 | class QPDFNameTreeObjectHelper: public QPDFObjectHelper | 42 | class QPDFNameTreeObjectHelper: public QPDFObjectHelper |
| 40 | { | 43 | { |
| @@ -54,6 +57,66 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper | @@ -54,6 +57,66 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper | ||
| 54 | QPDF_DLL | 57 | QPDF_DLL |
| 55 | bool findObject(std::string const& utf8, QPDFObjectHandle& oh); | 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 | // Return the contents of the name tree as a map. Note that name | 120 | // Return the contents of the name tree as a map. Note that name |
| 58 | // trees may be very large, so this may use a lot of RAM. It is | 121 | // trees may be very large, so this may use a lot of RAM. It is |
| 59 | // more efficient to use QPDFNameTreeObjectHelper's iterator. | 122 | // more efficient to use QPDFNameTreeObjectHelper's iterator. |
include/qpdf/QPDFNumberTreeObjectHelper.hh
| @@ -33,6 +33,8 @@ | @@ -33,6 +33,8 @@ | ||
| 33 | // PDF spec (ISO 32000) for a description of number trees. | 33 | // PDF spec (ISO 32000) for a description of number trees. |
| 34 | 34 | ||
| 35 | class NNTreeImpl; | 35 | class NNTreeImpl; |
| 36 | +class NNTreeIterator; | ||
| 37 | +class NNTreeDetails; | ||
| 36 | 38 | ||
| 37 | class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | 39 | class QPDFNumberTreeObjectHelper: public QPDFObjectHelper |
| 38 | { | 40 | { |
| @@ -73,6 +75,65 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | @@ -73,6 +75,65 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | ||
| 73 | bool findObjectAtOrBelow(numtree_number idx, QPDFObjectHandle& oh, | 75 | bool findObjectAtOrBelow(numtree_number idx, QPDFObjectHandle& oh, |
| 74 | numtree_number& offset); | 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 | // Return the contents of the number tree as a map. Note that | 137 | // Return the contents of the number tree as a map. Note that |
| 77 | // number trees may be very large, so this may use a lot of RAM. | 138 | // number trees may be very large, so this may use a lot of RAM. |
| 78 | // It is more efficient to use QPDFNumberTreeObjectHelper's | 139 | // It is more efficient to use QPDFNumberTreeObjectHelper's |
libqpdf/QPDFNameTreeObjectHelper.cc
| @@ -48,19 +48,85 @@ QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() | @@ -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 | bool | 117 | bool |
| 52 | QPDFNameTreeObjectHelper::hasName(std::string const& name) | 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 | bool | 124 | bool |
| 59 | QPDFNameTreeObjectHelper::findObject( | 125 | QPDFNameTreeObjectHelper::findObject( |
| 60 | std::string const& name, QPDFObjectHandle& oh) | 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 | return false; | 131 | return false; |
| 66 | } | 132 | } |
| @@ -72,11 +138,6 @@ std::map<std::string, QPDFObjectHandle> | @@ -72,11 +138,6 @@ std::map<std::string, QPDFObjectHandle> | ||
| 72 | QPDFNameTreeObjectHelper::getAsMap() const | 138 | QPDFNameTreeObjectHelper::getAsMap() const |
| 73 | { | 139 | { |
| 74 | std::map<std::string, QPDFObjectHandle> result; | 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 | return result; | 142 | return result; |
| 82 | } | 143 | } |
libqpdf/QPDFNumberTreeObjectHelper.cc
| @@ -44,41 +44,107 @@ QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(QPDFObjectHandle oh) : | @@ -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 | QPDFNumberTreeObjectHelper::numtree_number | 113 | QPDFNumberTreeObjectHelper::numtree_number |
| 48 | QPDFNumberTreeObjectHelper::getMin() | 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 | return 0; | 119 | return 0; |
| 54 | } | 120 | } |
| 55 | - return (*i).first.getIntValue(); | 121 | + return (*i).first; |
| 56 | } | 122 | } |
| 57 | 123 | ||
| 58 | QPDFNumberTreeObjectHelper::numtree_number | 124 | QPDFNumberTreeObjectHelper::numtree_number |
| 59 | QPDFNumberTreeObjectHelper::getMax() | 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 | return 0; | 130 | return 0; |
| 65 | } | 131 | } |
| 66 | - return (*i).first.getIntValue(); | 132 | + return (*i).first; |
| 67 | } | 133 | } |
| 68 | 134 | ||
| 69 | bool | 135 | bool |
| 70 | QPDFNumberTreeObjectHelper::hasIndex(numtree_number idx) | 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 | bool | 142 | bool |
| 77 | QPDFNumberTreeObjectHelper::findObject( | 143 | QPDFNumberTreeObjectHelper::findObject( |
| 78 | numtree_number idx, QPDFObjectHandle& oh) | 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 | return false; | 149 | return false; |
| 84 | } | 150 | } |
| @@ -91,13 +157,13 @@ QPDFNumberTreeObjectHelper::findObjectAtOrBelow( | @@ -91,13 +157,13 @@ QPDFNumberTreeObjectHelper::findObjectAtOrBelow( | ||
| 91 | numtree_number idx, QPDFObjectHandle& oh, | 157 | numtree_number idx, QPDFObjectHandle& oh, |
| 92 | numtree_number& offset) | 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 | return false; | 163 | return false; |
| 98 | } | 164 | } |
| 99 | oh = (*i).second; | 165 | oh = (*i).second; |
| 100 | - offset = idx - (*i).first.getIntValue(); | 166 | + offset = idx - (*i).first; |
| 101 | return true; | 167 | return true; |
| 102 | } | 168 | } |
| 103 | 169 | ||
| @@ -105,11 +171,6 @@ std::map<QPDFNumberTreeObjectHelper::numtree_number, QPDFObjectHandle> | @@ -105,11 +171,6 @@ std::map<QPDFNumberTreeObjectHelper::numtree_number, QPDFObjectHandle> | ||
| 105 | QPDFNumberTreeObjectHelper::getAsMap() const | 171 | QPDFNumberTreeObjectHelper::getAsMap() const |
| 106 | { | 172 | { |
| 107 | std::map<numtree_number, QPDFObjectHandle> result; | 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 | return result; | 175 | return result; |
| 115 | } | 176 | } |
libtests/nntree.cc
| 1 | #include <qpdf/QPDFNumberTreeObjectHelper.hh> | 1 | #include <qpdf/QPDFNumberTreeObjectHelper.hh> |
| 2 | +#include <qpdf/QPDFNameTreeObjectHelper.hh> | ||
| 2 | #include <qpdf/QPDF.hh> | 3 | #include <qpdf/QPDF.hh> |
| 3 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 4 | #include <iostream> | 5 | #include <iostream> |
| 5 | 6 | ||
| 7 | +static bool any_failures = false; | ||
| 8 | + | ||
| 6 | bool report(QPDFObjectHandle oh, long long item, long long exp_item) | 9 | bool report(QPDFObjectHandle oh, long long item, long long exp_item) |
| 7 | { | 10 | { |
| 8 | QPDFNumberTreeObjectHelper nh(oh); | 11 | QPDFNumberTreeObjectHelper nh(oh); |
| @@ -56,7 +59,7 @@ bool report(QPDFObjectHandle oh, long long item, long long exp_item) | @@ -56,7 +59,7 @@ bool report(QPDFObjectHandle oh, long long item, long long exp_item) | ||
| 56 | return failed; | 59 | return failed; |
| 57 | } | 60 | } |
| 58 | 61 | ||
| 59 | -int main() | 62 | +void test_bsearch() |
| 60 | { | 63 | { |
| 61 | QPDF q; | 64 | QPDF q; |
| 62 | q.emptyPDF(); | 65 | q.emptyPDF(); |
| @@ -78,8 +81,7 @@ int main() | @@ -78,8 +81,7 @@ int main() | ||
| 78 | return node; | 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 | if (report(oh, item, exp)) | 85 | if (report(oh, item, exp)) |
| 84 | { | 86 | { |
| 85 | any_failures = true; | 87 | any_failures = true; |
| @@ -119,6 +121,133 @@ int main() | @@ -119,6 +121,133 @@ int main() | ||
| 119 | 121 | ||
| 120 | if (! any_failures) | 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,14 +3,15 @@ require 5.008; | ||
| 3 | use warnings; | 3 | use warnings; |
| 4 | use strict; | 4 | use strict; |
| 5 | 5 | ||
| 6 | +chdir("nntree") or die "chdir testdir failed: $!\n"; | ||
| 7 | + | ||
| 6 | require TestDriver; | 8 | require TestDriver; |
| 7 | 9 | ||
| 8 | my $td = new TestDriver('nntree'); | 10 | my $td = new TestDriver('nntree'); |
| 9 | 11 | ||
| 10 | $td->runtest("nntree", | 12 | $td->runtest("nntree", |
| 11 | {$td->COMMAND => "nntree"}, | 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 | $td->NORMALIZE_NEWLINES); | 15 | $td->NORMALIZE_NEWLINES); |
| 15 | 16 | ||
| 16 | $td->report(1); | 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,4 +7,13 @@ | ||
| 7 | 20 twenty -> twenty. | 7 | 20 twenty -> twenty. |
| 8 | 22 twenty-two -> twenty-two! | 8 | 22 twenty-two -> twenty-two! |
| 9 | 29 twenty-nine -> twenty-nine! | 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 | test 48 done | 19 | test 48 done |
qpdf/qtest/qpdf/number-tree.out
| @@ -12,4 +12,18 @@ | @@ -12,4 +12,18 @@ | ||
| 12 | 22 twenty-two | 12 | 22 twenty-two |
| 13 | 23 twenty-three | 13 | 23 twenty-three |
| 14 | 29 twenty-nine | 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 | test 46 done | 29 | test 46 done |
qpdf/test_driver.cc
| @@ -1749,13 +1749,17 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1749,13 +1749,17 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1749 | // number-tree.pdf | 1749 | // number-tree.pdf |
| 1750 | QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); | 1750 | QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); |
| 1751 | QPDFNumberTreeObjectHelper ntoh(qtest); | 1751 | QPDFNumberTreeObjectHelper ntoh(qtest); |
| 1752 | + for (auto iter: ntoh) | ||
| 1753 | + { | ||
| 1754 | + std::cout << iter.first << " " | ||
| 1755 | + << iter.second.getStringValue() | ||
| 1756 | + << std::endl; | ||
| 1757 | + } | ||
| 1752 | QPDFNumberTreeObjectHelper::idx_map ntoh_map = ntoh.getAsMap(); | 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 | << std::endl; | 1763 | << std::endl; |
| 1760 | } | 1764 | } |
| 1761 | assert(1 == ntoh.getMin()); | 1765 | assert(1 == ntoh.getMin()); |
| @@ -1793,13 +1797,17 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1793,13 +1797,17 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1793 | // name-tree.pdf | 1797 | // name-tree.pdf |
| 1794 | QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); | 1798 | QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); |
| 1795 | QPDFNameTreeObjectHelper ntoh(qtest); | 1799 | QPDFNameTreeObjectHelper ntoh(qtest); |
| 1800 | + for (auto iter: ntoh) | ||
| 1801 | + { | ||
| 1802 | + std::cout << iter.first << " -> " | ||
| 1803 | + << iter.second.getStringValue() | ||
| 1804 | + << std::endl; | ||
| 1805 | + } | ||
| 1796 | std::map<std::string, QPDFObjectHandle> ntoh_map = ntoh.getAsMap(); | 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 | << std::endl; | 1811 | << std::endl; |
| 1804 | } | 1812 | } |
| 1805 | assert(ntoh.hasName("11 elephant")); | 1813 | assert(ntoh.hasName("11 elephant")); |
| @@ -1809,6 +1817,9 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1809,6 +1817,9 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1809 | assert(! ntoh.findObject("potato", oh)); | 1817 | assert(! ntoh.findObject("potato", oh)); |
| 1810 | assert(ntoh.findObject("07 sev\xe2\x80\xa2n", oh)); | 1818 | assert(ntoh.findObject("07 sev\xe2\x80\xa2n", oh)); |
| 1811 | assert("seven!" == oh.getStringValue()); | 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 | else if (n == 49) | 1824 | else if (n == 49) |
| 1814 | { | 1825 | { |