41 #include <QDomDocument>
42 #include <QDomElement>
44 #include <QImageReader>
46 #include <QSvgRenderer>
47 #include <QNetworkRequest>
48 #include <QNetworkReply>
50 #include <QCoreApplication>
97 double boundRectWidthMM;
98 double boundRectHeightMM;
102 boundRectWidthMM = mPictureWidth;
103 boundRectHeightMM = mPictureHeight;
104 imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
108 boundRectWidthMM = rect().width();
109 boundRectHeightMM = rect().height();
110 imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
114 boundRectWidthMM = rect().width();
115 boundRectHeightMM = rect().height();
116 int imageRectWidthPixels = mImage.width();
117 int imageRectHeightPixels = mImage.height();
118 imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM,
119 QSize( imageRectWidthPixels, imageRectHeightPixels ) );
123 boundRectWidthMM = rect().width();
124 boundRectHeightMM = rect().height();
130 if ( mResizeMode ==
Zoom )
136 painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
137 painter->rotate( mPictureRotation );
138 painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
143 double diffX = rect().width() - boundRectWidthMM;
144 double diffY = rect().height() - boundRectHeightMM;
148 switch ( mPictureAnchor )
166 switch ( mPictureAnchor )
184 painter->translate( dX, dY );
191 painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
192 painter->rotate( mPictureRotation );
193 painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
199 mSVG.render( painter, QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ) );
203 painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
212 QSizeF currentPictureSize = pictureSize();
213 QSizeF newSize = targetSize;
216 mPictureWidth = targetSize.width();
217 mPictureHeight = targetSize.height();
221 if ( mResizeMode ==
ZoomResizeFrame && !rect().isEmpty() && !( currentPictureSize.isEmpty() ) )
223 QSizeF targetImageSize;
226 targetImageSize = currentPictureSize;
232 tr.rotate( mPictureRotation );
233 QRectF rotatedBounds = tr.mapRect( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ) );
234 targetImageSize = QSizeF( rotatedBounds.width(), rotatedBounds.height() );
239 if ( std::fabs( rect().width() - targetSize.width() ) <
240 std::fabs( rect().height() - targetSize.height() ) )
242 newSize.setHeight( targetImageSize.height() * newSize.width() / targetImageSize.width() );
246 newSize.setWidth( targetImageSize.width() * newSize.height() / targetImageSize.height() );
251 if ( !( currentPictureSize.isEmpty() ) )
254 newSize.setWidth( sizeMM.
width() * 25.4 /
mLayout->renderContext().dpi() );
255 newSize.setHeight( sizeMM.
height() * 25.4 /
mLayout->renderContext().dpi() );
263 QRectF( 0, 0, newSize.width(), newSize.height() ), mPictureRotation );
264 mPictureWidth = rotatedImageRect.width();
265 mPictureHeight = rotatedImageRect.height();
269 mPictureWidth = newSize.width();
270 mPictureHeight = newSize.height();
273 if ( newSize != targetSize )
282 QRect QgsLayoutItemPicture::clippedImageRect(
double &boundRectWidthMM,
double &boundRectHeightMM, QSize imageRectPixels )
284 int boundRectWidthPixels = boundRectWidthMM *
mLayout->renderContext().dpi() / 25.4;
285 int boundRectHeightPixels = boundRectHeightMM *
mLayout->renderContext().dpi() / 25.4;
288 boundRectWidthMM = boundRectWidthPixels * 25.4 /
mLayout->renderContext().dpi();
289 boundRectHeightMM = boundRectHeightPixels * 25.4 /
mLayout->renderContext().dpi();
296 switch ( mPictureAnchor )
306 leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
311 leftClip = imageRectPixels.width() - boundRectWidthPixels;
316 switch ( mPictureAnchor )
326 topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
331 topClip = imageRectPixels.height() - boundRectHeightPixels;
335 return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
345 QVariant source( mSourcePath );
348 mHasExpressionError =
false;
354 source = sourceProperty.
value( *evalContext, source, &ok );
355 if ( !ok || !source.canConvert( QMetaType::QString ) )
357 mHasExpressionError =
true;
361 else if ( source.type() != QVariant::ByteArray )
363 source = source.toString().trimmed();
364 QgsDebugMsgLevel( QStringLiteral(
"exprVal PictureSource:%1" ).arg( source.toString() ), 2 );
368 loadPicture( source );
371 void QgsLayoutItemPicture::loadRemotePicture(
const QString &url )
381 loop.exec( QEventLoop::ExcludeUserInputEvents );
383 QNetworkReply *reply = fetcher.
reply();
386 QImageReader imageReader( reply );
387 mImage = imageReader.read();
396 void QgsLayoutItemPicture::loadLocalPicture(
const QString &path )
399 pic.setFileName( path );
407 QFileInfo sourceFileInfo( pic );
408 QString sourceFileSuffix = sourceFileInfo.suffix();
409 if ( sourceFileSuffix.compare( QLatin1String(
"svg" ), Qt::CaseInsensitive ) == 0 )
418 mSVG.load( svgContent );
419 if ( mSVG.isValid() )
422 QRect viewBox = mSVG.viewBox();
423 mDefaultSvgSize.setWidth( viewBox.width() );
424 mDefaultSvgSize.setHeight( viewBox.height() );
434 QImageReader imageReader( pic.fileName() );
435 if ( imageReader.read( &mImage ) )
447 void QgsLayoutItemPicture::loadPictureUsingCache(
const QString &path )
449 if ( path.isEmpty() )
462 bool fitsInCache =
false;
463 bool isMissing =
false;
465 if ( mImage.isNull() || isMissing )
479 mSVG.load( svgContent );
483 QRect viewBox = mSVG.viewBox();
484 mDefaultSvgSize.setWidth( viewBox.width() );
485 mDefaultSvgSize.setHeight( viewBox.height() );
496 void QgsLayoutItemPicture::updateNorthArrowRotation(
double rotation )
502 void QgsLayoutItemPicture::loadPicture(
const QVariant &data )
504 mIsMissingImage =
false;
505 QVariant imageData( data );
506 mEvaluatedPath = data.toString();
508 if ( mEvaluatedPath.startsWith( QLatin1String(
"base64:" ), Qt::CaseInsensitive ) && mMode ==
FormatUnknown )
510 QByteArray base64 = mEvaluatedPath.mid( 7 ).toLocal8Bit();
511 imageData = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
514 if ( imageData.type() == QVariant::ByteArray )
516 if ( mImage.loadFromData( imageData.toByteArray() ) )
521 else if ( mMode ==
FormatUnknown && mEvaluatedPath.startsWith( QLatin1String(
"http" ) ) )
524 loadRemotePicture( mEvaluatedPath );
529 loadLocalPicture( mEvaluatedPath );
533 loadPictureUsingCache( mEvaluatedPath );
540 else if ( mHasExpressionError || !mEvaluatedPath.isEmpty() )
544 mIsMissingImage =
true;
545 QString badFile( QStringLiteral(
":/images/composer/missing_image.svg" ) );
546 mSVG.load( badFile );
547 if ( mSVG.isValid() )
550 QRect viewBox = mSVG.viewBox();
551 mDefaultSvgSize.setWidth( viewBox.width() );
552 mDefaultSvgSize.setHeight( viewBox.height() );
561 QRectF QgsLayoutItemPicture::boundedImageRect(
double deviceWidth,
double deviceHeight )
563 double imageToDeviceRatio;
564 if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
566 imageToDeviceRatio = deviceWidth / mImage.width();
567 double height = imageToDeviceRatio * mImage.height();
568 return QRectF( 0, 0, deviceWidth, height );
572 imageToDeviceRatio = deviceHeight / mImage.height();
573 double width = imageToDeviceRatio * mImage.width();
574 return QRectF( 0, 0, width, deviceHeight );
578 QRectF QgsLayoutItemPicture::boundedSVGRect(
double deviceWidth,
double deviceHeight )
580 double imageToSvgRatio;
581 if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
583 imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
584 double width = mDefaultSvgSize.width() * imageToSvgRatio;
585 return QRectF( 0, 0, width, deviceHeight );
589 imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
590 double height = mDefaultSvgSize.height() * imageToSvgRatio;
591 return QRectF( 0, 0, deviceWidth, height );
595 QSizeF QgsLayoutItemPicture::pictureSize()
599 return mDefaultSvgSize;
603 return QSizeF( mImage.width(), mImage.height() );
607 return QSizeF( 0, 0 );
613 return mIsMissingImage;
618 return mEvaluatedPath;
621 void QgsLayoutItemPicture::shapeChanged()
623 if ( mMode ==
FormatSVG && !mLoadingSvg )
633 double oldRotation = mPictureRotation;
634 mPictureRotation = rotation;
636 if ( mResizeMode ==
Zoom )
639 QSizeF currentPictureSize = pictureSize();
641 mPictureWidth = rotatedImageRect.width();
642 mPictureHeight = rotatedImageRect.height();
647 QSizeF currentPictureSize = pictureSize();
648 QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
655 tr.rotate( mPictureRotation );
656 QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
659 newRect.moveCenter( oldRect.center() );
718 QString imagePath = mSourcePath;
722 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
725 imagePath = pathResolver.
writePath( imagePath );
727 elem.setAttribute( QStringLiteral(
"file" ), imagePath );
728 elem.setAttribute( QStringLiteral(
"pictureWidth" ), QString::number( mPictureWidth ) );
729 elem.setAttribute( QStringLiteral(
"pictureHeight" ), QString::number( mPictureHeight ) );
730 elem.setAttribute( QStringLiteral(
"resizeMode" ), QString::number(
static_cast< int >( mResizeMode ) ) );
731 elem.setAttribute( QStringLiteral(
"anchorPoint" ), QString::number(
static_cast< int >( mPictureAnchor ) ) );
734 elem.setAttribute( QStringLiteral(
"svgBorderWidth" ), QString::number( mSvgStrokeWidth ) );
735 elem.setAttribute( QStringLiteral(
"mode" ), mMode );
738 elem.setAttribute( QStringLiteral(
"pictureRotation" ), QString::number( mPictureRotation ) );
741 elem.setAttribute( QStringLiteral(
"mapUuid" ), QString() );
745 elem.setAttribute( QStringLiteral(
"mapUuid" ), mNorthArrowHandler->
linkedMap()->
uuid() );
747 elem.setAttribute( QStringLiteral(
"northMode" ), mNorthArrowHandler->
northMode() );
748 elem.setAttribute( QStringLiteral(
"northOffset" ), mNorthArrowHandler->
northOffset() );
754 mPictureWidth = itemElem.attribute( QStringLiteral(
"pictureWidth" ), QStringLiteral(
"10" ) ).toDouble();
755 mPictureHeight = itemElem.attribute( QStringLiteral(
"pictureHeight" ), QStringLiteral(
"10" ) ).toDouble();
762 mSvgStrokeWidth = itemElem.attribute( QStringLiteral(
"svgBorderWidth" ), QStringLiteral(
"0.2" ) ).toDouble();
763 mMode =
static_cast< Format >( itemElem.attribute( QStringLiteral(
"mode" ), QString::number(
FormatUnknown ) ).toInt() );
765 QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral(
"ComposerItem" ) );
766 if ( !composerItemList.isEmpty() )
768 QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
770 if ( !
qgsDoubleNear( composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
773 mPictureRotation = composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble();
777 mDefaultSvgSize = QSize( 0, 0 );
779 if ( itemElem.hasAttribute( QStringLiteral(
"sourceExpression" ) ) )
782 QString sourceExpression = itemElem.attribute( QStringLiteral(
"sourceExpression" ), QString() );
783 QString useExpression = itemElem.attribute( QStringLiteral(
"useExpression" ) );
784 bool expressionActive;
785 expressionActive = ( useExpression.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 );
790 QString imagePath = itemElem.attribute( QStringLiteral(
"file" ) );
794 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
797 imagePath = pathResolver.
readPath( imagePath );
799 mSourcePath = imagePath;
802 if ( !
qgsDoubleNear( itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
804 mPictureRotation = itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble();
809 mNorthArrowHandler->
setNorthOffset( itemElem.attribute( QStringLiteral(
"northOffset" ), QStringLiteral(
"0" ) ).toDouble() );
812 mRotationMapUuid = itemElem.attribute( QStringLiteral(
"mapUuid" ) );
844 mPictureAnchor = anchor;
850 mSvgFillColor = color;
856 mSvgStrokeColor = color;
862 mSvgStrokeWidth = width;
877 if ( !
mLayout || mRotationMapUuid.isEmpty() )
883 mNorthArrowHandler->
setLinkedMap( qobject_cast< QgsLayoutItemMap * >(
mLayout->itemByUuid( mRotationMapUuid,
true ) ) );