mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-18 00:03:05 -04:00
Begin port of HTML item
This commit is contained in:
parent
0e71505fe8
commit
f1d1e454d8
@ -409,6 +409,7 @@
|
||||
%Include layout/qgslayoutframe.sip
|
||||
%Include layout/qgslayoutitem.sip
|
||||
%Include layout/qgslayoutitemgroup.sip
|
||||
%Include layout/qgslayoutitemhtml.sip
|
||||
%Include layout/qgslayoutitemlabel.sip
|
||||
%Include layout/qgslayoutitemlegend.sip
|
||||
%Include layout/qgslayoutitemmap.sip
|
||||
|
227
python/core/layout/qgslayoutitemhtml.sip
Normal file
227
python/core/layout/qgslayoutitemhtml.sip
Normal file
@ -0,0 +1,227 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/layout/qgslayoutitemhtml.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsLayoutItemHtml: QgsLayoutMultiFrame
|
||||
{
|
||||
%Docstring
|
||||
A layout multiframe subclass for HTML content.
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslayoutitemhtml.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
enum ContentMode
|
||||
{
|
||||
Url,
|
||||
ManualHtml
|
||||
};
|
||||
|
||||
QgsLayoutItemHtml( QgsLayout *layout );
|
||||
%Docstring
|
||||
Constructor for QgsLayoutItemHtml, with the specified parent ``layout``.
|
||||
%End
|
||||
|
||||
~QgsLayoutItemHtml();
|
||||
|
||||
void setContentMode( ContentMode mode );
|
||||
%Docstring
|
||||
Sets the source ``mode`` for item's HTML content.
|
||||
.. seealso:: contentMode()
|
||||
.. seealso:: setUrl()
|
||||
.. seealso:: setHtml()
|
||||
%End
|
||||
|
||||
ContentMode contentMode() const;
|
||||
%Docstring
|
||||
Returns the source mode for item's HTML content.
|
||||
.. seealso:: setContentMode()
|
||||
.. seealso:: url()
|
||||
.. seealso:: html()
|
||||
:rtype: ContentMode
|
||||
%End
|
||||
|
||||
void setUrl( const QUrl &url );
|
||||
%Docstring
|
||||
Sets the ``url`` for content to display in the item when the item is using
|
||||
the QgsLayoutItemHtml.Url mode. Content is automatically fetched and the
|
||||
HTML item refreshed after calling this function.
|
||||
.. seealso:: url()
|
||||
.. seealso:: contentMode()
|
||||
%End
|
||||
|
||||
QUrl url() const;
|
||||
%Docstring
|
||||
Returns the URL of the content displayed in the item if the item is using
|
||||
the QgsLayoutItemHtml.Url mode.
|
||||
.. seealso:: setUrl()
|
||||
.. seealso:: contentMode()
|
||||
:rtype: QUrl
|
||||
%End
|
||||
|
||||
void setHtml( const QString &html );
|
||||
%Docstring
|
||||
Sets the ``html`` to display in the item when the item is using
|
||||
the QgsLayoutItemHtml.ManualHtml mode. Setting the HTML using this function
|
||||
does not automatically refresh the item's contents. Call loadHtml to trigger
|
||||
a refresh of the item after setting the HTML content.
|
||||
.. seealso:: html()
|
||||
.. seealso:: contentMode()
|
||||
.. seealso:: loadHtml()
|
||||
%End
|
||||
|
||||
QString html() const;
|
||||
%Docstring
|
||||
Returns the HTML source displayed in the item if the item is using
|
||||
the QgsLayoutItemHtml.ManualHtml mode.
|
||||
.. seealso:: setHtml()
|
||||
.. seealso:: contentMode()
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
bool evaluateExpressions() const;
|
||||
%Docstring
|
||||
Returns whether html item will evaluate QGIS expressions prior to rendering
|
||||
the HTML content. If set, any content inside [% %] tags will be
|
||||
treated as a QGIS expression and evaluated against the current atlas
|
||||
feature.
|
||||
.. seealso:: setEvaluateExpressions()
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
void setEvaluateExpressions( bool evaluateExpressions );
|
||||
%Docstring
|
||||
Sets whether the html item will evaluate QGIS expressions prior to rendering
|
||||
the HTML content. If set, any content inside [% %] tags will be
|
||||
treated as a QGIS expression and evaluated against the current atlas
|
||||
feature.
|
||||
.. seealso:: evaluateExpressions()
|
||||
%End
|
||||
|
||||
bool useSmartBreaks() const;
|
||||
%Docstring
|
||||
Returns whether html item is using smart breaks. Smart breaks prevent
|
||||
the html frame contents from breaking mid-way though a line of text.
|
||||
.. seealso:: setUseSmartBreaks()
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
void setUseSmartBreaks( bool useSmartBreaks );
|
||||
%Docstring
|
||||
Sets whether the html item should use smart breaks. Smart breaks prevent
|
||||
the html frame contents from breaking mid-way though a line of text.
|
||||
.. seealso:: useSmartBreaks()
|
||||
%End
|
||||
|
||||
void setMaxBreakDistance( double distance );
|
||||
%Docstring
|
||||
Sets the maximum ``distance`` allowed when calculating where to place page breaks
|
||||
in the html. This distance is the maximum amount of empty space allowed
|
||||
at the bottom of a frame after calculating the optimum break location. Setting
|
||||
a larger value will result in better choice of page break location, but more
|
||||
wasted space at the bottom of frames. This setting is only effective if
|
||||
useSmartBreaks is true.
|
||||
.. seealso:: maxBreakDistance()
|
||||
.. seealso:: setUseSmartBreaks()
|
||||
%End
|
||||
|
||||
double maxBreakDistance() const;
|
||||
%Docstring
|
||||
Returns the maximum distance allowed when calculating where to place page breaks
|
||||
in the html. This distance is the maximum amount of empty space allowed
|
||||
at the bottom of a frame after calculating the optimum break location. This setting
|
||||
is only effective if useSmartBreaks is true.
|
||||
.. seealso:: setMaxBreakDistance()
|
||||
.. seealso:: useSmartBreaks()
|
||||
:rtype: float
|
||||
%End
|
||||
|
||||
void setUserStylesheet( const QString &stylesheet );
|
||||
%Docstring
|
||||
Sets the user ``stylesheet`` CSS rules to use while rendering the HTML content. These
|
||||
allow for overriding the styles specified within the HTML source. Setting the stylesheet
|
||||
using this function does not automatically refresh the item's contents. Call loadHtml
|
||||
to trigger a refresh of the item after setting the stylesheet rules.
|
||||
.. seealso:: userStylesheet()
|
||||
.. seealso:: setUserStylesheetEnabled()
|
||||
.. seealso:: loadHtml()
|
||||
%End
|
||||
|
||||
QString userStylesheet() const;
|
||||
%Docstring
|
||||
Returns the user stylesheet CSS rules used while rendering the HTML content. These
|
||||
overriding the styles specified within the HTML source.
|
||||
.. seealso:: setUserStylesheet()
|
||||
.. seealso:: userStylesheetEnabled()
|
||||
:rtype: str
|
||||
%End
|
||||
|
||||
void setUserStylesheetEnabled( const bool enabled );
|
||||
%Docstring
|
||||
Sets whether user stylesheets are ``enabled`` for the HTML content.
|
||||
.. seealso:: userStylesheetEnabled()
|
||||
.. seealso:: setUserStylesheet()
|
||||
%End
|
||||
|
||||
bool userStylesheetEnabled() const;
|
||||
%Docstring
|
||||
Returns whether user stylesheets are enabled for the HTML content.
|
||||
.. seealso:: setUserStylesheetEnabled()
|
||||
.. seealso:: userStylesheet()
|
||||
:rtype: bool
|
||||
%End
|
||||
|
||||
virtual QString displayName() const;
|
||||
|
||||
virtual QSizeF totalSize() const;
|
||||
|
||||
virtual void render( QgsRenderContext &context, const QRectF &renderExtent, const int frameIndex,
|
||||
const QStyleOptionGraphicsItem *itemStyle = 0 );
|
||||
|
||||
virtual bool writeXml( QDomElement &elem, QDomDocument &doc, bool ignoreFrames = false ) const;
|
||||
|
||||
virtual bool readXml( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames = false );
|
||||
|
||||
virtual double findNearbyPageBreak( double yPos );
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
void loadHtml( const bool useCache = false, const QgsExpressionContext *context = 0 );
|
||||
%Docstring
|
||||
Reloads the html source from the url and redraws the item.
|
||||
\param useCache set to true to use a cached copy of remote html
|
||||
content
|
||||
\param context expression context for evaluating data defined urls and expressions in html
|
||||
.. seealso:: setUrl
|
||||
.. seealso:: url
|
||||
%End
|
||||
|
||||
virtual void recalculateFrameSizes();
|
||||
|
||||
%Docstring
|
||||
Recalculates the frame sizes for the current viewport dimensions
|
||||
%End
|
||||
void refreshExpressionContext();
|
||||
|
||||
void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/layout/qgslayoutitemhtml.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -373,6 +373,7 @@ SET(QGIS_CORE_SRCS
|
||||
layout/qgslayoutitem.cpp
|
||||
layout/qgslayoutitemgroup.cpp
|
||||
layout/qgslayoutitemgroupundocommand.cpp
|
||||
layout/qgslayoutitemhtml.cpp
|
||||
layout/qgslayoutitemlabel.cpp
|
||||
layout/qgslayoutitemlegend.cpp
|
||||
layout/qgslayoutitemmap.cpp
|
||||
@ -735,6 +736,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
layout/qgslayoutitem.h
|
||||
layout/qgslayoutitemgroup.h
|
||||
layout/qgslayoutitemgroupundocommand.h
|
||||
layout/qgslayoutitemhtml.h
|
||||
layout/qgslayoutitemlabel.h
|
||||
layout/qgslayoutitemlegend.h
|
||||
layout/qgslayoutitemmap.h
|
||||
|
@ -336,18 +336,6 @@ void QgsComposerHtml::addFrame( QgsComposerFrame *frame, bool recalcFrameSizes )
|
||||
}
|
||||
}
|
||||
|
||||
bool candidateSort( QPair<int, int> c1, QPair<int, int> c2 )
|
||||
{
|
||||
if ( c1.second < c2.second )
|
||||
return true;
|
||||
else if ( c1.second > c2.second )
|
||||
return false;
|
||||
else if ( c1.first > c2.first )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
double QgsComposerHtml::findNearbyPageBreak( double yPos )
|
||||
{
|
||||
if ( !mWebPage || !mRenderedPage || !mUseSmartBreaks )
|
||||
@ -400,7 +388,18 @@ double QgsComposerHtml::findNearbyPageBreak( double yPos )
|
||||
}
|
||||
|
||||
//sort candidate rows by number of changes ascending, row number descending
|
||||
std::sort( candidates.begin(), candidates.end(), candidateSort );
|
||||
std::sort( candidates.begin(), candidates.end(),
|
||||
[]( QPair<int, int> c1, QPair<int, int> c2 )->bool
|
||||
{
|
||||
if ( c1.second < c2.second )
|
||||
return true;
|
||||
else if ( c1.second > c2.second )
|
||||
return false;
|
||||
else if ( c1.first > c2.first )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
} );
|
||||
//first candidate is now the largest row with smallest number of changes
|
||||
|
||||
//OK, now take the mid point of the best candidate position
|
||||
|
567
src/core/layout/qgslayoutitemhtml.cpp
Normal file
567
src/core/layout/qgslayoutitemhtml.cpp
Normal file
@ -0,0 +1,567 @@
|
||||
/***************************************************************************
|
||||
qgslayoutitemhtml.cpp
|
||||
------------------------------------------------------------
|
||||
begin : October 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslayoutitemhtml.h"
|
||||
#include "qgslayoutframe.h"
|
||||
#include "qgslayout.h"
|
||||
#include "qgsnetworkaccessmanager.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsnetworkcontentfetcher.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsdistancearea.h"
|
||||
#include "qgsjsonutils.h"
|
||||
#include "qgsmapsettings.h"
|
||||
#include "qgswebpage.h"
|
||||
#include "qgswebframe.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
#include <QNetworkReply>
|
||||
|
||||
QgsLayoutItemHtml::QgsLayoutItemHtml( QgsLayout *layout )
|
||||
: QgsLayoutMultiFrame( layout )
|
||||
, mContentMode( QgsLayoutItemHtml::Url )
|
||||
, mLoaded( false )
|
||||
, mHtmlUnitsToLayoutUnits( 1.0 )
|
||||
, mEvaluateExpressions( true )
|
||||
, mUseSmartBreaks( true )
|
||||
, mMaxBreakDistance( 10 )
|
||||
, mEnableUserStylesheet( false )
|
||||
{
|
||||
mDistanceArea = new QgsDistanceArea();
|
||||
mHtmlUnitsToLayoutUnits = htmlUnitsToLayoutUnits();
|
||||
mWebPage = new QgsWebPage();
|
||||
mWebPage->setIdentifier( tr( "Layout HTML item" ) );
|
||||
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
|
||||
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
|
||||
|
||||
//This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
|
||||
QPalette palette = mWebPage->palette();
|
||||
palette.setBrush( QPalette::Base, Qt::transparent );
|
||||
mWebPage->setPalette( palette );
|
||||
|
||||
mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
|
||||
connect( mWebPage, &QWebPage::loadFinished, this, &QgsLayoutItemHtml::frameLoaded );
|
||||
|
||||
#if 0 //TODO
|
||||
if ( mLayout )
|
||||
{
|
||||
connect( mLayout, &QgsComposition::itemRemoved, this, &QgsComposerMultiFrame::handleFrameRemoval );
|
||||
}
|
||||
|
||||
if ( mComposition && mComposition->atlasMode() == QgsComposition::PreviewAtlas )
|
||||
{
|
||||
//a html item added while atlas preview is enabled needs to have the expression context set,
|
||||
//otherwise fields in the html aren't correctly evaluated until atlas preview feature changes (#9457)
|
||||
setExpressionContext( mComposition->atlasComposition().feature(), mComposition->atlasComposition().coverageLayer() );
|
||||
}
|
||||
|
||||
//connect to atlas feature changes
|
||||
//to update the expression context
|
||||
connect( &mComposition->atlasComposition(), &QgsAtlasComposition::featureChanged, this, &QgsLayoutItemHtml::refreshExpressionContext );
|
||||
#endif
|
||||
|
||||
mFetcher = new QgsNetworkContentFetcher();
|
||||
connect( mFetcher, &QgsNetworkContentFetcher::finished, this, [ = ] { frameLoaded(); } );
|
||||
|
||||
}
|
||||
|
||||
QgsLayoutItemHtml::~QgsLayoutItemHtml()
|
||||
{
|
||||
delete mDistanceArea;
|
||||
delete mWebPage;
|
||||
delete mRenderedPage;
|
||||
mFetcher->deleteLater();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setUrl( const QUrl &url )
|
||||
{
|
||||
if ( !mWebPage )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mUrl = url;
|
||||
loadHtml( true );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setHtml( const QString &html )
|
||||
{
|
||||
mHtml = html;
|
||||
//TODO - this signal should be emitted, but without changing the signal which sets the html
|
||||
//to an equivalent of editingFinished it causes a lot of problems. Need to investigate
|
||||
//ways of doing this using QScintilla widgets.
|
||||
//emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setEvaluateExpressions( bool evaluateExpressions )
|
||||
{
|
||||
mEvaluateExpressions = evaluateExpressions;
|
||||
loadHtml( true );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::loadHtml( const bool useCache, const QgsExpressionContext *context )
|
||||
{
|
||||
if ( !mWebPage )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QgsExpressionContext scopedContext = createExpressionContext();
|
||||
const QgsExpressionContext *evalContext = context ? context : &scopedContext;
|
||||
|
||||
QString loadedHtml;
|
||||
switch ( mContentMode )
|
||||
{
|
||||
case QgsLayoutItemHtml::Url:
|
||||
{
|
||||
|
||||
QString currentUrl = mUrl.toString();
|
||||
|
||||
//data defined url set?
|
||||
bool ok = false;
|
||||
currentUrl = mDataDefinedProperties.valueAsString( QgsLayoutObject::SourceUrl, *evalContext, currentUrl, &ok );
|
||||
if ( ok )
|
||||
{
|
||||
currentUrl = currentUrl.trimmed();
|
||||
QgsDebugMsg( QString( "exprVal Source Url:%1" ).arg( currentUrl ) );
|
||||
}
|
||||
if ( currentUrl.isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( !( useCache && currentUrl == mLastFetchedUrl ) )
|
||||
{
|
||||
loadedHtml = fetchHtml( QUrl( currentUrl ) );
|
||||
mLastFetchedUrl = currentUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadedHtml = mFetchedHtml;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case QgsLayoutItemHtml::ManualHtml:
|
||||
loadedHtml = mHtml;
|
||||
break;
|
||||
}
|
||||
|
||||
//evaluate expressions
|
||||
if ( mEvaluateExpressions )
|
||||
{
|
||||
loadedHtml = QgsExpression::replaceExpressionText( loadedHtml, evalContext, mDistanceArea );
|
||||
}
|
||||
|
||||
mLoaded = false;
|
||||
|
||||
//reset page size. otherwise viewport size increases but never decreases again
|
||||
mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToLayoutUnits, 0 ) );
|
||||
|
||||
//set html, using the specified url as base if in Url mode or the project file if in manual mode
|
||||
const QUrl baseUrl = mContentMode == QgsLayoutItemHtml::Url ?
|
||||
QUrl( mActualFetchedUrl ) :
|
||||
QUrl::fromLocalFile( mLayout->project()->fileInfo().absoluteFilePath() );
|
||||
mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
|
||||
|
||||
//set user stylesheet
|
||||
QWebSettings *settings = mWebPage->settings();
|
||||
if ( mEnableUserStylesheet && ! mUserStylesheet.isEmpty() )
|
||||
{
|
||||
QByteArray ba;
|
||||
ba.append( mUserStylesheet.toUtf8() );
|
||||
QUrl cssFileURL = QUrl( "data:text/css;charset=utf-8;base64," + ba.toBase64() );
|
||||
settings->setUserStyleSheetUrl( cssFileURL );
|
||||
}
|
||||
else
|
||||
{
|
||||
settings->setUserStyleSheetUrl( QUrl() );
|
||||
}
|
||||
|
||||
while ( !mLoaded )
|
||||
{
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
//inject JSON feature
|
||||
if ( !mAtlasFeatureJSON.isEmpty() )
|
||||
{
|
||||
mWebPage->mainFrame()->evaluateJavaScript( QStringLiteral( "if ( typeof setFeature === \"function\" ) { setFeature(%1); }" ).arg( mAtlasFeatureJSON ) );
|
||||
//needs an extra process events here to give JavaScript a chance to execute
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
recalculateFrameSizes();
|
||||
//trigger a repaint
|
||||
emit contentsChanged();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::frameLoaded( bool ok )
|
||||
{
|
||||
Q_UNUSED( ok );
|
||||
mLoaded = true;
|
||||
}
|
||||
|
||||
double QgsLayoutItemHtml::maxFrameWidth() const
|
||||
{
|
||||
double maxWidth = 0;
|
||||
for ( QgsLayoutFrame *frame : mFrameItems )
|
||||
{
|
||||
maxWidth = std::max( maxWidth, static_cast< double >( frame->boundingRect().width() ) );
|
||||
}
|
||||
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::recalculateFrameSizes()
|
||||
{
|
||||
if ( frameCount() < 1 ) return;
|
||||
|
||||
QSize contentsSize = mWebPage->mainFrame()->contentsSize();
|
||||
|
||||
//find maximum frame width
|
||||
double maxWidth = maxFrameWidth();
|
||||
//set content width to match maximum frame width
|
||||
contentsSize.setWidth( maxWidth * mHtmlUnitsToLayoutUnits );
|
||||
|
||||
mWebPage->setViewportSize( contentsSize );
|
||||
mSize.setWidth( contentsSize.width() / mHtmlUnitsToLayoutUnits );
|
||||
mSize.setHeight( contentsSize.height() / mHtmlUnitsToLayoutUnits );
|
||||
if ( contentsSize.isValid() )
|
||||
{
|
||||
renderCachedImage();
|
||||
}
|
||||
QgsLayoutMultiFrame::recalculateFrameSizes();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::renderCachedImage()
|
||||
{
|
||||
//render page to cache image
|
||||
if ( mRenderedPage )
|
||||
{
|
||||
delete mRenderedPage;
|
||||
}
|
||||
mRenderedPage = new QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
|
||||
if ( mRenderedPage->isNull() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mRenderedPage->fill( Qt::transparent );
|
||||
QPainter painter;
|
||||
painter.begin( mRenderedPage );
|
||||
mWebPage->mainFrame()->render( &painter );
|
||||
painter.end();
|
||||
}
|
||||
|
||||
QString QgsLayoutItemHtml::fetchHtml( const QUrl &url )
|
||||
{
|
||||
//pause until HTML fetch
|
||||
mLoaded = false;
|
||||
mFetcher->fetchContent( url );
|
||||
|
||||
while ( !mLoaded )
|
||||
{
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
mFetchedHtml = mFetcher->contentAsString();
|
||||
mActualFetchedUrl = mFetcher->reply()->url().toString();
|
||||
return mFetchedHtml;
|
||||
}
|
||||
|
||||
QSizeF QgsLayoutItemHtml::totalSize() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::render( QgsRenderContext &context, const QRectF &renderExtent, const int,
|
||||
const QStyleOptionGraphicsItem * )
|
||||
{
|
||||
if ( !mWebPage )
|
||||
return;
|
||||
|
||||
QPainter *painter = context.painter();
|
||||
painter->save();
|
||||
// painter is scaled to dots, so scale back to layout units
|
||||
painter->scale( context.scaleFactor() / mHtmlUnitsToLayoutUnits, context.scaleFactor() / mHtmlUnitsToLayoutUnits );
|
||||
painter->translate( 0.0, -renderExtent.top() * mHtmlUnitsToLayoutUnits );
|
||||
mWebPage->mainFrame()->render( painter, QRegion( renderExtent.left(), renderExtent.top() * mHtmlUnitsToLayoutUnits, renderExtent.width() * mHtmlUnitsToLayoutUnits, renderExtent.height() * mHtmlUnitsToLayoutUnits ) );
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
double QgsLayoutItemHtml::htmlUnitsToLayoutUnits()
|
||||
{
|
||||
if ( !mLayout )
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
return mLayout->convertToLayoutUnits( QgsLayoutMeasurement( mLayout->context().dpi() / 72.0, QgsUnitTypes::LayoutMillimeters ) ); //webkit seems to assume a standard dpi of 96
|
||||
}
|
||||
|
||||
bool candidateSort( QPair<int, int> c1, QPair<int, int> c2 )
|
||||
{
|
||||
if ( c1.second < c2.second )
|
||||
return true;
|
||||
else if ( c1.second > c2.second )
|
||||
return false;
|
||||
else if ( c1.first > c2.first )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
double QgsLayoutItemHtml::findNearbyPageBreak( double yPos )
|
||||
{
|
||||
if ( !mWebPage || !mRenderedPage || !mUseSmartBreaks )
|
||||
{
|
||||
return yPos;
|
||||
}
|
||||
|
||||
//convert yPos to pixels
|
||||
int idealPos = yPos * htmlUnitsToLayoutUnits();
|
||||
|
||||
//if ideal break pos is past end of page, there's nothing we need to do
|
||||
if ( idealPos >= mRenderedPage->height() )
|
||||
{
|
||||
return yPos;
|
||||
}
|
||||
|
||||
int maxSearchDistance = mMaxBreakDistance * htmlUnitsToLayoutUnits();
|
||||
|
||||
//loop through all lines just before ideal break location, up to max distance
|
||||
//of maxSearchDistance
|
||||
int changes = 0;
|
||||
QRgb currentColor;
|
||||
bool currentPixelTransparent = false;
|
||||
bool previousPixelTransparent = false;
|
||||
QRgb pixelColor;
|
||||
QList< QPair<int, int> > candidates;
|
||||
int minRow = std::max( idealPos - maxSearchDistance, 0 );
|
||||
for ( int candidateRow = idealPos; candidateRow >= minRow; --candidateRow )
|
||||
{
|
||||
changes = 0;
|
||||
currentColor = qRgba( 0, 0, 0, 0 );
|
||||
//check all pixels in this line
|
||||
for ( int col = 0; col < mRenderedPage->width(); ++col )
|
||||
{
|
||||
//count how many times the pixels change color in this row
|
||||
//eventually, we select a row to break at with the minimum number of color changes
|
||||
//since this is likely a line break, or gap between table cells, etc
|
||||
//but very unlikely to be midway through a text line or picture
|
||||
pixelColor = mRenderedPage->pixel( col, candidateRow );
|
||||
currentPixelTransparent = qAlpha( pixelColor ) == 0;
|
||||
if ( pixelColor != currentColor && !( currentPixelTransparent && previousPixelTransparent ) )
|
||||
{
|
||||
//color has changed
|
||||
currentColor = pixelColor;
|
||||
changes++;
|
||||
}
|
||||
previousPixelTransparent = currentPixelTransparent;
|
||||
}
|
||||
candidates.append( qMakePair( candidateRow, changes ) );
|
||||
}
|
||||
|
||||
//sort candidate rows by number of changes ascending, row number descending
|
||||
std::sort( candidates.begin(), candidates.end(), candidateSort );
|
||||
//first candidate is now the largest row with smallest number of changes
|
||||
|
||||
//OK, now take the mid point of the best candidate position
|
||||
//we do this so that the spacing between text lines is likely to be split in half
|
||||
//otherwise the html will be broken immediately above a line of text, which
|
||||
//looks a little messy
|
||||
int maxCandidateRow = candidates[0].first;
|
||||
int minCandidateRow = maxCandidateRow + 1;
|
||||
int minCandidateChanges = candidates[0].second;
|
||||
|
||||
QList< QPair<int, int> >::iterator it;
|
||||
for ( it = candidates.begin(); it != candidates.end(); ++it )
|
||||
{
|
||||
if ( ( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
|
||||
{
|
||||
//no longer in a consecutive block of rows of minimum pixel color changes
|
||||
//so return the row mid-way through the block
|
||||
//first converting back to mm
|
||||
return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToLayoutUnits();
|
||||
}
|
||||
minCandidateRow = ( *it ).first;
|
||||
}
|
||||
|
||||
//above loop didn't work for some reason
|
||||
//return first candidate converted to mm
|
||||
return candidates[0].first / htmlUnitsToLayoutUnits();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setUseSmartBreaks( bool useSmartBreaks )
|
||||
{
|
||||
mUseSmartBreaks = useSmartBreaks;
|
||||
recalculateFrameSizes();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setMaxBreakDistance( double maxBreakDistance )
|
||||
{
|
||||
mMaxBreakDistance = maxBreakDistance;
|
||||
recalculateFrameSizes();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setUserStylesheet( const QString &stylesheet )
|
||||
{
|
||||
mUserStylesheet = stylesheet;
|
||||
//TODO - this signal should be emitted, but without changing the signal which sets the css
|
||||
//to an equivalent of editingFinished it causes a lot of problems. Need to investigate
|
||||
//ways of doing this using QScintilla widgets.
|
||||
//emit changed();
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setUserStylesheetEnabled( const bool stylesheetEnabled )
|
||||
{
|
||||
if ( mEnableUserStylesheet != stylesheetEnabled )
|
||||
{
|
||||
mEnableUserStylesheet = stylesheetEnabled;
|
||||
loadHtml( true );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsLayoutItemHtml::displayName() const
|
||||
{
|
||||
return tr( "<HTML frame>" );
|
||||
}
|
||||
|
||||
bool QgsLayoutItemHtml::writeXml( QDomElement &elem, QDomDocument &doc, bool ignoreFrames ) const
|
||||
{
|
||||
QDomElement htmlElem = doc.createElement( QStringLiteral( "ComposerHtml" ) );
|
||||
htmlElem.setAttribute( QStringLiteral( "contentMode" ), QString::number( static_cast< int >( mContentMode ) ) );
|
||||
htmlElem.setAttribute( QStringLiteral( "url" ), mUrl.toString() );
|
||||
htmlElem.setAttribute( QStringLiteral( "html" ), mHtml );
|
||||
htmlElem.setAttribute( QStringLiteral( "evaluateExpressions" ), mEvaluateExpressions ? "true" : "false" );
|
||||
htmlElem.setAttribute( QStringLiteral( "useSmartBreaks" ), mUseSmartBreaks ? "true" : "false" );
|
||||
htmlElem.setAttribute( QStringLiteral( "maxBreakDistance" ), QString::number( mMaxBreakDistance ) );
|
||||
htmlElem.setAttribute( QStringLiteral( "stylesheet" ), mUserStylesheet );
|
||||
htmlElem.setAttribute( QStringLiteral( "stylesheetEnabled" ), mEnableUserStylesheet ? "true" : "false" );
|
||||
|
||||
bool state = _writeXml( htmlElem, doc, ignoreFrames );
|
||||
elem.appendChild( htmlElem );
|
||||
return state;
|
||||
}
|
||||
|
||||
bool QgsLayoutItemHtml::readXml( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
|
||||
{
|
||||
if ( !ignoreFrames )
|
||||
{
|
||||
deleteFrames();
|
||||
}
|
||||
|
||||
//first create the frames
|
||||
if ( !_readXml( itemElem, doc, ignoreFrames ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool contentModeOK;
|
||||
mContentMode = static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( QStringLiteral( "contentMode" ) ).toInt( &contentModeOK ) );
|
||||
if ( !contentModeOK )
|
||||
{
|
||||
mContentMode = QgsLayoutItemHtml::Url;
|
||||
}
|
||||
mEvaluateExpressions = itemElem.attribute( QStringLiteral( "evaluateExpressions" ), QStringLiteral( "true" ) ) == QLatin1String( "true" );
|
||||
mUseSmartBreaks = itemElem.attribute( QStringLiteral( "useSmartBreaks" ), QStringLiteral( "true" ) ) == QLatin1String( "true" );
|
||||
mMaxBreakDistance = itemElem.attribute( QStringLiteral( "maxBreakDistance" ), QStringLiteral( "10" ) ).toDouble();
|
||||
mHtml = itemElem.attribute( QStringLiteral( "html" ) );
|
||||
mUserStylesheet = itemElem.attribute( QStringLiteral( "stylesheet" ) );
|
||||
mEnableUserStylesheet = itemElem.attribute( QStringLiteral( "stylesheetEnabled" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
|
||||
|
||||
//finally load the set url
|
||||
QString urlString = itemElem.attribute( QStringLiteral( "url" ) );
|
||||
if ( !urlString.isEmpty() )
|
||||
{
|
||||
mUrl = urlString;
|
||||
}
|
||||
loadHtml( true );
|
||||
|
||||
//since frames had to be created before, we need to emit a changed signal to refresh the widget
|
||||
emit changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::setExpressionContext( const QgsFeature &feature, QgsVectorLayer *layer )
|
||||
{
|
||||
mExpressionFeature = feature;
|
||||
mExpressionLayer = layer;
|
||||
|
||||
//setup distance area conversion
|
||||
if ( layer )
|
||||
{
|
||||
mDistanceArea->setSourceCrs( layer->crs() );
|
||||
}
|
||||
else if ( mLayout )
|
||||
{
|
||||
#if 0 //TODO
|
||||
//set to composition's mapsettings' crs
|
||||
QgsComposerMap *referenceMap = mComposition->referenceMap();
|
||||
if ( referenceMap )
|
||||
mDistanceArea->setSourceCrs( referenceMap->crs() );
|
||||
#endif
|
||||
}
|
||||
if ( mLayout )
|
||||
{
|
||||
mDistanceArea->setEllipsoid( mLayout->project()->ellipsoid() );
|
||||
}
|
||||
|
||||
// create JSON representation of feature
|
||||
QgsJsonExporter exporter( layer );
|
||||
exporter.setIncludeRelated( true );
|
||||
mAtlasFeatureJSON = exporter.exportFeature( feature );
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::refreshExpressionContext()
|
||||
{
|
||||
QgsVectorLayer *vl = nullptr;
|
||||
QgsFeature feature;
|
||||
|
||||
#if 0 //TODO
|
||||
if ( mComposition->atlasComposition().enabled() )
|
||||
{
|
||||
vl = mComposition->atlasComposition().coverageLayer();
|
||||
}
|
||||
if ( mComposition->atlasMode() != QgsComposition::AtlasOff )
|
||||
{
|
||||
feature = mComposition->atlasComposition().feature();
|
||||
}
|
||||
#endif
|
||||
|
||||
setExpressionContext( feature, vl );
|
||||
loadHtml( true );
|
||||
}
|
||||
|
||||
void QgsLayoutItemHtml::refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property )
|
||||
{
|
||||
QgsExpressionContext context = createExpressionContext();
|
||||
|
||||
//updates data defined properties and redraws item to match
|
||||
if ( property == QgsLayoutObject::SourceUrl || property == QgsLayoutObject::AllProperties )
|
||||
{
|
||||
loadHtml( true, &context );
|
||||
}
|
||||
}
|
269
src/core/layout/qgslayoutitemhtml.h
Normal file
269
src/core/layout/qgslayoutitemhtml.h
Normal file
@ -0,0 +1,269 @@
|
||||
/***************************************************************************
|
||||
qgslayoutitemhtml.h
|
||||
------------------------------------------------------------
|
||||
begin : October 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSLAYOUTITEMHTML_H
|
||||
#define QGSLAYOUTITEMHTML_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis.h"
|
||||
#include "qgslayoutmultiframe.h"
|
||||
#include "qgsfeature.h"
|
||||
#include <QUrl>
|
||||
|
||||
class QgsWebPage;
|
||||
class QImage;
|
||||
class QgsVectorLayer;
|
||||
class QgsNetworkContentFetcher;
|
||||
class QgsDistanceArea;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* A layout multiframe subclass for HTML content.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
class CORE_EXPORT QgsLayoutItemHtml: public QgsLayoutMultiFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
//! Source modes for the HTML content to render in the item
|
||||
enum ContentMode
|
||||
{
|
||||
Url, //!< Using this mode item fetches its content via a url
|
||||
ManualHtml //!< HTML content is manually set for the item
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for QgsLayoutItemHtml, with the specified parent \a layout.
|
||||
*/
|
||||
QgsLayoutItemHtml( QgsLayout *layout );
|
||||
|
||||
~QgsLayoutItemHtml();
|
||||
|
||||
/**
|
||||
* Sets the source \a mode for item's HTML content.
|
||||
* \see contentMode()
|
||||
* \see setUrl()
|
||||
* \see setHtml()
|
||||
*/
|
||||
void setContentMode( ContentMode mode ) { mContentMode = mode; }
|
||||
|
||||
/**
|
||||
* Returns the source mode for item's HTML content.
|
||||
* \see setContentMode()
|
||||
* \see url()
|
||||
* \see html()
|
||||
*/
|
||||
ContentMode contentMode() const { return mContentMode; }
|
||||
|
||||
/**
|
||||
* Sets the \a url for content to display in the item when the item is using
|
||||
* the QgsLayoutItemHtml::Url mode. Content is automatically fetched and the
|
||||
* HTML item refreshed after calling this function.
|
||||
* \see url()
|
||||
* \see contentMode()
|
||||
*/
|
||||
void setUrl( const QUrl &url );
|
||||
|
||||
/**
|
||||
* Returns the URL of the content displayed in the item if the item is using
|
||||
* the QgsLayoutItemHtml::Url mode.
|
||||
* \see setUrl()
|
||||
* \see contentMode()
|
||||
*/
|
||||
QUrl url() const { return mUrl; }
|
||||
|
||||
/**
|
||||
* Sets the \a html to display in the item when the item is using
|
||||
* the QgsLayoutItemHtml::ManualHtml mode. Setting the HTML using this function
|
||||
* does not automatically refresh the item's contents. Call loadHtml to trigger
|
||||
* a refresh of the item after setting the HTML content.
|
||||
* \see html()
|
||||
* \see contentMode()
|
||||
* \see loadHtml()
|
||||
*/
|
||||
void setHtml( const QString &html );
|
||||
|
||||
/**
|
||||
* Returns the HTML source displayed in the item if the item is using
|
||||
* the QgsLayoutItemHtml::ManualHtml mode.
|
||||
* \see setHtml()
|
||||
* \see contentMode()
|
||||
*/
|
||||
QString html() const { return mHtml; }
|
||||
|
||||
/**
|
||||
* Returns whether html item will evaluate QGIS expressions prior to rendering
|
||||
* the HTML content. If set, any content inside [% %] tags will be
|
||||
* treated as a QGIS expression and evaluated against the current atlas
|
||||
* feature.
|
||||
* \see setEvaluateExpressions()
|
||||
*/
|
||||
bool evaluateExpressions() const { return mEvaluateExpressions; }
|
||||
|
||||
/**
|
||||
* Sets whether the html item will evaluate QGIS expressions prior to rendering
|
||||
* the HTML content. If set, any content inside [% %] tags will be
|
||||
* treated as a QGIS expression and evaluated against the current atlas
|
||||
* feature.
|
||||
* \see evaluateExpressions()
|
||||
*/
|
||||
void setEvaluateExpressions( bool evaluateExpressions );
|
||||
|
||||
/**
|
||||
* Returns whether html item is using smart breaks. Smart breaks prevent
|
||||
* the html frame contents from breaking mid-way though a line of text.
|
||||
* \see setUseSmartBreaks()
|
||||
*/
|
||||
bool useSmartBreaks() const { return mUseSmartBreaks; }
|
||||
|
||||
/**
|
||||
* Sets whether the html item should use smart breaks. Smart breaks prevent
|
||||
* the html frame contents from breaking mid-way though a line of text.
|
||||
* \see useSmartBreaks()
|
||||
*/
|
||||
void setUseSmartBreaks( bool useSmartBreaks );
|
||||
|
||||
/**
|
||||
* Sets the maximum \a distance allowed when calculating where to place page breaks
|
||||
* in the html. This distance is the maximum amount of empty space allowed
|
||||
* at the bottom of a frame after calculating the optimum break location. Setting
|
||||
* a larger value will result in better choice of page break location, but more
|
||||
* wasted space at the bottom of frames. This setting is only effective if
|
||||
* useSmartBreaks is true.
|
||||
* \see maxBreakDistance()
|
||||
* \see setUseSmartBreaks()
|
||||
*/
|
||||
void setMaxBreakDistance( double distance );
|
||||
|
||||
/**
|
||||
* Returns the maximum distance allowed when calculating where to place page breaks
|
||||
* in the html. This distance is the maximum amount of empty space allowed
|
||||
* at the bottom of a frame after calculating the optimum break location. This setting
|
||||
* is only effective if useSmartBreaks is true.
|
||||
* \see setMaxBreakDistance()
|
||||
* \see useSmartBreaks()
|
||||
*/
|
||||
double maxBreakDistance() const { return mMaxBreakDistance; }
|
||||
|
||||
/**
|
||||
* Sets the user \a stylesheet CSS rules to use while rendering the HTML content. These
|
||||
* allow for overriding the styles specified within the HTML source. Setting the stylesheet
|
||||
* using this function does not automatically refresh the item's contents. Call loadHtml
|
||||
* to trigger a refresh of the item after setting the stylesheet rules.
|
||||
* \see userStylesheet()
|
||||
* \see setUserStylesheetEnabled()
|
||||
* \see loadHtml()
|
||||
*/
|
||||
void setUserStylesheet( const QString &stylesheet );
|
||||
|
||||
/**
|
||||
* Returns the user stylesheet CSS rules used while rendering the HTML content. These
|
||||
* overriding the styles specified within the HTML source.
|
||||
* \see setUserStylesheet()
|
||||
* \see userStylesheetEnabled()
|
||||
*/
|
||||
QString userStylesheet() const { return mUserStylesheet; }
|
||||
|
||||
/**
|
||||
* Sets whether user stylesheets are \a enabled for the HTML content.
|
||||
* \see userStylesheetEnabled()
|
||||
* \see setUserStylesheet()
|
||||
*/
|
||||
void setUserStylesheetEnabled( const bool enabled );
|
||||
|
||||
/**
|
||||
* Returns whether user stylesheets are enabled for the HTML content.
|
||||
* \see setUserStylesheetEnabled()
|
||||
* \see userStylesheet()
|
||||
*/
|
||||
bool userStylesheetEnabled() const { return mEnableUserStylesheet; }
|
||||
|
||||
QString displayName() const override;
|
||||
QSizeF totalSize() const override;
|
||||
void render( QgsRenderContext &context, const QRectF &renderExtent, const int frameIndex,
|
||||
const QStyleOptionGraphicsItem *itemStyle = nullptr ) override;
|
||||
|
||||
bool writeXml( QDomElement &elem, QDomDocument &doc, bool ignoreFrames = false ) const override;
|
||||
bool readXml( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames = false ) override;
|
||||
//overridden to break frames without dividing lines of text
|
||||
double findNearbyPageBreak( double yPos ) override;
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
* Reloads the html source from the url and redraws the item.
|
||||
* \param useCache set to true to use a cached copy of remote html
|
||||
* content
|
||||
* \param context expression context for evaluating data defined urls and expressions in html
|
||||
* \see setUrl
|
||||
* \see url
|
||||
*/
|
||||
void loadHtml( const bool useCache = false, const QgsExpressionContext *context = nullptr );
|
||||
|
||||
//! Recalculates the frame sizes for the current viewport dimensions
|
||||
void recalculateFrameSizes() override;
|
||||
void refreshExpressionContext();
|
||||
|
||||
void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
|
||||
|
||||
private slots:
|
||||
void frameLoaded( bool ok = true );
|
||||
|
||||
private:
|
||||
ContentMode mContentMode;
|
||||
QUrl mUrl;
|
||||
QgsWebPage *mWebPage = nullptr;
|
||||
QString mHtml;
|
||||
QString mFetchedHtml;
|
||||
QString mLastFetchedUrl;
|
||||
QString mActualFetchedUrl; //may be different if page was redirected
|
||||
bool mLoaded;
|
||||
QSizeF mSize; //total size in mm
|
||||
double mHtmlUnitsToLayoutUnits;
|
||||
QImage *mRenderedPage = nullptr;
|
||||
bool mEvaluateExpressions;
|
||||
bool mUseSmartBreaks;
|
||||
double mMaxBreakDistance;
|
||||
|
||||
QgsFeature mExpressionFeature;
|
||||
QgsVectorLayer *mExpressionLayer = nullptr;
|
||||
QgsDistanceArea *mDistanceArea = nullptr;
|
||||
|
||||
QString mUserStylesheet;
|
||||
bool mEnableUserStylesheet;
|
||||
|
||||
//! JSON string representation of current atlas feature
|
||||
QString mAtlasFeatureJSON;
|
||||
|
||||
QgsNetworkContentFetcher *mFetcher = nullptr;
|
||||
|
||||
double htmlUnitsToLayoutUnits(); //calculate scale factor
|
||||
|
||||
//renders a snapshot of the page to a cached image
|
||||
void renderCachedImage();
|
||||
|
||||
//fetches html content from a url and returns it as a string
|
||||
QString fetchHtml( const QUrl &url );
|
||||
|
||||
//! Sets the current feature, the current layer and a list of local variable substitutions for evaluating expressions
|
||||
void setExpressionContext( const QgsFeature &feature, QgsVectorLayer *layer );
|
||||
|
||||
//! Calculates the max width of frames in the html multiframe
|
||||
double maxFrameWidth() const;
|
||||
};
|
||||
|
||||
#endif // QGSLAYOUTITEMHTML_H
|
@ -55,6 +55,7 @@ void QgsLayoutMultiFrame::addFrame( QgsLayoutFrame *frame, bool recalcFrameSizes
|
||||
|
||||
mFrameItems.push_back( frame );
|
||||
frame->mMultiFrame = this;
|
||||
connect( frame, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutMultiFrame::recalculateFrameSizes );
|
||||
connect( frame, &QgsLayoutFrame::destroyed, this, &QgsLayoutMultiFrame::handleFrameRemoval );
|
||||
if ( mLayout )
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user