QGIS API Documentation 3.99.0-Master (26c88405ac0)
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 "qgsconfig.h"
19#include "qgswebenginepage.h"
20
21#include <QEventLoop>
22#include <QSizeF>
23#include <QWebEnginePage>
24
25#include "moc_qgswebenginepage.cpp"
26
27#ifdef HAVE_PDF4QT
28#include "qgspdfrenderer.h"
29#include <QTemporaryFile>
30#else
31#include "qgsexception.h"
32#endif
33
35 : QObject( parent )
36 , mPage{ std::make_unique< QWebEnginePage >() }
37{
38 // proxy some signals from the page
39 connect( mPage.get(), &QWebEnginePage::loadStarted, this, &QgsWebEnginePage::loadStarted );
40 connect( mPage.get(), &QWebEnginePage::loadProgress, this, &QgsWebEnginePage::loadProgress );
41 connect( mPage.get(), &QWebEnginePage::loadFinished, this, &QgsWebEnginePage::loadFinished );
42}
43
45
46QWebEnginePage *QgsWebEnginePage::page()
47{
48 return mPage.get();
49}
50
51bool QgsWebEnginePage::setContent( const QByteArray &data, const QString &mimeType, const QUrl &baseUrl, bool blocking )
52{
53 mCachedSize = QSize();
54 if ( blocking )
55 {
56 QEventLoop loop;
57 bool finished = false;
58 bool result = true;
59 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok )
60 {
61 finished = true;
62 result = ok;
63 loop.exit();
64 } );
65 mPage->setContent( data, mimeType, baseUrl );
66 if ( !finished )
67 {
68 loop.exec( QEventLoop::ExcludeUserInputEvents );
69 }
70 if ( result )
71 handlePostBlockingLoadOperations();
72 return result;
73 }
74 else
75 {
76 mPage->setContent( data, mimeType, baseUrl );
77 return true;
78 }
79}
80
81bool QgsWebEnginePage::setHtml( const QString &html, const QUrl &baseUrl, bool blocking )
82{
83 mCachedSize = QSize();
84 if ( blocking )
85 {
86 QEventLoop loop;
87 bool finished = false;
88 bool result = true;
89 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok )
90 {
91 finished = true;
92 result = ok;
93 loop.exit();
94 } );
95 mPage->setHtml( html, baseUrl );
96 if ( !finished )
97 {
98 loop.exec( QEventLoop::ExcludeUserInputEvents );
99 }
100 if ( result )
101 handlePostBlockingLoadOperations();
102
103 return result;
104 }
105 else
106 {
107 mPage->setHtml( html, baseUrl );
108 return true;
109 }
110}
111
112bool QgsWebEnginePage::setUrl( const QUrl &url, bool blocking )
113{
114 mCachedSize = QSize();
115 if ( blocking )
116 {
117 QEventLoop loop;
118 bool finished = false;
119 bool result = true;
120 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok )
121 {
122 finished = true;
123 result = ok;
124 loop.exit();
125 } );
126 mPage->setUrl( url );
127 if ( !finished )
128 {
129 loop.exec( QEventLoop::ExcludeUserInputEvents );
130 }
131 if ( result )
132 handlePostBlockingLoadOperations();
133
134 return result;
135 }
136 else
137 {
138 mPage->setUrl( url );
139 return true;
140 }
141}
142
144{
145 if ( mCachedSize.isValid() )
146 return mCachedSize;
147
148 QEventLoop loop;
149 int width = -1;
150 int height = -1;
151 bool finished = false;
152 mPage->runJavaScript( "[document.documentElement.scrollWidth, document.documentElement.scrollHeight];", [&width, &height, &loop, &finished]( QVariant result )
153 {
154 width = result.toList().value( 0 ).toInt();
155 height = result.toList().value( 1 ).toInt();
156 finished = true;
157 loop.exit();
158 } );
159 if ( !finished )
160 {
161 loop.exec( QEventLoop::ExcludeUserInputEvents );
162 }
163
164
165 mCachedSize = QSize( width, height );
166 return mCachedSize;
167}
168
169void QgsWebEnginePage::handlePostBlockingLoadOperations()
170{
171 // Following a blocking content load, do some other quick calculations which involve local event loops.
172 // This allows callers to avoid having to make another later call to a method which would other involve a local event loop.
173 QEventLoop loop;
174 int width = 0;
175 int height = 0;
176 bool finished = false;
177 mPage->runJavaScript( "[document.documentElement.scrollWidth, document.documentElement.scrollHeight];", [&width, &height, &loop, &finished]( QVariant result )
178 {
179 width = result.toList().value( 0 ).toInt();
180 height = result.toList().value( 1 ).toInt();
181 finished = true;
182 loop.exit();
183 } );
184 if ( !finished )
185 {
186 loop.exec( QEventLoop::ExcludeUserInputEvents );
187 }
188
189 mCachedSize = QSize( width, height );
190}
191
192#ifdef HAVE_PDF4QT
193bool QgsWebEnginePage::render( QPainter *painter, const QRectF &painterRect )
194{
195 const QSize actualSize = documentSize();
196
197 // TODO -- is this ALWAYS 96?
198 static constexpr double dpi = 96.0;
199 const QSizeF pageSize = QSizeF( actualSize.width() / dpi, actualSize.height() / dpi );
200
201 QEventLoop loop;
202 bool finished = false;
203 bool printOk = false;
204 QString renderedPdfPath;
205 connect( mPage.get(), &QWebEnginePage::pdfPrintingFinished, &loop, [&loop, &finished, &printOk, &renderedPdfPath]( const QString & pdfPath, bool success )
206 {
207 finished = true;
208 renderedPdfPath = pdfPath;
209 printOk = success;
210 loop.exit();
211 } );
212
213 // generate file name for temporary intermediate PDF file
214 QTemporaryFile f;
215 f.open();
216 f.close();
217
218 const QPageLayout layout = QPageLayout( QPageSize( pageSize, QPageSize::Inch ),
219 QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ),
220 QPageLayout::Inch, QMarginsF( 0, 0, 0, 0 ) );
221 mPage->printToPdf( f.fileName(), layout );
222
223 if ( !finished )
224 {
225 loop.exec( QEventLoop::ExcludeUserInputEvents );
226 }
227
228 if ( printOk )
229 {
230 QgsPdfRenderer renderer( renderedPdfPath );
231 renderer.render( painter, painterRect, 0 );
232 }
233 return printOk;
234}
235#else
236bool QgsWebEnginePage::render( QPainter *, const QRectF & )
237{
238 throw QgsNotSupportedException( QObject::tr( "Rendering web pages requires a QGIS build with PDF4Qt library support" ) );
239}
240#endif
Custom exception class which is raised when an operation is not supported.
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.