QGIS API Documentation  2.2.0-Valmiera
 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 : [email protected]
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 <QWebFrame>
26 #include <QWebPage>
27 #include <QEventLoop>
28 
30  QgsComposerItem( composition ), mHtmlState( 0 ), mHtmlUnitsToMM( 1.0 ),
31  mHtmlLoaded( false ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ),
32  mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop ),
33  mExpressionFeature( 0 ), mExpressionLayer( 0 )
34 {
36 
37  //get default composer font from settings
38  QSettings settings;
39  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
40  if ( !defaultFontString.isEmpty() )
41  {
42  mFont.setFamily( defaultFontString );
43  }
44 
45  //default to a 10 point font size
46  mFont.setPointSizeF( 10 );
47 
49  {
50  //a label added while atlas preview is enabled needs to have the expression context set,
51  //otherwise fields in the label aren't correctly evaluated until atlas preview feature changes (#9457)
53  }
54 }
55 
57 {
58 }
59 
60 void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
61 {
62  Q_UNUSED( itemStyle );
63  Q_UNUSED( pWidget );
64  if ( !painter )
65  {
66  return;
67  }
68 
69  drawBackground( painter );
70  painter->save();
71 
72  double penWidth = pen().widthF();
73  QRectF painterRect( penWidth + mMargin, penWidth + mMargin, rect().width() - 2 * penWidth - 2 * mMargin, rect().height() - 2 * penWidth - 2 * mMargin );
74 
75  QString textToDraw = displayText();
76 
77  if ( mHtmlState )
78  {
79  painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
80 
81  QWebPage* webPage = new QWebPage();
82 
83  //Setup event loop and timeout for rendering html
84  QEventLoop loop;
85  QTimer timeoutTimer;
86  timeoutTimer.setSingleShot( true );
87 
88  //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
89  QPalette palette = webPage->palette();
90  palette.setBrush( QPalette::Base, Qt::transparent );
91  webPage->setPalette( palette );
92  //webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
93 
94  webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
95  webPage->mainFrame()->setZoomFactor( 10.0 );
96  webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
97  webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
98 
99  // QGIS segfaults when rendering web page while in composer if html
100  // contains images. So if we are not printing the composition, then
101  // disable image loading
104  {
105  webPage->settings()->setAttribute( QWebSettings::AutoLoadImages, false );
106  }
107 
108  //Connect timeout and webpage loadFinished signals to loop
109  connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
110  connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
111 
112  //mHtmlLoaded tracks whether the QWebPage has completed loading
113  //its html contents, set it initially to false. The loadingHtmlFinished slot will
114  //set this to true after html is loaded.
115  mHtmlLoaded = false;
116  connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
117 
118  webPage->mainFrame()->setHtml( textToDraw );
119 
120  //For very basic html labels with no external assets, the html load will already be
121  //complete before we even get a chance to start the QEventLoop. Make sure we check
122  //this before starting the loop
123  if ( !mHtmlLoaded )
124  {
125  // Start a 20 second timeout in case html loading will never complete
126  timeoutTimer.start( 20000 );
127  // Pause until html is loaded
128  loop.exec();
129  }
130 
131  webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
132  }
133  else
134  {
135  painter->setPen( QPen( QColor( mFontColor ) ) );
136  painter->setFont( mFont );
137 
138  QFontMetricsF fontSize( mFont );
139 
140  //debug
141  //painter->setPen( QColor( Qt::red ) );
142  //painter->drawRect( painterRect );
143  drawText( painter, painterRect, textToDraw, mFont, mHAlignment, mVAlignment );
144  }
145 
146  painter->restore();
147 
148  drawFrame( painter );
149  if ( isSelected() )
150  {
151  drawSelectionBoxes( painter );
152  }
153 }
154 
155 /*Track when QWebPage has finished loading its html contents*/
157 {
158  Q_UNUSED( result );
159  mHtmlLoaded = true;
160 }
161 
163 {
164  if ( !mComposition )
165  {
166  return 1.0;
167  }
168 
169  //TODO : fix this more precisely so that the label's default text size is the same with or without "display as html"
170  return ( mComposition->printResolution() / 72.0 ); //webkit seems to assume a standard dpi of 72
171 }
172 
173 void QgsComposerLabel::setText( const QString& text )
174 {
175  mText = text;
176  emit itemChanged();
177 }
178 
179 void QgsComposerLabel::setExpressionContext( QgsFeature* feature, QgsVectorLayer* layer, QMap<QString, QVariant> substitutions )
180 {
181  mExpressionFeature = feature;
182  mExpressionLayer = layer;
183  mSubstitutions = substitutions;
184  // Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
185  update();
186 }
187 
189 {
190  QString displayText = mText;
191  replaceDateText( displayText );
192  QMap<QString, QVariant> subs = mSubstitutions;
193  subs[ "$page" ] = QVariant(( int )mComposition->itemPageNumber( this ) + 1 );
195 }
196 
197 void QgsComposerLabel::replaceDateText( QString& text ) const
198 {
199  QString constant = "$CURRENT_DATE";
200  int currentDatePos = text.indexOf( constant );
201  if ( currentDatePos != -1 )
202  {
203  //check if there is a bracket just after $CURRENT_DATE
204  QString formatText;
205  int openingBracketPos = text.indexOf( "(", currentDatePos );
206  int closingBracketPos = text.indexOf( ")", openingBracketPos + 1 );
207  if ( openingBracketPos != -1 &&
208  closingBracketPos != -1 &&
209  ( closingBracketPos - openingBracketPos ) > 1 &&
210  openingBracketPos == currentDatePos + constant.size() )
211  {
212  formatText = text.mid( openingBracketPos + 1, closingBracketPos - openingBracketPos - 1 );
213  text.replace( currentDatePos, closingBracketPos - currentDatePos + 1, QDate::currentDate().toString( formatText ) );
214  }
215  else //no bracket
216  {
217  text.replace( "$CURRENT_DATE", QDate::currentDate().toString() );
218  }
219  }
220 }
221 
222 void QgsComposerLabel::setFont( const QFont& f )
223 {
224  mFont = f;
225 }
226 
228 {
229  double textWidth = textWidthMillimeters( mFont, displayText() );
230  double fontAscent = fontAscentMillimeters( mFont );
231 
232  double width = textWidth + 2 * mMargin + 2 * pen().widthF() + 1;
233  double height = fontAscent + 2 * mMargin + 2 * pen().widthF() + 1;
234 
235  //keep alignment point constant
236  double xShift = 0;
237  double yShift = 0;
238  itemShiftAdjustSize( width, height, xShift, yShift );
239 
240  setSceneRect( QRectF( pos().x() + xShift, pos().y() + yShift, width, height ) );
241 }
242 
244 {
245  return mFont;
246 }
247 
248 bool QgsComposerLabel::writeXML( QDomElement& elem, QDomDocument & doc ) const
249 {
250  QString alignment;
251 
252  if ( elem.isNull() )
253  {
254  return false;
255  }
256 
257  QDomElement composerLabelElem = doc.createElement( "ComposerLabel" );
258 
259  composerLabelElem.setAttribute( "htmlState", mHtmlState );
260 
261  composerLabelElem.setAttribute( "labelText", mText );
262  composerLabelElem.setAttribute( "margin", QString::number( mMargin ) );
263 
264  composerLabelElem.setAttribute( "halign", mHAlignment );
265  composerLabelElem.setAttribute( "valign", mVAlignment );
266 
267  //font
268  QDomElement labelFontElem = doc.createElement( "LabelFont" );
269  labelFontElem.setAttribute( "description", mFont.toString() );
270  composerLabelElem.appendChild( labelFontElem );
271 
272  //font color
273  QDomElement fontColorElem = doc.createElement( "FontColor" );
274  fontColorElem.setAttribute( "red", mFontColor.red() );
275  fontColorElem.setAttribute( "green", mFontColor.green() );
276  fontColorElem.setAttribute( "blue", mFontColor.blue() );
277  composerLabelElem.appendChild( fontColorElem );
278 
279  elem.appendChild( composerLabelElem );
280  return _writeXML( composerLabelElem, doc );
281 }
282 
283 bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument& doc )
284 {
285  QString alignment;
286 
287  if ( itemElem.isNull() )
288  {
289  return false;
290  }
291 
292  //restore label specific properties
293 
294  //text
295  mText = itemElem.attribute( "labelText" );
296 
297  //html state
298  mHtmlState = itemElem.attribute( "htmlState" ).toInt();
299 
300  //margin
301  mMargin = itemElem.attribute( "margin" ).toDouble();
302 
303  //Horizontal alignment
304  mHAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "halign" ).toInt() );
305 
306  //Vertical alignment
307  mVAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "valign" ).toInt() );
308 
309  //font
310  QDomNodeList labelFontList = itemElem.elementsByTagName( "LabelFont" );
311  if ( labelFontList.size() > 0 )
312  {
313  QDomElement labelFontElem = labelFontList.at( 0 ).toElement();
314  mFont.fromString( labelFontElem.attribute( "description" ) );
315  }
316 
317  //font color
318  QDomNodeList fontColorList = itemElem.elementsByTagName( "FontColor" );
319  if ( fontColorList.size() > 0 )
320  {
321  QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
322  int red = fontColorElem.attribute( "red", "0" ).toInt();
323  int green = fontColorElem.attribute( "green", "0" ).toInt();
324  int blue = fontColorElem.attribute( "blue", "0" ).toInt();
325  mFontColor = QColor( red, green, blue );
326  }
327  else
328  {
329  mFontColor = QColor( 0, 0, 0 );
330  }
331 
332  //restore general composer item properties
333  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
334  if ( composerItemList.size() > 0 )
335  {
336  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
337 
338  //rotation
339  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
340  {
341  //check for old (pre 2.1) rotation attribute
342  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
343  }
344 
345  _readXML( composerItemElem, doc );
346  }
347  emit itemChanged();
348  return true;
349 }
350 
351 void QgsComposerLabel::itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const
352 {
353  //keep alignment point constant
354  double currentWidth = rect().width();
355  double currentHeight = rect().height();
356  xShift = 0;
357  yShift = 0;
358 
359  if ( mItemRotation >= 0 && mItemRotation < 90 )
360  {
361  if ( mHAlignment == Qt::AlignHCenter )
362  {
363  xShift = - ( newWidth - currentWidth ) / 2.0;
364  }
365  else if ( mHAlignment == Qt::AlignRight )
366  {
367  xShift = - ( newWidth - currentWidth );
368  }
369  if ( mVAlignment == Qt::AlignVCenter )
370  {
371  yShift = -( newHeight - currentHeight ) / 2.0;
372  }
373  else if ( mVAlignment == Qt::AlignBottom )
374  {
375  yShift = - ( newHeight - currentHeight );
376  }
377  }
378  if ( mItemRotation >= 90 && mItemRotation < 180 )
379  {
380  if ( mHAlignment == Qt::AlignHCenter )
381  {
382  yShift = -( newHeight - currentHeight ) / 2.0;
383  }
384  else if ( mHAlignment == Qt::AlignRight )
385  {
386  yShift = -( newHeight - currentHeight );
387  }
388  if ( mVAlignment == Qt::AlignTop )
389  {
390  xShift = -( newWidth - currentWidth );
391  }
392  else if ( mVAlignment == Qt::AlignVCenter )
393  {
394  xShift = -( newWidth - currentWidth / 2.0 );
395  }
396  }
397  else if ( mItemRotation >= 180 && mItemRotation < 270 )
398  {
399  if ( mHAlignment == Qt::AlignHCenter )
400  {
401  xShift = -( newWidth - currentWidth ) / 2.0;
402  }
403  else if ( mHAlignment == Qt::AlignLeft )
404  {
405  xShift = -( newWidth - currentWidth );
406  }
407  if ( mVAlignment == Qt::AlignVCenter )
408  {
409  yShift = ( newHeight - currentHeight ) / 2.0;
410  }
411  else if ( mVAlignment == Qt::AlignTop )
412  {
413  yShift = ( newHeight - currentHeight );
414  }
415  }
416  else if ( mItemRotation >= 270 && mItemRotation < 360 )
417  {
418  if ( mHAlignment == Qt::AlignHCenter )
419  {
420  yShift = -( newHeight - currentHeight ) / 2.0;
421  }
422  else if ( mHAlignment == Qt::AlignLeft )
423  {
424  yShift = -( newHeight - currentHeight );
425  }
426  if ( mVAlignment == Qt::AlignBottom )
427  {
428  xShift = -( newWidth - currentWidth );
429  }
430  else if ( mVAlignment == Qt::AlignVCenter )
431  {
432  xShift = -( newWidth - currentWidth / 2.0 );
433  }
434  }
435 }