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