QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgswebenginepage.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgswebenginepage.h
3 -------------------
4 begin : December 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgswebenginepage.h"
19#include "moc_qgswebenginepage.cpp"
20#include "qgsconfig.h"
21#include <QWebEnginePage>
22#include <QEventLoop>
23#include <QSizeF>
24
25#ifdef HAVE_PDF4QT
26#include "qgspdfrenderer.h"
27#include <QTemporaryFile>
28#else
29#include "qgsexception.h"
30#endif
31
33 : QObject( parent )
34 , mPage{ std::make_unique< QWebEnginePage >() }
35{
36 // proxy some signals from the page
37 connect( mPage.get(), &QWebEnginePage::loadStarted, this, &QgsWebEnginePage::loadStarted );
38 connect( mPage.get(), &QWebEnginePage::loadProgress, this, &QgsWebEnginePage::loadProgress );
39 connect( mPage.get(), &QWebEnginePage::loadFinished, this, &QgsWebEnginePage::loadFinished );
40}
41
43
44QWebEnginePage *QgsWebEnginePage::page()
45{
46 return mPage.get();
47}
48
49bool QgsWebEnginePage::setContent( const QByteArray &data, const QString &mimeType, const QUrl &baseUrl, bool blocking )
50{
51 mCachedSize = QSize();
52 if ( blocking )
53 {
54 QEventLoop loop;
55 bool finished = false;
56 bool result = true;
57 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok )
58 {
59 finished = true;
60 result = ok;
61 loop.exit();
62 } );
63 mPage->setContent( data, mimeType, baseUrl );
64 if ( !finished )
65 {
66 loop.exec( QEventLoop::ExcludeUserInputEvents );
67 }
68 if ( result )
69 handlePostBlockingLoadOperations();
70 return result;
71 }
72 else
73 {
74 mPage->setContent( data, mimeType, baseUrl );
75 return true;
76 }
77}
78
79bool QgsWebEnginePage::setHtml( const QString &html, const QUrl &baseUrl, bool blocking )
80{
81 mCachedSize = QSize();
82 if ( blocking )
83 {
84 QEventLoop loop;
85 bool finished = false;
86 bool result = true;
87 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok )
88 {
89 finished = true;
90 result = ok;
91 loop.exit();
92 } );
93 mPage->setHtml( html, baseUrl );
94 if ( !finished )
95 {
96 loop.exec( QEventLoop::ExcludeUserInputEvents );
97 }
98 if ( result )
99 handlePostBlockingLoadOperations();
100
101 return result;
102 }
103 else
104 {
105 mPage->setHtml( html, baseUrl );
106 return true;
107 }
108}
109
110bool QgsWebEnginePage::setUrl( const QUrl &url, bool blocking )
111{
112 mCachedSize = QSize();
113 if ( blocking )
114 {
115 QEventLoop loop;
116 bool finished = false;
117 bool result = true;
118 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok )
119 {
120 finished = true;
121 result = ok;
122 loop.exit();
123 } );
124 mPage->setUrl( url );
125 if ( !finished )
126 {
127 loop.exec( QEventLoop::ExcludeUserInputEvents );
128 }
129 if ( result )
130 handlePostBlockingLoadOperations();
131
132 return result;
133 }
134 else
135 {
136 mPage->setUrl( url );
137 return true;
138 }
139}
140
142{
143 if ( mCachedSize.isValid() )
144 return mCachedSize;
145
146 QEventLoop loop;
147 int width = -1;
148 int height = -1;
149 bool finished = false;
150 mPage->runJavaScript( "[document.documentElement.scrollWidth, document.documentElement.scrollHeight];", [&width, &height, &loop, &finished]( QVariant result )
151 {
152 width = result.toList().value( 0 ).toInt();
153 height = result.toList().value( 1 ).toInt();
154 finished = true;
155 loop.exit();
156 } );
157 if ( !finished )
158 {
159 loop.exec( QEventLoop::ExcludeUserInputEvents );
160 }
161
162
163 mCachedSize = QSize( width, height );
164 return mCachedSize;
165}
166
167void QgsWebEnginePage::handlePostBlockingLoadOperations()
168{
169 // Following a blocking content load, do some other quick calculations which involve local event loops.
170 // This allows callers to avoid having to make another later call to a method which would other involve a local event loop.
171 QEventLoop loop;
172 int width = 0;
173 int height = 0;
174 bool finished = false;
175 mPage->runJavaScript( "[document.documentElement.scrollWidth, document.documentElement.scrollHeight];", [&width, &height, &loop, &finished]( QVariant result )
176 {
177 width = result.toList().value( 0 ).toInt();
178 height = result.toList().value( 1 ).toInt();
179 finished = true;
180 loop.exit();
181 } );
182 if ( !finished )
183 {
184 loop.exec( QEventLoop::ExcludeUserInputEvents );
185 }
186
187 mCachedSize = QSize( width, height );
188}
189
190#ifdef HAVE_PDF4QT
191bool QgsWebEnginePage::render( QPainter *painter, const QRectF &painterRect )
192{
193 const QSize actualSize = documentSize();
194
195 // TODO -- is this ALWAYS 96?
196 static constexpr double dpi = 96.0;
197 const QSizeF pageSize = QSizeF( actualSize.width() / dpi, actualSize.height() / dpi );
198
199 QEventLoop loop;
200 bool finished = false;
201 bool printOk = false;
202 QString renderedPdfPath;
203 connect( mPage.get(), &QWebEnginePage::pdfPrintingFinished, &loop, [&loop, &finished, &printOk, &renderedPdfPath]( const QString & pdfPath, bool success )
204 {
205 finished = true;
206 renderedPdfPath = pdfPath;
207 printOk = success;
208 loop.exit();
209 } );
210
211 // generate file name for temporary intermediate PDF file
212 QTemporaryFile f;
213 f.open();
214 f.close();
215
216 const QPageLayout layout = QPageLayout( QPageSize( pageSize, QPageSize::Inch ),
217 QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ),
218 QPageLayout::Inch, QMarginsF( 0, 0, 0, 0 ) );
219 mPage->printToPdf( f.fileName(), layout );
220
221 if ( !finished )
222 {
223 loop.exec( QEventLoop::ExcludeUserInputEvents );
224 }
225
226 if ( printOk )
227 {
228 QgsPdfRenderer renderer( renderedPdfPath );
229 renderer.render( painter, painterRect, 0 );
230 }
231 return printOk;
232}
233#else
234bool QgsWebEnginePage::render( QPainter *, const QRectF & )
235{
236 throw QgsNotSupportedException( QObject::tr( "Rendering web pages requires a QGIS build with PDF4Qt library support" ) );
237}
238#endif
Custom exception class which is raised when an operation is not supported.
Utility class for rendering PDF documents.
bool setContent(const QByteArray &data, const QString &mimeType=QString(), const QUrl &baseUrl=QUrl(), bool blocking=false)
Sets the content of the web page to data.
void loadStarted()
This signal is emitted when the page starts loading content.
bool setHtml(const QString &html, const QUrl &baseUrl=QUrl(), bool blocking=false)
Sets the content of this page to html.
bool setUrl(const QUrl &url, bool blocking=false)
Sets the url of the web page to be displayed.
void loadFinished(bool ok)
This signal is emitted when the page finishes loading content.
QgsWebEnginePage(QObject *parent=nullptr)
Constructor for QgsWebEnginePage, with the specified parent widget.
bool render(QPainter *painter, const QRectF &painterRect)
Renders the web page contents to a painter.
void loadProgress(int progress)
This signal is emitted when the global progress status changes.
~QgsWebEnginePage() override
QWebEnginePage * page()
Returns a reference to the QWebEnginePage.
QSize documentSize() const
Returns the size of the page document, in pixels.