Skip to content

Commit

Permalink
Allow relative links in composer labels
Browse files Browse the repository at this point in the history
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 committed Apr 4, 2016
1 parent 5414637 commit f696b0a
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 55 deletions.
7 changes: 5 additions & 2 deletions src/core/composer/qgscomposerhtml.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -184,8 +184,11 @@ void QgsComposerHtml::loadHtml( const bool useCache, const QgsExpressionContext
//reset page size. otherwise viewport size increases but never decreases again //reset page size. otherwise viewport size increases but never decreases again
mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToMM, 0 ) ); mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToMM, 0 ) );


//set html, using the specified url as base if in Url mode //set html, using the specified url as base if in Url mode or the project file if in manual mode
mWebPage->mainFrame()->setHtml( loadedHtml, mContentMode == QgsComposerHtml::Url ? QUrl( mActualFetchedUrl ) : QUrl() ); const QUrl baseUrl = mContentMode == QgsComposerHtml::Url ?
QUrl( mActualFetchedUrl ) :
QUrl::fromLocalFile( QgsProject::instance()->fileInfo().absoluteFilePath() );
mWebPage->mainFrame()->setHtml( loadedHtml, baseUrl );


//set user stylesheet //set user stylesheet
QWebSettings* settings = mWebPage->settings(); QWebSettings* settings = mWebPage->settings();
Expand Down
113 changes: 61 additions & 52 deletions src/core/composer/qgscomposerlabel.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -79,11 +79,27 @@ QgsComposerLabel::QgsComposerLabel( QgsComposition *composition )
//to update the expression context //to update the expression context
connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshExpressionContext() ) ); 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() QgsComposerLabel::~QgsComposerLabel()
{ {
delete mDistanceArea; delete mDistanceArea;
delete mWebPage;
} }


void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget ) void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
Expand All @@ -110,79 +126,66 @@ void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem*
double yPenAdjust = mMarginY < 0 ? -penWidth : penWidth; double yPenAdjust = mMarginY < 0 ? -penWidth : penWidth;
QRectF painterRect( xPenAdjust + mMarginX, yPenAdjust + mMarginY, rect().width() - 2 * xPenAdjust - 2 * mMarginX, rect().height() - 2 * yPenAdjust - 2 * mMarginY ); QRectF painterRect( xPenAdjust + mMarginX, yPenAdjust + mMarginY, rect().width() - 2 * xPenAdjust - 2 * mMarginX, rect().height() - 2 * yPenAdjust - 2 * mMarginY );


QString textToDraw = displayText();

if ( mHtmlState ) if ( mHtmlState )
{ {
painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 ); painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
QWebPage *webPage = new QWebPage(); mWebPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
webPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() ); mWebPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() );

mWebPage->mainFrame()->render( painter );
//Setup event loop and timeout for rendering html }
QEventLoop loop; else
QTimer timeoutTimer; {
timeoutTimer.setSingleShot( true ); const QString textToDraw = displayText();

painter->setFont( mFont );
//This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/ //debug
QPalette palette = webPage->palette(); //painter->setPen( QColor( Qt::red ) );
palette.setBrush( QPalette::Base, Qt::transparent ); //painter->drawRect( painterRect );
webPage->setPalette( palette ); QgsComposerUtils::drawText( painter, painterRect, textToDraw, mFont, mFontColor, mHAlignment, mVAlignment, Qt::TextWordWrap );
//webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ? }


webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) ); painter->restore();
webPage->mainFrame()->setZoomFactor( 10.0 );
webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff ); drawFrame( painter );
webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff ); if ( isSelected() )
webPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() ); {

drawSelectionBoxes( painter );
// 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 void QgsComposerLabel::contentChanged()
connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) ); {
connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) ); if ( mHtmlState )
{
const QString textToDraw = displayText();


//mHtmlLoaded tracks whether the QWebPage has completed loading //mHtmlLoaded tracks whether the QWebPage has completed loading
//its html contents, set it initially to false. The loadingHtmlFinished slot will //its html contents, set it initially to false. The loadingHtmlFinished slot will
//set this to true after html is loaded. //set this to true after html is loaded.
mHtmlLoaded = false; 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 //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 //complete before we even get a chance to start the QEventLoop. Make sure we check
//this before starting the loop //this before starting the loop
if ( !mHtmlLoaded ) 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 // 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 ); timeoutTimer.start( 20000 );

// Pause until html is loaded // Pause until html is loaded
loop.exec(); 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 );
} }
} }


