Skip to content

Commit f696b0a

Browse files
author
Patrick Valsecchi
committed
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.
1 parent 5414637 commit f696b0a

File tree

7 files changed

+90
-55
lines changed

7 files changed

+90
-55
lines changed

src/core/composer/qgscomposerhtml.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,11 @@ void QgsComposerHtml::loadHtml( const bool useCache, const QgsExpressionContext
184184
//reset page size. otherwise viewport size increases but never decreases again
185185
mWebPage->setViewportSize( QSize( maxFrameWidth() * mHtmlUnitsToMM, 0 ) );
186186

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

190193
//set user stylesheet
191194
QWebSettings* settings = mWebPage->settings();

src/core/composer/qgscomposerlabel.cpp

+61-52
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,27 @@ QgsComposerLabel::QgsComposerLabel( QgsComposition *composition )
7979
//to update the expression context
8080
connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshExpressionContext() ) );
8181
}
82+
83+
mWebPage = new QWebPage( this );
84+
mWebPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
85+
86+
//This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
87+
QPalette palette = mWebPage->palette();
88+
palette.setBrush( QPalette::Base, Qt::transparent );
89+
mWebPage->setPalette( palette );
90+
//webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
91+
92+
mWebPage->mainFrame()->setZoomFactor( 10.0 );
93+
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
94+
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
95+
96+
connect( mWebPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
8297
}
8398

8499
QgsComposerLabel::~QgsComposerLabel()
85100
{
86101
delete mDistanceArea;
102+
delete mWebPage;
87103
}
88104

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

113-
QString textToDraw = displayText();
114-
115129
if ( mHtmlState )
116130
{
117131
painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
118-
QWebPage *webPage = new QWebPage();
119-
webPage->setNetworkAccessManager( QgsNetworkAccessManager::instance() );
120-
121-
//Setup event loop and timeout for rendering html
122-
QEventLoop loop;
123-
QTimer timeoutTimer;
124-
timeoutTimer.setSingleShot( true );
125-
126-
//This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
127-
QPalette palette = webPage->palette();
128-
palette.setBrush( QPalette::Base, Qt::transparent );
129-
webPage->setPalette( palette );
130-
//webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
131-
132-
webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
133-
webPage->mainFrame()->setZoomFactor( 10.0 );
134-
webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
135-
webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
136-
webPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() );
137-
138-
// QGIS segfaults when rendering web page while in composer if html
139-
// contains images. So if we are not printing the composition, then
140-
// disable image loading
141-
if ( mComposition->plotStyle() != QgsComposition::Print &&
142-
mComposition->plotStyle() != QgsComposition::Postscript )
143-
{
144-
webPage->settings()->setAttribute( QWebSettings::AutoLoadImages, false );
145-
}
132+
mWebPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
133+
mWebPage->settings()->setUserStyleSheetUrl( createStylesheetUrl() );
134+
mWebPage->mainFrame()->render( painter );
135+
}
136+
else
137+
{
138+
const QString textToDraw = displayText();
139+
painter->setFont( mFont );
140+
//debug
141+
//painter->setPen( QColor( Qt::red ) );
142+
//painter->drawRect( painterRect );
143+
QgsComposerUtils::drawText( painter, painterRect, textToDraw, mFont, mFontColor, mHAlignment, mVAlignment, Qt::TextWordWrap );
144+
}
145+
146+
painter->restore();
147+
148+
drawFrame( painter );
149+
if ( isSelected() )
150+
{
151+
drawSelectionBoxes( painter );
152+
}
153+
}
146154

