QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
18#include "qgsdistancearea.h"
19#include "qgsexpression.h"
20#include "qgsjsonutils.h"
21#include "qgslayout.h"
22#include "qgslayoutframe.h"
23#include "qgslayoutitemlabel.h"
24#include "qgslayoutitemmap.h"
27#include "qgslogger.h"
28#include "qgsmapsettings.h"
29#include "qgsmessagelog.h"
32#include "qgsproject.h"
33#include "qgsvectorlayer.h"
34#include "qgswebframe.h"
35#include "qgswebpage.h"
36
37#include <QAbstractTextDocumentLayout>
38#include <QCoreApplication>
39#include <QImage>
40#include <QNetworkReply>
41#include <QPainter>
42#include <QString>
43#include <QThread>
44#include <QUrl>
45
46#include "moc_qgslayoutitemhtml.cpp"
47
48using namespace Qt::StringLiterals;
49
50// clazy:excludeall=lambda-in-connect
51
54{
55 mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
56
57 // only possible on the main thread!
58 if ( QThread::currentThread() == QApplication::instance()->thread() )
59 {
60 mWebPage = std::make_unique< QgsWebPage >();
61 }
62 else
63 {
64 QgsMessageLog::logMessage( QObject::tr( "Cannot load HTML content in background threads" ) );
65 }
66
67 if ( mWebPage )
68 {
69 mWebPage->setIdentifier( tr( "Layout HTML item" ) );
70 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
71 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
72
73 //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
74 QPalette palette = mWebPage->palette();
75 palette.setBrush( QPalette::Base, Qt::transparent );
76 mWebPage->setPalette( palette );
77
78 mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
79 }
80
81 //a html item added to a layout needs to have the initial expression context set,
82 //otherwise fields in the html aren't correctly evaluated until atlas preview feature changes (#9457)
83 setExpressionContext( mLayout->reportContext().feature(), mLayout->reportContext().layer() );
84
85 connect( &mLayout->reportContext(), &QgsLayoutReportContext::changed, this, &QgsLayoutItemHtml::refreshExpressionContext );
86
87 mFetcher = new QgsNetworkContentFetcher();
88}
89
91{
92 mFetcher->deleteLater();
93}
94
99
101{
102 return QgsApplication::getThemeIcon( u"/mLayoutItemHtml.svg"_s );
103}
104
109
111{
113 QgsLayoutFrame *frame = new QgsLayoutFrame( label->layout(), html );
114 frame->setVisible( label->isVisible() );
115 frame->setLocked( label->isLocked() );
116 frame->setItemOpacity( label->itemOpacity() );
117 frame->setRotation( label->rotation() );
118 frame->setReferencePoint( label->referencePoint() );
119 frame->attemptMove( label->positionWithUnits() );
120 frame->attemptResize( label->sizeWithUnits() );
121 frame->setZValue( label->zValue() );
122 frame->setParentGroup( label->parentGroup() );
123 frame->setBackgroundColor( label->backgroundColor() );
124 frame->setFrameEnabled( label->frameEnabled() );
125 frame->setFrameJoinStyle( label->frameJoinStyle() );
126 frame->setFrameStrokeWidth( label->frameStrokeWidth() );
127 frame->setFrameStrokeColor( label->frameStrokeColor() );
128 html->addFrame( frame );
129 html->setContentMode( QgsLayoutItemHtml::ManualHtml );
130 html->setHtml( label->currentText() );
131 html->setUserStylesheetEnabled( true );
132 html->setUserStylesheet( label->createStylesheet() );
133 html->loadHtml();
134 return html;
135}
136
138{
139 if ( !mWebPage )
140 {
141 return;
142 }
143
144 mUrl = url;
145 loadHtml( true );
146 emit changed();
147}
148
149void QgsLayoutItemHtml::setHtml( const QString &html )
150{
151 mHtml = html;
152 //TODO - this signal should be emitted, but without changing the signal which sets the html
153 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
154 //ways of doing this using QScintilla widgets.
155 //emit changed();
156}
157
159{
160 mEvaluateExpressions = evaluateExpressions;
161 loadHtml( true );
162 emit changed();
163}
164
165void QgsLayoutItemHtml::loadHtml( const bool useCache, const QgsExpressionContext *context )
166{
167 if ( !mWebPage )
168 {
169 return;
170 }
171
172 const QgsExpressionContext scopedContext = createExpressionContext();
173 const QgsExpressionContext *evalContext = context ? context : &scopedContext;
174
175 QString loadedHtml;
176 switch ( mContentMode )
177 {
179 {
180
181 QString currentUrl = mUrl.toString();
182
183 //data defined url set?
184 bool ok = false;
185 currentUrl = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::SourceUrl, *evalContext, currentUrl, &ok );
186 if ( ok )
187 {
188 currentUrl = currentUrl.trimmed();
189 QgsDebugMsgLevel( u"exprVal Source Url:%1"_s.arg( currentUrl ), 2 );
190 }
191 if ( currentUrl.isEmpty() )
192 {
193 return;
194 }
195 if ( !( useCache && currentUrl == mLastFetchedUrl ) )
196 {
197 loadedHtml = fetchHtml( QUrl( currentUrl ) );
198 mLastFetchedUrl = currentUrl;
199 }
200 else
201 {
202 loadedHtml = mFetchedHtml;
203 }
204
205 break;
206 }
208 loadedHtml = mHtml;
209 break;
210 }
211
212 //evaluate expressions
213 if ( mEvaluateExpressions )
214 {
215 loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, evalContext, &mDistanceArea );
216 }
217
218 bool loaded = false;
219
220 QEventLoop loop;
221 connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
222 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
223
224 //reset page size. otherwise viewport size increases but never decreases again
225 mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
226
227 //set html, using the specified url as base if in Url mode or the project file if in manual mode
228 const QUrl baseUrl = mContentMode == QgsLayoutItemHtml::Url ?
229 QUrl( mActualFetchedUrl ) :
230 QUrl::fromLocalFile( mLayout->project()->absoluteFilePath() );
231
232 mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
233
234 //set user stylesheet
235 QWebSettings *settings = mWebPage->settings();
236 if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
237 {
238 QByteArray ba;
239 ba.append( mUserStylesheet.toUtf8() );
240 const QUrl cssFileURL = QUrl( QString( "data:text/css;charset=utf-8;base64," + ba.toBase64() ) );
241 settings->setUserStyleSheetUrl( cssFileURL );
242 }
243 else
244 {
245 settings->setUserStyleSheetUrl( QUrl() );
246 }
247
248 if ( !loaded )
249 loop.exec( QEventLoop::ExcludeUserInputEvents );
250
252 //trigger a repaint
253 emit contentsChanged();
254}
255
256double QgsLayoutItemHtml::maxFrameWidth() const
257{
258 double maxWidth = 0;
260 {
261 maxWidth = std::max( maxWidth, static_cast< double >( frame->boundingRect().width() ) );
262 }
263
264 return maxWidth;
265}
266
268{
269 if ( frameCount() < 1 )
270 return;
271
272 if ( !mWebPage )
273 return;
274
275 QSize contentsSize = mWebPage->mainFrame()->contentsSize();
276
277 //find maximum frame width
278 const double maxWidth = maxFrameWidth();
279 //set content width to match maximum frame width
280 contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
281
282 mWebPage->setViewportSize( contentsSize );
283 mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
284 mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
285 if ( contentsSize.isValid() )
286 {
287 renderCachedImage();
288 }
290 emit changed();
291}
292
293void QgsLayoutItemHtml::renderCachedImage()
294{
295 if ( !mWebPage )
296 return;
297
298 //render page to cache image
299 mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
300 if ( mRenderedPage.isNull() )
301 {
302 return;
303 }
304 mRenderedPage.fill( Qt::transparent );
305 QPainter painter;
306 painter.begin( &mRenderedPage );
307 mWebPage->mainFrame()->render( &painter );
308 painter.end();
309}
310
311QString QgsLayoutItemHtml::fetchHtml( const QUrl &url )
312{
313 //pause until HTML fetch
314 bool loaded = false;
315 QEventLoop loop;
316 connect( mFetcher, &QgsNetworkContentFetcher::finished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
317 mFetcher->fetchContent( url );
318
319 if ( !loaded )
320 loop.exec( QEventLoop::ExcludeUserInputEvents );
321
322 mFetchedHtml = mFetcher->contentAsString();
323 mActualFetchedUrl = mFetcher->reply()->url().toString();
324 return mFetchedHtml;
325}
326
328{
329 return mSize;
330}
331
332void QgsLayoutItemHtml::render( QgsLayoutItemRenderContext &context, const QRectF &renderExtent, const int frameIndex )
333{
334 Q_UNUSED( renderExtent )
335 if ( mLayout->renderContext().isPreviewRender() )
336 {
337 if ( QgsLayoutFrame *currentFrame = frame( frameIndex ) )
338 {
339 QPainter *painter = context.renderContext().painter();
340
341 // painter is scaled to dots, so scale back to layout units
342 const QRectF painterRect = QRectF( currentFrame->rect().left() * context.renderContext().scaleFactor(),
343 currentFrame->rect().top() * context.renderContext().scaleFactor(),
344 currentFrame->rect().width() * context.renderContext().scaleFactor(),
345 currentFrame->rect().height() * context.renderContext().scaleFactor()
346 );
347
348 painter->setBrush( QBrush( QColor( 255, 125, 125, 125 ) ) );
349 painter->setPen( Qt::NoPen );
350 painter->drawRect( painterRect );
351 painter->setBrush( Qt::NoBrush );
352
353 painter->setPen( QColor( 200, 0, 0, 255 ) );
354 QTextDocument td;
355 td.setTextWidth( painterRect.width() );
356 td.setHtml( u"<span style=\"color: rgb(200,0,0);\"><b>%1</b><br>%2</span>"_s.arg(
357 tr( "WebKit not available!" ),
358 tr( "The item cannot be rendered because this QGIS install was built without WebKit support." ) ) );
359 painter->setClipRect( painterRect );
360 QAbstractTextDocumentLayout::PaintContext ctx;
361 td.documentLayout()->draw( painter, ctx );
362 }
363 }
364}
365
366double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
367{
368 if ( !mLayout )
369 {
370 return 1.0;
371 }
372
373 return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( mLayout->renderContext().dpi() / 72.0, Qgis::LayoutUnit::Millimeters ) ); //webkit seems to assume a standard dpi of 96
374}
375
376bool candidateSort( QPair<int, int> c1, QPair<int, int> c2 )
377{
378 if ( c1.second < c2.second )
379 return true;
380 else if ( c1.second > c2.second )
381 return false;
382 else if ( c1.first > c2.first )
383 return true;
384 else
385 return false;
386}
387
389{
390 if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
391 {
392 return yPos;
393 }
394
395 //convert yPos to pixels
396 const int idealPos = yPos * htmlUnitsToLayoutUnits();
397
398 //if ideal break pos is past end of page, there's nothing we need to do
399 if ( idealPos >= mRenderedPage.height() )
400 {
401 return yPos;
402 }
403
404 const int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
405
406 //loop through all lines just before ideal break location, up to max distance
407 //of maxSearchDistance
408 int changes = 0;
409 QRgb currentColor;
410 bool currentPixelTransparent = false;
411 bool previousPixelTransparent = false;
412 QRgb pixelColor;
413 QList< QPair<int, int> > candidates;
414 const int minRow = std::max( idealPos - maxSearchDistance, 0 );
415 for ( int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
416 {
417 changes = 0;
418 currentColor = qRgba( 0, 0, 0, 0 );
419 //check all pixels in this line
420 for ( int col = 0; col < mRenderedPage.width(); ++col )
421 {
422 //count how many times the pixels change color in this row
423 //eventually, we select a row to break at with the minimum number of color changes
424 //since this is likely a line break, or gap between table cells, etc
425 //but very unlikely to be midway through a text line or picture
426 pixelColor = mRenderedPage.pixel( col, candidateRow );
427 currentPixelTransparent = qAlpha( pixelColor ) == 0;
428 if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
429 {
430 //color has changed
431 currentColor = pixelColor;
432 changes++;
433 }
434 previousPixelTransparent = currentPixelTransparent;
435 }
436 candidates.append( qMakePair( candidateRow, changes ) );
437 }
438
439 //sort candidate rows by number of changes ascending, row number descending
440 std::sort( candidates.begin(), candidates.end(), candidateSort );
441 //first candidate is now the largest row with smallest number of changes
442
443 //OK, now take the mid point of the best candidate position
444 //we do this so that the spacing between text lines is likely to be split in half
445 //otherwise the html will be broken immediately above a line of text, which
446 //looks a little messy
447 const int maxCandidateRow = candidates[0].first;
448 int minCandidateRow = maxCandidateRow + 1;
449 const int minCandidateChanges = candidates[0].second;
450
451 QList< QPair<int, int> >::iterator it;
452 for ( it = candidates.begin(); it != candidates.end(); ++it )
453 {
454 if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
455 {
456 //no longer in a consecutive block of rows of minimum pixel color changes
457 //so return the row mid-way through the block
458 //first converting back to mm
459 return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
460 }
461 minCandidateRow = ( *it ).first;
462 }
463
464 //above loop didn't work for some reason
465 //return first candidate converted to mm
466 return candidates[0].first / htmlUnitsToLayoutUnits();
467}
468
470{
471 mUseSmartBreaks = useSmartBreaks;
473 emit changed();
474}
475
477{
478 mMaxBreakDistance = maxBreakDistance;
480 emit changed();
481}
482
483void QgsLayoutItemHtml::setUserStylesheet( const QString &stylesheet )
484{
485 mUserStylesheet = stylesheet;
486 //TODO - this signal should be emitted, but without changing the signal which sets the css
487 //to an equivalent of editingFinished it causes a lot of problems. Need to investigate
488 //ways of doing this using QScintilla widgets.
489 //emit changed();
490}
491
492void QgsLayoutItemHtml::setUserStylesheetEnabled( const bool stylesheetEnabled )
493{
494 if ( mEnableUserStylesheet != stylesheetEnabled )
495 {
496 mEnableUserStylesheet = stylesheetEnabled;
497 loadHtml( true );
498 emit changed();
499 }
500}
501
503{
504 return tr( "<HTML frame>" );
505}
506
507bool QgsLayoutItemHtml::writePropertiesToElement( QDomElement &htmlElem, QDomDocument &, const QgsReadWriteContext & ) const
508{
509 htmlElem.setAttribute( u"contentMode"_s, QString::number( static_cast< int >( mContentMode ) ) );
510 htmlElem.setAttribute( u"url"_s, mUrl.toString() );
511 htmlElem.setAttribute( u"html"_s, mHtml );
512 htmlElem.setAttribute( u"evaluateExpressions"_s, mEvaluateExpressions ? "true" : "false" );
513 htmlElem.setAttribute( u"useSmartBreaks"_s, mUseSmartBreaks ? "true" : "false" );
514 htmlElem.setAttribute( u"maxBreakDistance"_s, QString::number( mMaxBreakDistance ) );
515 htmlElem.setAttribute( u"stylesheet"_s, mUserStylesheet );
516 htmlElem.setAttribute( u"stylesheetEnabled"_s, mEnableUserStylesheet ? "true" : "false" );
517 return true;
518}
519
520bool QgsLayoutItemHtml::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext & )
521{
522 bool contentModeOK;
523 mContentMode = static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( u"contentMode"_s ).toInt( &contentModeOK ) );
524 if ( !contentModeOK )
525 {
526 mContentMode = QgsLayoutItemHtml::Url;
527 }
528 mEvaluateExpressions = itemElem.attribute( u"evaluateExpressions"_s, u"true"_s ) == "true"_L1;
529 mUseSmartBreaks = itemElem.attribute( u"useSmartBreaks"_s, u"true"_s ) == "true"_L1;
530 mMaxBreakDistance = itemElem.attribute( u"maxBreakDistance"_s, u"10"_s ).toDouble();
531 mHtml = itemElem.attribute( u"html"_s );
532 mUserStylesheet = itemElem.attribute( u"stylesheet"_s );
533 mEnableUserStylesheet = itemElem.attribute( u"stylesheetEnabled"_s, u"false"_s ) == "true"_L1;
534
535 //finally load the set url
536 const QString urlString = itemElem.attribute( u"url"_s );
537 if ( !urlString.isEmpty() )
538 {
539 mUrl = urlString;
540 }
541 loadHtml( true );
542
543 //since frames had to be created before, we need to emit a changed signal to refresh the widget
544 emit changed();
545 return true;
546}
547
548void QgsLayoutItemHtml::setExpressionContext( const QgsFeature &feature, QgsVectorLayer *layer )
549{
550 mExpressionFeature = feature;
551 mExpressionLayer = layer;
552
553 //setup distance area conversion
554 if ( layer )
555 {
556 mDistanceArea.setSourceCrs( layer->crs(), mLayout->project()->transformContext() );
557 }
558 else if ( mLayout )
559 {
560 //set to composition's mapsettings' crs
561 QgsLayoutItemMap *referenceMap = mLayout->referenceMap();
562 if ( referenceMap )
563 mDistanceArea.setSourceCrs( referenceMap->crs(), mLayout->project()->transformContext() );
564 }
565 if ( mLayout )
566 {
567 mDistanceArea.setEllipsoid( mLayout->project()->ellipsoid() );
568 }
569
570 if ( feature.isValid() )
571 {
572 // create JSON representation of feature
573 QgsJsonExporter exporter( layer );
574 exporter.setIncludeRelated( true );
575 mAtlasFeatureJSON = exporter.exportFeature( feature );
576 }
577 else
578 {
579 mAtlasFeatureJSON.clear();
580 }
581}
582
583void QgsLayoutItemHtml::refreshExpressionContext()
584{
585 QgsVectorLayer *vl = nullptr;
586 QgsFeature feature;
587
588 if ( mLayout )
589 {
590 vl = mLayout->reportContext().layer();
591 feature = mLayout->reportContext().feature();
592 }
593
594 setExpressionContext( feature, vl );
595 loadHtml( true );
596}
597
599{
601
602 //updates data defined properties and redraws item to match
604 {
605 loadHtml( true, &context );
606 }
607}
608
609//JavascriptExecutorLoop
611
612void JavascriptExecutorLoop::done()
613{
614 mDone = true;
615 quit();
616}
617
618void JavascriptExecutorLoop::execIfNotDone()
619{
620 if ( !mDone )
621 exec( QEventLoop::ExcludeUserInputEvents );
622
623 // gross, but nothing else works, so f*** it.. it's not worth spending a day trying to find a non-hacky way
624 // to force the web page to update following the js execution
625 for ( int i = 0; i < 100; i++ )
626 qApp->processEvents();
627}
628
629void JavascriptExecutorLoop::reportError( const QString &error )
630{
631 mDone = true;
632 QgsMessageLog::logMessage( tr( "HTML setFeature function error: %1" ).arg( error ), tr( "Layout" ) );
633 quit();
634}
635
A collection of stubs to mimic the API of a QWebSettings on systems where QtWebkit is not available.
Definition qgswebpage.h:37
@ Millimeters
Millimeters.
Definition qgis.h:5276
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.
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:60
bool isValid() const
Returns the validity of this feature.
Base class for frame items, which form a layout multiframe item.
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 ...
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
double maxBreakDistance() const
Returns the maximum distance allowed when calculating where to place page breaks in the html.
static QgsLayoutItemHtml * createFromLabel(QgsLayoutItemLabel *label)
Returns a new QgsLayoutItemHtml matching the content and rendering of a given label.
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.
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.
A layout item subclass for text labels.
QString currentText() const
Returns the text as it appears on the label (with evaluated expressions and other dynamic content).
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.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
QColor backgroundColor(bool useDataDefined=true) const
Returns the background color for this item.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutItemGroup * parentGroup() const
Returns the item's parent group, if the item is part of a QgsLayoutItemGroup group.
QgsLayoutMeasurement frameStrokeWidth() const
Returns the frame's stroke width.
bool isLocked() const
Returns true if the item is locked, and cannot be interacted with using the mouse.
double itemOpacity() const
Returns the item's opacity.
ReferencePoint referencePoint() const
Returns the reference point for positioning of the layout item.
QgsLayoutPoint positionWithUnits() const
Returns the item's current position, including units.
bool frameEnabled() const
Returns true if the item includes a frame.
QColor frameStrokeColor() const
Returns the frame's stroke color.
Qt::PenJoinStyle frameJoinStyle() const
Returns the join style used for drawing the item's frame.
int frameCount() const
Returns the number of frames associated with this multiframe.
QgsLayoutMultiFrame(QgsLayout *layout)
Construct a new multiframe item, attached to the specified layout.
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.
int frameIndex(QgsLayoutFrame *frame) const
Returns the index of a frame within the multiframe.
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.
void changed()
Emitted certain settings in the context is changed, e.g.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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.
A container for the context for various read/write operations on 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.
Represents a vector layer which manages a vector based dataset.
bool candidateSort(QPair< int, int > c1, QPair< int, int > c2)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63