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
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
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();
Expand Down
113 changes: 61 additions & 52 deletions src/core/composer/qgscomposerlabel.cpp
Expand Up @@ -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 )
Expand All @@ -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 );
}
}

Expand All @@ -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
Expand All @@ -224,6 +229,7 @@ void QgsComposerLabel::setHtmlState( int state )
}

mHtmlState = state;
contentChanged();

if ( mComposition && id().isEmpty() )
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -289,6 +296,7 @@ void QgsComposerLabel::refreshExpressionContext()
}
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
contentChanged();

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

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

/** \ingroup MapComposer
* 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*/
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;

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

QWebPage* mWebPage;
};

#endif
2 changes: 1 addition & 1 deletion src/core/qgsmultirenderchecker.cpp
Expand Up @@ -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

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

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

private:
QgsComposition* mComposition;
Expand Down Expand Up @@ -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"
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.