Allow relative links in composer labels

Now labels and HTML boxes can contain relative URLs. If we don't have
a base URL, the project file will be used as a base URL (closes #7236).

Remove the exception for the labels where the images where not
loaded (unless in in PDF or image mode). It was because of a crash. Qt didn't
like having the HTML loading to be done synchronously during painting.

Fix a leak when rendering labels.
This commit is contained in:
Patrick Valsecchi 2016-03-18 09:53:22 +01:00
parent 541463718d
commit f696b0a34d
7 changed files with 92 additions and 57 deletions

View File

@ -184,8 +184,11 @@ void QgsComposerHtml::loadHtml( const bool useCache, const QgsExpressionContext
//reset page size. otherwise viewport size increases but never decreases again
mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToMM, 0 ) );
//set html, using the specified url as base if in Url mode
mWebPage->mainFrame()->setHtml( loadedHtml, mContentMode == QgsComposerHtml::Url ? QUrl( mActualFetchedUrl ) : QUrl() );
//set html, using the specified url as base if in Url mode or the project file if in manual mode
const QUrl baseUrl = mContentMode == QgsComposerHtml::Url ?
QUrl( mActualFetchedUrl ) :
QUrl::fromLocalFile( QgsProject::instance()->fileInfo().absoluteFilePath() );
mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );
//set user stylesheet
QWebSettings* settings = mWebPage->settings();

View File

@ -79,11 +79,27 @@ QgsComposerLabel::QgsComposerLabel( QgsComposition *composition )
//to update the expression context
connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshExpressionContext() ) );
}
mWebPage = new QWebPage( this );
mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
//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 );
//webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
mWebPage->mainFrame()->setZoomFactor( 10.0 );
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
connect( mWebPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
}
QgsComposerLabel::~QgsComposerLabel()
{
delete mDistanceArea;
delete mWebPage;
}
void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
@ -110,66 +126,16 @@ void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem*
double yPenAdjust = mMarginY < 0 ? -penWidth : penWidth;
QRectF painterRect( xPenAdjust + mMarginX, yPenAdjust + mMarginY, rect().width() - 2 * xPenAdjust - 2 * mMarginX, rect().height() - 2 * yPenAdjust - 2 * mMarginY );
QString textToDraw = displayText();
if ( mHtmlState )
{
painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
QWebPage *webPage = new QWebPage();
webPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
//Setup event loop and timeout for rendering html
QEventLoop loop;
QTimer timeoutTimer;
timeoutTimer.setSingleShot( true );
//This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
QPalette palette = webPage->palette();
palette.setBrush( QPalette::Base, Qt::transparent );
webPage->setPalette( palette );
//webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
webPage->mainFrame()->setZoomFactor( 10.0 );
webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
webPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() );
// QGIS segfaults when rendering web page while in composer if html
// contains images. So if we are not printing the composition, then
// disable image loading
if ( mComposition->plotStyle() != QgsComposition::Print &&
mComposition->plotStyle() != QgsComposition::Postscript )
{
webPage->settings()->setAttribute( QWebSettings::AutoLoadImages, false );
}
//Connect timeout and webpage loadFinished signals to loop
connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
//mHtmlLoaded tracks whether the QWebPage has completed loading
//its html contents, set it initially to false. The loadingHtmlFinished slot will
//set this to true after html is loaded.
mHtmlLoaded = false;
connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
webPage->mainFrame()->setHtml( textToDraw );
//For very basic html labels with no external assets, the html load will already be
//complete before we even get a chance to start the QEventLoop. Make sure we check
//this before starting the loop
if ( !mHtmlLoaded )
{
// Start a 20 second timeout in case html loading will never complete
timeoutTimer.start( 20000 );
// Pause until html is loaded
loop.exec();
}
webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
mWebPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
mWebPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() );
mWebPage->mainFrame()->render( painter );
}
else
{
const QString textToDraw = displayText();
painter->setFont( mFont );
//debug
//painter->setPen( QColor( Qt::red ) );
@ -186,6 +152,43 @@ void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem*
}
}
void QgsComposerLabel::contentChanged()
{
if ( mHtmlState )
{
const QString textToDraw = displayText();
//mHtmlLoaded tracks whether the QWebPage has completed loading
//its html contents, set it initially to false. The loadingHtmlFinished slot will
//set this to true after html is loaded.
mHtmlLoaded = false;
const QUrl baseUrl = QUrl::fromLocalFile( QgsProject::instance()->fileInfo().absoluteFilePath() );
mWebPage->mainFrame()->setHtml( textToDraw, baseUrl );
//For very basic html labels with no external assets, the html load will already be
//complete before we even get a chance to start the QEventLoop. Make sure we check
//this before starting the loop
if ( !mHtmlLoaded )
{
//Setup event loop and timeout for rendering html
QEventLoop loop;
//Connect timeout and webpage loadFinished signals to loop
connect( mWebPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
// Start a 20 second timeout in case html loading will never complete
QTimer timeoutTimer;
timeoutTimer.setSingleShot( true );
connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
timeoutTimer.start( 20000 );
// Pause until html is loaded
loop.exec();
}
}
}
/*Track when QWebPage has finished loading its html contents*/
void QgsComposerLabel::loadingHtmlFinished( bool result )
{
@ -209,6 +212,8 @@ void QgsComposerLabel::setText( const QString& text )
mText = text;
emit itemChanged();
contentChanged();
if ( mComposition && id().isEmpty() && !mHtmlState )
{
//notify the model that the display name has changed
@ -224,6 +229,7 @@ void QgsComposerLabel::setHtmlState( int state )
}
mHtmlState = state;
contentChanged();
if ( mComposition && id().isEmpty() )
{
@ -253,6 +259,7 @@ void QgsComposerLabel::setExpressionContext( QgsFeature *feature, QgsVectorLayer
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
}
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
contentChanged();
// Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
update();
@ -289,6 +296,7 @@ void QgsComposerLabel::refreshExpressionContext()
}
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
contentChanged();
update();
}
@ -488,6 +496,7 @@ bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument&
_readXML( composerItemElem, doc );
}
emit itemChanged();
contentChanged();
return true;
}

View File

@ -23,6 +23,7 @@
class QgsVectorLayer;
class QgsFeature;
class QgsDistanceArea;
class QWebPage;
/** \ingroup MapComposer
* A label that can be placed onto a map composition.
@ -184,6 +185,9 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
/** Helper function to calculate x/y shift for adjustSizeToText() depending on rotation, current size and alignment*/
void itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const;
/** Called when the content is changed to handle HTML loading */
void contentChanged();
// Font
QFont mFont;
@ -212,6 +216,7 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
QMap<QString, QVariant> mSubstitutions;
QgsDistanceArea* mDistanceArea;
QWebPage* mWebPage;
};
#endif

View File

@ -154,7 +154,7 @@ bool QgsCompositionChecker::testComposition( QString &theReport, int page, int p
//QRectF targetArea( 0, 0, 3507, 2480 );
mComposition->renderPage( &expectedPainter, page );
expectedPainter.end();
newImage.save( mExpectedImageFile, "PNG" );
newImage.save( controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png", "PNG" );
return true;
#endif //0

View File

@ -24,6 +24,7 @@
#include "qgsvectordataprovider.h"
#include "qgsmultirenderchecker.h"
#include "qgsfontutils.h"
#include "qgsproject.h"
#include <QObject>
#include <QtTest/QtTest>
@ -55,6 +56,7 @@ class TestQgsComposerLabel : public QObject
void marginMethods(); //tests getting/setting margins
void render();
void renderAsHtml();
void renderAsHtmlRelative();
private:
QgsComposition* mComposition;
@ -265,5 +267,21 @@ void TestQgsComposerLabel::renderAsHtml()
QVERIFY( checker.testComposition( mReport, 0, 0 ) );
}
void TestQgsComposerLabel::renderAsHtmlRelative()
{
QgsProject::instance()->setFileName( QString( TEST_DATA_DIR ) + QDir::separator() + "test.qgs" );
mComposerLabel->setFontColor( QColor( 200, 40, 60 ) );
mComposerLabel->setText( "test <img src=\"small_sample_image.png\" />" );
mComposerLabel->setFont( QgsFontUtils::getStandardTestFont( "Bold", 48 ) );
mComposerLabel->setPos( 70, 70 );
mComposerLabel->adjustSizeToText();
mComposerLabel->setHtmlState( 1 );
mComposerLabel->update();
QgsCompositionChecker checker( "composerlabel_renderhtmlrelative", mComposition );
checker.setControlPathPrefix( "composer_label" );
QVERIFY( checker.testComposition( mReport, 0, 0 ) );
}
QTEST_MAIN( TestQgsComposerLabel )
#include "testqgscomposerlabel.moc"