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