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
qpdfpagerenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com>
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 <private/qobject_p.h>
7#include <QMutex>
8#include <QPointer>
9#include <QThread>
10
12
13class RenderWorker : public QObject
14{
16
17public:
20
21 void setDocument(QPdfDocument *document);
22
23public Q_SLOTS:
26
30
31private:
32 QPointer<QPdfDocument> m_document;
33 QMutex m_mutex;
34};
35
64
66
67
68RenderWorker::RenderWorker()
69 : m_document(nullptr)
70{
71}
72
76
77void RenderWorker::setDocument(QPdfDocument *document)
78{
79 const QMutexLocker locker(&m_mutex);
80
81 if (m_document == document)
82 return;
83
84 m_document = document;
85}
86
87void RenderWorker::requestPage(quint64 requestId, int pageNumber, QSize imageSize,
88 QPdfDocumentRenderOptions options)
89{
90 const QMutexLocker locker(&m_mutex);
91
92 if (!m_document || m_document->status() != QPdfDocument::Status::Ready)
93 return;
94
95 const QImage image = m_document->render(pageNumber, imageSize, options);
96
97 emit pageRendered(pageNumber, imageSize, image, options, requestId);
98}
99
101
103{
104 if (m_renderThread) {
105 m_renderThread->quit();
106 m_renderThread->wait();
107 }
108}
109
111{
112 if (m_requests.isEmpty())
113 return;
114
115 const PageRequest request = m_requests.takeFirst();
116 m_pendingRequests.append(request);
117
118 QMetaObject::invokeMethod(m_renderWorker.data(), "requestPage", Qt::QueuedConnection,
119 Q_ARG(quint64, request.id), Q_ARG(int, request.pageNumber),
120 Q_ARG(QSize, request.imageSize), Q_ARG(QPdfDocumentRenderOptions,
121 request.options));
122}
123
124void QPdfPageRendererPrivate::requestFinished(int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId)
125{
126 Q_UNUSED(image);
127 Q_UNUSED(requestId);
128 const auto it = std::find_if(m_pendingRequests.begin(), m_pendingRequests.end(),
129 [page, imageSize, options](const PageRequest &request){ return request.pageNumber == page && request.imageSize == imageSize && request.options == options; });
130
131 if (it != m_pendingRequests.end())
132 m_pendingRequests.erase(it);
133}
134
135/*!
136 \class QPdfPageRenderer
137 \since 5.11
138 \inmodule QtPdf
139
140 \brief The QPdfPageRenderer class encapsulates the rendering of pages of a PDF document.
141
142 The QPdfPageRenderer contains a queue that collects all render requests that are invoked through
143 requestPage(). Depending on the configured RenderMode the QPdfPageRenderer processes this queue
144 in the main UI thread on next event loop invocation (\c RenderMode::SingleThreaded) or in a separate worker thread
145 (\c RenderMode::MultiThreaded) and emits the result through the pageRendered() signal for each request once
146 the rendering is done.
147
148 \sa QPdfDocument
149*/
150
151
152/*!
153 Constructs a page renderer object with parent object \a parent.
154*/
155QPdfPageRenderer::QPdfPageRenderer(QObject *parent)
156 : QObject(parent), d_ptr(new QPdfPageRendererPrivate)
157{
158 qRegisterMetaType<QPdfDocumentRenderOptions>();
159
160 connect(d_ptr->m_renderWorker.data(), &RenderWorker::pageRendered, this,
161 [this](int page, QSize imageSize, const QImage &image,
162 QPdfDocumentRenderOptions options, quint64 requestId) {
163 d_ptr->requestFinished(page, imageSize, image, options, requestId);
164 emit pageRendered(page, imageSize, image, options, requestId);
165 d_ptr->handleNextRequest();
166 });
167}
168
169/*!
170 Destroys the page renderer object.
171*/
172QPdfPageRenderer::~QPdfPageRenderer()
173{
174}
175
176/*!
177 \enum QPdfPageRenderer::RenderMode
178
179 This enum describes how the pages are rendered.
180
181 \value MultiThreaded All pages are rendered in a separate worker thread.
182 \value SingleThreaded All pages are rendered in the main UI thread (default).
183
184 \sa renderMode(), setRenderMode()
185*/
186
187/*!
188 \property QPdfPageRenderer::renderMode
189 \brief The mode the renderer uses to render the pages.
190
191 By default, this property is \c RenderMode::SingleThreaded.
192
193 \sa setRenderMode(), RenderMode
194*/
195
196/*!
197 Returns the mode of how the pages are rendered.
198
199 \sa RenderMode
200*/
201QPdfPageRenderer::RenderMode QPdfPageRenderer::renderMode() const
202{
203 return d_ptr->m_renderMode;
204}
205
206/*!
207 Sets the mode of how the pages are rendered to \a mode.
208
209 \sa RenderMode
210*/
211void QPdfPageRenderer::setRenderMode(RenderMode mode)
212{
213 if (d_ptr->m_renderMode == mode)
214 return;
215
216 d_ptr->m_renderMode = mode;
217 emit renderModeChanged(d_ptr->m_renderMode);
218
219 if (d_ptr->m_renderMode == RenderMode::MultiThreaded) {
220 d_ptr->m_renderThread = new QThread;
221 d_ptr->m_renderWorker->moveToThread(d_ptr->m_renderThread);
222 d_ptr->m_renderThread->start();
223 } else {
224 d_ptr->m_renderThread->quit();
225 d_ptr->m_renderThread->wait();
226 delete d_ptr->m_renderThread;
227 d_ptr->m_renderThread = nullptr;
228
229 // pulling the object from another thread should be fine, once that thread is deleted
230 d_ptr->m_renderWorker->moveToThread(this->thread());
231 }
232}
233
234/*!
235 \property QPdfPageRenderer::document
236 \brief The document instance this object renders the pages from.
237
238 By default, this property is \c nullptr.
239
240 \sa document(), setDocument(), QPdfDocument
241*/
242
243/*!
244 Returns the document this objects renders the pages from, or a \c nullptr
245 if none has been set before.
246
247 \sa QPdfDocument
248*/
249QPdfDocument* QPdfPageRenderer::document() const
250{
251 return d_ptr->m_document;
252}
253
254/*!
255 Sets the \a document this object renders the pages from.
256
257 \sa QPdfDocument
258*/
259void QPdfPageRenderer::setDocument(QPdfDocument *document)
260{
261 if (d_ptr->m_document == document)
262 return;
263
264 d_ptr->m_document = document;
265 emit documentChanged(d_ptr->m_document);
266
267 d_ptr->m_renderWorker->setDocument(d_ptr->m_document);
268}
269
270/*!
271 Requests the renderer to render the page \a pageNumber into a QImage of size \a imageSize
272 according to the provided \a options.
273
274 Once the rendering is done the pageRendered() signal is emitted with the result as parameters.
275
276 The return value is an ID that uniquely identifies the render request. If a request with the
277 same parameters is still in the queue, the ID of that queued request is returned.
278*/
279quint64 QPdfPageRenderer::requestPage(int pageNumber, QSize imageSize,
280 QPdfDocumentRenderOptions options)
281{
282 if (!d_ptr->m_document || d_ptr->m_document->status() != QPdfDocument::Status::Ready)
283 return 0;
284
285 for (const auto &request : std::as_const(d_ptr->m_pendingRequests)) {
286 if (request.pageNumber == pageNumber
287 && request.imageSize == imageSize
288 && request.options == options)
289 return request.id;
290 }
291
292 const auto id = d_ptr->m_requestIdCounter++;
293
294 QPdfPageRendererPrivate::PageRequest request;
295 request.id = id;
296 request.pageNumber = pageNumber;
297 request.imageSize = imageSize;
298 request.options = options;
299
300 d_ptr->m_requests.append(request);
301
302 d_ptr->handleNextRequest();
303
304 return id;
305}
306
307QT_END_NAMESPACE
308
309#include "qpdfpagerenderer.moc"
310#include "moc_qpdfpagerenderer.cpp"
QList< PageRequest > m_pendingRequests
QList< PageRequest > m_requests
void requestFinished(int page, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId)
QScopedPointer< RenderWorker > m_renderWorker
QPointer< QPdfDocument > m_document
void setDocument(QPdfDocument *document)
Q_DECLARE_TYPEINFO(QPdfPageRendererPrivate::PageRequest, Q_PRIMITIVE_TYPE)