mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 19:08:59 +00:00
Add C++ iterator API around array and dictionary objects
This commit is contained in:
parent
35e7859bc7
commit
de0b11fc47
14
ChangeLog
14
ChangeLog
@ -1,5 +1,19 @@
|
|||||||
2021-01-29 Jay Berkenbilt <ejb@ql.org>
|
2021-01-29 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Add wrappers QPDFDictItems and QPDFArrayItems around
|
||||||
|
QPDFObjectHandle that provide a C++ iterator API, including C++11
|
||||||
|
range-for iteration, over arrays and dictionaries. With this, you
|
||||||
|
can do
|
||||||
|
|
||||||
|
for (auto i: QPDFDictItems(oh))
|
||||||
|
{
|
||||||
|
// i.first is a string, i.second is a QPDFObjectHandle
|
||||||
|
}
|
||||||
|
for (auto i: QPDFArrayItems(oh))
|
||||||
|
{
|
||||||
|
// i is a QPDFObjectHandle
|
||||||
|
}
|
||||||
|
|
||||||
* QPDFObjectHandle::is* methods to check type now return false on
|
* QPDFObjectHandle::is* methods to check type now return false on
|
||||||
uninitialized objects rather than crashing or throwing a logic
|
uninitialized objects rather than crashing or throwing a logic
|
||||||
error.
|
error.
|
||||||
|
@ -631,7 +631,8 @@ class QPDFObjectHandle
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
std::string getInlineImageValue();
|
std::string getInlineImageValue();
|
||||||
|
|
||||||
// Methods for array objects; see also name and array objects
|
// Methods for array objects; see also name and array objects. See
|
||||||
|
// also QPDFArrayItems later in this file.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
int getArrayNItems();
|
int getArrayNItems();
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
@ -655,7 +656,8 @@ class QPDFObjectHandle
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
Matrix getArrayAsMatrix();
|
Matrix getArrayAsMatrix();
|
||||||
|
|
||||||
// Methods for dictionary objects
|
// Methods for dictionary objects. See also QPDFDictItems later in
|
||||||
|
// this file.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
bool hasKey(std::string const&);
|
bool hasKey(std::string const&);
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
@ -1224,4 +1226,179 @@ class QPDFObjectHandle
|
|||||||
bool reserved;
|
bool reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QPDFDictItems
|
||||||
|
{
|
||||||
|
// This class allows C++-style iteration, including range-for
|
||||||
|
// iteration, around dictionaries. You can write
|
||||||
|
|
||||||
|
// for (auto iter: QPDFDictItems(dictionary_obj))
|
||||||
|
// {
|
||||||
|
// // iter.first is a string
|
||||||
|
// // iter.second is a QPDFObjectHandle
|
||||||
|
// }
|
||||||
|
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
QPDFDictItems(QPDFObjectHandle& oh);
|
||||||
|
|
||||||
|
class iterator: public std::iterator<
|
||||||
|
std::bidirectional_iterator_tag,
|
||||||
|
std::pair<std::string, QPDFObjectHandle>>
|
||||||
|
{
|
||||||
|
friend class QPDFDictItems;
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
virtual ~iterator() = default;
|
||||||
|
QPDF_DLL
|
||||||
|
iterator& operator++();
|
||||||
|
QPDF_DLL
|
||||||
|
iterator operator++(int)
|
||||||
|
{
|
||||||
|
iterator t = *this;
|
||||||
|
++(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
QPDF_DLL
|
||||||
|
iterator& operator--();
|
||||||
|
QPDF_DLL
|
||||||
|
iterator operator--(int)
|
||||||
|
{
|
||||||
|
iterator t = *this;
|
||||||
|
--(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
QPDF_DLL
|
||||||
|
reference operator*();
|
||||||
|
QPDF_DLL
|
||||||
|
pointer operator->();
|
||||||
|
QPDF_DLL
|
||||||
|
bool operator==(iterator const& other) const;
|
||||||
|
QPDF_DLL
|
||||||
|
bool operator!=(iterator const& other) const
|
||||||
|
{
|
||||||
|
return ! operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
iterator(QPDFObjectHandle& oh, bool for_begin);
|
||||||
|
void updateIValue();
|
||||||
|
|
||||||
|
class Members
|
||||||
|
{
|
||||||
|
friend class QPDFDictItems::iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
~Members() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Members(QPDFObjectHandle& oh, bool for_begin);
|
||||||
|
Members() = delete;
|
||||||
|
Members(Members const&) = delete;
|
||||||
|
|
||||||
|
QPDFObjectHandle& oh;
|
||||||
|
std::set<std::string> keys;
|
||||||
|
std::set<std::string>::iterator iter;
|
||||||
|
bool is_end;
|
||||||
|
};
|
||||||
|
PointerHolder<Members> m;
|
||||||
|
value_type ivalue;
|
||||||
|
};
|
||||||
|
|
||||||
|
QPDF_DLL
|
||||||
|
iterator begin();
|
||||||
|
QPDF_DLL
|
||||||
|
iterator end();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPDFObjectHandle& oh;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QPDFArrayItems
|
||||||
|
{
|
||||||
|
// This class allows C++-style iteration, including range-for
|
||||||
|
// iteration, around arrays. You can write
|
||||||
|
|
||||||
|
// for (auto iter: QPDFArrayItems(array_obj))
|
||||||
|
// {
|
||||||
|
// // iter is a QPDFObjectHandle
|
||||||
|
// }
|
||||||
|
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
QPDFArrayItems(QPDFObjectHandle& oh);
|
||||||
|
|
||||||
|
class iterator: public std::iterator<
|
||||||
|
std::bidirectional_iterator_tag,
|
||||||
|
QPDFObjectHandle>
|
||||||
|
{
|
||||||
|
friend class QPDFArrayItems;
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
virtual ~iterator() = default;
|
||||||
|
QPDF_DLL
|
||||||
|
iterator& operator++();
|
||||||
|
QPDF_DLL
|
||||||
|
iterator operator++(int)
|
||||||
|
{
|
||||||
|
iterator t = *this;
|
||||||
|
++(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
QPDF_DLL
|
||||||
|
iterator& operator--();
|
||||||
|
QPDF_DLL
|
||||||
|
iterator operator--(int)
|
||||||
|
{
|
||||||
|
iterator t = *this;
|
||||||
|
--(*this);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
QPDF_DLL
|
||||||
|
reference operator*();
|
||||||
|
QPDF_DLL
|
||||||
|
pointer operator->();
|
||||||
|
QPDF_DLL
|
||||||
|
bool operator==(iterator const& other) const;
|
||||||
|
QPDF_DLL
|
||||||
|
bool operator!=(iterator const& other) const
|
||||||
|
{
|
||||||
|
return ! operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
iterator(QPDFObjectHandle& oh, bool for_begin);
|
||||||
|
void updateIValue();
|
||||||
|
|
||||||
|
class Members
|
||||||
|
{
|
||||||
|
friend class QPDFArrayItems::iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QPDF_DLL
|
||||||
|
~Members() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Members(QPDFObjectHandle& oh, bool for_begin);
|
||||||
|
Members() = delete;
|
||||||
|
Members(Members const&) = delete;
|
||||||
|
|
||||||
|
QPDFObjectHandle& oh;
|
||||||
|
int item_number;
|
||||||
|
bool is_end;
|
||||||
|
};
|
||||||
|
PointerHolder<Members> m;
|
||||||
|
value_type ivalue;
|
||||||
|
};
|
||||||
|
|
||||||
|
QPDF_DLL
|
||||||
|
iterator begin();
|
||||||
|
QPDF_DLL
|
||||||
|
iterator end();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPDFObjectHandle& oh;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // QPDFOBJECTHANDLE_HH
|
#endif // QPDFOBJECTHANDLE_HH
|
||||||
|
@ -3079,3 +3079,180 @@ QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e)
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::QPDFDictItems(QPDFObjectHandle& oh) :
|
||||||
|
oh(oh)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator&
|
||||||
|
QPDFDictItems::iterator::operator++()
|
||||||
|
{
|
||||||
|
++this->m->iter;
|
||||||
|
updateIValue();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator&
|
||||||
|
QPDFDictItems::iterator::operator--()
|
||||||
|
{
|
||||||
|
--this->m->iter;
|
||||||
|
updateIValue();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator::reference
|
||||||
|
QPDFDictItems::iterator:: operator*()
|
||||||
|
{
|
||||||
|
updateIValue();
|
||||||
|
return this->ivalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator::pointer
|
||||||
|
QPDFDictItems::iterator::operator->()
|
||||||
|
{
|
||||||
|
updateIValue();
|
||||||
|
return &this->ivalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFDictItems::iterator::operator==(iterator const& other) const
|
||||||
|
{
|
||||||
|
if (this->m->is_end && other.m->is_end)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this->m->is_end || other.m->is_end)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (this->ivalue.first == other.ivalue.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator::iterator(QPDFObjectHandle& oh, bool for_begin) :
|
||||||
|
m(new Members(oh, for_begin))
|
||||||
|
{
|
||||||
|
updateIValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFDictItems::iterator::updateIValue()
|
||||||
|
{
|
||||||
|
this->m->is_end = (this->m->iter == this->m->keys.end());
|
||||||
|
if (this->m->is_end)
|
||||||
|
{
|
||||||
|
this->ivalue.first = "";
|
||||||
|
this->ivalue.second = QPDFObjectHandle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->ivalue.first = *(this->m->iter);
|
||||||
|
this->ivalue.second = this->m->oh.getKey(this->ivalue.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator::Members::Members(
|
||||||
|
QPDFObjectHandle& oh, bool for_begin) :
|
||||||
|
oh(oh)
|
||||||
|
{
|
||||||
|
this->keys = oh.getKeys();
|
||||||
|
this->iter = for_begin ? this->keys.begin() : this->keys.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator
|
||||||
|
QPDFDictItems::begin()
|
||||||
|
{
|
||||||
|
return iterator(oh, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFDictItems::iterator
|
||||||
|
QPDFDictItems::end()
|
||||||
|
{
|
||||||
|
return iterator(oh, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::QPDFArrayItems(QPDFObjectHandle& oh) :
|
||||||
|
oh(oh)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator&
|
||||||
|
QPDFArrayItems::iterator::operator++()
|
||||||
|
{
|
||||||
|
if (! this->m->is_end)
|
||||||
|
{
|
||||||
|
++this->m->item_number;
|
||||||
|
updateIValue();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator&
|
||||||
|
QPDFArrayItems::iterator::operator--()
|
||||||
|
{
|
||||||
|
if (this->m->item_number > 0)
|
||||||
|
{
|
||||||
|
--this->m->item_number;
|
||||||
|
updateIValue();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator::reference
|
||||||
|
QPDFArrayItems::iterator:: operator*()
|
||||||
|
{
|
||||||
|
updateIValue();
|
||||||
|
return this->ivalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator::pointer
|
||||||
|
QPDFArrayItems::iterator::operator->()
|
||||||
|
{
|
||||||
|
updateIValue();
|
||||||
|
return &this->ivalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFArrayItems::iterator::operator==(iterator const& other) const
|
||||||
|
{
|
||||||
|
return (this->m->item_number == other.m->item_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator::iterator(QPDFObjectHandle& oh, bool for_begin) :
|
||||||
|
m(new Members(oh, for_begin))
|
||||||
|
{
|
||||||
|
updateIValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFArrayItems::iterator::updateIValue()
|
||||||
|
{
|
||||||
|
this->m->is_end = (this->m->item_number >= this->m->oh.getArrayNItems());
|
||||||
|
if (this->m->is_end)
|
||||||
|
{
|
||||||
|
this->ivalue = QPDFObjectHandle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->ivalue = this->m->oh.getArrayItem(this->m->item_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator::Members::Members(
|
||||||
|
QPDFObjectHandle& oh, bool for_begin) :
|
||||||
|
oh(oh)
|
||||||
|
{
|
||||||
|
this->item_number = for_begin ? 0 : oh.getArrayNItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator
|
||||||
|
QPDFArrayItems::begin()
|
||||||
|
{
|
||||||
|
return iterator(oh, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPDFArrayItems::iterator
|
||||||
|
QPDFArrayItems::end()
|
||||||
|
{
|
||||||
|
return iterator(oh, false);
|
||||||
|
}
|
||||||
|
@ -4847,6 +4847,17 @@ print "\n";
|
|||||||
Library Enhancements
|
Library Enhancements
|
||||||
</para>
|
</para>
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Add <classname>QPDFDictItems</classname> and
|
||||||
|
<classname>QPDFArrayItems</classname> wrappers around
|
||||||
|
<classname>QPDFObjectHandle</classname>, allowing C++-style
|
||||||
|
iteration, including range-for iteration, over dictionary
|
||||||
|
and array QPDFObjectHandles. See comments in
|
||||||
|
<filename>include/qpdf/QPDFObjectHandle.hh</filename> for
|
||||||
|
details.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Add <function>warn</function> to
|
Add <function>warn</function> to
|
||||||
|
@ -347,30 +347,29 @@ void runtest(int n, char const* filename1, char const* arg2)
|
|||||||
else if (qtest.isArray())
|
else if (qtest.isArray())
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "main QTest array");
|
QTC::TC("qpdf", "main QTest array");
|
||||||
int nitems = qtest.getArrayNItems();
|
|
||||||
std::cout << "/QTest is an array with "
|
std::cout << "/QTest is an array with "
|
||||||
<< nitems << " items" << std::endl;
|
<< qtest.getArrayNItems() << " items" << std::endl;
|
||||||
for (int i = 0; i < nitems; ++i)
|
int i = 0;
|
||||||
|
for (auto& iter: QPDFArrayItems(qtest))
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "main QTest array indirect",
|
QTC::TC("qpdf", "main QTest array indirect",
|
||||||
qtest.getArrayItem(i).isIndirect() ? 1 : 0);
|
iter.isIndirect() ? 1 : 0);
|
||||||
std::cout << " item " << i << " is "
|
std::cout << " item " << i << " is "
|
||||||
<< (qtest.getArrayItem(i).isIndirect() ? "in" : "")
|
<< (iter.isIndirect() ? "in" : "")
|
||||||
<< "direct" << std::endl;
|
<< "direct" << std::endl;
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (qtest.isDictionary())
|
else if (qtest.isDictionary())
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "main QTest dictionary");
|
QTC::TC("qpdf", "main QTest dictionary");
|
||||||
std::cout << "/QTest is a dictionary" << std::endl;
|
std::cout << "/QTest is a dictionary" << std::endl;
|
||||||
std::set<std::string> keys = qtest.getKeys();
|
for (auto& iter: QPDFDictItems(qtest))
|
||||||
for (std::set<std::string>::iterator iter = keys.begin();
|
|
||||||
iter != keys.end(); ++iter)
|
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "main QTest dictionary indirect",
|
QTC::TC("qpdf", "main QTest dictionary indirect",
|
||||||
(qtest.getKey(*iter).isIndirect() ? 1 : 0));
|
iter.second.isIndirect() ? 1 : 0);
|
||||||
std::cout << " " << *iter << " is "
|
std::cout << " " << iter.first << " is "
|
||||||
<< (qtest.getKey(*iter).isIndirect() ? "in" : "")
|
<< (iter.second.isIndirect() ? "in" : "")
|
||||||
<< "direct" << std::endl;
|
<< "direct" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1539,7 +1538,38 @@ void runtest(int n, char const* filename1, char const* arg2)
|
|||||||
QPDFObjectHandle integer = qtest.getKey("/Integer");
|
QPDFObjectHandle integer = qtest.getKey("/Integer");
|
||||||
QPDFObjectHandle null = QPDFObjectHandle::newNull();
|
QPDFObjectHandle null = QPDFObjectHandle::newNull();
|
||||||
assert(array.isArray());
|
assert(array.isArray());
|
||||||
|
{
|
||||||
|
// Exercise iterators directly
|
||||||
|
QPDFArrayItems ai(array);
|
||||||
|
auto i = ai.begin();
|
||||||
|
assert(i->getName() == "/Item0");
|
||||||
|
auto& i_value = *i;
|
||||||
|
--i;
|
||||||
|
assert(i->getName() == "/Item0");
|
||||||
|
++i;
|
||||||
|
++i;
|
||||||
|
++i;
|
||||||
|
assert(i == ai.end());
|
||||||
|
++i;
|
||||||
|
assert(i == ai.end());
|
||||||
|
assert(! i_value.isInitialized());
|
||||||
|
--i;
|
||||||
|
assert(i_value.getName() == "/Item2");
|
||||||
|
assert(i->getName() == "/Item2");
|
||||||
|
}
|
||||||
assert(dictionary.isDictionary());
|
assert(dictionary.isDictionary());
|
||||||
|
{
|
||||||
|
// Exercise iterators directly
|
||||||
|
QPDFDictItems di(dictionary);
|
||||||
|
auto i = di.begin();
|
||||||
|
assert(i->first == "/Key1");
|
||||||
|
auto& i_value = *i;
|
||||||
|
assert(i->second.getName() == "/Value1");
|
||||||
|
++i;
|
||||||
|
++i;
|
||||||
|
assert(i == di.end());
|
||||||
|
assert(! i_value.second.isInitialized());
|
||||||
|
}
|
||||||
assert("" == qtest.getStringValue());
|
assert("" == qtest.getStringValue());
|
||||||
array.getArrayItem(-1).assertNull();
|
array.getArrayItem(-1).assertNull();
|
||||||
array.getArrayItem(16059).assertNull();
|
array.getArrayItem(16059).assertNull();
|
||||||
@ -1599,6 +1629,10 @@ void runtest(int n, char const* filename1, char const* arg2)
|
|||||||
(r1.lly > 3.39) && (r1.lly < 3.41) &&
|
(r1.lly > 3.39) && (r1.lly < 3.41) &&
|
||||||
(r1.urx > 5.59) && (r1.urx < 5.61) &&
|
(r1.urx > 5.59) && (r1.urx < 5.61) &&
|
||||||
(r1.ury > 7.79) && (r1.ury < 7.81));
|
(r1.ury > 7.79) && (r1.ury < 7.81));
|
||||||
|
QPDFObjectHandle uninitialized;
|
||||||
|
assert(! uninitialized.isInitialized());
|
||||||
|
assert(! uninitialized.isInteger());
|
||||||
|
assert(! uninitialized.isDictionary());
|
||||||
}
|
}
|
||||||
else if (n == 43)
|
else if (n == 43)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user