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