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