Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpdfbookmarkmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "qpdfdocument.h"
9
10#include "third_party/pdfium/public/fpdf_doc.h"
11#include "third_party/pdfium/public/fpdfview.h"
12
13#include <QMetaEnum>
14#include <QPointer>
15#include <QScopedPointer>
16#include <private/qabstractitemmodel_p.h>
17
19
20Q_PDF_LOGGING_CATEGORY(qLcBM, "qt.pdf.bookmarks")
21
22class BookmarkNode
23{
24public:
25 explicit BookmarkNode(BookmarkNode *parentNode = nullptr)
26 : m_parentNode(parentNode)
27 {
28 }
29
30 ~BookmarkNode()
31 {
32 clear();
33 }
34
35 void clear()
36 {
37 qDeleteAll(m_childNodes);
38 m_childNodes.clear();
39 }
40
41 void appendChild(BookmarkNode *child)
42 {
43 m_childNodes.append(child);
44 }
45
46 BookmarkNode *child(int row) const
47 {
48 return m_childNodes.at(row);
49 }
50
51 int childCount() const
52 {
53 return m_childNodes.size();
54 }
55
56 int row() const
57 {
58 if (m_parentNode)
59 return m_parentNode->m_childNodes.indexOf(const_cast<BookmarkNode*>(this));
60
61 return 0;
62 }
63
64 BookmarkNode *parentNode() const
65 {
66 return m_parentNode;
67 }
68
69 QString title() const
70 {
71 return m_title;
72 }
73
74 void setTitle(const QString &title)
75 {
76 m_title = title;
77 }
78
79 int level() const
80 {
81 return m_level;
82 }
83
84 void setLevel(int level)
85 {
86 m_level = level;
87 }
88
89 int pageNumber() const
90 {
91 return m_pageNumber;
92 }
93
94 void setPageNumber(int pageNumber)
95 {
96 m_pageNumber = pageNumber;
97 }
98
99 QPointF location() const
100 {
101 return m_location;
102 }
103
104 void setLocation(qreal x, qreal y)
105 {
106 m_location = QPointF(x, y);
107 }
108
109 qreal zoom() const
110 {
111 return m_zoom;
112 }
113
114 void setZoom(qreal zoom)
115 {
116 m_zoom = zoom;
117 }
118
119private:
120 QList<BookmarkNode*> m_childNodes;
121 BookmarkNode *m_parentNode;
122
123 QString m_title;
124 int m_level = 0;
125 int m_pageNumber = 0;
126 QPointF m_location;
127 qreal m_zoom = 0;
128};
129
130
132{
134 : m_rootNode(new BookmarkNode(nullptr))
135 , m_document(nullptr)
136 {
137 }
138
139 void rebuild()
140 {
141 const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Status::Ready);
142
143 if (documentAvailable) {
144 q->beginResetModel();
145 m_rootNode->clear();
146 QPdfMutexLocker lock;
147 appendChildNode(m_rootNode.data(), nullptr, 0, m_document->d->doc);
148 lock.unlock();
149 q->endResetModel();
150 } else {
151 if (m_rootNode->childCount() == 0) {
152 return;
153 } else {
154 q->beginResetModel();
155 m_rootNode->clear();
156 q->endResetModel();
157 }
158 }
159 }
160
161 void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document)
162 {
163 FPDF_BOOKMARK bookmark = FPDFBookmark_GetFirstChild(document, parentBookmark);
164
165 while (bookmark) {
166 BookmarkNode *childBookmarkNode = nullptr;
167
168 childBookmarkNode = new BookmarkNode(parentBookmarkNode);
169 parentBookmarkNode->appendChild(childBookmarkNode);
170 Q_ASSERT(childBookmarkNode);
171
172 const int titleLength = int(FPDFBookmark_GetTitle(bookmark, nullptr, 0));
173
174 QList<char16_t> titleBuffer(titleLength);
175 FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.size()));
176
177 const FPDF_DEST dest = FPDFBookmark_GetDest(document, bookmark);
178 const int pageNumber = FPDFDest_GetDestPageIndex(document, dest);
179 const qreal pageHeight = m_document->pagePointSize(pageNumber).height();
180 FPDF_BOOL hasX, hasY, hasZoom;
181 FS_FLOAT x, y, zoom;
182 bool ok = FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom);
183 if (ok) {
184 if (hasX && hasY)
185 childBookmarkNode->setLocation(x, pageHeight - y);
186 if (hasZoom)
187 childBookmarkNode->setZoom(zoom);
188 } else {
189 qCWarning(qLcBM) << "bookmark with invalid location and/or zoom" << x << y << zoom;
190 }
191
192 childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data()));
193 childBookmarkNode->setLevel(level);
194 childBookmarkNode->setPageNumber(pageNumber);
195
196 // recurse down
197 appendChildNode(childBookmarkNode, bookmark, level + 1, document);
198
199 bookmark = FPDFBookmark_GetNextSibling(document, bookmark);
200 }
201 }
202
204 {
205 rebuild();
206 }
207
209
213};
214
215
216/*!
217 \class QPdfBookmarkModel
218 \since 5.10
219 \inmodule QtPdf
220 \inherits QAbstractItemModel
221
222 \brief The QPdfBookmarkModel class holds a tree of links (anchors)
223 within a PDF document, such as the table of contents.
224
225 This is used in the \l {Model/View Programming} paradigm to display a
226 table of contents in the form of a tree or list.
227*/
228
229/*!
230 \enum QPdfBookmarkModel::Role
231
232 \value Title The name of the bookmark for display.
233 \value Level The level of indentation.
234 \value Page The page number of the destination (int).
235 \value Location The position of the destination (QPointF).
236 \value Zoom The suggested zoom level (qreal).
237 \omitvalue NRoles
238*/
239
240/*!
241 Constructs a new bookmark model with parent object \a parent.
242*/
243QPdfBookmarkModel::QPdfBookmarkModel(QObject *parent)
244 : QAbstractItemModel(parent), d(new QPdfBookmarkModelPrivate)
245{
246 d->q = this;
247 d->m_roleNames = QAbstractItemModel::roleNames();
248 QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));
249 for (int r = Qt::UserRole; r < int(Role::NRoles); ++r)
250 d->m_roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower());
251}
252
253/*!
254 Destroys the model.
255*/
256QPdfBookmarkModel::~QPdfBookmarkModel() = default;
257
258QPdfDocument* QPdfBookmarkModel::document() const
259{
260 return d->m_document;
261}
262
263/*!
264 \property QPdfBookmarkModel::document
265 \brief the PDF document in which bookmarks are to be found.
266*/
267void QPdfBookmarkModel::setDocument(QPdfDocument *document)
268{
269 if (d->m_document == document)
270 return;
271
272 if (d->m_document)
273 disconnect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
274
275 d->m_document = document;
276 emit documentChanged(d->m_document);
277
278 if (d->m_document)
279 connect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
280
281 d->rebuild();
282}
283
284/*!
285 \reimp
286*/
287int QPdfBookmarkModel::columnCount(const QModelIndex &parent) const
288{
289 Q_UNUSED(parent);
290 return 1;
291}
292
293/*!
294 \reimp
295*/
296QHash<int, QByteArray> QPdfBookmarkModel::roleNames() const
297{
298 return d->m_roleNames;
299}
300
301/*!
302 \reimp
303*/
304QVariant QPdfBookmarkModel::data(const QModelIndex &index, int role) const
305{
306 if (!index.isValid())
307 return QVariant();
308
309 const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer());
310 switch (Role(role)) {
311 case Role::Title:
312 return node->title();
313 case Role::Level:
314 return node->level();
315 case Role::Page:
316 return node->pageNumber();
317 case Role::Location:
318 return node->location();
319 case Role::Zoom:
320 return node->zoom();
321 case Role::NRoles:
322 break;
323 }
324 if (role == Qt::DisplayRole)
325 return node->title();
326 return QVariant();
327}
328
329/*!
330 \reimp
331*/
332QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &parent) const
333{
334 if (!hasIndex(row, column, parent))
335 return QModelIndex();
336
337 BookmarkNode *parentNode;
338
339 if (!parent.isValid())
340 parentNode = d->m_rootNode.data();
341 else
342 parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
343
344 BookmarkNode *childNode = parentNode->child(row);
345 if (childNode)
346 return createIndex(row, column, childNode);
347 else
348 return QModelIndex();
349}
350
351/*!
352 \reimp
353*/
354QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const
355{
356 if (!index.isValid())
357 return QModelIndex();
358
359 const BookmarkNode *childNode = static_cast<BookmarkNode*>(index.internalPointer());
360 BookmarkNode *parentNode = childNode->parentNode();
361
362 if (parentNode == d->m_rootNode.data())
363 return QModelIndex();
364
365 return createIndex(parentNode->row(), 0, parentNode);
366}
367
368/*!
369 \reimp
370*/
371int QPdfBookmarkModel::rowCount(const QModelIndex &parent) const
372{
373 if (parent.column() > 0)
374 return 0;
375
376 BookmarkNode *parentNode = nullptr;
377
378 if (!parent.isValid())
379 parentNode = d->m_rootNode.data();
380 else
381 parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
382
383 return parentNode->childCount();
384}
385
386QT_END_NAMESPACE
387
388#include "moc_qpdfbookmarkmodel.cpp"
#define Q_PDF_LOGGING_CATEGORY(name,...)
QHash< int, QByteArray > m_roleNames
QPointer< QPdfDocument > m_document
void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document)
QScopedPointer< BookmarkNode > m_rootNode