147-
//Connect timeout and webpage loadFinished signals to loop
148-
connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
149-
connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
155+
void QgsComposerLabel::contentChanged()
156+
{
157+
if ( mHtmlState )
158+
{
159+
const QString textToDraw = displayText();
150160

151161
//mHtmlLoaded tracks whether the QWebPage has completed loading
152162
//its html contents, set it initially to false. The loadingHtmlFinished slot will
153163
//set this to true after html is loaded.
154164
mHtmlLoaded = false;
155-
connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
156165

157-
webPage->mainFrame()->setHtml( textToDraw );
166+
const QUrl baseUrl = QUrl::fromLocalFile( QgsProject::instance()->fileInfo().absoluteFilePath() );
167+
mWebPage->mainFrame()->setHtml( textToDraw, baseUrl );
158168

159169
//For very basic html labels with no external assets, the html load will already be
160170
//complete before we even get a chance to start the QEventLoop. Make sure we check
161171
//this before starting the loop
162172
if ( !mHtmlLoaded )
163173
{
174+
//Setup event loop and timeout for rendering html
175+
QEventLoop loop;
176+
177+
//Connect timeout and webpage loadFinished signals to loop
178+
connect( mWebPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
179+
164180
// Start a 20 second timeout in case html loading will never complete
181+
QTimer timeoutTimer;
182+
timeoutTimer.setSingleShot( true );
183+
connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
165184
timeoutTimer.start( 20000 );
185+
166186
// Pause until html is loaded
167187
loop.exec();
168188
}
169-
webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
170-
}
171-
else
172-
{
173-
painter->setFont( mFont );
174-
//debug
175-
//painter->setPen( QColor( Qt::red ) );
176-
//painter->drawRect( painterRect );
177-
QgsComposerUtils::drawText( painter, painterRect, textToDraw, mFont, mFontColor, mHAlignment, mVAlignment, Qt::TextWordWrap );
178-
}
179-
180-
painter->restore();
181-
182-
drawFrame( painter );
183-
if ( isSelected() )
184-
{
185-
drawSelectionBoxes( painter );
186189
}
187190
}
188191

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

215+
contentChanged();
216+
212217
if ( mComposition && id().isEmpty() && !mHtmlState )
213218
{
214219
//notify the model that the display name has changed
@@ -224,6 +229,7 @@ void QgsComposerLabel::setHtmlState( int state )
224229
}
225230

226231
mHtmlState = state;
232+
contentChanged();
227233

228234
if ( mComposition && id().isEmpty() )
229235
{
@@ -253,6 +259,7 @@ void QgsComposerLabel::setExpressionContext( QgsFeature *feature, QgsVectorLayer
253259
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
254260
}
255261
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
262+
contentChanged();
256263

257264
// Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
258265
update();
@@ -289,6 +296,7 @@ void QgsComposerLabel::refreshExpressionContext()
289296
}
290297
mDistanceArea->setEllipsoidalMode( mComposition->mapSettings().hasCrsTransformEnabled() );
291298
mDistanceArea->setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
299+
contentChanged();
292300

293301
update();
294302
}
@@ -488,6 +496,7 @@ bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument&
488496
_readXML( composerItemElem, doc );
489497
}
490498
emit itemChanged();
499+
contentChanged();
491500
return true;
492501
}
493502

src/core/composer/qgscomposerlabel.h

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class QgsVectorLayer;
2424
class QgsFeature;
2525
class QgsDistanceArea;
26+
class QWebPage;
2627

2728
/** \ingroup MapComposer
2829
* A label that can be placed onto a map composition.
@@ -184,6 +185,9 @@ class CORE_EXPORT QgsComposerLabel: public QgsComposerItem
184185
/** Helper function to calculate x/y shift for adjustSizeToText() depending on rotation, current size and alignment*/
185186
void itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const;
186187

188+
/** Called when the content is changed to handle HTML loading */
189+
void contentChanged();
190+
187191
// Font
188192
QFont mFont;
189193

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

219+
QWebPage* mWebPage;
215220
};
216221

217222
#endif

src/core/qgsmultirenderchecker.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ bool QgsCompositionChecker::testComposition( QString &theReport, int page, int p
154154
//QRectF targetArea( 0, 0, 3507, 2480 );
155155
mComposition->renderPage( &expectedPainter, page );
156156
expectedPainter.end();
157-
newImage.save( mExpectedImageFile, "PNG" );
157+
newImage.save( controlImagePath() + QDir::separator() + "expected_" + mTestName + ".png", "PNG" );
158158
return true;
159159
#endif //0
160160

tests/src/core/testqgscomposerlabel.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "qgsvectordataprovider.h"
2525
#include "qgsmultirenderchecker.h"
2626
#include "qgsfontutils.h"
27+
#include "qgsproject.h"
2728

2829
#include <QObject>
2930
#include <QtTest/QtTest>
@@ -55,6 +56,7 @@ class TestQgsComposerLabel : public QObject
5556
void marginMethods(); //tests getting/setting margins
5657
void render();
5758
void renderAsHtml();
59+
void renderAsHtmlRelative();
5860

5961
private:
6062
QgsComposition* mComposition;
@@ -265,5 +267,21 @@ void TestQgsComposerLabel::renderAsHtml()
265267
QVERIFY( checker.testComposition( mReport, 0, 0 ) );
266268
}
267269

270+
void TestQgsComposerLabel::renderAsHtmlRelative()
271+
{
272+
QgsProject::instance()->setFileName( QString( TEST_DATA_DIR ) + QDir::separator() + "test.qgs" );
273+
mComposerLabel->setFontColor( QColor( 200, 40, 60 ) );
274+
mComposerLabel->setText( "test <img src=\"small_sample_image.png\" />" );
275+
mComposerLabel->setFont( QgsFontUtils::getStandardTestFont( "Bold", 48 ) );
276+
mComposerLabel->setPos( 70, 70 );
277+
mComposerLabel->adjustSizeToText();
278+
mComposerLabel->setHtmlState( 1 );
279+
mComposerLabel->update();
280+
281+
QgsCompositionChecker checker( "composerlabel_renderhtmlrelative", mComposition );
282+
checker.setControlPathPrefix( "composer_label" );
283+
QVERIFY( checker.testComposition( mReport, 0, 0 ) );
284+
}
285+
268286
QTEST_MAIN( TestQgsComposerLabel )
269287
#include "testqgscomposerlabel.moc"

0 commit comments

Comments
 (0)