17#include "moc_qgslayoutitemhtml.cpp" 
   37#include <QCoreApplication> 
   40#include <QNetworkReply> 
   43#include <QAbstractTextDocumentLayout> 
   50  mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
 
   53  if ( QThread::currentThread() == QApplication::instance()->thread() )
 
   55    mWebPage = std::make_unique< QgsWebPage >();
 
   64    mWebPage->setIdentifier( tr( 
"Layout HTML item" ) );
 
   65    mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
 
   66    mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
 
   69    QPalette palette = mWebPage->palette();
 
   70    palette.setBrush( QPalette::Base, Qt::transparent );
 
   71    mWebPage->setPalette( palette );
 
   78  setExpressionContext( 
mLayout->reportContext().feature(), 
mLayout->reportContext().layer() );
 
 
   87  mFetcher->deleteLater();
 
 
  109  frame->setVisible( label->isVisible() );
 
  112  frame->setRotation( label->rotation() );
 
  116  frame->setZValue( label->zValue() );
 
  126  html->setUserStylesheetEnabled( 
true );
 
  127  html->setUserStylesheet( label->createStylesheet() );
 
 
  171  switch ( mContentMode )
 
  176      QString currentUrl = mUrl.toString();
 
  183        currentUrl = currentUrl.trimmed();
 
  184        QgsDebugMsgLevel( QStringLiteral( 
"exprVal Source Url:%1" ).arg( currentUrl ), 2 );
 
  186      if ( currentUrl.isEmpty() )
 
  190      if ( !( useCache && currentUrl == mLastFetchedUrl ) )
 
  192        loadedHtml = fetchHtml( QUrl( currentUrl ) );
 
  193        mLastFetchedUrl = currentUrl;
 
  197        loadedHtml = mFetchedHtml;
 
  208  if ( mEvaluateExpressions )
 
  216  connect( mWebPage.get(), &QWebPage::loadFinished, &loop, [&loaded, &loop ] { loaded = true; loop.quit(); } );
 
  220  mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
 
  224                       QUrl( mActualFetchedUrl ) :
 
  225                       QUrl::fromLocalFile( 
mLayout->project()->absoluteFilePath() );
 
  227  mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
 
  231  if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
 
  234    ba.append( mUserStylesheet.toUtf8() );
 
  235    const QUrl cssFileURL = QUrl( QString( 
"data:text/css;charset=utf-8;base64," + ba.toBase64() ) );
 
  236    settings->setUserStyleSheetUrl( cssFileURL );
 
  240    settings->setUserStyleSheetUrl( QUrl() );
 
  244    loop.exec( QEventLoop::ExcludeUserInputEvents );
 
  248  if ( !mAtlasFeatureJSON.isEmpty() )
 
  250    JavascriptExecutorLoop jsLoop;
 
  252    mWebPage->mainFrame()->addToJavaScriptWindowObject( QStringLiteral( 
"loop" ), &jsLoop );
 
  253    mWebPage->mainFrame()->evaluateJavaScript( QStringLiteral( 
"if ( typeof setFeature === \"function\" ) { try{ setFeature(%1); } catch (err) { loop.reportError(err.message); } }; loop.done();" ).arg( mAtlasFeatureJSON ) );
 
  255    jsLoop.execIfNotDone();
 
 
  264double QgsLayoutItemHtml::maxFrameWidth()
 const 
  269    maxWidth = std::max( maxWidth, 
static_cast< double >( 
frame->boundingRect().width() ) );
 
  283  QSize contentsSize = mWebPage->mainFrame()->contentsSize();
 
  286  const double maxWidth = maxFrameWidth();
 
  288  contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
 
  290  mWebPage->setViewportSize( contentsSize );
 
  291  mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
 
  292  mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
 
  293  if ( contentsSize.isValid() )
 
 
  301void QgsLayoutItemHtml::renderCachedImage()
 
  307  mRenderedPage = QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
 
  308  if ( mRenderedPage.isNull() )
 
  312  mRenderedPage.fill( Qt::transparent );
 
  314  painter.begin( &mRenderedPage );
 
  315  mWebPage->mainFrame()->render( &painter );
 
  319QString QgsLayoutItemHtml::fetchHtml( 
const QUrl &url )
 
  328    loop.exec( QEventLoop::ExcludeUserInputEvents );
 
  331  mActualFetchedUrl = mFetcher->
