QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgslayoutitemhtml.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemhtml.cpp
3 ------------------------------------------------------------
4 begin : October 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgslayoutitemhtml.h"
17#include "qgslayoutframe.h"
18#include "qgslayout.h"
20#include "qgsmessagelog.h"
21#include "qgsexpression.h"
22#include "qgslogger.h"
24#include "qgsvectorlayer.h"
25#include "qgsproject.h"
26#include "qgsdistancearea.h"
27#include "qgsjsonutils.h"
28#include "qgsmapsettings.h"
29#include "qgswebpage.h"
30#include "qgswebframe.h"
31#include "qgslayoutitemmap.h"
34
35#include <QCoreApplication>
36#include <QPainter>
37#include <QImage>
38#include <QNetworkReply>
39#include <QThread>
40#include <QUrl>
41
42// clazy:excludeall=lambda-in-connect
43
45 : QgsLayoutMultiFrame( layout )
46{
47 mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
48
49 // only possible on the main thread!
50 if ( QThread::currentThread() == QApplication::instance()->thread() )
51 {
52 mWebPage = std::make_unique< QgsWebPage >();
53 }
54 else
55 {
56 QgsMessageLog::logMessage( QObject::tr( "Cannot load HTML content in background threads" ) );
57 }
58
59 if ( mWebPage )
60 {
61 mWebPage->setIdentifier( tr( "Layout HTML item" ) );
62 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
63 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
64
65 //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
66 QPalette palette = mWebPage->palette();
67 palette.setBrush( QPalette::Base, Qt::transparent );
68 mWebPage->setPalette( palette );
69
70 mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
71 }
72
73 //a html item added to a layout needs to have the initial expression context set,
74 //otherwise fields in the html aren't correctly evaluated until atlas preview feature changes (#9457)
75 setExpressionContext( mLayout->reportContext().feature(), mLayout->reportContext().layer() );
76
77 connect( &mLayout->reportContext(), &QgsLayoutReportContext::changed, this, &QgsLayoutItemHtml::refreshExpressionContext );
78
79 mFetcher = new QgsNetworkContentFetcher();
80}
81
83{
84 mFetcher->deleteLater();
85}
86
88{
90}
91
93{
94 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemHtml.svg" ) );
95}
96
98{
99 return new QgsLayoutItemHtml( layout );
100}
101
102void QgsLayoutItemHtml::setUrl( const QUrl &url )
103{
104 if ( !mWebPage )
105 {
106 return;
107 }
108
109 mUrl = url;
110 loadHtml( true );
111 emit changed();
112}
113
114void QgsLayoutItemHtml::setHtml( const QString &html )
115{
116 mHtml = html;
117 //TODO - this signal should be emitted, but without changing the signal which sets the html
118 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
119 //ways of doing this using QScintilla widgets.
120 //emit changed();
121}
122
123void QgsLayoutItemHtml::setEvaluateExpressions( bool evaluateExpressions )
124{
125 mEvaluateExpressions = evaluateExpressions;
126 loadHtml( true );
127 emit changed();
128}
129
130void QgsLayoutItemHtml::loadHtml( const bool useCache, const QgsExpressionContext *context )
131{
132 if ( !mWebPage )
133 {
134 return;
135 }
136
137 const QgsExpressionContext scopedContext = createExpressionContext();
138 const QgsExpressionContext *evalContext = context ? context : &scopedContext;
139
140 QString loadedHtml;
141 switch ( mContentMode )
142 {
144 {
145
146 QString currentUrl = mUrl.toString();
147
148 //data defined url set?
149 bool ok = false;
150 currentUrl = mDataDefinedProperties.valueAsString( QgsLayoutObject::SourceUrl, *evalContext, currentUrl, &ok );
151 if ( ok )
152 {
153 currentUrl = currentUrl.trimmed();
154 QgsDebugMsg( QStringLiteral( "exprVal Source Url:%1" ).arg( currentUrl ) );
155 }
156 if ( currentUrl.isEmpty() )
157 {
158 return;
159 }
160 if ( !( useCache && currentUrl == mLastFetchedUrl ) )
161 {
162 loadedHtml = fetchHtml( QUrl( currentUrl ) );
163 mLastFetchedUrl = currentUrl;
164 }
165 else
166 {
167 loadedHtml = mFetchedHtml;
168 }
169
170 break;
171 }
173 loadedHtml = mHtml;
174 break;
175 }
176
177 //evaluate expressions
178 if ( mEvaluateExpressions )
179 {
180 loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, evalContext, &mDistanceArea );
181 }
182
183 bool loaded = false;
184
185 QEventLoop loop;
186 connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
187 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
188
189 //reset page size. otherwise viewport size increases but never decreases again
190 mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
191
192 //set html, using the specified url as base if in Url mode or the project file if in manual mode
193 const QUrl baseUrl = mContentMode == QgsLayoutItemHtml::Url ?
194 QUrl( mActualFetchedUrl ) :
195 QUrl::fromLocalFile( mLayout->project()->absoluteFilePath() );
196
197 mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
198
199 //set user stylesheet
200 QWebSettings *settings = mWebPage->settings();
201 if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
202 {
203 QByteArray ba;
204 ba.append( mUserStylesheet.toUtf8() );
205 const QUrl cssFileURL = QUrl( QString( "data:text/css;charset=utf-8;base64," + ba.toBase64() ) );
206 settings->setUserStyleSheetUrl( cssFileURL );
207 }
208 else
209 {
210 settings->setUserStyleSheetUrl( QUrl() );
211 }
212
213 if ( !loaded )
214 loop.exec( QEventLoop::ExcludeUserInputEvents );
215
216 //inject JSON feature
217 if ( !mAtlasFeatureJSON.isEmpty() )
218 {
219 JavascriptExecutorLoop jsLoop;
220
221 mWebPage->mainFrame()->addToJavaScriptWindowObject( QStringLiteral( "loop" ), &jsLoop );
222 mWebPage->mainFrame()->evaluateJavaScript( QStringLiteral( "if ( typeof setFeature === \"function\" ) { try{ setFeature(%1); } catch (err) { loop.reportError(err.message); } }; loop.done();" ).arg( mAtlasFeatureJSON ) );
223
224 jsLoop.execIfNotDone();
225 }
226
228 //trigger a repaint
229 emit contentsChanged();
230}
231
232double QgsLayoutItemHtml::maxFrameWidth() const
233{
234 double maxWidth = 0;
236 {
237 maxWidth = std::max( maxWidth, static_cast< double >( frame->boundingRect().width() ) );
238 }
239
240 return maxWidth;
241}
242
244{
245 if ( frameCount() < 1 )
246 return;
247
248 if ( !mWebPage )
249 return;
250
251 QSize contentsSize = mWebPage->mainFrame()->contentsSize();
252
253 //find maximum frame width
254 const double maxWidth = maxFrameWidth();
255 //set content width to match maximum frame width
256 contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
257
258 mWebPage->setViewportSize( contentsSize );
259 mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
260 mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
261 if ( contentsSize.isValid() )
262 {
263 renderCachedImage();
264 }
266 emit changed();
267}
268
269void QgsLayoutItemHtml::renderCachedImage()
270{
271 if ( !mWebPage )
272 return;
273
274 //render page to cache image
275 mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
276 if ( mRenderedPage.isNull() )
277 {
278 return;
279 }
280 mRenderedPage.fill( Qt::transparent );
281 QPainter painter;
282 painter.begin( &mRenderedPage );
283 mWebPage->mainFrame()->render( &painter );
284 painter.end();
285}
286
287QString QgsLayoutItemHtml::fetchHtml( const QUrl &url )
288{
289 //pause until HTML fetch
290 bool loaded = false;
291 QEventLoop loop;
292 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
293 mFetcher->fetchContent( url );
294
295 if ( !loaded )
296 loop.exec( QEventLoop::ExcludeUserInputEvents );
297
298 mFetchedHtml = mFetcher->contentAsString();
299 mActualFetchedUrl = mFetcher->reply()->url().toString();
300 return mFetchedHtml;
301}
302
304{
305 return mSize;
306}
307
308void QgsLayoutItemHtml::render( QgsLayoutItemRenderContext &context, const QRectF &renderExtent, const int )
309{
310 if ( !mWebPage )
311 return;
312
313 QPainter *painter = context.renderContext().painter();
314 const QgsScopedQPainterState painterState( painter );
315 // painter is scaled to dots, so scale back to layout units
316 painter->scale( context.renderContext().scaleFactor() / mHtmlUnitsToLayoutUnits, context.renderContext().scaleFactor() / mHtmlUnitsToLayoutUnits );
317 painter->translate( 0.0, -renderExtent.top() * mHtmlUnitsToLayoutUnits );
318 mWebPage->mainFrame()->render( painter, QRegion( renderExtent.left(), renderExtent.top() * mHtmlUnitsToLayoutUnits, renderExtent.width() * mHtmlUnitsToLayoutUnits, renderExtent.height() * mHtmlUnitsToLayoutUnits ) );
319}
320
321double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
322{
323 if ( !mLayout )
324 {
325 return 1.0;
326 }
327
328 return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( mLayout->renderContext().dpi() / 72.0, Qgis::LayoutUnit::Millimeters ) ); //webkit seems to assume a standard dpi of 96
329}
330
331bool candidateSort( QPair<int, int> c1, QPair<int, int> c2 )
332{
333 if ( c1.second < c2.second )
334 return true;
335 else if ( c1.second > c2.second )
336 return false;
337 else if ( c1.first > c2.first )
338 return true;
339 else
340 return false;
341}
342
344{
345 if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
346 {
347 return yPos;
348 }
349
350 //convert yPos to pixels
351 const int idealPos = yPos * htmlUnitsToLayoutUnits();
352
353 //if ideal break pos is past end of page, there's nothing we need to do
354 if ( idealPos >= mRenderedPage.height() )
355 {
356 return yPos;
357 }
358
359 const int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
360
361 //loop through all lines just before ideal break location, up to max distance
362 //of maxSearchDistance
363 int changes = 0;
364 QRgb currentColor;
365 bool currentPixelTransparent = false;
366 bool previousPixelTransparent = false;
367 QRgb pixelColor;
368 QList< QPair<int, int> > candidates;
369 const int minRow = std::max( idealPos - maxSearchDistance, 0 );
370 for ( int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
371 {
372 changes = 0;
373 currentColor = qRgba( 0, 0, 0, 0 );
374 //check all pixels in this line
375 for ( int col = 0; col < mRenderedPage.width(); ++col )
376 {
377 //count how many times the pixels change color in this row
378 //eventually, we select a row to break at with the minimum number of color changes
379 //since this is likely a line break, or gap between table cells, etc
380 //but very unlikely to be midway through a text line or picture
381 pixelColor = mRenderedPage.pixel( col, candidateRow );
382 currentPixelTransparent = qAlpha( pixelColor ) == 0;
383 if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
384 {
385 //color has changed
386 currentColor = pixelColor;
387 changes++;
388 }
389 previousPixelTransparent = currentPixelTransparent;
390 }
391 candidates.append( qMakePair( candidateRow, changes ) );
392 }
393
394 //sort candidate rows by number of changes ascending, row number descending
395 std::sort( candidates.begin(), candidates.end(), candidateSort );
396 //first candidate is now the largest row with smallest number of changes
397
398 //OK, now take the mid point of the best candidate position
399 //we do this so that the spacing between text lines is likely to be split in half
400 //otherwise the html will be broken immediately above a line of text, which
401 //looks a little messy
402 const int maxCandidateRow = candidates[0].first;
403 int minCandidateRow = maxCandidateRow + 1;
404 const int minCandidateChanges = candidates[0].second;
405
406 QList< QPair<int, int> >::iterator it;
407 for ( it = candidates.begin(); it != candidates.end(); ++it )
408 {
409 if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
410 {
411 //no longer in a consecutive block of rows of minimum pixel color changes
412 //so return the row mid-way through the block
413 //first converting back to mm
414 return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
415 }
416 minCandidateRow = ( *it ).first;
417 }
418
419 //above loop didn't work for some reason
420 //return first candidate converted to mm
421 return candidates[0].first / htmlUnitsToLayoutUnits();
422}
423
424void QgsLayoutItemHtml::setUseSmartBreaks( bool useSmartBreaks )
425{
426 mUseSmartBreaks = useSmartBreaks;
428 emit changed();
429}
430
431void QgsLayoutItemHtml::setMaxBreakDistance( double maxBreakDistance )
432{
433 mMaxBreakDistance = maxBreakDistance;
435 emit changed();
436}
437
438void QgsLayoutItemHtml::setUserStylesheet( const QString &stylesheet )
439{
440 mUserStylesheet = stylesheet;
441 //TODO - this signal should be emitted, but without changing the signal which sets the css
442 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
443 //ways of doing this using QScintilla widgets.
444 //emit changed();
445}
446
447void QgsLayoutItemHtml::setUserStylesheetEnabled( const bool stylesheetEnabled )
448{
449 if ( mEnableUserStylesheet != stylesheetEnabled )
450 {
451 mEnableUserStylesheet = stylesheetEnabled;
452 loadHtml( true );
453 emit changed();
454 }
455}
456
458{
459 return tr( "<HTML frame>" );
460}
461
462bool QgsLayoutItemHtml::writePropertiesToElement( QDomElement &htmlElem, QDomDocument &, const QgsReadWriteContext & ) const
463{
464 htmlElem.setAttribute( QStringLiteral( "contentMode" ), QString::number( static_cast< int >( mContentMode ) ) );
465 htmlElem.setAttribute( QStringLiteral( "url" ), mUrl.toString() );
466 htmlElem.setAttribute( QStringLiteral( "html" ), mHtml );
467 htmlElem.setAttribute( QStringLiteral( "evaluateExpressions" ), mEvaluateExpressions ? "true" : "false" );
468 htmlElem.setAttribute( QStringLiteral( "useSmartBreaks" ), mUseSmartBreaks ? "true" : "false" );
469 htmlElem.setAttribute( QStringLiteral( "maxBreakDistance" ), QString::number( mMaxBreakDistance ) );
470 htmlElem.setAttribute( QStringLiteral( "stylesheet" ), mUserStylesheet );
471 htmlElem.setAttribute( QStringLiteral( "stylesheetEnabled" ), mEnableUserStylesheet ? "true" : "false" );
472 return true;
473}
474
475bool QgsLayoutItemHtml::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
476{
477 bool contentModeOK;
478 mContentMode = static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( QStringLiteral( "contentMode" ) ).toInt( &contentModeOK ) );
479 if ( !contentModeOK )
480 {
481 mContentMode = QgsLayoutItemHtml::Url;
482 }
483 mEvaluateExpressions = itemElem.attribute( QStringLiteral( "evaluateExpressions" ), QStringLiteral( "true" ) ) == QLatin1String( "true" );
484 mUseSmartBreaks = itemElem.attribute( QStringLiteral( "useSmartBreaks" ), QStringLiteral( "true" ) ) == QLatin1String( "true" );
485 mMaxBreakDistance = itemElem.attribute( QStringLiteral( "maxBreakDistance" ), QStringLiteral( "10" ) ).toDouble();
486 mHtml = itemElem.attribute( QStringLiteral( "html" ) );
487 mUserStylesheet = itemElem.attribute( QStringLiteral( "stylesheet" ) );
488 mEnableUserStylesheet = itemElem.attribute( QStringLiteral( "stylesheetEnabled" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
489
490 //finally load the set url
491 const QString urlString = itemElem.attribute( QStringLiteral( "url" ) );
492 if ( !urlString.isEmpty() )
493 {
494 mUrl = urlString;
495 }
496 loadHtml( true );
497
498 //since frames had to be created before, we need to emit a changed signal to refresh the widget
499 emit changed();
500 return true;
501}
502
503void QgsLayoutItemHtml::setExpressionContext( const QgsFeature &feature, QgsVectorLayer *layer )
504{
505 mExpressionFeature = feature;
506 mExpressionLayer = layer;
507
508 //setup distance area conversion
509 if ( layer )
510 {
511 mDistanceArea.setSourceCrs( layer->crs(), mLayout->project()->transformContext() );
512 }
513 else if ( mLayout )
514 {
515 //set to composition's mapsettings' crs
516 QgsLayoutItemMap *referenceMap = mLayout->referenceMap();
517 if ( referenceMap )
518 mDistanceArea.setSourceCrs( referenceMap->crs(), mLayout->project()->transformContext() );
519 }
520 if ( mLayout )
521 {
522 mDistanceArea.setEllipsoid( mLayout->project()->ellipsoid() );
523 }
524
525 if ( feature.isValid() )
526 {
527 // create JSON representation of feature
528 QgsJsonExporter exporter( layer );
529 exporter.setIncludeRelated( true );
530 mAtlasFeatureJSON = exporter.exportFeature( feature );
531 }
532 else
533 {
534 mAtlasFeatureJSON.clear();
535 }
536}
537
538void QgsLayoutItemHtml::refreshExpressionContext()
539{
540 QgsVectorLayer *vl = nullptr;
541 QgsFeature feature;
542
543 if ( mLayout )
544 {
545 vl = mLayout->reportContext().layer();
546 feature = mLayout->reportContext().feature();
547 }
548
549 setExpressionContext( feature, vl );
550 loadHtml( true );
551}
552
554{
556
557 //updates data defined properties and redraws item to match
558 if ( property == QgsLayoutObject::SourceUrl || property == QgsLayoutObject::AllProperties )
559 {
560 loadHtml( true, &context );
561 }
562}
563
564//JavascriptExecutorLoop
566
567void JavascriptExecutorLoop::done()
568{
569 mDone = true;
570 quit();
571}
572
573void JavascriptExecutorLoop::execIfNotDone()
574{
575 if ( !mDone )
576 exec( QEventLoop::ExcludeUserInputEvents );
577
578 // gross, but nothing else works, so f*** it.. it's not worth spending a day trying to find a non-hacky way
579 // to force the web page to update following the js execution
580 for ( int i = 0; i < 100; i++ )
581 qApp->processEvents();
582}
583
584void JavascriptExecutorLoop::reportError( const QString &error )
585{
586 mDone = true;
587 QgsMessageLog::logMessage( tr( "HTML setFeature function error: %1" ).arg( error ), tr( "Layout" ) );
588 quit();
589}
590
The QWebSettings class is a collection of stubs to mimic the API of a QWebSettings on systems where Q...
Definition: qgswebpage.h:43
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:219
Handles exporting QgsFeature features to GeoJSON features.
Definition: qgsjsonutils.h:41
Base class for frame items, which form a layout multiframe item.
A layout multiframe subclass for HTML content.
int type() const override
Returns unique multiframe type id.
QSizeF totalSize() const override
Returns the total size of the multiframe's content, in layout units.
void setUrl(const QUrl &url)
Sets the url for content to display in the item when the item is using the QgsLayoutItemHtml::Url mod...
ContentMode
Source modes for the HTML content to render in the item.
@ ManualHtml
HTML content is manually set for the item.
@ Url
Using this mode item fetches its content via a url.
void setEvaluateExpressions(bool evaluateExpressions)
Sets whether the html item will evaluate QGIS expressions prior to rendering the HTML content.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
QString html() const
Returns the HTML source displayed in the item if the item is using the QgsLayoutItemHtml::ManualHtml ...
~QgsLayoutItemHtml() override
double maxBreakDistance() const
Returns the maximum distance allowed when calculating where to place page breaks in the html.
static QgsLayoutItemHtml * create(QgsLayout *layout)
Returns a new QgsLayoutItemHtml for the specified parent layout.
bool evaluateExpressions() const
Returns whether html item will evaluate QGIS expressions prior to rendering the HTML content.
double findNearbyPageBreak(double yPos) override
Finds the optimal position to break a frame at.
QUrl url() const
Returns the URL of the content displayed in the item if the item is using the QgsLayoutItemHtml::Url ...
void setMaxBreakDistance(double distance)
Sets the maximum distance allowed when calculating where to place page breaks in the html.
void setUserStylesheetEnabled(bool enabled)
Sets whether user stylesheets are enabled for the HTML content.
void setHtml(const QString &html)
Sets the html to display in the item when the item is using the QgsLayoutItemHtml::ManualHtml mode.
QString displayName() const override
Returns the multiframe display name.
void setUseSmartBreaks(bool useSmartBreaks)
Sets whether the html item should use smart breaks.
void recalculateFrameSizes() override
Recalculates the frame sizes for the current viewport dimensions.
void setUserStylesheet(const QString &stylesheet)
Sets the user stylesheet CSS rules to use while rendering the HTML content.
QIcon icon() const override
Returns the item's icon.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
QgsLayoutItemHtml(QgsLayout *layout)
Constructor for QgsLayoutItemHtml, with the specified parent layout.
void loadHtml(bool useCache=false, const QgsExpressionContext *context=nullptr)
Reloads the html source from the url and redraws the item.
void render(QgsLayoutItemRenderContext &context, const QRectF &renderExtent, int frameIndex) override
Renders a portion of the multiframe's content into a render context.
bool useSmartBreaks() const
Returns whether html item is using smart breaks.
Layout graphical items for displaying a map.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
@ LayoutHtml
Html multiframe item.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:71
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
Abstract base class for layout items with the ability to distribute the content to several frames (Qg...
int frameCount() const
Returns the number of frames associated with this multiframe.
void contentsChanged()
Emitted when the contents of the multi frame have changed and the frames must be redrawn.
QgsLayoutFrame * frame(int index) const
Returns the child frame at a specified index from the multiframe.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QList< QgsLayoutFrame * > mFrameItems
virtual void recalculateFrameSizes()
Recalculates the portion of the multiframe item which is shown in each of its component frames.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ AllProperties
All properties for item.
@ SourceUrl
Html source url.
void changed()
Emitted certain settings in the context is changed, e.g.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
HTTP network content fetcher.
void finished()
Emitted when content has loaded.
QNetworkReply * reply()
Returns a reference to the network reply.
QString contentAsString() const
Returns the fetched content as a string.
void fetchContent(const QUrl &url, const QString &authcfg=QString())
Fetches content from a remote URL and handles redirects.
The class is used as a container of context for various read/write operations on other objects.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
Scoped object for saving and restoring a QPainter object's state.
Represents a vector layer which manages a vector based data sets.
bool candidateSort(QPair< int, int > c1, QPair< int, int > c2)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38