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