33 #include <QCoreApplication> 36 #include <QNetworkReply> 43 mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
46 if ( QThread::currentThread() == QApplication::instance()->thread() )
48 mWebPage = qgis::make_unique< QgsWebPage >();
57 mWebPage->setIdentifier( tr(
"Layout HTML item" ) );
58 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
59 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
62 QPalette palette = mWebPage->palette();
63 palette.setBrush( QPalette::Base, Qt::transparent );
64 mWebPage->setPalette( palette );
71 setExpressionContext(
mLayout->reportContext().feature(),
mLayout->reportContext().layer() );
80 mFetcher->deleteLater();
137 switch ( mContentMode )
142 QString currentUrl = mUrl.toString();
149 currentUrl = currentUrl.trimmed();
150 QgsDebugMsg( QStringLiteral(
"exprVal Source Url:%1" ).arg( currentUrl ) );
152 if ( currentUrl.isEmpty() )
156 if ( !( useCache && currentUrl == mLastFetchedUrl ) )
158 loadedHtml = fetchHtml( QUrl( currentUrl ) );
159 mLastFetchedUrl = currentUrl;
163 loadedHtml = mFetchedHtml;
174 if ( mEvaluateExpressions )
182 connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop ] { loaded =
true; loop.quit(); } );
186 mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
190 QUrl( mActualFetchedUrl ) :
191 QUrl::fromLocalFile(
mLayout->project()->absoluteFilePath() );
193 mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
197 if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
200 ba.append( mUserStylesheet.toUtf8() );
201 QUrl cssFileURL = QUrl(
"data:text/css;charset=utf-8;base64," + ba.toBase64() );
202 settings->setUserStyleSheetUrl( cssFileURL );
206 settings->setUserStyleSheetUrl( QUrl() );
210 loop.exec( QEventLoop::ExcludeUserInputEvents );
213 if ( !mAtlasFeatureJSON.isEmpty() )
215 JavascriptExecutorLoop jsLoop;
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 ) );
220 jsLoop.execIfNotDone();
228 double QgsLayoutItemHtml::maxFrameWidth()
const 233 maxWidth = std::max( maxWidth, static_cast< double >(
frame->boundingRect().width() ) );
247 QSize contentsSize = mWebPage->mainFrame()->contentsSize();
250 double maxWidth = maxFrameWidth();
252 contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
254 mWebPage->setViewportSize( contentsSize );
255 mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
256 mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
257 if ( contentsSize.isValid() )
265 void QgsLayoutItemHtml::renderCachedImage()
271 mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
272 if ( mRenderedPage.isNull() )
276 mRenderedPage.fill( Qt::transparent );
278 painter.begin( &mRenderedPage );
279 mWebPage->mainFrame()->render( &painter );
283 QString QgsLayoutItemHtml::fetchHtml(
const QUrl &
url )
292 loop.exec( QEventLoop::ExcludeUserInputEvents );
295 mActualFetchedUrl = mFetcher->
reply()->url().toString();
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 ) );
318 double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
330 if ( c1.second < c2.second )
332 else if ( c1.second > c2.second )
334 else if ( c1.first > c2.first )
342 if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
348 int idealPos = yPos * htmlUnitsToLayoutUnits();
351 if ( idealPos >= mRenderedPage.height() )
356 int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
362 bool currentPixelTransparent =
false;
363 bool previousPixelTransparent =
false;
365 QList< QPair<int, int> > candidates;
366 int minRow = std::max( idealPos - maxSearchDistance, 0 );
367 for (
int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
370 currentColor = qRgba( 0, 0, 0, 0 );
372 for (
int col = 0; col < mRenderedPage.width(); ++col )
378 pixelColor = mRenderedPage.pixel( col, candidateRow );
379 currentPixelTransparent = qAlpha( pixelColor ) == 0;
380 if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
383 currentColor = pixelColor;
386 previousPixelTransparent = currentPixelTransparent;
388 candidates.append( qMakePair( candidateRow, changes ) );
392 std::sort( candidates.begin(), candidates.end(),
candidateSort );
399 int maxCandidateRow = candidates[0].first;
400 int minCandidateRow = maxCandidateRow + 1;
401 int minCandidateChanges = candidates[0].second;
403 QList< QPair<int, int> >::iterator it;
404 for ( it = candidates.begin(); it != candidates.end(); ++it )
406 if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
411 return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
413 minCandidateRow = ( *it ).first;
418 return candidates[0].first / htmlUnitsToLayoutUnits();
437 mUserStylesheet = stylesheet;
446 if ( mEnableUserStylesheet != stylesheetEnabled )
448 mEnableUserStylesheet = stylesheetEnabled;
456 return tr(
"<HTML frame>" );
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" );
476 if ( !contentModeOK )
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" );
488 QString urlString = itemElem.attribute( QStringLiteral(
"url" ) );
489 if ( !urlString.isEmpty() )
502 mExpressionFeature = feature;
503 mExpressionLayer = layer;
531 mAtlasFeatureJSON.clear();
535 void QgsLayoutItemHtml::refreshExpressionContext()
542 vl =
mLayout->reportContext().layer();
543 feature =
mLayout->reportContext().feature();
546 setExpressionContext( feature, vl );
564 void JavascriptExecutorLoop::done()
570 void JavascriptExecutorLoop::execIfNotDone()
573 exec( QEventLoop::ExcludeUserInputEvents );
577 for (
int i = 0; i < 100; i++ )
578 qApp->processEvents();
581 void JavascriptExecutorLoop::reportError(
const QString &error )
bool isValid() const
Returns the validity of this feature.
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.
void changed()
Emitted certain settings in the context is changed, e.g.
QIcon icon() const override
Returns the item's icon.
The QWebSettings class is a collection of stubs to mimic the API of a QWebSettings on systems where Q...
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...
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'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's render context.
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.
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.
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.
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.
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'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'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
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
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...