mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-23 03:18:59 +00:00
e50ff67d00
The old code didn't put one there, and it's not really useful. It just makes the file (trivially) larger.
135 lines
4.5 KiB
C++
135 lines
4.5 KiB
C++
#include <qpdf/QPDFPageLabelDocumentHelper.hh>
|
|
|
|
#include <qpdf/QTC.hh>
|
|
|
|
QPDFPageLabelDocumentHelper::QPDFPageLabelDocumentHelper(QPDF& qpdf) :
|
|
QPDFDocumentHelper(qpdf),
|
|
m(new Members())
|
|
{
|
|
QPDFObjectHandle root = qpdf.getRoot();
|
|
if (root.hasKey("/PageLabels")) {
|
|
m->labels =
|
|
std::make_shared<QPDFNumberTreeObjectHelper>(root.getKey("/PageLabels"), this->qpdf);
|
|
}
|
|
}
|
|
|
|
bool
|
|
QPDFPageLabelDocumentHelper::hasPageLabels()
|
|
{
|
|
return nullptr != m->labels;
|
|
}
|
|
|
|
QPDFObjectHandle
|
|
QPDFPageLabelDocumentHelper::getLabelForPage(long long page_idx)
|
|
{
|
|
QPDFObjectHandle result(QPDFObjectHandle::newNull());
|
|
if (!hasPageLabels()) {
|
|
return result;
|
|
}
|
|
QPDFNumberTreeObjectHelper::numtree_number offset = 0;
|
|
QPDFObjectHandle label;
|
|
if (!m->labels->findObjectAtOrBelow(page_idx, label, offset)) {
|
|
return result;
|
|
}
|
|
if (!label.isDictionary()) {
|
|
return result;
|
|
}
|
|
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;
|
|
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.isNull()) {
|
|
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) {
|
|
QTC::TC("qpdf", "QPDFPageLabelDocumentHelper skip first");
|
|
skip_first = true;
|
|
}
|
|
}
|
|
}
|
|
if (!skip_first) {
|
|
new_labels.push_back(QPDFObjectHandle::newInteger(new_start_idx));
|
|
new_labels.push_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.push_back(QPDFObjectHandle::newInteger(i + idx_offset));
|
|
new_labels.push_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", "/D"_qpdf);
|
|
break;
|
|
case pl_alpha_lower:
|
|
num.replaceKey("/S", "/a"_qpdf);
|
|
break;
|
|
case pl_alpha_upper:
|
|
num.replaceKey("/S", "/A"_qpdf);
|
|
break;
|
|
case pl_roman_lower:
|
|
num.replaceKey("/S", "/r"_qpdf);
|
|
break;
|
|
case pl_roman_upper:
|
|
num.replaceKey("/S", "/R"_qpdf);
|
|
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;
|
|
}
|