Coverage Report

Created: 2025-08-29 06:53

/src/qpdf/libqpdf/QPDFOutlineDocumentHelper.cc
Line
Count
Source (jump to first uncovered line)
1
#include <qpdf/QPDFOutlineDocumentHelper.hh>
2
3
#include <qpdf/QTC.hh>
4
5
QPDFOutlineDocumentHelper::QPDFOutlineDocumentHelper(QPDF& qpdf) :
6
1.84k
    QPDFDocumentHelper(qpdf),
7
1.84k
    m(new Members())
8
1.84k
{
9
1.84k
    QPDFObjectHandle root = qpdf.getRoot();
10
1.84k
    if (!root.hasKey("/Outlines")) {
11
127
        return;
12
127
    }
13
1.72k
    QPDFObjectHandle outlines = root.getKey("/Outlines");
14
1.72k
    if (!(outlines.isDictionary() && outlines.hasKey("/First"))) {
15
13
        return;
16
13
    }
17
1.70k
    QPDFObjectHandle cur = outlines.getKey("/First");
18
1.70k
    QPDFObjGen::set seen;
19
3.44k
    while (!cur.isNull() && seen.add(cur)) {
20
1.74k
        m->outlines.push_back(QPDFOutlineObjectHelper::Accessor::create(cur, *this, 1));
21
1.74k
        cur = cur.getKey("/Next");
22
1.74k
    }
23
1.70k
}
24
25
bool
26
QPDFOutlineDocumentHelper::hasOutlines()
27
0
{
28
0
    return !m->outlines.empty();
29
0
}
30
31
std::vector<QPDFOutlineObjectHelper>
32
QPDFOutlineDocumentHelper::getTopLevelOutlines()
33
1.54k
{
34
1.54k
    return m->outlines;
35
1.54k
}
36
37
void
38
QPDFOutlineDocumentHelper::initializeByPage()
39
0
{
40
0
    std::list<QPDFOutlineObjectHelper> queue;
41
0
    queue.insert(queue.end(), m->outlines.begin(), m->outlines.end());
42
43
0
    while (!queue.empty()) {
44
0
        QPDFOutlineObjectHelper oh = queue.front();
45
0
        queue.pop_front();
46
0
        m->by_page[oh.getDestPage().getObjGen()].push_back(oh);
47
0
        std::vector<QPDFOutlineObjectHelper> kids = oh.getKids();
48
0
        queue.insert(queue.end(), kids.begin(), kids.end());
49
0
    }
50
0
}
51
52
std::vector<QPDFOutlineObjectHelper>
53
QPDFOutlineDocumentHelper::getOutlinesForPage(QPDFObjGen og)
54
0
{
55
0
    if (m->by_page.empty()) {
56
0
        initializeByPage();
57
0
    }
58
0
    std::vector<QPDFOutlineObjectHelper> result;
59
0
    if (m->by_page.contains(og)) {
60
0
        result = m->by_page[og];
61
0
    }
62
0
    return result;
63
0
}
64
65
QPDFObjectHandle
66
QPDFOutlineDocumentHelper::resolveNamedDest(QPDFObjectHandle name)
67
3.02k
{
68
3.02k
    QPDFObjectHandle result;
69
3.02k
    if (name.isName()) {
70
148
        if (!m->dest_dict) {
71
33
            m->dest_dict = qpdf.getRoot().getKey("/Dests");
72
33
        }
73
148
        QTC::TC("qpdf", "QPDFOutlineDocumentHelper name named dest");
74
148
        result = m->dest_dict.getKeyIfDict(name.getName());
75
2.87k
    } else if (name.isString()) {
76
2.87k
        if (!m->names_dest) {
77
1.51k
            auto dests = qpdf.getRoot().getKey("/Names").getKeyIfDict("/Dests");
78
1.51k
            if (dests.isDictionary()) {
79
1.21k
                m->names_dest = std::make_shared<QPDFNameTreeObjectHelper>(dests, qpdf);
80
1.21k
            }
81
1.51k
        }
82
2.87k
        if (m->names_dest) {
83
2.57k
            if (m->names_dest->findObject(name.getUTF8Value(), result)) {
84
378
                QTC::TC("qpdf", "QPDFOutlineDocumentHelper string named dest");
85
378
            }
86
2.57k
        }
87
2.87k
    }
88
3.02k
    if (!result) {
89
2.07k
        return QPDFObjectHandle::newNull();
90
2.07k
    }
91
951
    if (result.isDictionary()) {
92
74
        QTC::TC("qpdf", "QPDFOutlineDocumentHelper named dest dictionary");
93
74
        return result.getKey("/D");
94
74
    }
95
877
    return result;
96
951
}