33 #include <QCoreApplication> 36 #include <QNetworkReply> 41 mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
44 if ( QThread::currentThread() == QApplication::instance()->thread() )
46 mWebPage = qgis::make_unique< QgsWebPage >();
55 mWebPage->setIdentifier( tr(
"Layout HTML item" ) );
56 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
57 mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
60 QPalette palette = mWebPage->palette();
61 palette.setBrush( QPalette::Base, Qt::transparent );
62 mWebPage->setPalette( palette );
69 setExpressionContext(
mLayout->reportContext().feature(),
mLayout->reportContext().layer() );
78 mFetcher->deleteLater();
135 switch ( mContentMode )
140 QString currentUrl = mUrl.toString();
147 currentUrl = currentUrl.trimmed();
148 QgsDebugMsg( QStringLiteral(
"exprVal Source Url:%1" ).arg( currentUrl ) );
150 if ( currentUrl.isEmpty() )
154 if ( !( useCache && currentUrl == mLastFetchedUrl ) )
156 loadedHtml = fetchHtml( QUrl( currentUrl ) );
157 mLastFetchedUrl = currentUrl;
161 loadedHtml = mFetchedHtml;
172 if ( mEvaluateExpressions )
180 connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop ] { loaded =
true; loop.quit(); } );
184 mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
188 QUrl( mActualFetchedUrl ) :
189 QUrl::fromLocalFile(
mLayout->project()->absoluteFilePath() );
191 mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
195 if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
198 ba.append( mUserStylesheet.toUtf8() );
199 QUrl cssFileURL = QUrl(
"data:text/css;charset=utf-8;base64," + ba.toBase64() );
200 settings->setUserStyleSheetUrl( cssFileURL );
204 settings->setUserStyleSheetUrl( QUrl() );
208 loop.exec( QEventLoop::ExcludeUserInputEvents );
211 if ( !mAtlasFeatureJSON.isEmpty() )
213 JavascriptExecutorLoop jsLoop;
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 ) );
218 jsLoop.execIfNotDone();
226 double QgsLayoutItemHtml::maxFrameWidth()
const 231 maxWidth = std::max( maxWidth, static_cast< double >(
frame->boundingRect().width() ) );
245 QSize contentsSize = mWebPage->mainFrame()->contentsSize();
248 double maxWidth = maxFrameWidth();
250 contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
252 mWebPage->setViewportSize( contentsSize );
253 mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
254 mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
255 if ( contentsSize.isValid() )
263 void QgsLayoutItemHtml::renderCachedImage()
269 mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
270 if ( mRenderedPage.isNull() )
274 mRenderedPage.fill( Qt::transparent );
276 painter.begin( &mRenderedPage );
277 mWebPage->mainFrame()->render( &painter );
281 QString QgsLayoutItemHtml::fetchHtml(
const QUrl &
url )
290 loop.exec( QEventLoop::ExcludeUserInputEvents );
293 mActualFetchedUrl = mFetcher->
reply()->url().toString();
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 ) );
316 double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
328 if ( c1.second < c2.second )
330 else if ( c1.second > c2.second )
332 else if ( c1.first > c2.first )
340 if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
346 int idealPos = yPos * htmlUnitsToLayoutUnits();
349 if ( idealPos >= mRenderedPage.height() )
354 int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
360 bool currentPixelTransparent =
false;
361 bool previousPixelTransparent =
false;
363 QList< QPair<int, int> > candidates;
364 int minRow = std::max( idealPos - maxSearchDistance, 0 );
365 for (
int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
368 currentColor = qRgba( 0, 0, 0, 0 );
370 for (
int col = 0; col < mRenderedPage.width(); ++col )
376 pixelColor = mRenderedPage.pixel( col, candidateRow );
377 currentPixelTransparent = qAlpha( pixelColor ) == 0;
378 if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
381 currentColor = pixelColor;
384 previousPixelTransparent = currentPixelTransparent;
386 candidates.append( qMakePair( candidateRow, changes ) );
390 std::sort( candidates.begin(), candidates.end(),
candidateSort );
397 int maxCandidateRow = candidates[0].first;
398 int minCandidateRow = maxCandidateRow + 1;
399 int minCandidateChanges = candidates[0].second;
401 QList< QPair<int, int> >::iterator it;
402 for ( it = candidates.begin(); it != candidates.end(); ++it )
404 if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
409 return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
411 minCandidateRow = ( *it ).first;
416 return candidates[0].first / htmlUnitsToLayoutUnits();
435 mUserStylesheet = stylesheet;
444 if ( mEnableUserStylesheet != stylesheetEnabled )
446 mEnableUserStylesheet = stylesheetEnabled;
454 return tr(
"<HTML frame>" );
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" );
474 if ( !contentModeOK )
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" );
486 QString urlString = itemElem.attribute( QStringLiteral(
"url" ) );
487 if ( !urlString.isEmpty() )
500 mExpressionFeature = feature;
501 mExpressionLayer = layer;
529 mAtlasFeatureJSON.clear();
533 void QgsLayoutItemHtml::refreshExpressionContext()
540 vl =
mLayout->reportContext().layer();
541 feature =
mLayout->reportContext().feature();
544 setExpressionContext( feature, vl );
562 void JavascriptExecutorLoop::done()
568 void JavascriptExecutorLoop::execIfNotDone()
571 exec( QEventLoop::ExcludeUserInputEvents );
575 for (
int i = 0; i < 100; i++ )
576 qApp->processEvents();
579 void JavascriptExecutorLoop::reportError(
const QString &error )
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' 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.
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...
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's icon.
The QWebSettings class is a collection of stubs to mimic the API of a QWebSettings on systems where Q...
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...
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'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'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.
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.
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.
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.
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'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'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
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...