1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
#include <qpdf/QPDFPageLabelDocumentHelper.hh>
#include <qpdf/QPDFNumberTreeObjectHelper.hh>
#include <qpdf/QPDFObjectHandle_private.hh>
#include <qpdf/QPDF_private.hh>
using namespace qpdf;
class QPDFPageLabelDocumentHelper::Members
{
public:
Members() = default;
Members(Members const&) = delete;
~Members() = default;
std::unique_ptr<QPDFNumberTreeObjectHelper> labels;
};
QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF& qpdf) :
QPDFDocumentHelper(qpdf),
m(std::make_shared<Members>())
{
validate();
}
QPDFPageLabelDocumentHelper&
QPDFPageLabelDocumentHelper::get(QPDF& qpdf)
{
return qpdf.doc().page_labels();
}
void
QPDFPageLabelDocumentHelper::validate(bool repair)
{
m->labels = nullptr;
if (Dictionary labels = qpdf.getRoot()["/PageLabels"]) {
m->labels = std::make_unique<QPDFNumberTreeObjectHelper>(
labels, qpdf, [](QPDFObjectHandle const& o) -> bool { return o.isDictionary(); }, true);
m->labels->validate(repair);
}
}
bool
QPDFPageLabelDocumentHelper::hasPageLabels()
{
return m->labels != nullptr;
}
QPDFObjectHandle
QPDFPageLabelDocumentHelper::getLabelForPage(long long page_idx)
{
if (!hasPageLabels()) {
return QPDFObjectHandle::newNull();
}
QPDFNumberTreeObjectHelper::numtree_number offset = 0;
QPDFObjectHandle label;
if (!m->labels->findObjectAtOrBelow(page_idx, label, offset)) {
return QPDFObjectHandle::newNull();
}
QPDFObjectHandle S = label.getKey("/S"); // type (D, R, r, A, a)
QPDFObjectHandle P = label.getKey("/P"); // prefix
QPDFObjectHandle St = label.getKey("/St"); // starting number
long long start = 1;
if (St.isInteger()) {
start = St.getIntValue();
}
QIntC::range_check(start, offset);
start += offset;
auto result = QPDFObjectHandle::newDictionary();
result.replaceKey("/S", S);
result.replaceKey("/P", P);
result.replaceKey("/St", QPDFObjectHandle::newInteger(start));
return result;
}
void
QPDFPageLabelDocumentHelper::getLabelsForPageRange(
long long start_idx,
long long end_idx,
long long new_start_idx,
std::vector<QPDFObjectHandle>& new_labels)
{
// Start off with a suitable label for the first page. For every remaining page, if that page
// has an explicit entry, copy it. Otherwise, let the subsequent page just sequence from the
// prior entry. If there is no entry for the first page, fabricate one that would match how the
// page would look in a new file in which it also didn't have an explicit label.
QPDFObjectHandle label = getLabelForPage(start_idx);
if (label.null()) {
label = QPDFObjectHandle::newDictionary();
label.replaceKey("/St", QPDFObjectHandle::newInteger(1 + new_start_idx));
}
// See if the new label is redundant based on the previous entry in the vector. If so, don't add
// it.
size_t size = new_labels.size();
bool skip_first = false;
if (size >= 2) {
QPDFObjectHandle last = new_labels.at(size - 1);
QPDFObjectHandle last_idx = new_labels.at(size - 2);
if (last_idx.isInteger() && last.isDictionary() &&
(label.getKey("/S").unparse() == last.getKey("/S").unparse()) &&
(label.getKey("/P").unparse() == last.getKey("/P").unparse()) &&
label.getKey("/St").isInteger() && last.getKey("/St").isInteger()) {
long long int st_delta =
label.getKey("/St").getIntValue() - last.getKey("/St").getIntValue();
long long int idx_delta = new_start_idx - last_idx.getIntValue();
if (st_delta == idx_delta) {
skip_first = true;
}
}
}
if (!skip_first) {
new_labels.emplace_back(QPDFObjectHandle::newInteger(new_start_idx));
new_labels.emplace_back(label);
}
long long int idx_offset = new_start_idx - start_idx;
for (long long i = start_idx + 1; i <= end_idx; ++i) {
if (m->labels->hasIndex(i) && (label = getLabelForPage(i)).isDictionary()) {
new_labels.emplace_back(QPDFObjectHandle::newInteger(i + idx_offset));
new_labels.emplace_back(label);
}
}
}
QPDFObjectHandle
QPDFPageLabelDocumentHelper::pageLabelDict(
qpdf_page_label_e label_type, int start_num, std::string_view prefix)
{
auto num = QPDFObjectHandle::newDictionary();
switch (label_type) {
case pl_none:
break;
case pl_digits:
num.replaceKey("/S", Name("/D"));
break;
case pl_alpha_lower:
num.replaceKey("/S", Name("/a"));
break;
case pl_alpha_upper:
num.replaceKey("/S", Name("/A"));
break;
case pl_roman_lower:
num.replaceKey("/S", Name("/r"));
break;
case pl_roman_upper:
num.replaceKey("/S", Name("/R"));
break;
}
if (!prefix.empty()) {
num.replaceKey("/P", QPDFObjectHandle::newUnicodeString(std::string(prefix)));
}
if (start_num != 1) {
num.replaceKey("/St", QPDFObjectHandle::newInteger(start_num));
}
return num;
}
|