|
QGIS API Documentation
master-59fd5e0
|
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 }