Qt 6.x
The Qt SDK
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
4#include "qpdfbookmarkmodel.h"
5
6#include "qpdfdocument.h"
7#include "qpdfdocument_p.h"
8
9#include "third_party/pdfium/public/fpdf_doc.h"
10#include "third_party/pdfium/public/fpdfview.h"
11
12#include <QLoggingCategory>
13#include <QMetaEnum>
14#include <QPointer>
15#include <QScopedPointer>
16#include <private/qabstractitemmodel_p.h>
17
19
20Q_LOGGING_CATEGORY(qLcBM, "qt.pdf.bookmarks")
21
23{
24public:
25 explicit BookmarkNode(BookmarkNode *parentNode = nullptr)
26 : m_parentNode(parentNode)
27 {
28 }
29
31 {
32 clear();
33 }
34
35 void clear()
36 {
37 qDeleteAll(m_childNodes);
38 m_childNodes.clear();
39 }
40
42 {
43 m_childNodes.append(child);
44 }
45
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
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
100 {
101 return m_location;
102 }
103
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{
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();
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 double pageHeight = 11.69 * 72; // A4 height
180 {
181 // get actual page height
182 const QPdfMutexLocker lock;
183 FPDF_PAGE pdfPage = FPDF_LoadPage(document, pageNumber);
184 if (pdfPage)
185 pageHeight = FPDF_GetPageHeight(pdfPage);
186 else
187 qCWarning(qLcBM) << "failed to load page" << pageNumber;
188 }
189
190 FPDF_BOOL hasX, hasY, hasZoom;
191 FS_FLOAT x, y, zoom;
192 bool ok = FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom);
193 if (ok) {
194 if (hasX && hasY)
195 childBookmarkNode->setLocation(x, pageHeight - y);
196 if (hasZoom)
197 childBookmarkNode->setZoom(zoom);
198 } else {
199 qCWarning(qLcBM) << "bookmark with invalid location and/or zoom" << x << y << zoom;
200 }
201
202 childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data()));
203 childBookmarkNode->setLevel(level);
204 childBookmarkNode->setPageNumber(pageNumber);
205
206 // recurse down
207 appendChildNode(childBookmarkNode, bookmark, level + 1, document);
208
209 bookmark = FPDFBookmark_GetNextSibling(document, bookmark);
210 }
211 }
212
214 {
215 rebuild();
216 }
217
219
223};
224
225
255{
256 d->q = this;
257 d->m_roleNames = QAbstractItemModel::roleNames();
258 QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));
259 for (int r = Qt::UserRole; r < int(Role::NRoles); ++r)
260 d->m_roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower());
261}
262
267
269{
270 return d->m_document;
271}
272
274{
275 if (d->m_document == document)
276 return;
277
278 if (d->m_document)
279 disconnect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
280
281 d->m_document = document;
282 emit documentChanged(d->m_document);
283
284 if (d->m_document)
285 connect(d->m_document, SIGNAL(statusChanged(QPdfDocument::Status)), this, SLOT(_q_documentStatusChanged()));
286
287 d->rebuild();
288}
289
294{
296 return 1;
297}
298
303{
304 return d->m_roleNames;
305}
306
311{
312 if (!index.isValid())
313 return QVariant();
314
315 const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer());
316 switch (Role(role)) {
317 case Role::Title:
318 return node->title();
319 case Role::Level:
320 return node->level();
321 case Role::Page:
322 return node->pageNumber();
323 case Role::Location:
324 return node->location();
325 case Role::Zoom:
326 return node->zoom();
327 case Role::NRoles:
328 break;
329 }
330 if (role == Qt::DisplayRole)
331 return node->title();
332 return QVariant();
333}
334
339{
340 if (!hasIndex(row, column, parent))
341 return QModelIndex();
342
343 BookmarkNode *parentNode;
344
345 if (!parent.isValid())
346 parentNode = d->m_rootNode.data();
347 else
348 parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
349
350 BookmarkNode *childNode = parentNode->child(row);
351 if (childNode)
352 return createIndex(row, column, childNode);
353 else
354 return QModelIndex();
355}
356
361{
362 if (!index.isValid())
363 return QModelIndex();
364
365 const BookmarkNode *childNode = static_cast<BookmarkNode*>(index.internalPointer());
366 BookmarkNode *parentNode = childNode->parentNode();
367
368 if (parentNode == d->m_rootNode.data())
369 return QModelIndex();
370
371 return createIndex(parentNode->row(), 0, parentNode);
372}
373
378{
379 if (parent.column() > 0)
380 return 0;
381
382 BookmarkNode *parentNode = nullptr;
383
384 if (!parent.isValid())
385 parentNode = d->m_rootNode.data();
386 else
387 parentNode = static_cast<BookmarkNode*>(parent.internalPointer());
388
389 return parentNode->childCount();
390}
391
393
394#include "moc_qpdfbookmarkmodel.cpp"
QString title() const
void setLevel(int level)
int childCount() const
BookmarkNode * child(int row) const
qreal zoom() const
void setZoom(qreal zoom)
void setTitle(const QString &title)
BookmarkNode(BookmarkNode *parentNode=nullptr)
void setLocation(qreal x, qreal y)
void setPageNumber(int pageNumber)
int pageNumber() const
void appendChild(BookmarkNode *child)
BookmarkNode * parentNode() const
QPointF location() const
Q_INVOKABLE int const QModelIndex & parent
Returns the parent of the model item with the given index.
Q_INVOKABLE bool hasIndex(int row, int column, const QModelIndex &parent=QModelIndex()) const
Returns {true} if the model returns a valid QModelIndex for row and column with parent,...
virtual QHash< int, QByteArray > roleNames() const
QModelIndex createIndex(int row, int column, const void *data=nullptr) const
Creates a model index for the given row and column with the internal pointer ptr.
\inmodule QtCore
Definition qbytearray.h:57
QByteArray toLower() const &
Definition qbytearray.h:190
\inmodule QtCore
Definition qhash.h:818
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
pointer data()
Definition qlist.h:414
\inmodule QtCore
const char * valueToKey(int value) const
Returns the string that is used as the name of the given enumeration value, or \nullptr if value is n...
\inmodule QtCore
constexpr int column() const noexcept
Returns the column this model index refers to.
void * internalPointer() const noexcept
Returns a {void} {*} pointer used by the model to associate the index with the internal data structur...
constexpr bool isValid() const noexcept
Returns {true} if this model index is valid; otherwise returns {false}.
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
The QPdfBookmarkModel class holds a tree of of links (anchors) within a PDF document,...
void documentChanged(QPdfDocument *document)
~QPdfBookmarkModel() override
Destroys the model.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
QPdfDocument * document
void setDocument(QPdfDocument *document)
Role
\value Title The name of the bookmark for display.
QVariant data(const QModelIndex &index, int role) const override
\reimp
int columnCount(const QModelIndex &parent=QModelIndex()) const override
\reimp
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
\reimp
QHash< int, QByteArray > roleNames() const override
\reimp
The QPdfDocument class loads a PDF document and renders pages from it.
Status status
This property holds the current status of the document.
Status
This enum describes the current status of the document.
\inmodule QtCore\reentrant
Definition qpoint.h:214
\inmodule QtCore
Definition qpointer.h:18
\inmodule QtCore
T * data() const noexcept
Returns the value of the pointer referenced by this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:5883
\inmodule QtCore
Definition qvariant.h:64
void statusChanged(QDeclarativeComponent::Status status)
[1]
Definition qlogging.cpp:9
b clear()
qDeleteAll(list.begin(), list.end())
Combined button and popup list for selecting options.
@ UserRole
@ DisplayRole
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLint GLint GLint GLint GLint x
[0]
GLenum GLuint GLint level
GLuint index
[2]
GLboolean r
[2]
GLint y
GLenum GLenum GLsizei void GLsizei void * column
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
double qreal
Definition qtypes.h:92
obj metaObject() -> className()
QObject::connect nullptr
QString title
[35]
myObject disconnect()
[26]
QReadWriteLock lock
[0]
QLayoutItem * child
[0]
void appendChildNode(BookmarkNode *parentBookmarkNode, FPDF_BOOKMARK parentBookmark, int level, FPDF_DOCUMENT document)
QScopedPointer< BookmarkNode > m_rootNode
QHash< int, QByteArray > m_roleNames
QPointer< QPdfDocument > m_document
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent