Skip to content
Permalink
Browse files

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.
  • Loading branch information
Patrick Valsecchi
Patrick Valsecchi committed Apr 4, 2016
1 parent 5414637 commit f696b0a34deede45e3dc4bc3be3d818543cd89c4
@@ -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();
@@ -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,79 +126,66 @@ 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 );
}
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 ) );
//painter->drawRect( painterRect );
QgsComposerUtils::drawText( painter, painterRect, textToDraw, mFont, mFontColor, mHAlignment, mVAlignment, Qt::TextWordWrap );
}

painter->restore();

drawFrame( painter );
if ( isSelected() )
{
drawSelectionBoxes( painter );
}
}

//Connect timeout and webpage loadFinished signals to loop
connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
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;
connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );

webPage->mainFrame()->setHtml( textToDraw );
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();
}
webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
}
else
{
painter->setFont( mFont );
//debug
//painter->setPen( QColor( Qt::red ) );
//painter->drawRect( painterRect );
QgsComposerUtils::drawText( painter, painterRect, textToDraw, mFont, mFontColor, mHAlignment, mVAlignment, Qt::TextWordWrap );
}

painter->restore();

drawFrame( painter );
if ( isSelected() )
{
drawSelectionBoxes( painter );
}
}

@@ -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;
}

@@ -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
@@ -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

@@ -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"
Binary file not shown.
Binary file not shown.

0 comments on commit f696b0a

Please sign in to comment.
You can’t perform that action at this time.