QGIS API Documentation  2.14.0-Essen
qgscomposerpicture.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerpicture.cpp
3  -------------------
4  begin : September 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 "qgscomposerpicture.h"
19 #include "qgscomposerutils.h"
20 #include "qgscomposermap.h"
21 #include "qgscomposition.h"
22 #include "qgsatlascomposition.h"
23 #include "qgsproject.h"
24 #include "qgsexpression.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsmessagelog.h"
27 #include "qgsdatadefined.h"
29 #include <QDomDocument>
30 #include <QDomElement>
31 #include <QFileInfo>
32 #include <QImageReader>
33 #include <QPainter>
34 #include <QSvgRenderer>
35 #include <QNetworkRequest>
36 #include <QNetworkReply>
37 #include <QEventLoop>
38 #include <QCoreApplication>
39 
41  : QgsComposerItem( composition )
42  , mMode( Unknown )
43  , mPictureRotation( 0 )
44  , mRotationMap( nullptr )
45  , mResizeMode( QgsComposerPicture::Zoom )
46  , mPictureAnchor( UpperLeft )
47  , mHasExpressionError( false )
48 {
49  mPictureWidth = rect().width();
50  init();
51 }
52 
54  mMode( Unknown ),
55  mPictureRotation( 0 ),
56  mRotationMap( nullptr ),
57  mResizeMode( QgsComposerPicture::Zoom ),
58  mPictureAnchor( UpperLeft ),
59  mHasExpressionError( false )
60 {
61  mPictureHeight = rect().height();
62  init();
63 }
64 
65 void QgsComposerPicture::init()
66 {
67  //default to no background
68  setBackgroundEnabled( false );
69 
70  //data defined strings
72 
73  //insert PictureSource data defined property (only required due to deprecated API elements,
74  //remove after 3.0
76 
77  //connect some signals
78 
79  //connect to atlas feature changing
80  //to update the picture source expression
81  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshPicture() ) );
82 
83  //connect to composer print resolution changing
84  connect( mComposition, SIGNAL( printResolutionChanged() ), this, SLOT( recalculateSize() ) );
85 }
86 
88 {
89 
90 }
91 
92 void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
93 {
94  Q_UNUSED( itemStyle );
95  Q_UNUSED( pWidget );
96  if ( !painter )
97  {
98  return;
99  }
100  if ( !shouldDrawItem() )
101  {
102  return;
103  }
104 
105  drawBackground( painter );
106 
107  //int newDpi = ( painter->device()->logicalDpiX() + painter->device()->logicalDpiY() ) / 2;
108 
109  //picture resizing
110  if ( mMode != Unknown )
111  {
112  double boundRectWidthMM;
113  double boundRectHeightMM;
114  QRect imageRect;
115  if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame )
116  {
117  boundRectWidthMM = mPictureWidth;
118  boundRectHeightMM = mPictureHeight;
119  imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
120  }
121  else if ( mResizeMode == QgsComposerPicture::Stretch )
122  {
123  boundRectWidthMM = rect().width();
124  boundRectHeightMM = rect().height();
125  imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
126  }
127  else if ( mResizeMode == QgsComposerPicture::Clip )
128  {
129  boundRectWidthMM = rect().width();
130  boundRectHeightMM = rect().height();
131  int imageRectWidthPixels = mImage.width();
132  int imageRectHeightPixels = mImage.height();
133  imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM,
134  QSize( imageRectWidthPixels, imageRectHeightPixels ) );
135  }
136  else
137  {
138  boundRectWidthMM = rect().width();
139  boundRectHeightMM = rect().height();
140  imageRect = QRect( 0, 0, rect().width() * mComposition->printResolution() / 25.4,
141  rect().height() * mComposition->printResolution() / 25.4 );
142  }
143  painter->save();
144  //antialiasing on
145  painter->setRenderHint( QPainter::Antialiasing, true );
146 
147  //zoom mode - calculate anchor point and rotation
148  if ( mResizeMode == Zoom )
149  {
150  //TODO - allow placement modes with rotation set. for now, setting a rotation
151  //always places picture in center of frame
152  if ( !qgsDoubleNear( mPictureRotation, 0.0 ) )
153  {
154  painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
155  painter->rotate( mPictureRotation );
156  painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
157  }
158  else
159  {
160  //shift painter to edge/middle of frame depending on placement
161  double diffX = rect().width() - boundRectWidthMM;
162  double diffY = rect().height() - boundRectHeightMM;
163 
164  double dX = 0;
165  double dY = 0;
166  switch ( mPictureAnchor )
167  {
168  case UpperLeft:
169  case MiddleLeft:
170  case LowerLeft:
171  //nothing to do
172  break;
173  case UpperMiddle:
174  case Middle:
175  case LowerMiddle:
176  dX = diffX / 2.0;
177  break;
178  case UpperRight:
179  case MiddleRight:
180  case LowerRight:
181  dX = diffX;
182  break;
183  }
184  switch ( mPictureAnchor )
185  {
186  case UpperLeft:
187  case UpperMiddle:
188  case UpperRight:
189  //nothing to do
190  break;
191  case MiddleLeft:
192  case Middle:
193  case MiddleRight:
194  dY = diffY / 2.0;
195  break;
196  case LowerLeft:
197  case LowerMiddle:
198  case LowerRight:
199  dY = diffY;
200  break;
201  }
202  painter->translate( dX, dY );
203  }
204  }
205  else if ( mResizeMode == ZoomResizeFrame )
206  {
207  if ( !qgsDoubleNear( mPictureRotation, 0.0 ) )
208  {
209  painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
210  painter->rotate( mPictureRotation );
211  painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
212  }
213  }
214 
215  if ( mMode == SVG )
216  {
217  mSVG.render( painter, QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ) );
218  }
219  else if ( mMode == RASTER )
220  {
221  painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
222  }
223 
224  painter->restore();
225  }
226 
227  //frame and selection boxes
228  drawFrame( painter );
229  if ( isSelected() )
230  {
231  drawSelectionBoxes( painter );
232  }
233 }
234 
235 QRect QgsComposerPicture::clippedImageRect( double &boundRectWidthMM, double &boundRectHeightMM, QSize imageRectPixels )
236 {
237  int boundRectWidthPixels = boundRectWidthMM * mComposition->printResolution() / 25.4;
238  int boundRectHeightPixels = boundRectHeightMM * mComposition->printResolution() / 25.4;
239 
240  //update boundRectWidth/Height so that they exactly match pixel bounds
241  boundRectWidthMM = boundRectWidthPixels * 25.4 / mComposition->printResolution();
242  boundRectHeightMM = boundRectHeightPixels * 25.4 / mComposition->printResolution();
243 
244  //calculate part of image which fits in bounds
245  int leftClip = 0;
246  int topClip = 0;
247 
248  //calculate left crop
249  switch ( mPictureAnchor )
250  {
251  case UpperLeft:
252  case MiddleLeft:
253  case LowerLeft:
254  leftClip = 0;
255  break;
256  case UpperMiddle:
257  case Middle:
258  case LowerMiddle:
259  leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
260  break;
261  case UpperRight:
262  case MiddleRight:
263  case LowerRight:
264  leftClip = imageRectPixels.width() - boundRectWidthPixels;
265  break;
266  }
267 
268  //calculate top crop
269  switch ( mPictureAnchor )
270  {
271  case UpperLeft:
272  case UpperMiddle:
273  case UpperRight:
274  topClip = 0;
275  break;
276  case MiddleLeft:
277  case Middle:
278  case MiddleRight:
279  topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
280  break;
281  case LowerLeft:
282  case LowerMiddle:
283  case LowerRight:
284  topClip = imageRectPixels.height() - boundRectHeightPixels;
285  break;
286  }
287 
288  return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
289 }
290 
292 {
293  setPicturePath( path );
294 }
295 
297 {
298  const QgsExpressionContext* evalContext = context;
300  if ( !evalContext )
301  {
302  scopedContext.reset( createExpressionContext() );
303  evalContext = scopedContext.data();
304  }
305 
306  QString source = mSourcePath;
307 
308  //data defined source set?
309  mHasExpressionError = false;
310  QVariant exprVal;
312  {
313  if ( dataDefinedEvaluate( QgsComposerObject::PictureSource, exprVal, *evalContext ) )
314  {
315  source = exprVal.toString().trimmed();
316  QgsDebugMsg( QString( "exprVal PictureSource:%1" ).arg( source ) );
317  }
318  else
319  {
320  mHasExpressionError = true;
321  source = QString();
322  QgsMessageLog::logMessage( tr( "Picture expression eval error" ) );
323  }
324  }
325 
326  loadPicture( source );
327 }
328 
329 void QgsComposerPicture::loadRemotePicture( const QString &url )
330 {
331  //remote location
332 
333  QgsNetworkContentFetcher fetcher;
334  //pause until HTML fetch
335  mLoaded = false;
336  fetcher.fetchContent( QUrl( url ) );
337  connect( &fetcher, SIGNAL( finished() ), this, SLOT( remotePictureLoaded() ) );
338 
339  while ( !mLoaded )
340  {
341  qApp->processEvents();
342  }
343 
344  QNetworkReply* reply = fetcher.reply();
345  if ( reply )
346  {
347  QImageReader imageReader( reply );
348  mImage = imageReader.read();
349  mMode = RASTER;
350  reply->deleteLater();
351  }
352  else
353  {
354  mMode = Unknown;
355  }
356 }
357 
358 void QgsComposerPicture::loadLocalPicture( const QString &path )
359 {
360  QFile pic;
361  pic.setFileName( path );
362 
363  if ( !pic.exists() )
364  {
365  mMode = Unknown;
366  }
367  else
368  {
369  QFileInfo sourceFileInfo( pic );
370  QString sourceFileSuffix = sourceFileInfo.suffix();
371  if ( sourceFileSuffix.compare( "svg", Qt::CaseInsensitive ) == 0 )
372  {
373  //try to open svg
374  mSVG.load( pic.fileName() );
375  if ( mSVG.isValid() )
376  {
377  mMode = SVG;
378  QRect viewBox = mSVG.viewBox(); //take width/height ratio from view box instead of default size
379  mDefaultSvgSize.setWidth( viewBox.width() );
380  mDefaultSvgSize.setHeight( viewBox.height() );
381  }
382  else
383  {
384  mMode = Unknown;
385  }
386  }
387  else
388  {
389  //try to open raster with QImageReader
390  QImageReader imageReader( pic.fileName() );
391  if ( imageReader.read( &mImage ) )
392  {
393  mMode = RASTER;
394  }
395  else
396  {
397  mMode = Unknown;
398  }
399  }
400  }
401 
402 }
403 
404 void QgsComposerPicture::remotePictureLoaded()
405 {
406  mLoaded = true;
407 }
408 
409 void QgsComposerPicture::loadPicture( const QString &path )
410 {
411  if ( path.startsWith( "http" ) )
412  {
413  //remote location
414  loadRemotePicture( path );
415  }
416  else
417  {
418  //local location
419  loadLocalPicture( path );
420  }
421  if ( mMode != Unknown ) //make sure we start with a new QImage
422  {
423  recalculateSize();
424  }
425  else if ( mHasExpressionError || !( path.isEmpty() ) )
426  {
427  //trying to load an invalid file or bad expression, show cross picture
428  mMode = SVG;
429  QString badFile( ":/images/composer/missing_image.svg" );
430  mSVG.load( badFile );
431  if ( mSVG.isValid() )
432  {
433  mMode = SVG;
434  QRect viewBox = mSVG.viewBox(); //take width/height ratio from view box instead of default size
435  mDefaultSvgSize.setWidth( viewBox.width() );
436  mDefaultSvgSize.setHeight( viewBox.height() );
437  recalculateSize();
438  }
439  }
440 
441  emit itemChanged();
442 }
443 
444 QRectF QgsComposerPicture::boundedImageRect( double deviceWidth, double deviceHeight )
445 {
446  double imageToDeviceRatio;
447  if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
448  {
449  imageToDeviceRatio = deviceWidth / mImage.width();
450  double height = imageToDeviceRatio * mImage.height();
451  return QRectF( 0, 0, deviceWidth, height );
452  }
453  else
454  {
455  imageToDeviceRatio = deviceHeight / mImage.height();
456  double width = imageToDeviceRatio * mImage.width();
457  return QRectF( 0, 0, width, deviceHeight );
458  }
459 }
460 
461 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight )
462 {
463  double imageToSvgRatio;
464  if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
465  {
466  imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
467  double width = mDefaultSvgSize.width() * imageToSvgRatio;
468  return QRectF( 0, 0, width, deviceHeight );
469  }
470  else
471  {
472  imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
473  double height = mDefaultSvgSize.height() * imageToSvgRatio;
474  return QRectF( 0, 0, deviceWidth, height );
475  }
476 }
477 
478 QSizeF QgsComposerPicture::pictureSize()
479 {
480  if ( mMode == SVG )
481  {
482  return mDefaultSvgSize;
483  }
484  else if ( mMode == RASTER )
485  {
486  return QSizeF( mImage.width(), mImage.height() );
487  }
488  else
489  {
490  return QSizeF( 0, 0 );
491  }
492 }
493 
494 #if 0
495 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight )
496 {
497  double imageToSvgRatio;
498  if ( deviceWidth / mDefaultSvgSize.width() < deviceHeight / mDefaultSvgSize.height() )
499  {
500  imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
501  double height = mDefaultSvgSize.height() * imageToSvgRatio;
502  return QRectF( 0, 0, deviceWidth, height );
503  }
504  else
505  {
506  imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
507  double width = mDefaultSvgSize.width() * imageToSvgRatio;
508  return QRectF( 0, 0, width, deviceHeight );
509  }
510 }
511 #endif //0
512 
513 void QgsComposerPicture::setSceneRect( const QRectF& rectangle )
514 {
515 
516  QSizeF currentPictureSize = pictureSize();
517 
518  if ( mResizeMode == QgsComposerPicture::Clip )
519  {
520  QgsComposerItem::setSceneRect( rectangle );
521  mPictureWidth = rectangle.width();
522  mPictureHeight = rectangle.height();
523  return;
524  }
525 
526  QRectF newRect = rectangle;
527 
528  if ( mResizeMode == ZoomResizeFrame && !rect().isEmpty() && !( currentPictureSize.isEmpty() ) )
529  {
530  QSizeF targetImageSize;
531  if ( qgsDoubleNear( mPictureRotation, 0.0 ) )
532  {
533  targetImageSize = currentPictureSize;
534  }
535  else
536  {
537  //calculate aspect ratio of bounds of rotated image
538  QTransform tr;
539  tr.rotate( mPictureRotation );
540  QRectF rotatedBounds = tr.mapRect( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ) );
541  targetImageSize = QSizeF( rotatedBounds.width(), rotatedBounds.height() );
542  }
543 
544  //if height has changed more than width, then fix width and set height correspondingly
545  //else, do the opposite
546  if ( qAbs( rect().width() - rectangle.width() ) <
547  qAbs( rect().height() - rectangle.height() ) )
548  {
549  newRect.setHeight( targetImageSize.height() * newRect.width() / targetImageSize.width() );
550  }
551  else
552  {
553  newRect.setWidth( targetImageSize.width() * newRect.height() / targetImageSize.height() );
554  }
555  }
556  else if ( mResizeMode == FrameToImageSize )
557  {
558  if ( !( currentPictureSize.isEmpty() ) )
559  {
560  newRect.setWidth( currentPictureSize.width() * 25.4 / mComposition->printResolution() );
561  newRect.setHeight( currentPictureSize.height() * 25.4 / mComposition->printResolution() );
562  }
563  }
564 
565  //find largest scaling of picture with this rotation which fits in item
566  if ( mResizeMode == Zoom || mResizeMode == ZoomResizeFrame )
567  {
568  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), newRect, mPictureRotation );
569  mPictureWidth = rotatedImageRect.width();
570  mPictureHeight = rotatedImageRect.height();
571  }
572  else
573  {
574  mPictureWidth = newRect.width();
575  mPictureHeight = newRect.height();
576  }
577 
579  emit itemChanged();
580 }
581 
583 {
584  //kept for compatibility for QGIS2.0 api
585  setPictureRotation( r );
586 }
587 
589 {
590  double oldRotation = mPictureRotation;
591  mPictureRotation = r;
592 
593  if ( mResizeMode == Zoom )
594  {
595  //find largest scaling of picture with this rotation which fits in item
596  QSizeF currentPictureSize = pictureSize();
597  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation );
598  mPictureWidth = rotatedImageRect.width();
599  mPictureHeight = rotatedImageRect.height();
600  update();
601  }
602  else if ( mResizeMode == ZoomResizeFrame )
603  {
604  QSizeF currentPictureSize = pictureSize();
605  QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
606 
607  //calculate actual size of image inside frame
608  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), oldRotation );
609 
610  //rotate image rect by new rotation and get bounding box
611  QTransform tr;
612  tr.rotate( mPictureRotation );
613  QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
614 
615  //keep the center in the same location
616  newRect.moveCenter( oldRect.center() );
618  emit itemChanged();
619  }
620 
621  emit pictureRotationChanged( mPictureRotation );
622 }
623 
624 void QgsComposerPicture::setRotationMap( int composerMapId )
625 {
626  if ( !mComposition )
627  {
628  return;
629  }
630 
631  if ( composerMapId == -1 ) //disable rotation from map
632  {
633  QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
634  mRotationMap = nullptr;
635  }
636 
637  const QgsComposerMap* map = mComposition->getComposerMapById( composerMapId );
638  if ( !map )
639  {
640  return;
641  }
642  if ( mRotationMap )
643  {
644  QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
645  }
646  mPictureRotation = map->mapRotation();
647  QObject::connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
648  mRotationMap = map;
649  update();
650  emit pictureRotationChanged( mPictureRotation );
651 }
652 
654 {
655  mResizeMode = mode;
657  || ( mode == QgsComposerPicture::Zoom && !qgsDoubleNear( mPictureRotation, 0.0 ) ) )
658  {
659  //call set scene rect to force item to resize to fit picture
660  recalculateSize();
661  }
662  update();
663 }
664 
666 {
667  //call set scene rect with current position/size, as this will trigger the
668  //picture item to recalculate its frame and image size
669  setSceneRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) );
670 }
671 
673 {
674  const QgsExpressionContext* evalContext = context;
676  if ( !evalContext )
677  {
678  scopedContext.reset( createExpressionContext() );
679  evalContext = scopedContext.data();
680  }
681 
683  {
684  refreshPicture( evalContext );
685  }
686 
687  QgsComposerItem::refreshDataDefinedProperty( property, evalContext );
688 }
689 
691 {
693  refreshPicture();
694 }
695 
697 {
699  refreshPicture();
700 }
701 
703 {
704  return picturePath();
705 }
706 
708 {
709  mSourcePath = path;
710  refreshPicture();
711 }
712 
714 {
715  return mSourcePath;
716 }
717 
719 {
720  if ( elem.isNull() )
721  {
722  return false;
723  }
724  QDomElement composerPictureElem = doc.createElement( "ComposerPicture" );
725  composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourcePath ) );
726  composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) );
727  composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) );
728  composerPictureElem.setAttribute( "resizeMode", QString::number( static_cast< int >( mResizeMode ) ) );
729  composerPictureElem.setAttribute( "anchorPoint", QString::number( static_cast< int >( mPictureAnchor ) ) );
730 
731  //rotation
732  composerPictureElem.setAttribute( "pictureRotation", QString::number( mPictureRotation ) );
733  if ( !mRotationMap )
734  {
735  composerPictureElem.setAttribute( "mapId", -1 );
736  }
737  else
738  {
739  composerPictureElem.setAttribute( "mapId", mRotationMap->id() );
740  }
741 
742  _writeXML( composerPictureElem, doc );
743  elem.appendChild( composerPictureElem );
744  return true;
745 }
746 
747 bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocument& doc )
748 {
749  if ( itemElem.isNull() )
750  {
751  return false;
752  }
753 
754  mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble();
755  mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble();
756  mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() );
757  //when loading from xml, default to anchor point of middle to match pre 2.4 behaviour
758  mPictureAnchor = static_cast< QgsComposerItem::ItemPositionMode >( itemElem.attribute( "anchorPoint", QString::number( QgsComposerItem::Middle ) ).toInt() );
759 
760  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
761  if ( !composerItemList.isEmpty() )
762  {
763  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
764 
765  if ( !qgsDoubleNear( composerItemElem.attribute( "rotation", "0" ).toDouble(), 0.0 ) )
766  {
767  //in versions prior to 2.1 picture rotation was stored in the rotation attribute
768  mPictureRotation = composerItemElem.attribute( "rotation", "0" ).toDouble();
769  }
770 
771  _readXML( composerItemElem, doc );
772  }
773 
774  mDefaultSvgSize = QSize( 0, 0 );
775 
776  if ( itemElem.hasAttribute( "sourceExpression" ) )
777  {
778  //update pre 2.5 picture expression to use data defined expression
779  QString sourceExpression = itemElem.attribute( "sourceExpression", "" );
780  QString useExpression = itemElem.attribute( "useExpression" );
781  bool expressionActive;
782  if ( useExpression.compare( "true", Qt::CaseInsensitive ) == 0 )
783  {
784  expressionActive = true;
785  }
786  else
787  {
788  expressionActive = false;
789  }
790 
791  setDataDefinedProperty( QgsComposerObject::PictureSource, expressionActive, true, sourceExpression, QString() );
792  }
793 
794  mSourcePath = QgsProject::instance()->readPath( itemElem.attribute( "file" ) );
795 
796  //picture rotation
797  if ( !qgsDoubleNear( itemElem.attribute( "pictureRotation", "0" ).toDouble(), 0.0 ) )
798  {
799  mPictureRotation = itemElem.attribute( "pictureRotation", "0" ).toDouble();
800  }
801 
802  //rotation map
803  int rotationMapId = itemElem.attribute( "mapId", "-1" ).toInt();
804  if ( rotationMapId == -1 )
805  {
806  mRotationMap = nullptr;
807  }
808  else if ( mComposition )
809  {
810 
811  if ( mRotationMap )
812  {
813  QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
814  }
815  mRotationMap = mComposition->getComposerMapById( rotationMapId );
816  QObject::connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
817  }
818 
819  refreshPicture();
820 
821  emit itemChanged();
822  return true;
823 }
824 
826 {
827  if ( !mRotationMap )
828  {
829  return -1;
830  }
831  else
832  {
833  return mRotationMap->id();
834  }
835 }
836 
838 {
839  mPictureAnchor = anchor;
840  update();
841 }
842 
844 {
846 }
847 
849 {
851 }
852 
853 bool QgsComposerPicture::imageSizeConsideringRotation( double& width, double& height ) const
854 {
855  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
857  return QgsComposerItem::imageSizeConsideringRotation( width, height, mPictureRotation );
859 }
860 
861 bool QgsComposerPicture::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
862 {
863  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
865  return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mPictureRotation );
867 }
868 
869 void QgsComposerPicture::sizeChangedByRotation( double& width, double& height )
870 {
871  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
873  return QgsComposerItem::sizeChangedByRotation( width, height, mPictureRotation );
875 }
QDomNodeList elementsByTagName(const QString &tagname) const
void setActive(bool active)
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
qreal x() const
qreal y() const
Q_DECL_DEPRECATED bool imageSizeConsideringRotation(double &width, double &height, double rotation) const
Calculates width and hight of the picture (in mm) such that it fits into the item frame with the give...
void setHeight(int height)
int width() const
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
void render(QPainter *painter)
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void itemChanged()
Emitted when the item changes.
bool writeXML(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom element.
QgsComposerPicture(QgsComposition *composition)
Q_DECL_DEPRECATED bool imageSizeConsideringRotation(double &width, double &height) const
Calculates width and hight of the picture (in mm) such that it fits into the item frame with the give...
Mode mode() const
Returns the current picture mode (image format).
bool isValid() const
QMap< QgsComposerObject::DataDefinedProperty, QString > mDataDefinedNames
Map of data defined properties for the item to string name to use when exporting item to xml...
QString fileName() const
A item that forms part of a map composition.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:407
void setFileName(const QString &name)
void save()
int height() const
QString expressionString() const
Returns the expression string of this QgsDataDefined.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QString picturePath() const
Returns the path of the source image.
QNetworkReply * reply()
Returns a reference to the network reply.
bool exists() const
void rotate(qreal angle)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
double toDouble(bool *ok) const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual QgsExpressionContext * createExpressionContext() const override
Creates an expression context relating to the item&#39;s current state.
QString tr(const char *sourceText, const char *disambiguation, int n)
QString readPath(QString filename) const
Turn filename read from the project file to an absolute path.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
void update(const QRectF &rect)
DataDefinedProperty
Data defined properties for different item types.
A composer class that displays svg files or raster format (jpg, png, ...)
void recalculateSize()
Forces a recalculation of the picture&#39;s frame size.
void setHeight(qreal height)
void reset(T *other)
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
QDomElement toElement() const
QgsDataDefined * dataDefinedProperty(const DataDefinedProperty property) const
Returns a reference to the data defined settings for one of the item&#39;s data defined properties...
bool isEmpty() const
QPointF pos() const
QString number(int n, int base)
void setPictureAnchor(QgsComposerItem::ItemPositionMode anchor)
Sets the picture&#39;s anchor point, which controls how it is placed within the picture item&#39;s frame...
bool isEmpty() const
QVariant property(const char *name) const
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint.
bool load(const QString &filename)
ResizeMode
Controls how pictures are scaled within the item&#39;s frame.
bool hasAttribute(const QString &name) const
HTTP network content fetcher.
virtual void drawSelectionBoxes(QPainter *p)
Draws additional graphics on selected items.
int printResolution() const
int width() const
void setAttribute(const QString &name, const QString &value)
bool isSelected() const
virtual void setResizeMode(ResizeMode mode)
Sets the resize mode used for drawing the picture within the item bounds.
int toInt(bool *ok, int base) const
void setWidth(int width)
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
bool isEmpty() const
QString trimmed() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
static QRectF largestRotatedRectWithinBounds(const QRectF &originalRect, const QRectF &boundsRect, const double rotation)
Calculates the largest scaled version of originalRect which fits within boundsRect, when it is rotated by a specified amount.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
QPointF center() const
virtual void setUsePictureExpression(bool useExpression)
Sets whether the picture should use an expression based image source path.
void deleteLater()
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual void setPictureExpression(const QString &expression)
Sets an expression to use for the picture source.
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item.
void setBackgroundEnabled(const bool drawBackground)
Set whether this item has a Background drawn around it or not.
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
Graphics scene for map printing.
void moveCenter(const QPointF &position)
Object representing map window.
T * data() const
Q_DECL_DEPRECATED QString pictureFile() const
Returns the path of the source image file.
Q_DECL_DEPRECATED bool cornerPointOnRotatedAndScaledRect(double &x, double &y, double width, double height) const
Calculates corner point after rotation and scaling.
bool dataDefinedEvaluate(const QgsComposerObject::DataDefinedProperty property, QVariant &expressionValue, const QgsExpressionContext &context=QgsExpressionContext()) const
Evaluate a data defined property and return the calculated value.
void pictureRotationChanged(double newRotation)
Is emitted on picture rotation change.
bool isNull() const
void restore()
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:408
int id() const
Get identification number.
QTransform & rotate(qreal angle, Qt::Axis axis)
Q_DECL_DEPRECATED void setPictureFile(const QString &path)
Sets the source file of the image (may be svg or a raster format).
QgsComposition * mComposition
Q_DECL_DEPRECATED bool cornerPointOnRotatedAndScaledRect(double &x, double &y, double width, double height, double rotation) const
Calculates corner point after rotation and scaling.
int width() const
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
qreal width() const
QString suffix() const
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
void setWidth(qreal width)
virtual void drawBackground(QPainter *p)
Draw background.
QImage read()
Q_DECL_DEPRECATED QString pictureExpression() const
Returns the expression the item is using for the picture source.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:381
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...
int height() const
void setDataDefinedProperty(const DataDefinedProperty property, const bool active, const bool useExpression, const QString &expression, const QString &field)
Sets parameters for a data defined property for the item.
void translate(const QPointF &offset)
void setSceneRect(const QRectF &rectangle) override
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
bool isActive() const
qreal height() const
int height() const
QgsAtlasComposition & atlasComposition()
virtual void setRotation(double r) override
Sets the picture rotation within the item bounds.
iterator insert(const Key &key, const T &value)
void setRotationMap(int composerMapId)
Sets the map object for rotation (by id).
void setPicturePath(const QString &path)
Sets the source path of the image (may be svg or a raster format).
QgsComposerItem(QgsComposition *composition, bool manageZValue=true)
Constructor.
virtual void setPictureRotation(double r)
Sets the picture rotation within the item bounds.
QDomElement createElement(const QString &tagName)
qreal height() const
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void fetchContent(const QUrl &url)
Fetches content from a remote URL and handles redirects.
int rotationMap() const
Returns the id of the rotation map.
int compare(const QString &other) const
QString toString() const
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
bool isActive() const
QRect mapRect(const QRect &rectangle) const
qreal width() const
void refreshPicture(const QgsExpressionContext *context=nullptr)
Recalculates the source image (if using an expression for picture&#39;s source) and reloads and redraws t...
Q_DECL_DEPRECATED void sizeChangedByRotation(double &width, double &height, double rotation)
Calculates width / height of the bounding box of a rotated rectangle.
Q_DECL_DEPRECATED bool usePictureExpression() const
Returns whether the picture item is using an expression for the image source.
QDomNode at(int index) const
Q_DECL_DEPRECATED void sizeChangedByRotation(double &width, double &height)
Calculates width / height of the bounding box of a rotated rectangle.
QRectF rect() const