Expand All @@ -209,6 +212,8 @@ void QgsComposerLabel::setText( const QString& text )
mText = text; mText = text;
emit itemChanged(); emit itemChanged();


contentChanged();

if ( mComposition && id().isEmpty() && !mHtmlState ) if ( mComposition && id().isEmpty() && !mHtmlState )
{ {
//notify the model that the display name has changed //notify the model that the display name has changed
Expand All @@ -224,6 +229,7 @@ void QgsComposerLabel::setHtmlState( int state )
} }


mHtmlState = state; mHtmlState = state;
contentChanged();


if ( mComposition && id().isEmpty() ) if ( mComposition && id().isEmpty() )
{ {
Expand Down Expand Up @@ -253,6 +259,7 @@ void QgsComposerLabel::setExpressionContext( QgsFeature *feature, QgsVectorLayer
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() ); mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
} }
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); 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 // Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
update(); update();
Expand Down Expand Up @@ -289,6 +296,7 @@ void QgsComposerLabel::refreshExpressionContext()
} }
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() ); mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
contentChanged();


update(); update();
} }
Expand Down Expand Up @@ -488,6 +496,7 @@ bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument&
_readXML( composerItemElem, doc ); _readXML( composerItemElem, doc );
} }
emit itemChanged(); emit itemChanged();
contentChanged();
return true; return true;
} }


Expand Down
5 changes: 5 additions & 0 deletions src/core/composer/qgscomposerlabel.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class QgsVectorLayer; class QgsVectorLayer;
class QgsFeature; class QgsFeature;
class QgsDistanceArea; class QgsDistanceArea;
class QWebPage;


/** \ingroup MapComposer /** \ingroup MapComposer
* A label that can be placed onto a map composition. * A label that can be placed onto a map composition.
Expand Down Expand Up @@ -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*/ /** 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; void itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const;


/** Called when the content is changed to handle HTML loading */
void contentChanged();

// Font // Font
QFont mFont; QFont mFont;


Expand Down Expand Up @@ -212,6 +216,7 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
QMap<QString, QVariant> mSubstitutions; QMap<QString, QVariant> mSubstitutions;
QgsDistanceArea* mDistanceArea; QgsDistanceArea* mDistanceArea;


QWebPage* mWebPage;
}; };


#endif #endif
2 changes: 1 addition & 1 deletion src/core/qgsmultirenderchecker.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ bool QgsCompositionChecker::testComposition( QString &theReport, int page, int p
//QRectF targetArea( 0, 0, 3507, 2480 ); //QRectF targetArea( 0, 0, 3507, 2480 );
mComposition->renderPage( &expectedPainter, page ); mComposition->renderPage( &expectedPainter, page );
expectedPainter.end(); expectedPainter.end();
newImage.save( mExpectedImageFile, "PNG" ); newImage.save( controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png", "PNG" );
return true; return true;
#endif //0 #endif //0


Expand Down
18 changes: 18 additions & 0 deletions tests/src/core/testqgscomposerlabel.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "qgsvectordataprovider.h" #include "qgsvectordataprovider.h"
#include "qgsmultirenderchecker.h" #include "qgsmultirenderchecker.h"
#include "qgsfontutils.h" #include "qgsfontutils.h"
#include "qgsproject.h"


#include <QObject> #include <QObject>
#include <QtTest/QtTest> #include <QtTest/QtTest>
Expand Down Expand Up @@ -55,6 +56,7 @@ class TestQgsComposerLabel : public QObject
void marginMethods(); //tests getting/setting margins void marginMethods(); //tests getting/setting margins
void render(); void render();
void renderAsHtml(); void renderAsHtml();
void renderAsHtmlRelative();


private: private:
QgsComposition* mComposition; QgsComposition* mComposition;
Expand Down Expand Up @@ -265,5 +267,21 @@ void TestQgsComposerLabel::renderAsHtml()
QVERIFY( checker.testComposition( mReport, 0, 0 ) ); 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 ) QTEST_MAIN( TestQgsComposerLabel )
#include "testqgscomposerlabel.moc" #include "testqgscomposerlabel.moc"
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f696b0a

Please sign in to comment.