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 );
211 QSizeF currentPictureSize = pictureSize();
212 QSizeF newSize = targetSize;
215 mPictureWidth = targetSize.width();
216 mPictureHeight = targetSize.height();
220 if ( mResizeMode ==
ZoomResizeFrame && !rect().isEmpty() && !( currentPictureSize.isEmpty() ) )
222 QSizeF targetImageSize;
225 targetImageSize = currentPictureSize;
231 tr.rotate( mPictureRotation );
232 QRectF rotatedBounds = tr.mapRect( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ) );
233 targetImageSize = QSizeF( rotatedBounds.width(), rotatedBounds.height() );
238 if ( std::fabs( rect().width() - targetSize.width() ) <
239 std::fabs( rect().height() - targetSize.height() ) )
241 newSize.setHeight( targetImageSize.height() * newSize.width() / targetImageSize.width() );
245 newSize.setWidth( targetImageSize.width() * newSize.height() / targetImageSize.height() );
250 if ( !( currentPictureSize.isEmpty() ) )
253 newSize.setWidth( sizeMM.
width() * 25.4 /
mLayout->renderContext().dpi() );
254 newSize.setHeight( sizeMM.
height() * 25.4 /
mLayout->renderContext().dpi() );
262 QRectF( 0, 0, newSize.width(), newSize.height() ), mPictureRotation );
263 mPictureWidth = rotatedImageRect.width();
264 mPictureHeight = rotatedImageRect.height();
268 mPictureWidth = newSize.width();
269 mPictureHeight = newSize.height();
272 if ( newSize != targetSize )
281 QRect QgsLayoutItemPicture::clippedImageRect(
double &boundRectWidthMM,
double &boundRectHeightMM, QSize imageRectPixels )
283 int boundRectWidthPixels = boundRectWidthMM *
mLayout->renderContext().dpi() / 25.4;
284 int boundRectHeightPixels = boundRectHeightMM *
mLayout->renderContext().dpi() / 25.4;
287 boundRectWidthMM = boundRectWidthPixels * 25.4 /
mLayout->renderContext().dpi();
288 boundRectHeightMM = boundRectHeightPixels * 25.4 /
mLayout->renderContext().dpi();
295 switch ( mPictureAnchor )
305 leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
310 leftClip = imageRectPixels.width() - boundRectWidthPixels;
315 switch ( mPictureAnchor )
325 topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
330 topClip = imageRectPixels.height() - boundRectHeightPixels;
334 return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
344 QVariant source( mSourcePath );
347 mHasExpressionError =
false;
353 source = sourceProperty.
value( *evalContext, source, &ok );
354 if ( !ok || !source.canConvert( QMetaType::QString ) )
356 mHasExpressionError =
true;
360 else if ( source.type() != QVariant::ByteArray )
362 source = source.toString().trimmed();
363 QgsDebugMsgLevel( QStringLiteral(
"exprVal PictureSource:%1" ).arg( source.toString() ), 2 );
367 loadPicture( source );
370 void QgsLayoutItemPicture::loadRemotePicture(
const QString &url )
380 loop.exec( QEventLoop::ExcludeUserInputEvents );
382 QNetworkReply *reply = fetcher.
reply();
385 QImageReader imageReader( reply );
386 mImage = imageReader.read();
395 void QgsLayoutItemPicture::loadLocalPicture(
const QString &path )
398 pic.setFileName( path );
406 QFileInfo sourceFileInfo( pic );
407 QString sourceFileSuffix = sourceFileInfo.suffix();
408 if ( sourceFileSuffix.compare( QLatin1String(
"svg" ), Qt::CaseInsensitive ) == 0 )
417 mSVG.load( svgContent );
418 if ( mSVG.isValid() )
421 QRect viewBox = mSVG.viewBox();
422 mDefaultSvgSize.setWidth( viewBox.width() );
423 mDefaultSvgSize.setHeight( viewBox.height() );
433 QImageReader imageReader( pic.fileName() );
434 if ( imageReader.read( &mImage ) )
446 void QgsLayoutItemPicture::loadPictureUsingCache(
const QString &path )
448 if ( path.isEmpty() )
461 bool fitsInCache =
false;
462 bool isMissing =
false;
464 if ( mImage.isNull() || isMissing )
478 mSVG.load( svgContent );
482 QRect viewBox = mSVG.viewBox();
483 mDefaultSvgSize.setWidth( viewBox.width() );
484 mDefaultSvgSize.setHeight( viewBox.height() );
495 void QgsLayoutItemPicture::updateNorthArrowRotation(
double rotation )
501 void QgsLayoutItemPicture::loadPicture(
const QVariant &data )
503 mIsMissingImage =
false;
504 QVariant imageData( data );
505 mEvaluatedPath = data.toString();
507 if ( mEvaluatedPath.startsWith( QLatin1String(
"base64:" ), Qt::CaseInsensitive ) && mMode ==
FormatUnknown )
509 QByteArray base64 = mEvaluatedPath.mid( 7 ).toLocal8Bit();
510 imageData = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
513 if ( imageData.type() == QVariant::ByteArray )
515 if ( mImage.loadFromData( imageData.toByteArray() ) )
520 else if ( mMode ==
FormatUnknown && mEvaluatedPath.startsWith( QLatin1String(
"http" ) ) )
523 loadRemotePicture( mEvaluatedPath );
528 loadLocalPicture( mEvaluatedPath );
532 loadPictureUsingCache( mEvaluatedPath );
539 else if ( mHasExpressionError || !mEvaluatedPath.isEmpty() )
543 mIsMissingImage =
true;
544 QString badFile( QStringLiteral(
":/images/composer/missing_image.svg" ) );
545 mSVG.load( badFile );
546 if ( mSVG.isValid() )
549 QRect viewBox = mSVG.viewBox();
550 mDefaultSvgSize.setWidth( viewBox.width() );
551 mDefaultSvgSize.setHeight( viewBox.height() );
560 QRectF QgsLayoutItemPicture::boundedImageRect(
double deviceWidth,
double deviceHeight )
562 double imageToDeviceRatio;
563 if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
565 imageToDeviceRatio = deviceWidth / mImage.width();
566 double height = imageToDeviceRatio * mImage.height();
567 return QRectF( 0, 0, deviceWidth, height );
571 imageToDeviceRatio = deviceHeight / mImage.height();
572 double width = imageToDeviceRatio * mImage.width();
573 return QRectF( 0, 0, width, deviceHeight );
577 QRectF QgsLayoutItemPicture::boundedSVGRect(
double deviceWidth,
double deviceHeight )
579 double imageToSvgRatio;
580 if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
582 imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
583 double width = mDefaultSvgSize.width() * imageToSvgRatio;
584 return QRectF( 0, 0, width, deviceHeight );
588 imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
589 double height = mDefaultSvgSize.height() * imageToSvgRatio;
590 return QRectF( 0, 0, deviceWidth, height );
594 QSizeF QgsLayoutItemPicture::pictureSize()
598 return mDefaultSvgSize;
602 return QSizeF( mImage.width(), mImage.height() );
606 return QSizeF( 0, 0 );
612 return mIsMissingImage;
617 return mEvaluatedPath;
620 void QgsLayoutItemPicture::shapeChanged()
622 if ( mMode ==
FormatSVG && !mLoadingSvg )
632 double oldRotation = mPictureRotation;
633 mPictureRotation = rotation;
635 if ( mResizeMode ==
Zoom )
638 QSizeF currentPictureSize = pictureSize();
640 mPictureWidth = rotatedImageRect.width();
641 mPictureHeight = rotatedImageRect.height();
646 QSizeF currentPictureSize = pictureSize();
647 QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
654 tr.rotate( mPictureRotation );
655 QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
658 newRect.moveCenter( oldRect.center() );
717 QString imagePath = mSourcePath;
721 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
724 imagePath = pathResolver.
writePath( imagePath );
726 elem.setAttribute( QStringLiteral(
"file" ), imagePath );
727 elem.setAttribute( QStringLiteral(
"pictureWidth" ), QString::number( mPictureWidth ) );
728 elem.setAttribute( QStringLiteral(
"pictureHeight" ), QString::number( mPictureHeight ) );
729 elem.setAttribute( QStringLiteral(
"resizeMode" ), QString::number(
static_cast< int >( mResizeMode ) ) );
730 elem.setAttribute( QStringLiteral(
"anchorPoint" ), QString::number(
static_cast< int >( mPictureAnchor ) ) );
733 elem.setAttribute( QStringLiteral(
"svgBorderWidth" ), QString::number( mSvgStrokeWidth ) );
734 elem.setAttribute( QStringLiteral(
"mode" ), mMode );
737 elem.setAttribute( QStringLiteral(
"pictureRotation" ), QString::number( mPictureRotation ) );
740 elem.setAttribute( QStringLiteral(
"mapUuid" ), QString() );
744 elem.setAttribute( QStringLiteral(
"mapUuid" ), mNorthArrowHandler->
linkedMap()->
uuid() );
746 elem.setAttribute( QStringLiteral(
"northMode" ), mNorthArrowHandler->
northMode() );
747 elem.setAttribute( QStringLiteral(
"northOffset" ), mNorthArrowHandler->
northOffset() );
753 mPictureWidth = itemElem.attribute( QStringLiteral(
"pictureWidth" ), QStringLiteral(
"10" ) ).toDouble();
754 mPictureHeight = itemElem.attribute( QStringLiteral(
"pictureHeight" ), QStringLiteral(
"10" ) ).toDouble();
761 mSvgStrokeWidth = itemElem.attribute( QStringLiteral(
"svgBorderWidth" ), QStringLiteral(
"0.2" ) ).toDouble();
762 mMode =
static_cast< Format >( itemElem.attribute( QStringLiteral(
"mode" ), QString::number(
FormatUnknown ) ).toInt() );
764 QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral(
"ComposerItem" ) );
765 if ( !composerItemList.isEmpty() )
767 QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
769 if ( !
qgsDoubleNear( composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
772 mPictureRotation = composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble();
776 mDefaultSvgSize = QSize( 0, 0 );
778 if ( itemElem.hasAttribute( QStringLiteral(
"sourceExpression" ) ) )
781 QString sourceExpression = itemElem.attribute( QStringLiteral(
"sourceExpression" ), QString() );
782 QString useExpression = itemElem.attribute( QStringLiteral(
"useExpression" ) );
783 bool expressionActive;
784 expressionActive = ( useExpression.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 );
789 QString imagePath = itemElem.attribute( QStringLiteral(
"file" ) );
793 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
796 imagePath = pathResolver.
readPath( imagePath );
798 mSourcePath = imagePath;
801 if ( !
qgsDoubleNear( itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
803 mPictureRotation = itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble();
808 mNorthArrowHandler->
setNorthOffset( itemElem.attribute( QStringLiteral(
"northOffset" ), QStringLiteral(
"0" ) ).toDouble() );
811 mRotationMapUuid = itemElem.attribute( QStringLiteral(
"mapUuid" ) );
843 mPictureAnchor = anchor;
849 mSvgFillColor = color;
855 mSvgStrokeColor = color;
861 mSvgStrokeWidth = width;
876 if ( !
mLayout || mRotationMapUuid.isEmpty() )
882 mNorthArrowHandler->
setLinkedMap( qobject_cast< QgsLayoutItemMap * >(
mLayout->itemByUuid( mRotationMapUuid,
true ) ) );