2021-01-16 13:31:56 +00:00
|
|
|
#include <qpdf/QPDF.hh>
|
2021-01-16 17:11:17 +00:00
|
|
|
#include <qpdf/QPDFNameTreeObjectHelper.hh>
|
2021-01-16 13:31:56 +00:00
|
|
|
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
|
|
|
|
#include <qpdf/QUtil.hh>
|
|
|
|
#include <iostream>
|
|
|
|
|
2021-01-16 17:11:17 +00:00
|
|
|
static bool any_failures = false;
|
|
|
|
|
2021-01-17 22:54:52 +00:00
|
|
|
bool
|
|
|
|
report(QPDF& q, QPDFObjectHandle oh, long long item, long long exp_item)
|
2021-01-16 13:31:56 +00:00
|
|
|
{
|
2021-01-17 22:54:52 +00:00
|
|
|
QPDFNumberTreeObjectHelper nh(oh, q);
|
2021-01-16 13:31:56 +00:00
|
|
|
QPDFObjectHandle o1;
|
|
|
|
long long offset = 0;
|
|
|
|
bool f1 = nh.findObjectAtOrBelow(item, o1, offset);
|
|
|
|
QPDFObjectHandle o2;
|
|
|
|
bool f2 = nh.findObject(item, o2);
|
|
|
|
|
|
|
|
bool failed = false;
|
|
|
|
auto show = [&failed, &oh, &item]() {
|
|
|
|
if (!failed) {
|
|
|
|
failed = true;
|
|
|
|
std::cout << "key = " << item << ", oh = " << oh.unparseResolved() << std::endl;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto mk_wanted = [](long long i) {
|
2022-09-21 16:49:21 +00:00
|
|
|
return ((i == -1) ? "end" : (std::to_string(i) + "/(-" + std::to_string(i) + "-)"));
|
2021-01-16 13:31:56 +00:00
|
|
|
};
|
|
|
|
std::string i1_wanted = mk_wanted(exp_item);
|
|
|
|
std::string i2_wanted = mk_wanted(item == exp_item ? item : -1);
|
|
|
|
auto mk_actual = [](bool found, long long v, QPDFObjectHandle& o) {
|
2022-09-21 16:49:21 +00:00
|
|
|
return (found ? std::to_string(v) + "/" + o.unparse() : "end");
|
2021-01-16 13:31:56 +00:00
|
|
|
};
|
|
|
|
std::string i1_actual = mk_actual(f1, item - offset, o1);
|
|
|
|
std::string i2_actual = mk_actual(f2, item, o2);
|
|
|
|
|
|
|
|
if (i1_wanted != i1_actual) {
|
|
|
|
show();
|
|
|
|
std::cout << "i1: wanted " << i1_wanted << ", got " << i1_actual << std::endl;
|
|
|
|
}
|
|
|
|
if (i2_wanted != i2_actual) {
|
|
|
|
show();
|
|
|
|
std::cout << "i2: wanted " << i2_wanted << ", got " << i2_actual << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
return failed;
|
|
|
|
}
|
|
|
|
|
2021-01-16 17:11:17 +00:00
|
|
|
void
|
|
|
|
test_bsearch()
|
2021-01-16 13:31:56 +00:00
|
|
|
{
|
|
|
|
QPDF q;
|
|
|
|
q.emptyPDF();
|
|
|
|
|
|
|
|
auto mk = [&q](std::vector<int> const& v) {
|
|
|
|
auto nums = QPDFObjectHandle::newArray();
|
|
|
|
for (auto i: v) {
|
2022-05-17 22:28:50 +00:00
|
|
|
nums.appendItem(QPDFObjectHandle::newInteger(i));
|
2022-09-21 16:49:21 +00:00
|
|
|
nums.appendItem(QPDFObjectHandle::newString("-" + std::to_string(i) + "-"));
|
2021-01-16 13:31:56 +00:00
|
|
|
}
|
|
|
|
auto limits = QPDFObjectHandle::newArray();
|
2022-05-17 22:28:50 +00:00
|
|
|
limits.appendItem(QPDFObjectHandle::newInteger(v.at(0)));
|
|
|
|
limits.appendItem(QPDFObjectHandle::newInteger(v.at(v.size() - 1)));
|
2021-01-16 13:31:56 +00:00
|
|
|
auto node = q.makeIndirectObject(QPDFObjectHandle::newDictionary());
|
2022-05-17 22:28:50 +00:00
|
|
|
node.replaceKey("/Nums", nums);
|
|
|
|
node.replaceKey("/Limits", limits);
|
2021-01-16 13:31:56 +00:00
|
|
|
return node;
|
|
|
|
};
|
|
|
|
|
2021-01-17 22:54:52 +00:00
|
|
|
auto r = [&q](QPDFObjectHandle& oh, int item, int exp) {
|
|
|
|
if (report(q, oh, item, exp)) {
|
2021-01-16 13:31:56 +00:00
|
|
|
any_failures = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto a = mk({2, 3, 5, 9, 11, 12, 14, 18});
|
|
|
|
r(a, 1, -1);
|
|
|
|
r(a, 2, 2);
|
|
|
|
r(a, 9, 9);
|
|
|
|
r(a, 11, 11);
|
|
|
|
r(a, 10, 9);
|
|
|
|
r(a, 7, 5);
|
|
|
|
r(a, 18, 18);
|
|
|
|
r(a, 19, 18);
|
|
|
|
|
|
|
|
auto b = mk({2, 4});
|
|
|
|
r(b, 1, -1);
|
|
|
|
r(b, 2, 2);
|
|
|
|
r(b, 3, 2);
|
|
|
|
r(b, 4, 4);
|
|
|
|
r(b, 5, 4);
|
|
|
|
|
|
|
|
auto c = mk({3});
|
|
|
|
r(c, 1, -1);
|
|
|
|
r(c, 3, 3);
|
|
|
|
r(c, 5, 3);
|
|
|
|
|
|
|
|
auto d = mk({2, 3, 5, 9, 10, 12, 14, 18, 19, 20});
|
|
|
|
r(d, 1, -1);
|
|
|
|
r(d, 2, 2);
|
|
|
|
r(d, 18, 18);
|
|
|
|
r(d, 14, 14);
|
|
|
|
r(d, 19, 19);
|
|
|
|
r(d, 20, 20);
|
|
|
|
r(d, 25, 20);
|
|
|
|
|
|
|
|
if (!any_failures) {
|
2021-01-16 17:11:17 +00:00
|
|
|
std::cout << "bsearch tests passed" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QPDFObjectHandle
|
|
|
|
new_node(QPDF& q, std::string const& key)
|
|
|
|
{
|
|
|
|
auto dict = QPDFObjectHandle::newDictionary();
|
|
|
|
dict.replaceKey(key, QPDFObjectHandle::newArray());
|
|
|
|
return q.makeIndirectObject(dict);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
check_find(QPDFNameTreeObjectHelper& nh, std::string const& key, bool prev_if_not_found)
|
|
|
|
{
|
|
|
|
auto i = nh.find(key, prev_if_not_found);
|
|
|
|
std::cout << "find " << key << " (" << prev_if_not_found << "): ";
|
|
|
|
if (i == nh.end()) {
|
|
|
|
std::cout << "not found";
|
|
|
|
} else {
|
|
|
|
std::cout << (*i).first << " -> " << (*i).second.unparse();
|
|
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
test_depth()
|
|
|
|
{
|
|
|
|
int constexpr NITEMS = 3;
|
|
|
|
QPDF q;
|
|
|
|
q.emptyPDF();
|
|
|
|
auto root = q.getRoot();
|
|
|
|
auto n0 = new_node(q, "/Kids");
|
|
|
|
root.replaceKey("/NT", n0);
|
|
|
|
auto k0 = root.getKey("/NT").getKey("/Kids");
|
|
|
|
for (int i1 = 0; i1 < NITEMS; ++i1) {
|
|
|
|
auto n1 = new_node(q, "/Kids");
|
|
|
|
k0.appendItem(n1);
|
|
|
|
auto k1 = n1.getKey("/Kids");
|
|
|
|
for (int i2 = 0; i2 < NITEMS; ++i2) {
|
|
|
|
auto n2 = new_node(q, "/Kids");
|
|
|
|
k1.appendItem(n2);
|
|
|
|
auto k2 = n2.getKey("/Kids");
|
|
|
|
for (int i3 = 0; i3 < NITEMS; ++i3) {
|
|
|
|
auto n3 = new_node(q, "/Names");
|
|
|
|
k2.appendItem(n3);
|
|
|
|
auto items = n3.getKey("/Names");
|
|
|
|
std::string first;
|
|
|
|
std::string last;
|
|
|
|
for (int i4 = 0; i4 < NITEMS; ++i4) {
|
|
|
|
int val = (((((i1 * NITEMS) + i2) * NITEMS) + i3) * NITEMS) + i4;
|
|
|
|
std::string str = QUtil::int_to_string(10 * val, 6);
|
2022-05-17 22:28:50 +00:00
|
|
|
items.appendItem(QPDFObjectHandle::newString(str));
|
|
|
|
items.appendItem(QPDFObjectHandle::newString("val " + str));
|
2021-01-16 17:11:17 +00:00
|
|
|
if (i4 == 0) {
|
|
|
|
first = str;
|
|
|
|
} else if (i4 == NITEMS - 1) {
|
|
|
|
last = str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto limits = QPDFObjectHandle::newArray();
|
|
|
|
n3.replaceKey("/Limits", limits);
|
2022-05-17 22:28:50 +00:00
|
|
|
limits.appendItem(QPDFObjectHandle::newString(first));
|
|
|
|
limits.appendItem(QPDFObjectHandle::newString(last));
|
2021-01-16 17:11:17 +00:00
|
|
|
}
|
|
|
|
auto limits = QPDFObjectHandle::newArray();
|
|
|
|
n2.replaceKey("/Limits", limits);
|
2022-05-17 22:28:50 +00:00
|
|
|
limits.appendItem(k2.getArrayItem(0).getKey("/Limits").getArrayItem(0));
|
|
|
|
limits.appendItem(k2.getArrayItem(NITEMS - 1).getKey("/Limits").getArrayItem(1));
|
2021-01-16 17:11:17 +00:00
|
|
|
}
|
|
|
|
auto limits = QPDFObjectHandle::newArray();
|
|
|
|
n1.replaceKey("/Limits", limits);
|
2022-05-17 22:28:50 +00:00
|
|
|
limits.appendItem(k1.getArrayItem(0).getKey("/Limits").getArrayItem(0));
|
|
|
|
limits.appendItem(k1.getArrayItem(NITEMS - 1).getKey("/Limits").getArrayItem(1));
|
2021-01-16 13:31:56 +00:00
|
|
|
}
|
2021-01-16 17:11:17 +00:00
|
|
|
|
2021-01-17 22:54:52 +00:00
|
|
|
QPDFNameTreeObjectHelper nh(n0, q);
|
2021-01-16 17:11:17 +00:00
|
|
|
std::cout << "--- forward ---" << std::endl;
|
|
|
|
for (auto i: nh) {
|
|
|
|
std::cout << i.first << " -> " << i.second.unparse() << std::endl;
|
|
|
|
}
|
|
|
|
std::cout << "--- backward ---" << std::endl;
|
|
|
|
for (auto i = nh.last(); i.valid(); --i) {
|
|
|
|
std::cout << (*i).first << " -> " << (*i).second.unparse() << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find
|
|
|
|
check_find(nh, "000300", false);
|
|
|
|
check_find(nh, "000305", true);
|
|
|
|
check_find(nh, "000305", false);
|
|
|
|
check_find(nh, "00000", false);
|
|
|
|
check_find(nh, "00000", true);
|
|
|
|
check_find(nh, "000800", false);
|
|
|
|
check_find(nh, "000805", false);
|
|
|
|
check_find(nh, "000805", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main()
|
|
|
|
{
|
|
|
|
test_bsearch();
|
|
|
|
test_depth();
|
|
|
|
|
|
|
|
return 0;
|
2021-01-16 13:31:56 +00:00
|
|
|
}
|