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