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