reply()->url().toString();
 
  351  painter->translate( 0.0, -renderExtent.top() * mHtmlUnitsToLayoutUnits );
 
  352  mWebPage->mainFrame()->render( painter, QRegion( renderExtent.left(), renderExtent.top() * mHtmlUnitsToLayoutUnits, renderExtent.width() * mHtmlUnitsToLayoutUnits, renderExtent.height() * mHtmlUnitsToLayoutUnits ) );
 
  354  Q_UNUSED( renderExtent )
 
  355  if ( 
mLayout->renderContext().isPreviewRender() )
 
  368      painter->setBrush( QBrush( QColor( 255, 125, 125, 125 ) ) );
 
  369      painter->setPen( Qt::NoPen );
 
  370      painter->drawRect( painterRect );
 
  371      painter->setBrush( Qt::NoBrush );
 
  373      painter->setPen( QColor( 200, 0, 0, 255 ) );
 
  375      td.setTextWidth( painterRect.width() );
 
  376      td.setHtml( QStringLiteral( 
"<span style=\"color: rgb(200,0,0);\"><b>%1</b><br>%2</span>" ).arg(
 
  377                    tr( 
"WebKit not available!" ),
 
  378                    tr( 
"The item cannot be rendered because this QGIS install was built without WebKit support." ) ) );
 
  379      painter->setClipRect( painterRect );
 
  380      QAbstractTextDocumentLayout::PaintContext ctx;
 
  381      td.documentLayout()->draw( painter, ctx );
 
 
  387double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
 
  399  if ( c1.second < c2.second )
 
  401  else if ( c1.second > c2.second )
 
  403  else if ( c1.first > c2.first )
 
 
  411  if ( !mWebPage || mRenderedPage.isNull() || !mUseSmartBreaks )
 
  417  const int idealPos = yPos * htmlUnitsToLayoutUnits();
 
  420  if ( idealPos >= mRenderedPage.height() )
 
  425  const int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
 
  431  bool currentPixelTransparent = 
false;
 
  432  bool previousPixelTransparent = 
