QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposerlabel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerlabel.cpp
3  -------------------
4  begin : January 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : blazek@itc.it
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscomposerlabel.h"
19 #include "qgscomposition.h"
20 #include "qgsexpression.h"
21 #include <QCoreApplication>
22 #include <QDate>
23 #include <QDomElement>
24 #include <QPainter>
25 #include <QSettings>
26 #include <QTimer>
27 #include <QWebFrame>
28 #include <QWebPage>
29 #include <QEventLoop>
30 
32  QgsComposerItem( composition ), mHtmlState( 0 ), mHtmlUnitsToMM( 1.0 ),
33  mHtmlLoaded( false ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ),
34  mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop ),
35  mExpressionFeature( 0 ), mExpressionLayer( 0 )
36 {
38 
39  //get default composer font from settings
40  QSettings settings;
41  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
42  if ( !defaultFontString.isEmpty() )
43  {
44  mFont.setFamily( defaultFontString );
45  }
46 
47  //default to a 10 point font size
48  mFont.setPointSizeF( 10 );
49 
51  {
52  //a label added while atlas preview is enabled needs to have the expression context set,
53  //otherwise fields in the label aren't correctly evaluated until atlas preview feature changes (#9457)
55  }
56 }
57 
59 {
60 }
61 
62 void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
63 {
64  Q_UNUSED( itemStyle );
65  Q_UNUSED( pWidget );
66  if ( !painter )
67  {
68  return;
69  }
70 
71  drawBackground( painter );
72  painter->save();
73 
74  double penWidth = pen().widthF();
75  QRectF painterRect( penWidth + mMargin, penWidth + mMargin, rect().width() - 2 * penWidth - 2 * mMargin, rect().height() - 2 * penWidth - 2 * mMargin );
76 
77  QString textToDraw = displayText();
78 
79  if ( mHtmlState )
80  {
81  painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
82 
83  QWebPage* webPage = new QWebPage();
84 
85  //Setup event loop and timeout for rendering html
86  QEventLoop loop;
87  QTimer timeoutTimer;
88  timeoutTimer.setSingleShot( true );
89 
90  //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
91  QPalette palette = webPage->palette();
92  palette.setBrush( QPalette::Base, Qt::transparent );
93  webPage->setPalette( palette );
94  //webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
95 
96  webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
97  webPage->mainFrame()->setZoomFactor( 10.0 );
98  webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
99  webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
100 
101  // QGIS segfaults when rendering web page while in composer if html
102  // contains images. So if we are not printing the composition, then
103  // disable image loading
106  {
107  webPage->settings()->setAttribute( QWebSettings::AutoLoadImages, false );
108  }
109 
110  //Connect timeout and webpage loadFinished signals to loop
111  connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
112  connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
113 
114  //mHtmlLoaded tracks whether the QWebPage has completed loading
115  //its html contents, set it initially to false. The loadingHtmlFinished slot will
116  //set this to true after html is loaded.
117  mHtmlLoaded = false;
118  connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
119 
120  webPage->mainFrame()->setHtml( textToDraw );
121 
122  //For very basic html labels with no external assets, the html load will already be
123  //complete before we even get a chance to start the QEventLoop. Make sure we check
124  //this before starting the loop
125  if ( !mHtmlLoaded )
126  {
127  // Start a 20 second timeout in case html loading will never complete
128  timeoutTimer.start( 20000 );
129  // Pause until html is loaded
130  loop.exec();
131  }
132 
133  webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
134  }
135  else
136  {
137  painter->setPen( QPen( QColor( mFontColor ) ) );
138  painter->setFont( mFont );
139 
140  QFontMetricsF fontSize( mFont );
141 
142  //debug
143  //painter->setPen( QColor( Qt::red ) );
144  //painter->drawRect( painterRect );
145  drawText( painter, painterRect, textToDraw, mFont, mHAlignment, mVAlignment );
146  }
147 
148  painter->restore();
149 
150  drawFrame( painter );
151  if ( isSelected() )
152  {
153  drawSelectionBoxes( painter );
154  }
155 }
156 
157 /*Track when QWebPage has finished loading its html contents*/
159 {
160  Q_UNUSED( result );
161  mHtmlLoaded = true;
162 }
163 
165 {
166  if ( !mComposition )
167  {
168  return 1.0;
169  }
170 
171  //TODO : fix this more precisely so that the label's default text size is the same with or without "display as html"
172  return ( mComposition->printResolution() / 72.0 ); //webkit seems to assume a standard dpi of 72
173 }
174 
175 void QgsComposerLabel::setText( const QString& text )
176 {
177  mText = text;
178  emit itemChanged();
179 }
180 
181 void QgsComposerLabel::setExpressionContext( QgsFeature* feature, QgsVectorLayer* layer, QMap<QString, QVariant> substitutions )
182 {
183  mExpressionFeature = feature;
184  mExpressionLayer = layer;
185  mSubstitutions = substitutions;
186  // Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
187  update();
188 }
189 
191 {
192  QString displayText = mText;
193  replaceDateText( displayText );
194  QMap<QString, QVariant> subs = mSubstitutions;
195  subs[ "$page" ] = QVariant(( int )mComposition->itemPageNumber( this ) + 1 );
197 }
198 
199 void QgsComposerLabel::replaceDateText( QString& text ) const
200 {
201  QString constant = "$CURRENT_DATE";
202  int currentDatePos = text.indexOf( constant );
203  if ( currentDatePos != -1 )
204  {
205  //check if there is a bracket just after $CURRENT_DATE
206  QString formatText;
207  int openingBracketPos = text.indexOf( "(", currentDatePos );
208  int closingBracketPos = text.indexOf( ")", openingBracketPos + 1 );
209  if ( openingBracketPos != -1 &&
210  closingBracketPos != -1 &&
211  ( closingBracketPos - openingBracketPos ) > 1 &&
212  openingBracketPos == currentDatePos + constant.size() )
213  {
214  formatText = text.mid( openingBracketPos + 1, closingBracketPos - openingBracketPos - 1 );
215  text.replace( currentDatePos, closingBracketPos - currentDatePos + 1, QDate::currentDate().toString( formatText ) );
216  }
217  else //no bracket
218  {
219  text.replace( "$CURRENT_DATE", QDate::currentDate().toString() );
220  }
221  }
222 }
223 
224 void QgsComposerLabel::setFont( const QFont& f )
225 {
226  mFont = f;
227 }
228 
230 {
231  double textWidth = textWidthMillimeters( mFont, displayText() );
232  double fontAscent = fontAscentMillimeters( mFont );
233 
234  double width = textWidth + 2 * mMargin + 2 * pen().widthF() + 1;
235  double height = fontAscent + 2 * mMargin + 2 * pen().widthF() + 1;
236 
237  //keep alignment point constant
238  double xShift = 0;
239  double yShift = 0;
240  itemShiftAdjustSize( width, height, xShift, yShift );
241 
242  setSceneRect( QRectF( pos().x() + xShift, pos().y() + yShift, width, height ) );
243 }
244 
246 {
247  return mFont;
248 }
249 
250 bool QgsComposerLabel::writeXML( QDomElement& elem, QDomDocument & doc ) const
251 {
252  QString alignment;
253 
254  if ( elem.isNull() )
255  {
256  return false;
257  }
258 
259  QDomElement composerLabelElem = doc.createElement( "ComposerLabel" );
260 
261  composerLabelElem.setAttribute( "htmlState", mHtmlState );
262 
263  composerLabelElem.setAttribute( "labelText", mText );
264  composerLabelElem.setAttribute( "margin", QString::number( mMargin ) );
265 
266  composerLabelElem.setAttribute( "halign", mHAlignment );
267  composerLabelElem.setAttribute( "valign", mVAlignment );
268 
269  //font
270  QDomElement labelFontElem = doc.createElement( "LabelFont" );
271  labelFontElem.setAttribute( "description", mFont.toString() );
272  composerLabelElem.appendChild( labelFontElem );
273 
274  //font color
275  QDomElement fontColorElem = doc.createElement( "FontColor" );
276  fontColorElem.setAttribute( "red", mFontColor.red() );
277  fontColorElem.setAttribute( "green", mFontColor.green() );
278  fontColorElem.setAttribute( "blue", mFontColor.blue() );
279  composerLabelElem.appendChild( fontColorElem );
280 
281  elem.appendChild( composerLabelElem );
282  return _writeXML( composerLabelElem, doc );
283 }
284 
285 bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument& doc )
286 {
287  QString alignment;
288 
289  if ( itemElem.isNull() )
290  {
291  return false;
292  }
293 
294  //restore label specific properties
295 
296  //text
297  mText = itemElem.attribute( "labelText" );
298 
299  //html state
300  mHtmlState = itemElem.attribute( "htmlState" ).toInt();
301 
302  //margin
303  mMargin = itemElem.attribute( "margin" ).toDouble();
304 
305  //Horizontal alignment
306  mHAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "halign" ).toInt() );
307 
308  //Vertical alignment
309  mVAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "valign" ).toInt() );
310 
311  //font
312  QDomNodeList labelFontList = itemElem.elementsByTagName( "LabelFont" );
313  if ( labelFontList.size() > 0 )
314  {
315  QDomElement labelFontElem = labelFontList.at( 0 ).toElement();
316  mFont.fromString( labelFontElem.attribute( "description" ) );
317  }
318 
319  //font color
320  QDomNodeList fontColorList = itemElem.elementsByTagName( "FontColor" );
321  if ( fontColorList.size() > 0 )
322  {
323  QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
324  int red = fontColorElem.attribute( "red", "0" ).toInt();
325  int green = fontColorElem.attribute( "green", "0" ).toInt();
326  int blue = fontColorElem.attribute( "blue", "0" ).toInt();
327  mFontColor = QColor( red, green, blue );
328  }
329  else
330  {
331  mFontColor = QColor( 0, 0, 0 );
332  }
333 
334  //restore general composer item properties
335  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
336  if ( composerItemList.size() > 0 )
337  {
338  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
339 
340  //rotation
341  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
342  {
343  //check for old (pre 2.1) rotation attribute
344  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
345  }
346 
347  _readXML( composerItemElem, doc );
348  }
349  emit itemChanged();
350  return true;
351 }
352 
353 void QgsComposerLabel::itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const
354 {
355  //keep alignment point constant
356  double currentWidth = rect().width();
357  double currentHeight = rect().height();
358  xShift = 0;
359  yShift = 0;
360 
361  if ( mItemRotation >= 0 && mItemRotation < 90 )
362  {
363  if ( mHAlignment == Qt::AlignHCenter )
364  {
365  xShift = - ( newWidth - currentWidth ) / 2.0;
366  }
367  else if ( mHAlignment == Qt::AlignRight )
368  {
369  xShift = - ( newWidth - currentWidth );
370  }
371  if ( mVAlignment == Qt::AlignVCenter )
372  {
373  yShift = -( newHeight - currentHeight ) / 2.0;
374  }
375  else if ( mVAlignment == Qt::AlignBottom )
376  {
377  yShift = - ( newHeight - currentHeight );
378  }
379  }
380  if ( mItemRotation >= 90 && mItemRotation < 180 )
381  {
382  if ( mHAlignment == Qt::AlignHCenter )
383  {
384  yShift = -( newHeight - currentHeight ) / 2.0;
385  }
386  else if ( mHAlignment == Qt::AlignRight )
387  {
388  yShift = -( newHeight - currentHeight );
389  }
390  if ( mVAlignment == Qt::AlignTop )
391  {
392  xShift = -( newWidth - currentWidth );
393  }
394  else if ( mVAlignment == Qt::AlignVCenter )
395  {
396  xShift = -( newWidth - currentWidth / 2.0 );
397  }
398  }
399  else if ( mItemRotation >= 180 && mItemRotation < 270 )
400  {
401  if ( mHAlignment == Qt::AlignHCenter )
402  {
403  xShift = -( newWidth - currentWidth ) / 2.0;
404  }
405  else if ( mHAlignment == Qt::AlignLeft )
406  {
407  xShift = -( newWidth - currentWidth );
408  }
409  if ( mVAlignment == Qt::AlignVCenter )
410  {
411  yShift = ( newHeight - currentHeight ) / 2.0;
412  }
413  else if ( mVAlignment == Qt::AlignTop )
414  {
415  yShift = ( newHeight - currentHeight );
416  }
417  }
418  else if ( mItemRotation >= 270 && mItemRotation < 360 )
419  {
420  if ( mHAlignment == Qt::AlignHCenter )
421  {
422  yShift = -( newHeight - currentHeight ) / 2.0;
423  }
424  else if ( mHAlignment == Qt::AlignLeft )
425  {
426  yShift = -( newHeight - currentHeight );
427  }
428  if ( mVAlignment == Qt::AlignBottom )
429  {
430  xShift = -( newWidth - currentWidth );
431  }
432  else if ( mVAlignment == Qt::AlignVCenter )
433  {
434  xShift = -( newWidth - currentWidth / 2.0 );
435  }
436  }
437 }
QgsComposition::AtlasMode atlasMode() const
Returns the current atlas mode of the composition.
QgsVectorLayer * mExpressionLayer
void replaceDateText(QString &text) const
Replaces replace '$CURRENT_DATE<(FORMAT)>' with the current date (e.g.
A item that forms part of a map composition.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
void setFont(const QFont &f)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
void itemChanged()
Used e.g.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
QgsFeature * mExpressionFeature
int itemPageNumber(const QgsComposerItem *) const
Returns on which page number (0-based) is displayed an item.
QFont font() const
virtual void drawSelectionBoxes(QPainter *p)
Draw selection boxes around item.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
void itemShiftAdjustSize(double newWidth, double newHeight, double &xShift, double &yShift) const
Helper function to calculate x/y shift for adjustSizeToText() depending on rotation, current size and alignment.
int printResolution() const
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint.
QMap< QString, QVariant > mSubstitutions
QgsComposerLabel(QgsComposition *composition)
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
Qt::AlignmentFlag mHAlignment
QgsComposition * mComposition
Graphics scene for map printing.
QgsFeature * currentFeature()
Returns the current atlas feature.
virtual void setItemRotation(double r, bool adjustPosition=false)
Sets the item rotation.
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
Qt::AlignmentFlag mVAlignment
virtual void drawBackground(QPainter *p)
Draw background.
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
void setText(const QString &text)
QgsAtlasComposition & atlasComposition()
void setExpressionContext(QgsFeature *feature, QgsVectorLayer *layer, QMap< QString, QVariant > substitutions=(QMap< QString, QVariant >()))
Sets the current feature, the current layer and a list of local variable substitutions for evaluating...
QgsVectorLayer * coverageLayer() const
QgsComposition::PlotStyle plotStyle() const
Represents a vector layer which manages a vector based data sets.
QString displayText() const
Returns the text as it appears on screen (with replaced data field)
double mItemRotation
Item rotation in degrees, clockwise.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
sets state from Dom document
static QString replaceExpressionText(const QString &action, const QgsFeature *feat, QgsVectorLayer *layer, const QMap< QString, QVariant > *substitutionMap=0)
This function currently replaces each expression between [% and %] in the string with the result of i...
void loadingHtmlFinished(bool)
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
void adjustSizeToText()
resizes the widget such that the text fits to the item.
bool writeXML(QDomElement &elem, QDomDocument &doc) const
stores state in Dom element