QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 finished = true;
61 result = ok;
62 loop.exit();
63 } );
64 mPage->setContent( data, mimeType, baseUrl );
65 if ( !finished )
66 {
67 loop.exec( QEventLoop::ExcludeUserInputEvents );
68 }
69 if ( result )
70 handlePostBlockingLoadOperations();
71 return result;
72 }
73 else
74 {
75 mPage->setContent( data, mimeType, baseUrl );
76 return true;
77 }
78}
79
80bool QgsWebEnginePage::setHtml( const QString &html, const QUrl &baseUrl, bool blocking )
81{
82 mCachedSize = QSize();
83 if ( blocking )
84 {
85 QEventLoop loop;
86 bool finished = false;
87 bool result = true;
88 connect( mPage.get(), &QWebEnginePage::loadFinished, &loop, [&loop, &finished, &result]( bool ok ) {
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 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 width = result.toList().value( 0 ).toInt();
151 height = result.toList().value( 1 ).toInt();
152 finished = true;
153 loop.exit();
154 } );
155 if ( !finished )
156 {
157 loop.exec( QEventLoop::ExcludeUserInputEvents );
158 }
159
160
161 mCachedSize = QSize( width, height );
162 return mCachedSize;
163}
164
165void QgsWebEnginePage::handlePostBlockingLoadOperations()
166{
167 // Following a blocking content load, do some other quick calculations which involve local event loops.
168 // This allows callers to avoid having to make another later call to a method which would other involve a local event loop.
169 QEventLoop loop;
170 int width = 0;
171 int height = 0;
172 bool finished = false;
173 mPage->runJavaScript( "[document.documentElement.scrollWidth, document.documentElement.scrollHeight];", [&width, &height, &loop, &finished]( QVariant result ) {
174 width = result.toList().value( 0 ).toInt();
175 height = result.toList().value( 1 ).toInt();
176 finished = true;
177 loop.exit();
178 } );
179 if ( !finished )
180 {
181 loop.exec( QEventLoop::ExcludeUserInputEvents );
182 }
183
184 mCachedSize = QSize( width, height );
185}
186
187#ifdef HAVE_PDF4QT
188bool QgsWebEnginePage::render( QPainter *painter, const QRectF &painterRect )
189{
190 const QSize actualSize = documentSize();
191
192 // TODO -- is this ALWAYS 96?
193 static constexpr double dpi = 96.0;
194 const QSizeF pageSize = QSizeF( actualSize.width() / dpi, actualSize.height() / dpi );
195
196 QEventLoop loop;
197 bool finished = false;
198 bool printOk = false;
199 QString renderedPdfPath;
200 connect( mPage.get(), &QWebEnginePage::pdfPrintingFinished, &loop, [&loop, &finished, &printOk, &renderedPdfPath]( const QString &pdfPath, bool success ) {
201 finished = true;
202 renderedPdfPath = pdfPath;
203 printOk = success;
204 loop.exit();
205 } );
206
207 // generate file name for temporary intermediate PDF file
208 QTemporaryFile f;
209 if ( !f.open() )
210 return false;
211
212 f.close();
213
214 const QPageLayout layout = QPageLayout( QPageSize( pageSize, QPageSize::Inch ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ), QPageLayout::Inch, QMarginsF( 0, 0, 0, 0 ) );
215 mPage->printToPdf( f.fileName(), layout );
216
217 if ( !finished )
218 {
219 loop.exec( QEventLoop::ExcludeUserInputEvents );
220 }
221
222 if ( printOk )
223 {
224 QgsPdfRenderer renderer( renderedPdfPath );
225 renderer.render( painter, painterRect, 0 );
226 }
227 return printOk;
228}
229#else
230bool QgsWebEnginePage::render( QPainter *, const QRectF & )
231{
232 throw QgsNotSupportedException( QObject::tr( "Rendering web pages requires a QGIS build with PDF4Qt library support" ) );
233}
234#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.