false;
 
  434  QList< QPair<int, int> > candidates;
 
  435  const int minRow = std::max( idealPos - maxSearchDistance, 0 );
 
  436  for ( 
int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
 
  439    currentColor = qRgba( 0, 0, 0, 0 );
 
  441    for ( 
int col = 0; col < mRenderedPage.width(); ++col )
 
  447      pixelColor = mRenderedPage.pixel( col, candidateRow );
 
  448      currentPixelTransparent = qAlpha( pixelColor ) == 0;
 
  449      if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
 
  452        currentColor = pixelColor;
 
  455      previousPixelTransparent = currentPixelTransparent;
 
  457    candidates.append( qMakePair( candidateRow, changes ) );
 
  461  std::sort( candidates.begin(), candidates.end(), 
candidateSort );
 
  468  const int maxCandidateRow = candidates[0].first;
 
  469  int minCandidateRow = maxCandidateRow + 1;
 
  470  const int minCandidateChanges = candidates[0].second;
 
  472  QList< QPair<int, int> >::iterator it;
 
  473  for ( it = candidates.begin(); it != candidates.end(); ++it )
 
  475    if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
 
  480      return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
 
  482    minCandidateRow = ( *it ).first;
 
  487  return candidates[0].first / htmlUnitsToLayoutUnits();
 
 
  506  mUserStylesheet = stylesheet;
 
 
  515  if ( mEnableUserStylesheet != stylesheetEnabled )
 
  517    mEnableUserStylesheet = stylesheetEnabled;
 
 
  525  return tr( 
"<HTML frame>" );
 
 
  530  htmlElem.setAttribute( QStringLiteral( 
"contentMode" ), QString::number( 
static_cast< int >( mContentMode ) ) );
 
  531  htmlElem.setAttribute( QStringLiteral( 
"url" ), mUrl.toString() );
 
  532  htmlElem.setAttribute( QStringLiteral( 
"html" ), mHtml );
 
  533  htmlElem.setAttribute( QStringLiteral( 
"evaluateExpressions" ), mEvaluateExpressions ? 
"true" : 
"false" );
 
  534  htmlElem.setAttribute( QStringLiteral( 
"useSmartBreaks" ), mUseSmartBreaks ? 
"true" : 
"false" );
 
  535  htmlElem.setAttribute( QStringLiteral( 
"maxBreakDistance" ), QString::number( mMaxBreakDistance ) );
 
  536  htmlElem.setAttribute( QStringLiteral( 
"stylesheet" ), mUserStylesheet );
 
  537  htmlElem.setAttribute( QStringLiteral( 
"stylesheetEnabled" ), mEnableUserStylesheet ? 
"true" : 
"false" );
 
 
  545  if ( !contentModeOK )
 
  549  mEvaluateExpressions = itemElem.attribute( QStringLiteral( 
"evaluateExpressions" ), QStringLiteral( 
"true" ) ) == QLatin1String( 
"true" );
 
  550  mUseSmartBreaks = itemElem.attribute( QStringLiteral( 
"useSmartBreaks" ), QStringLiteral( 
"true" ) ) == QLatin1String( 
"true" );
 
  551  mMaxBreakDistance = itemElem.attribute( QStringLiteral( 
"maxBreakDistance" ), QStringLiteral( 
"10" ) ).toDouble();
 
  552  mHtml = itemElem.attribute( QStringLiteral( 
"html" ) );
 
  553  mUserStylesheet = itemElem.attribute( QStringLiteral( 
"stylesheet" ) );
 
  554  mEnableUserStylesheet = itemElem.attribute( QStringLiteral( 
"stylesheetEnabled" ), QStringLiteral( 
"false" ) ) == QLatin1String( 
"true" );
 
  557  const QString urlString = itemElem.attribute( QStringLiteral( 
"url" ) );
 
  558  if ( !urlString.isEmpty() )
 
 
  571  mExpressionFeature = feature;
 
  572  mExpressionLayer = layer;
 
  595    exporter.setIncludeRelated( 
true );
 
  596    mAtlasFeatureJSON = exporter.exportFeature( feature );
 
  600    mAtlasFeatureJSON.clear();
 
  604void QgsLayoutItemHtml::refreshExpressionContext()
 
  611    vl = 
mLayout->reportContext().layer();
 
  612    feature = 
mLayout->reportContext().feature();
 
  615  setExpressionContext( feature, vl );
 
  633void JavascriptExecutorLoop::done()
 
  639void JavascriptExecutorLoop::execIfNotDone()
 
  642    exec( QEventLoop::ExcludeUserInputEvents );
 
  646  for ( 
int i = 0; i < 100; i++ )
 
  647    qApp->processEvents();
 
  650void JavascriptExecutorLoop::reportError( 
const QString &error )
 
A collection of stubs to mimic the API of a QWebSettings on systems where QtWebkit is not available.
 
@ 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...
 
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
 
~QgsLayoutItemHtml() 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.
 
Provides a method of storing measurements for use in QGIS layouts using a variety of different measur...
 
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
 
friend class QgsLayoutFrame
 
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.
 
@ SourceUrl
Html source url.
 
@ 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.
 
QgsCoordinateReferenceSystem crs
 
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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.
 
A container for the context for various read/write operations on 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 dataset.
 
bool candidateSort(QPair< int, int > c1, QPair< int, int > c2)
 
#define QgsDebugMsgLevel(str, level)