aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/QPDFPageLabelDocumentHelper.cc
blob: 08a35097b2e3bc8e595fce6f1f127c6310cab2b2 (plain) (blame)
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
#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")) {
        this->m->labels = std::make_shared<QPDFNumberTreeObjectHelper>(
            root.getKey("/PageLabels"), this->qpdf);
    }
}

bool
QPDFPageLabelDocumentHelper::hasPageLabels()
{
    return nullptr != this->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 (!this->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 (this->m->labels->hasIndex(i) &&
            (label = getLabelForPage(i)).isDictionary()) {
            new_labels.push_back(QPDFObjectHandle::newInteger(i + idx_offset));
            new_labels.push_back(label);
        }
    }
}