QGIS API Documentation  2.15.0-Master (af20121)
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 "qgssymbollayerv2utils.h"
30 #include "qgssvgcache.h"
31 #include <QDomDocument>
32 #include <QDomElement>
33 #include <QFileInfo>
34 #include <QImageReader>
35 #include <QPainter>
36 #include <QSvgRenderer>
37 #include <QNetworkRequest>
38 #include <QNetworkReply>
39 #include <QEventLoop>
40 #include <QCoreApplication>
41 
43  : QgsComposerItem( composition )
44  , mMode( Unknown )
45  , mPictureRotation( 0 )
46  , mRotationMap( nullptr )
47  , mResizeMode( QgsComposerPicture::Zoom )
48  , mPictureAnchor( UpperLeft )
49  , mSvgFillColor( QColor( 255, 255, 255 ) )
50  , mSvgBorderColor( QColor( 0, 0, 0 ) )
51  , mSvgBorderWidth( 0.2 )
52  , mHasExpressionError( false )
53  , mLoadingSvg( false )
54 {
55  mPictureWidth = rect().width();
56  init();
57 }
58 
60  : QgsComposerItem( nullptr )
61  , mMode( Unknown )
62  , mPictureRotation( 0 )
63  , mRotationMap( nullptr )
64  , mResizeMode( QgsComposerPicture::Zoom )
65  , mPictureAnchor( UpperLeft )
66  , mSvgFillColor( QColor( 255, 255, 255 ) )
67  , mSvgBorderColor( QColor( 0, 0, 0 ) )
68  , mSvgBorderWidth( 0.2 )
69  , mHasExpressionError( false )
70  , mLoadingSvg( false )
71 {
72  mPictureHeight = rect().height();
73  init();
74 }
75 
76 void QgsComposerPicture::init()
77 {
78  //default to no background
79  setBackgroundEnabled( false );
80 
81  //data defined strings
83 
84  //insert PictureSource data defined property (only required due to deprecated API elements,
85  //remove after 3.0
87 
88  //connect some signals
89 
90  //connect to atlas feature changing
91  //to update the picture source expression
92  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshPicture() ) );
93 
94  //connect to composer print resolution changing
95  connect( mComposition, SIGNAL( printResolutionChanged() ), this, SLOT( recalculateSize() ) );
96 }
97 
99 {
100 
101 }
102 
103 void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
104 {
105  Q_UNUSED( itemStyle );
106  Q_UNUSED( pWidget );
107  if ( !painter )
108  {
109  return;
110  }
111  if ( !shouldDrawItem() )
112  {
113  return;
114  }
115 
116  drawBackground( painter );
117 
118  //int newDpi = ( painter->device()->logicalDpiX() + painter->device()->logicalDpiY() ) / 2;
119 
120  //picture resizing
121  if ( mMode != Unknown )
122  {
123  double boundRectWidthMM;
124  double boundRectHeightMM;
125  QRect imageRect;
126  if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame )
127  {
128  boundRectWidthMM = mPictureWidth;
129  boundRectHeightMM = mPictureHeight;
130  imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
131  }
132  else if ( mResizeMode == QgsComposerPicture::Stretch )
133  {
134  boundRectWidthMM = rect().width();
135  boundRectHeightMM = rect().height();
136  imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
137  }
138  else if ( mResizeMode == QgsComposerPicture::Clip )
139  {
140  boundRectWidthMM = rect().width();
141  boundRectHeightMM = rect().height();
142  int imageRectWidthPixels = mImage.width();
143  int imageRectHeightPixels = mImage.height();
144  imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM,
145  QSize( imageRectWidthPixels, imageRectHeightPixels ) );
146  }
147  else
148  {
149  boundRectWidthMM = rect().width();
150  boundRectHeightMM = rect().height();
151  imageRect = QRect( 0, 0, rect().width() * mComposition->printResolution() / 25.4,
152  rect().height() * mComposition->printResolution() / 25.4 );
153  }
154  painter->save();
155  //antialiasing on
156  painter->setRenderHint( QPainter::Antialiasing, true );
157 
158  //zoom mode - calculate anchor point and rotation
159  if ( mResizeMode == Zoom )
160  {
161  //TODO - allow placement modes with rotation set. for now, setting a rotation
162  //always places picture in center of frame
163  if ( !qgsDoubleNear( mPictureRotation, 0.0 ) )
164  {
165  painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
166  painter->rotate( mPictureRotation );
167  painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
168  }
169  else
170  {
171  //shift painter to edge/middle of frame depending on placement
172  double diffX = rect().width() - boundRectWidthMM;
173  double diffY = rect().height() - boundRectHeightMM;
174 
175  double dX = 0;
176  double dY = 0;
177  switch ( mPictureAnchor )
178  {
179  case UpperLeft:
180  case MiddleLeft:
181  case LowerLeft:
182  //nothing to do
183  break;
184  case UpperMiddle:
185  case Middle:
186  case LowerMiddle:
187  dX = diffX / 2.0;
188  break;
189  case UpperRight:
190  case MiddleRight:
191  case LowerRight:
192  dX = diffX;
193  break;
194  }
195  switch ( mPictureAnchor )
196  {
197  case UpperLeft:
198  case UpperMiddle:
199  case UpperRight:
200  //nothing to do
201  break;
202  case MiddleLeft:
203  case Middle:
204  case MiddleRight:
205  dY = diffY / 2.0;
206  break;
207  case LowerLeft:
208  case LowerMiddle:
209  case LowerRight:
210  dY = diffY;
211  break;
212  }
213  painter->translate( dX, dY );
214  }
215  }
216  else if ( mResizeMode == ZoomResizeFrame )
217  {
218  if ( !qgsDoubleNear( mPictureRotation, 0.0 ) )
219  {
220  painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
221  painter->rotate( mPictureRotation );
222  painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
223  }
224  }
225 
226  if ( mMode == SVG )
227  {
228  mSVG.render( painter, QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ) );
229  }
230  else if ( mMode == RASTER )
231  {
232  painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
233  }
234 
235  painter->restore();
236  }
237 
238  //frame and selection boxes
239  drawFrame( painter );
240  if ( isSelected() )
241  {
242  drawSelectionBoxes( painter );
243  }
244 }
245 
246 QRect QgsComposerPicture::clippedImageRect( double &boundRectWidthMM, double &boundRectHeightMM, QSize imageRectPixels )
247 {
248  int boundRectWidthPixels = boundRectWidthMM * mComposition->printResolution() / 25.4;
249  int boundRectHeightPixels = boundRectHeightMM * mComposition->printResolution() / 25.4;
250 
251  //update boundRectWidth/Height so that they exactly match pixel bounds
252  boundRectWidthMM = boundRectWidthPixels * 25.4 / mComposition->printResolution();
253  boundRectHeightMM = boundRectHeightPixels * 25.4 / mComposition->printResolution();
254 
255  //calculate part of image which fits in bounds
256  int leftClip = 0;
257  int topClip = 0;
258 
259  //calculate left crop
260  switch ( mPictureAnchor )
261  {
262  case UpperLeft:
263  case MiddleLeft:
264  case LowerLeft:
265  leftClip = 0;
266  break;
267  case UpperMiddle:
268  case Middle:
269  case LowerMiddle:
270  leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
271  break;
272  case UpperRight:
273  case MiddleRight:
274  case LowerRight:
275  leftClip = imageRectPixels.width() - boundRectWidthPixels;
276  break;
277  }
278 
279  //calculate top crop
280  switch ( mPictureAnchor )
281  {
282  case UpperLeft:
283  case UpperMiddle:
284  case UpperRight:
285  topClip = 0;
286  break;
287  case MiddleLeft:
288  case Middle:
289  case MiddleRight:
290  topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
291  break;
292  case LowerLeft:
293  case LowerMiddle:
294  case LowerRight:
295  topClip = imageRectPixels.height() - boundRectHeightPixels;
296  break;
297  }
298 
299  return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
300 }
301 
303 {
304  setPicturePath( path );
305 }
306 
308 {
309  const QgsExpressionContext* evalContext = context;
311  if ( !evalContext )
312  {
313  scopedContext.reset( createExpressionContext() );
314  evalContext = scopedContext.data();
315  }
316 
317  QString source = mSourcePath;
318 
319  //data defined source set?
320  mHasExpressionError = false;
321  QVariant exprVal;
323  {
324  if ( dataDefinedEvaluate( QgsComposerObject::PictureSource, exprVal, *evalContext ) )
325  {
326  source = exprVal.toString().trimmed();
327  QgsDebugMsg( QString( "exprVal PictureSource:%1" ).arg( source ) );
328  }
329  else
330  {
331  mHasExpressionError = true;
332  source = QString();
333  QgsMessageLog::logMessage( tr( "Picture expression eval error" ) );
334  }
335  }
336 
337  loadPicture( source );
338 }
339 
340 void QgsComposerPicture::loadRemotePicture( const QString &url )
341 {
342  //remote location
343 
344  QgsNetworkContentFetcher fetcher;
345  //pause until HTML fetch
346  mLoaded = false;
347  fetcher.fetchContent( QUrl( url ) );
348  connect( &fetcher, SIGNAL( finished() ), this, SLOT( remotePictureLoaded() ) );
349 
350  while ( !mLoaded )
351  {
352  qApp->processEvents();
353  }
354 
355  QNetworkReply* reply = fetcher.reply();
356  if ( reply )
357  {
358  QImageReader imageReader( reply );
359  mImage = imageReader.read();
360  mMode = RASTER;
361  reply->deleteLater();
362  }
363  else
364  {
365  mMode = Unknown;
366  }
367 }
368 
369 void QgsComposerPicture::loadLocalPicture( const QString &path )
370 {
371  QFile pic;
372  pic.setFileName( path );
373 
374  if ( !pic.exists() )
375  {
376  mMode = Unknown;
377  }
378  else
379  {
380  QFileInfo sourceFileInfo( pic );
381  QString sourceFileSuffix = sourceFileInfo.suffix();
382  if ( sourceFileSuffix.compare( "svg", Qt::CaseInsensitive ) == 0 )
383  {
384  //try to open svg
385  const QByteArray &svgContent = QgsSvgCache::instance()->svgContent( pic.fileName(), rect().width(), mSvgFillColor, mSvgBorderColor, mSvgBorderWidth,
386  1.0, 1.0 );
387  mSVG.load( svgContent );
388  if ( mSVG.isValid() )
389  {
390  mMode = SVG;
391  QRect viewBox = mSVG.viewBox(); //take width/height ratio from view box instead of default size
392  mDefaultSvgSize.setWidth( viewBox.width() );
393  mDefaultSvgSize.setHeight( viewBox.height() );
394  }
395  else
396  {
397  mMode = Unknown;
398  }
399  }
400  else
401  {
402  //try to open raster with QImageReader
403  QImageReader imageReader( pic.fileName() );
404  if ( imageReader.read( &mImage ) )
405  {
406  mMode = RASTER;
407  }
408  else
409  {
410  mMode = Unknown;
411  }
412  }
413  }
414 
415 }
416 
417 void QgsComposerPicture::remotePictureLoaded()
418 {
419  mLoaded = true;
420 }
421 
422 void QgsComposerPicture::loadPicture( const QString &path )
423 {
424  if ( path.startsWith( "http" ) )
425  {
426  //remote location
427  loadRemotePicture( path );
428  }
429  else
430  {
431  //local location
432  loadLocalPicture( path );
433  }
434  if ( mMode != Unknown ) //make sure we start with a new QImage
435  {
436  recalculateSize();
437  }
438  else if ( mHasExpressionError || !( path.isEmpty() ) )
439  {
440  //trying to load an invalid file or bad expression, show cross picture
441  mMode = SVG;
442  QString badFile( ":/images/composer/missing_image.svg" );
443  mSVG.load( badFile );
444  if ( mSVG.isValid() )
445  {
446  mMode = SVG;
447  QRect viewBox = mSVG.viewBox(); //take width/height ratio from view box instead of default size
448  mDefaultSvgSize.setWidth( viewBox.width() );
449  mDefaultSvgSize.setHeight( viewBox.height() );
450  recalculateSize();
451  }
452  }
453 
454  emit itemChanged();
455 }
456 
457 QRectF QgsComposerPicture::boundedImageRect( double deviceWidth, double deviceHeight )
458 {
459  double imageToDeviceRatio;
460  if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
461  {
462  imageToDeviceRatio = deviceWidth / mImage.width();
463  double height = imageToDeviceRatio * mImage.height();
464  return QRectF( 0, 0, deviceWidth, height );
465  }
466  else
467  {
468  imageToDeviceRatio = deviceHeight / mImage.height();
469  double width = imageToDeviceRatio * mImage.width();
470  return QRectF( 0, 0, width, deviceHeight );
471  }
472 }
473 
474 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight )
475 {
476  double imageToSvgRatio;
477  if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
478  {
479  imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
480  double width = mDefaultSvgSize.width() * imageToSvgRatio;
481  return QRectF( 0, 0, width, deviceHeight );
482  }
483  else
484  {
485  imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
486  double height = mDefaultSvgSize.height() * imageToSvgRatio;
487  return QRectF( 0, 0, deviceWidth, height );
488  }
489 }
490 
491 QSizeF QgsComposerPicture::pictureSize()
492 {
493  if ( mMode == SVG )
494  {
495  return mDefaultSvgSize;
496  }
497  else if ( mMode == RASTER )
498  {
499  return QSizeF( mImage.width(), mImage.height() );
500  }
501  else
502  {
503  return QSizeF( 0, 0 );
504  }
505 }
506 
507 #if 0
508 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight )
509 {
510  double imageToSvgRatio;
511  if ( deviceWidth / mDefaultSvgSize.width() < deviceHeight / mDefaultSvgSize.height() )
512  {
513  imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
514  double height = mDefaultSvgSize.height() * imageToSvgRatio;
515  return QRectF( 0, 0, deviceWidth, height );
516  }
517  else
518  {
519  imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
520  double width = mDefaultSvgSize.width() * imageToSvgRatio;
521  return QRectF( 0, 0, width, deviceHeight );
522  }
523 }
524 #endif //0
525 
526 void QgsComposerPicture::setSceneRect( const QRectF& rectangle )
527 {
528  QSizeF currentPictureSize = pictureSize();
529 
530  if ( mResizeMode == QgsComposerPicture::Clip )
531  {
532  QgsComposerItem::setSceneRect( rectangle );
533  mPictureWidth = rectangle.width();
534  mPictureHeight = rectangle.height();
535  }
536  else
537  {
538  QRectF newRect = rectangle;
539 
540  if ( mResizeMode == ZoomResizeFrame && !rect().isEmpty() && !( currentPictureSize.isEmpty() ) )
541  {
542  QSizeF targetImageSize;
543  if ( qgsDoubleNear( mPictureRotation, 0.0 ) )
544  {
545  targetImageSize = currentPictureSize;
546  }
547  else
548  {
549  //calculate aspect ratio of bounds of rotated image
550  QTransform tr;
551  tr.rotate( mPictureRotation );
552  QRectF rotatedBounds = tr.mapRect( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ) );
553  targetImageSize = QSizeF( rotatedBounds.width(), rotatedBounds.height() );
554  }
555 
556  //if height has changed more than width, then fix width and set height correspondingly
557  //else, do the opposite
558  if ( qAbs( rect().width() - rectangle.width() ) <
559  qAbs( rect().height() - rectangle.height() ) )
560  {
561  newRect.setHeight( targetImageSize.height() * newRect.width() / targetImageSize.width() );
562  }
563  else
564  {
565  newRect.setWidth( targetImageSize.width() * newRect.height() / targetImageSize.height() );
566  }
567  }
568  else if ( mResizeMode == FrameToImageSize )
569  {
570  if ( !( currentPictureSize.isEmpty() ) )
571  {
572  newRect.setWidth( currentPictureSize.width() * 25.4 / mComposition->printResolution() );
573  newRect.setHeight( currentPictureSize.height() * 25.4 / mComposition->printResolution() );
574  }
575  }
576 
577  //find largest scaling of picture with this rotation which fits in item
578  if ( mResizeMode == Zoom || mResizeMode == ZoomResizeFrame )
579  {
580  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), newRect, mPictureRotation );
581  mPictureWidth = rotatedImageRect.width();
582  mPictureHeight = rotatedImageRect.height();
583  }
584  else
585  {
586  mPictureWidth = newRect.width();
587  mPictureHeight = newRect.height();
588  }
589 
591  emit itemChanged();
592  }
593 
594  if ( mMode == SVG && !mLoadingSvg )
595  {
596  mLoadingSvg = true;
597  refreshPicture();
598  mLoadingSvg = false;
599  }
600 }
601 
603 {
604  //kept for compatibility for QGIS2.0 api
605  setPictureRotation( r );
606 }
607 
609 {
610  double oldRotation = mPictureRotation;
611  mPictureRotation = r;
612 
613  if ( mResizeMode == Zoom )
614  {
615  //find largest scaling of picture with this rotation which fits in item
616  QSizeF currentPictureSize = pictureSize();
617  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation );
618  mPictureWidth = rotatedImageRect.width();
619  mPictureHeight = rotatedImageRect.height();
620  update();
621  }
622  else if ( mResizeMode == ZoomResizeFrame )
623  {
624  QSizeF currentPictureSize = pictureSize();
625  QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
626 
627  //calculate actual size of image inside frame
628  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), oldRotation );
629 
630  //rotate image rect by new rotation and get bounding box
631  QTransform tr;
632  tr.rotate( mPictureRotation );
633  QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
634 
635  //keep the center in the same location
636  newRect.moveCenter( oldRect.center() );
638  emit itemChanged();
639  }
640 
641  emit pictureRotationChanged( mPictureRotation );
642 }
643 
644 void QgsComposerPicture::setRotationMap( int composerMapId )
645 {
646  if ( !mComposition )
647  {
648  return;
649  }
650 
651  if ( composerMapId == -1 ) //disable rotation from map
652  {
653  QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
654  mRotationMap = nullptr;
655  }
656 
657  const QgsComposerMap* map = mComposition->getComposerMapById( composerMapId );
658  if ( !map )
659  {
660  return;
661  }
662  if ( mRotationMap )
663  {
664  QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
665  }
666  mPictureRotation = map->mapRotation();
667  QObject::connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setPictureRotation( double ) ) );
668  mRotationMap = map;
669  update();
670  emit pictureRotationChanged( mPictureRotation );
671 }
672 
674 {
675  mResizeMode = mode;
677  || ( mode == QgsComposerPicture::Zoom && !qgsDoubleNear( mPictureRotation, 0.0 ) ) )
678  {
679  //call set scene rect to force item to resize to fit picture
680  recalculateSize();
681  }
682  update();
683 }
684 
686 {
687  //call set scene rect with current position/size, as this will trigger the
688  //picture item to recalculate its frame and image size
689  setSceneRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) );
690 }
691 
693 {
694  const QgsExpressionContext* evalContext = context;
696  if ( !evalContext )
697  {
698  scopedContext.reset( createExpressionContext() );
699  evalContext = scopedContext.data();
700  }
701 
703  {
704  refreshPicture( evalContext );
705  }
706 
707  QgsComposerItem::refreshDataDefinedProperty( property, evalContext );
708 }
709 
711 {
713  refreshPicture();
714 }
715 
717 {
719  refreshPicture();
720 }
721 
723 {
724  return picturePath();
725 }
726 
728 {
729  mSourcePath = path;
730  refreshPicture();
731 }
732 
734 {
735  return mSourcePath;
736 }
737 
739 {
740  if ( elem.isNull() )
741  {
742  return false;
743  }
744  QDomElement composerPictureElem = doc.createElement( "ComposerPicture" );
745  composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourcePath ) );
746  composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) );
747  composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) );
748  composerPictureElem.setAttribute( "resizeMode", QString::number( static_cast< int >( mResizeMode ) ) );
749  composerPictureElem.setAttribute( "anchorPoint", QString::number( static_cast< int >( mPictureAnchor ) ) );
750  composerPictureElem.setAttribute( "svgFillColor", QgsSymbolLayerV2Utils::encodeColor( mSvgFillColor ) );
751  composerPictureElem.setAttribute( "svgBorderColor", QgsSymbolLayerV2Utils::encodeColor( mSvgBorderColor ) );
752  composerPictureElem.setAttribute( "svgBorderWidth", QString::number( mSvgBorderWidth ) );
753 
754  //rotation
755  composerPictureElem.setAttribute( "pictureRotation", QString::number( mPictureRotation ) );
756  if ( !mRotationMap )
757  {
758  composerPictureElem.setAttribute( "mapId", -1 );
759  }
760  else
761  {
762  composerPictureElem.setAttribute( "mapId", mRotationMap->id() );
763  }
764 
765  _writeXML( composerPictureElem, doc );
766  elem.appendChild( composerPictureElem );
767  return true;
768 }
769 
770 bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocument& doc )
771 {
772  if ( itemElem.isNull() )
773  {
774  return false;
775  }
776 
777  mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble();
778  mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble();
779  mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() );
780  //when loading from xml, default to anchor point of middle to match pre 2.4 behaviour
781  mPictureAnchor = static_cast< QgsComposerItem::ItemPositionMode >( itemElem.attribute( "anchorPoint", QString::number( QgsComposerItem::Middle ) ).toInt() );
782 
783  mSvgFillColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "svgFillColor", QgsSymbolLayerV2Utils::encodeColor( QColor( 255, 255, 255 ) ) ) );
784  mSvgBorderColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "svgBorderColor", QgsSymbolLayerV2Utils::encodeColor( QColor( 0, 0, 0 ) ) ) );
785  mSvgBorderWidth = itemElem.attribute( "svgBorderWidth", "0.2" ).toDouble();
786 
787  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
788  if ( !composerItemList.isEmpty() )
789  {
790  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
791 
792  if ( !qgsDoubleNear( composerItemElem.attribute( "rotation", "0" ).toDouble(), 0.0 ) )
793  {
794  //in versions prior to 2.1 picture rotation was stored in the rotation attribute
795  mPictureRotation = composerItemElem.attribute( "rotation", "0" ).toDouble();
796  }
797 
798  _readXML( composerItemElem, doc );
799  }
800 
801  mDefaultSvgSize = QSize( 0, 0 );
802 
803  if ( itemElem.hasAttribute( "sourceExpression" ) )
804  {
805  //update pre 2.5 picture expression to use data defined expression
806  QString sourceExpression = itemElem.attribute( "sourceExpression", "" );
807  QString useExpression = itemElem.attribute( "useExpression" );
808  bool expressionActive;
809  if ( useExpression.compare( "true", Qt::CaseInsensitive ) == 0 )
810  {
811  expressionActive = true;
812  }
813  else
814  {
815  expressionActive = false;
816  }
817 
818  setDataDefinedProperty( QgsComposerObject::PictureSource, expressionActive, true, sourceExpression, QString() );
819  }
820 
821  mSourcePath = QgsProject::instance()->readPath( itemElem.attribute( "file" ) );
822 
823  //picture rotation
824  if ( !qgsDoubleNear( itemElem.attribute( "pictureRotation", "0" ).toDouble(), 0.0 ) )
825  {
826  mPictureRotation = itemElem.attribute( "pictureRotation", "0" ).toDouble();
827  }
828 
829  //rotation map
830  int rotationMapId = itemElem.attribute( "mapId", "-1" ).toInt();
831  if ( rotationMapId == -1 )
832  {
833  mRotationMap = nullptr;
834  }
835  else if ( mComposition )
836  {
837 
838  if ( mRotationMap )
839  {
840  QObject::disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
841  }
842  mRotationMap = mComposition->getComposerMapById( rotationMapId );
843  QObject::connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( setRotation( double ) ) );
844  }
845 
846  refreshPicture();
847 
848  emit itemChanged();
849  return true;
850 }
851 
853 {
854  if ( !mRotationMap )
855  {
856  return -1;
857  }
858  else
859  {
860  return mRotationMap->id();
861  }
862 }
863 
865 {
866  mPictureAnchor = anchor;
867  update();
868 }
869 
871 {
872  mSvgFillColor = color;
873  refreshPicture();
874 }
875 
877 {
878  mSvgBorderColor = color;
879  refreshPicture();
880 }
881 
883 {
884  mSvgBorderWidth = width;
885  refreshPicture();
886 }
887 
889 {
891 }
892 
894 {
896 }
897 
898 bool QgsComposerPicture::imageSizeConsideringRotation( double& width, double& height ) const
899 {
900  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
902  return QgsComposerItem::imageSizeConsideringRotation( width, height, mPictureRotation );
904 }
905 
906 bool QgsComposerPicture::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
907 {
908  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
910  return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mPictureRotation );
912 }
913 
914 void QgsComposerPicture::sizeChangedByRotation( double& width, double& height )
915 {
916  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
918  return QgsComposerItem::sizeChangedByRotation( width, height, mPictureRotation );
920 }
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)
static QString encodeColor(const QColor &color)
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).
void setSvgBorderWidth(double width)
Sets the border width used for parameterized SVG files.
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:504
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)
Compare two doubles (but allow some difference)
Definition: qgis.h:352
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 setSvgFillColor(const QColor &color)
Sets the fill color used for parameterized SVG files.
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.
static QgsSvgCache * instance()
Definition: qgssvgcache.cpp:97
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.
const QByteArray & svgContent(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Get SVG content.
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:505
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
void setSvgBorderColor(const QColor &color)
Sets the border color used for parameterized SVG files.
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:388
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()
static QColor decodeColor(const QString &str)
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