QGIS API Documentation  master-3f58142
src/core/composer/qgscomposerlabel.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                          qgscomposerlabel.cpp
00003                              -------------------
00004     begin                : January 2005
00005     copyright            : (C) 2005 by Radim Blazek
00006     email                : blazek@itc.it
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgscomposerlabel.h"
00019 #include "qgscomposition.h"
00020 #include "qgsexpression.h"
00021 #include <QCoreApplication>
00022 #include <QDate>
00023 #include <QDomElement>
00024 #include <QPainter>
00025 #include <QWebFrame>
00026 #include <QWebPage>
00027 #include <QEventLoop>
00028 
00029 QgsComposerLabel::QgsComposerLabel( QgsComposition *composition ):
00030     QgsComposerItem( composition ), mHtmlState( 0 ), mHtmlUnitsToMM( 1.0 ),
00031     mHtmlLoaded( false ), mMargin( 1.0 ), mFontColor( QColor( 0, 0, 0 ) ),
00032     mHAlignment( Qt::AlignLeft ), mVAlignment( Qt::AlignTop ),
00033     mExpressionFeature( 0 ), mExpressionLayer( 0 )
00034 {
00035   mHtmlUnitsToMM = htmlUnitsToMM();
00036   //default font size is 10 point
00037   mFont.setPointSizeF( 10 );
00038 }
00039 
00040 QgsComposerLabel::~QgsComposerLabel()
00041 {
00042 }
00043 
00044 void QgsComposerLabel::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
00045 {
00046   Q_UNUSED( itemStyle );
00047   Q_UNUSED( pWidget );
00048   if ( !painter )
00049   {
00050     return;
00051   }
00052 
00053   drawBackground( painter );
00054   painter->save();
00055 
00056   double penWidth = pen().widthF();
00057   QRectF painterRect( penWidth + mMargin, penWidth + mMargin, mTextBoxWidth - 2 * penWidth - 2 * mMargin, mTextBoxHeight - 2 * penWidth - 2 * mMargin );
00058   painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
00059   painter->rotate( mRotation );
00060   painter->translate( -mTextBoxWidth / 2.0, -mTextBoxHeight / 2.0 );
00061 
00062   if ( mHtmlState )
00063   {
00064     painter->scale( 1.0 / mHtmlUnitsToMM / 10.0, 1.0 / mHtmlUnitsToMM / 10.0 );
00065 
00066     QWebPage* webPage = new QWebPage();
00067 
00068     //Setup event loop and timeout for rendering html
00069     QEventLoop loop;
00070     QTimer timeoutTimer;
00071     timeoutTimer.setSingleShot( true );
00072 
00073     //This makes the background transparent. Found on http://blog.qt.digia.com/blog/2009/06/30/transparent-qwebview-or-qwebpage/
00074     QPalette palette = webPage->palette();
00075     palette.setBrush( QPalette::Base, Qt::transparent );
00076     webPage->setPalette( palette );
00077     //webPage->setAttribute(Qt::WA_OpaquePaintEvent, false); //this does not compile, why ?
00078 
00079     webPage->setViewportSize( QSize( painterRect.width() * mHtmlUnitsToMM * 10.0, painterRect.height() * mHtmlUnitsToMM * 10.0 ) );
00080     webPage->mainFrame()->setZoomFactor( 10.0 );
00081     webPage->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
00082     webPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
00083 
00084     // QGIS segfaults when rendering web page while in composer if html
00085     // contains images. So if we are not printing the composition, then
00086     // disable image loading
00087     if ( mComposition->plotStyle() != QgsComposition::Print &&
00088          mComposition->plotStyle() != QgsComposition::Postscript )
00089     {
00090       webPage->settings()->setAttribute( QWebSettings::AutoLoadImages, false );
00091     }
00092 
00093     //Connect timeout and webpage loadFinished signals to loop
00094     connect( &timeoutTimer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
00095     connect( webPage, SIGNAL( loadFinished( bool ) ), &loop, SLOT( quit() ) );
00096 
00097     //mHtmlLoaded tracks whether the QWebPage has completed loading
00098     //its html contents, set it initially to false. The loadingHtmlFinished slot will
00099     //set this to true after html is loaded.
00100     mHtmlLoaded = false;
00101     connect( webPage, SIGNAL( loadFinished( bool ) ), SLOT( loadingHtmlFinished( bool ) ) );
00102 
00103     webPage->mainFrame()->setHtml( displayText() );
00104 
00105     //For very basic html labels with no external assets, the html load will already be
00106     //complete before we even get a chance to start the QEventLoop. Make sure we check
00107     //this before starting the loop
00108     if ( !mHtmlLoaded )
00109     {
00110       // Start a 20 second timeout in case html loading will never complete
00111       timeoutTimer.start( 20000 );
00112       // Pause until html is loaded
00113       loop.exec();
00114     }
00115 
00116     webPage->mainFrame()->render( painter );//DELETE WEBPAGE ?
00117   }
00118   else
00119   {
00120     painter->setPen( QPen( QColor( mFontColor ) ) );
00121     painter->setFont( mFont );
00122 
00123     QFontMetricsF fontSize( mFont );
00124 
00125     //debug
00126     //painter->setPen( QColor( Qt::red ) );
00127     //painter->drawRect( painterRect );
00128     drawText( painter, painterRect, displayText(), mFont, mHAlignment, mVAlignment );
00129   }
00130 
00131   painter->restore();
00132 
00133   drawFrame( painter );
00134   if ( isSelected() )
00135   {
00136     drawSelectionBoxes( painter );
00137   }
00138 }
00139 
00140 /*Track when QWebPage has finished loading its html contents*/
00141 void QgsComposerLabel::loadingHtmlFinished( bool result )
00142 {
00143   Q_UNUSED( result );
00144   mHtmlLoaded = true;
00145 }
00146 
00147 double QgsComposerLabel::htmlUnitsToMM()
00148 {
00149   if ( !mComposition )
00150   {
00151     return 1.0;
00152   }
00153 
00154   //TODO : fix this more precisely so that the label's default text size is the same with or without "display as html"
00155   return ( mComposition->printResolution() / 72.0 ); //webkit seems to assume a standard dpi of 72
00156 }
00157 
00158 void QgsComposerLabel::setText( const QString& text )
00159 {
00160   mText = text;
00161   emit itemChanged();
00162 }
00163 
00164 void QgsComposerLabel::setExpressionContext( QgsFeature* feature, QgsVectorLayer* layer, QMap<QString, QVariant> substitutions )
00165 {
00166   mExpressionFeature = feature;
00167   mExpressionLayer = layer;
00168   mSubstitutions = substitutions;
00169   // Force label to redraw -- fixes label printing for labels with blend modes when used with atlas
00170   update();
00171 }
00172 
00173 QString QgsComposerLabel::displayText() const
00174 {
00175   QString displayText = mText;
00176   replaceDateText( displayText );
00177   QMap<QString, QVariant> subs = mSubstitutions;
00178   subs[ "$page" ] = QVariant(( int )mComposition->itemPageNumber( this ) + 1 );
00179   return QgsExpression::replaceExpressionText( displayText, mExpressionFeature, mExpressionLayer, &subs );
00180 }
00181 
00182 void QgsComposerLabel::replaceDateText( QString& text ) const
00183 {
00184   QString constant = "$CURRENT_DATE";
00185   int currentDatePos = text.indexOf( constant );
00186   if ( currentDatePos != -1 )
00187   {
00188     //check if there is a bracket just after $CURRENT_DATE
00189     QString formatText;
00190     int openingBracketPos = text.indexOf( "(", currentDatePos );
00191     int closingBracketPos = text.indexOf( ")", openingBracketPos + 1 );
00192     if ( openingBracketPos != -1 &&
00193          closingBracketPos != -1 &&
00194          ( closingBracketPos - openingBracketPos ) > 1 &&
00195          openingBracketPos == currentDatePos + constant.size() )
00196     {
00197       formatText = text.mid( openingBracketPos + 1, closingBracketPos - openingBracketPos - 1 );
00198       text.replace( currentDatePos, closingBracketPos - currentDatePos + 1, QDate::currentDate().toString( formatText ) );
00199     }
00200     else //no bracket
00201     {
00202       text.replace( "$CURRENT_DATE", QDate::currentDate().toString() );
00203     }
00204   }
00205 }
00206 
00207 void QgsComposerLabel::setFont( const QFont& f )
00208 {
00209   mFont = f;
00210 }
00211 
00212 void QgsComposerLabel::adjustSizeToText()
00213 {
00214   double textWidth = textWidthMillimeters( mFont, displayText() );
00215   double fontAscent = fontAscentMillimeters( mFont );
00216 
00217   mTextBoxWidth = textWidth + 2 * mMargin + 2 * pen().widthF() + 1;
00218   mTextBoxHeight = fontAscent + 2 * mMargin + 2 * pen().widthF() + 1;
00219 
00220   double width = mTextBoxWidth;
00221   double height = mTextBoxHeight;
00222 
00223   sizeChangedByRotation( width, height );
00224 
00225   //keep alignment point constant
00226   double xShift = 0;
00227   double yShift = 0;
00228   itemShiftAdjustSize( width, height, xShift, yShift );
00229 
00230   QgsComposerItem::setSceneRect( QRectF( transform().dx() + xShift, transform().dy() + yShift, width, height ) );
00231 }
00232 
00233 QFont QgsComposerLabel::font() const
00234 {
00235   return mFont;
00236 }
00237 
00238 void QgsComposerLabel::setRotation( double r )
00239 {
00240   double width = mTextBoxWidth;
00241   double height = mTextBoxHeight;
00242   QgsComposerItem::setRotation( r );
00243   sizeChangedByRotation( width, height );
00244 
00245   double x = transform().dx() + rect().width() / 2.0 - width / 2.0;
00246   double y = transform().dy() + rect().height() / 2.0 - height / 2.0;
00247   QgsComposerItem::setSceneRect( QRectF( x, y, width, height ) );
00248 }
00249 
00250 void QgsComposerLabel::setSceneRect( const QRectF& rectangle )
00251 {
00252   if ( rectangle.width() != rect().width() || rectangle.height() != rect().height() )
00253   {
00254     double textBoxWidth = rectangle.width();
00255     double textBoxHeight = rectangle.height();
00256     imageSizeConsideringRotation( textBoxWidth, textBoxHeight );
00257     mTextBoxWidth = textBoxWidth;
00258     mTextBoxHeight = textBoxHeight;
00259   }
00260   QgsComposerItem::setSceneRect( rectangle );
00261 }
00262 
00263 bool QgsComposerLabel::writeXML( QDomElement& elem, QDomDocument & doc ) const
00264 {
00265   QString alignment;
00266 
00267   if ( elem.isNull() )
00268   {
00269     return false;
00270   }
00271 
00272   QDomElement composerLabelElem = doc.createElement( "ComposerLabel" );
00273 
00274   composerLabelElem.setAttribute( "htmlState", mHtmlState );
00275 
00276   composerLabelElem.setAttribute( "labelText", mText );
00277   composerLabelElem.setAttribute( "margin", QString::number( mMargin ) );
00278 
00279   composerLabelElem.setAttribute( "halign", mHAlignment );
00280   composerLabelElem.setAttribute( "valign", mVAlignment );
00281 
00282   //font
00283   QDomElement labelFontElem = doc.createElement( "LabelFont" );
00284   labelFontElem.setAttribute( "description", mFont.toString() );
00285   composerLabelElem.appendChild( labelFontElem );
00286 
00287   //font color
00288   QDomElement fontColorElem = doc.createElement( "FontColor" );
00289   fontColorElem.setAttribute( "red", mFontColor.red() );
00290   fontColorElem.setAttribute( "green", mFontColor.green() );
00291   fontColorElem.setAttribute( "blue", mFontColor.blue() );
00292   composerLabelElem.appendChild( fontColorElem );
00293 
00294   elem.appendChild( composerLabelElem );
00295   return _writeXML( composerLabelElem, doc );
00296 }
00297 
00298 bool QgsComposerLabel::readXML( const QDomElement& itemElem, const QDomDocument& doc )
00299 {
00300   QString alignment;
00301 
00302   if ( itemElem.isNull() )
00303   {
00304     return false;
00305   }
00306 
00307   //restore label specific properties
00308 
00309   //text
00310   mText = itemElem.attribute( "labelText" );
00311 
00312   //html state
00313   mHtmlState = itemElem.attribute( "htmlState" ).toInt();
00314 
00315   //margin
00316   mMargin = itemElem.attribute( "margin" ).toDouble();
00317 
00318   //Horizontal alignment
00319   mHAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "halign" ).toInt() );
00320 
00321   //Vertical alignment
00322   mVAlignment = ( Qt::AlignmentFlag )( itemElem.attribute( "valign" ).toInt() );
00323 
00324   //font
00325   QDomNodeList labelFontList = itemElem.elementsByTagName( "LabelFont" );
00326   if ( labelFontList.size() > 0 )
00327   {
00328     QDomElement labelFontElem = labelFontList.at( 0 ).toElement();
00329     mFont.fromString( labelFontElem.attribute( "description" ) );
00330   }
00331 
00332   //font color
00333   QDomNodeList fontColorList = itemElem.elementsByTagName( "FontColor" );
00334   if ( fontColorList.size() > 0 )
00335   {
00336     QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
00337     int red = fontColorElem.attribute( "red", "0" ).toInt();
00338     int green = fontColorElem.attribute( "green", "0" ).toInt();
00339     int blue = fontColorElem.attribute( "blue", "0" ).toInt();
00340     mFontColor = QColor( red, green, blue );
00341   }
00342   else
00343   {
00344     mFontColor = QColor( 0, 0, 0 );
00345   }
00346 
00347   //restore general composer item properties
00348   QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
00349   if ( composerItemList.size() > 0 )
00350   {
00351     QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
00352     _readXML( composerItemElem, doc );
00353   }
00354   emit itemChanged();
00355   return true;
00356 }
00357 
00358 void QgsComposerLabel::itemShiftAdjustSize( double newWidth, double newHeight, double& xShift, double& yShift ) const
00359 {
00360   //keep alignment point constant
00361   double currentWidth = rect().width();
00362   double currentHeight = rect().height();
00363   xShift = 0;
00364   yShift = 0;
00365 
00366   if ( mRotation >= 0 && mRotation < 90 )
00367   {
00368     if ( mHAlignment == Qt::AlignHCenter )
00369     {
00370       xShift = - ( newWidth - currentWidth ) / 2.0;
00371     }
00372     else if ( mHAlignment == Qt::AlignRight )
00373     {
00374       xShift = - ( newWidth - currentWidth );
00375     }
00376     if ( mVAlignment == Qt::AlignVCenter )
00377     {
00378       yShift = -( newHeight - currentHeight ) / 2.0;
00379     }
00380     else if ( mVAlignment == Qt::AlignBottom )
00381     {
00382       yShift = - ( newHeight - currentHeight );
00383     }
00384   }
00385   if ( mRotation >= 90 && mRotation < 180 )
00386   {
00387     if ( mHAlignment == Qt::AlignHCenter )
00388     {
00389       yShift = -( newHeight  - currentHeight ) / 2.0;
00390     }
00391     else if ( mHAlignment == Qt::AlignRight )
00392     {
00393       yShift = -( newHeight  - currentHeight );
00394     }
00395     if ( mVAlignment == Qt::AlignTop )
00396     {
00397       xShift = -( newWidth - currentWidth );
00398     }
00399     else if ( mVAlignment == Qt::AlignVCenter )
00400     {
00401       xShift = -( newWidth - currentWidth / 2.0 );
00402     }
00403   }
00404   else if ( mRotation >= 180 && mRotation < 270 )
00405   {
00406     if ( mHAlignment == Qt::AlignHCenter )
00407     {
00408       xShift = -( newWidth - currentWidth ) / 2.0;
00409     }
00410     else if ( mHAlignment == Qt::AlignLeft )
00411     {
00412       xShift = -( newWidth - currentWidth );
00413     }
00414     if ( mVAlignment == Qt::AlignVCenter )
00415     {
00416       yShift = ( newHeight - currentHeight ) / 2.0;
00417     }
00418     else if ( mVAlignment == Qt::AlignTop )
00419     {
00420       yShift = ( newHeight - currentHeight );
00421     }
00422   }
00423   else if ( mRotation >= 270 && mRotation < 360 )
00424   {
00425     if ( mHAlignment == Qt::AlignHCenter )
00426     {
00427       yShift = -( newHeight  - currentHeight ) / 2.0;
00428     }
00429     else if ( mHAlignment == Qt::AlignLeft )
00430     {
00431       yShift = -( newHeight  - currentHeight );
00432     }
00433     if ( mVAlignment == Qt::AlignBottom )
00434     {
00435       xShift = -( newWidth - currentWidth );
00436     }
00437     else if ( mVAlignment == Qt::AlignVCenter )
00438     {
00439       xShift = -( newWidth - currentWidth / 2.0 );
00440     }
00441   }
00442 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines