39 #include <QDomDocument> 40 #include <QDomElement> 42 #include <QImageReader> 44 #include <QSvgRenderer> 45 #include <QNetworkRequest> 46 #include <QNetworkReply> 48 #include <QCoreApplication> 93 double boundRectWidthMM;
94 double boundRectHeightMM;
98 boundRectWidthMM = mPictureWidth;
99 boundRectHeightMM = mPictureHeight;
100 imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
104 boundRectWidthMM = rect().width();
105 boundRectHeightMM = rect().height();
106 imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
110 boundRectWidthMM = rect().width();
111 boundRectHeightMM = rect().height();
112 int imageRectWidthPixels = mImage.width();
113 int imageRectHeightPixels = mImage.height();
114 imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM,
115 QSize( imageRectWidthPixels, imageRectHeightPixels ) );
119 boundRectWidthMM = rect().width();
120 boundRectHeightMM = rect().height();
126 if ( mResizeMode ==
Zoom )
132 painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
133 painter->rotate( mPictureRotation );
134 painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
139 double diffX = rect().width() - boundRectWidthMM;
140 double diffY = rect().height() - boundRectHeightMM;
144 switch ( mPictureAnchor )
162 switch ( mPictureAnchor )
180 painter->translate( dX, dY );
187 painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
188 painter->rotate( mPictureRotation );
189 painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
195 mSVG.render( painter, QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ) );
199 painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
208 QSizeF currentPictureSize = pictureSize();
209 QSizeF newSize = targetSize;
212 mPictureWidth = targetSize.width();
213 mPictureHeight = targetSize.height();
217 if ( mResizeMode ==
ZoomResizeFrame && !rect().isEmpty() && !( currentPictureSize.isEmpty() ) )
219 QSizeF targetImageSize;
222 targetImageSize = currentPictureSize;
228 tr.rotate( mPictureRotation );
229 QRectF rotatedBounds = tr.mapRect( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ) );
230 targetImageSize = QSizeF( rotatedBounds.width(), rotatedBounds.height() );
235 if ( std::fabs( rect().width() - targetSize.width() ) <
236 std::fabs( rect().height() - targetSize.height() ) )
238 newSize.setHeight( targetImageSize.height() * newSize.width() / targetImageSize.width() );
242 newSize.setWidth( targetImageSize.width() * newSize.height() / targetImageSize.height() );
247 if ( !( currentPictureSize.isEmpty() ) )
250 newSize.setWidth( sizeMM.
width() * 25.4 /
mLayout->renderContext().dpi() );
251 newSize.setHeight( sizeMM.
height() * 25.4 /
mLayout->renderContext().dpi() );
259 QRectF( 0, 0, newSize.width(), newSize.height() ), mPictureRotation );
260 mPictureWidth = rotatedImageRect.width();
261 mPictureHeight = rotatedImageRect.height();
265 mPictureWidth = newSize.width();
266 mPictureHeight = newSize.height();
269 if ( newSize != targetSize )
278 QRect QgsLayoutItemPicture::clippedImageRect(
double &boundRectWidthMM,
double &boundRectHeightMM, QSize imageRectPixels )
280 int boundRectWidthPixels = boundRectWidthMM *
mLayout->renderContext().dpi() / 25.4;
281 int boundRectHeightPixels = boundRectHeightMM *
mLayout->renderContext().dpi() / 25.4;
284 boundRectWidthMM = boundRectWidthPixels * 25.4 /
mLayout->renderContext().dpi();
285 boundRectHeightMM = boundRectHeightPixels * 25.4 /
mLayout->renderContext().dpi();
292 switch ( mPictureAnchor )
302 leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
307 leftClip = imageRectPixels.width() - boundRectWidthPixels;
312 switch ( mPictureAnchor )
322 topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
327 topClip = imageRectPixels.height() - boundRectHeightPixels;
331 return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
341 QVariant source( mSourcePath );
344 mHasExpressionError =
false;
349 source = sourceProperty.
value( *evalContext, source, &ok );
350 if ( !ok || !source.canConvert( QMetaType::QString ) )
352 mHasExpressionError =
true;
356 else if ( source.type() != QVariant::ByteArray )
358 source = source.toString().trimmed();
359 QgsDebugMsgLevel( QStringLiteral(
"exprVal PictureSource:%1" ).arg( source.toString() ), 2 );
363 loadPicture( source );
366 void QgsLayoutItemPicture::loadRemotePicture(
const QString &url )
376 loop.exec( QEventLoop::ExcludeUserInputEvents );
378 QNetworkReply *reply = fetcher.
reply();
381 QImageReader imageReader( reply );
382 mImage = imageReader.read();
391 void QgsLayoutItemPicture::loadLocalPicture(
const QString &path )
394 pic.setFileName( path );
402 QFileInfo sourceFileInfo( pic );
403 QString sourceFileSuffix = sourceFileInfo.suffix();
404 if ( sourceFileSuffix.compare( QLatin1String(
"svg" ), Qt::CaseInsensitive ) == 0 )
413 mSVG.load( svgContent );
414 if ( mSVG.isValid() )
417 QRect viewBox = mSVG.viewBox();
418 mDefaultSvgSize.setWidth( viewBox.width() );
419 mDefaultSvgSize.setHeight( viewBox.height() );
429 QImageReader imageReader( pic.fileName() );
430 if ( imageReader.read( &mImage ) )
452 void QgsLayoutItemPicture::updateMapRotation()
458 double rotation = mRotationMap->mapRotation() + mRotationMap->rotation();
461 switch ( mNorthMode )
468 QgsPointXY center = mRotationMap->extent().center();
480 QgsDebugMsg( QStringLiteral(
"Caught exception %1" ).arg( e.
what() ) );
486 rotation += mNorthOffset;
490 void QgsLayoutItemPicture::loadPicture(
const QVariant &data )
492 mIsMissingImage =
false;
493 QVariant imageData( data );
494 mEvaluatedPath = data.toString();
496 if ( mEvaluatedPath.startsWith( QLatin1String(
"base64:" ), Qt::CaseInsensitive ) )
498 QByteArray base64 = mEvaluatedPath.mid( 7 ).toLocal8Bit();
499 imageData = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
502 if ( imageData.type() == QVariant::ByteArray )
504 if ( mImage.loadFromData( imageData.toByteArray() ) )
509 else if ( mEvaluatedPath.startsWith( QLatin1String(
"http" ) ) )
512 loadRemotePicture( mEvaluatedPath );
517 loadLocalPicture( mEvaluatedPath );
523 else if ( mHasExpressionError || !mEvaluatedPath.isEmpty() )
527 mIsMissingImage =
true;
528 QString badFile( QStringLiteral(
":/images/composer/missing_image.svg" ) );
529 mSVG.load( badFile );
530 if ( mSVG.isValid() )
533 QRect viewBox = mSVG.viewBox();
534 mDefaultSvgSize.setWidth( viewBox.width() );
535 mDefaultSvgSize.setHeight( viewBox.height() );
544 QRectF QgsLayoutItemPicture::boundedImageRect(
double deviceWidth,
double deviceHeight )
546 double imageToDeviceRatio;
547 if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
549 imageToDeviceRatio = deviceWidth / mImage.width();
550 double height = imageToDeviceRatio * mImage.height();
551 return QRectF( 0, 0, deviceWidth, height );
555 imageToDeviceRatio = deviceHeight / mImage.height();
556 double width = imageToDeviceRatio * mImage.width();
557 return QRectF( 0, 0, width, deviceHeight );
561 QRectF QgsLayoutItemPicture::boundedSVGRect(
double deviceWidth,
double deviceHeight )
563 double imageToSvgRatio;
564 if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
566 imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
567 double width = mDefaultSvgSize.width() * imageToSvgRatio;
568 return QRectF( 0, 0, width, deviceHeight );
572 imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
573 double height = mDefaultSvgSize.height() * imageToSvgRatio;
574 return QRectF( 0, 0, deviceWidth, height );
578 QSizeF QgsLayoutItemPicture::pictureSize()
582 return mDefaultSvgSize;
586 return QSizeF( mImage.width(), mImage.height() );
590 return QSizeF( 0, 0 );
596 return mIsMissingImage;
601 return mEvaluatedPath;
604 void QgsLayoutItemPicture::shapeChanged()
606 if ( mMode ==
FormatSVG && !mLoadingSvg )
616 double oldRotation = mPictureRotation;
617 mPictureRotation = rotation;
619 if ( mResizeMode ==
Zoom )
622 QSizeF currentPictureSize = pictureSize();
624 mPictureWidth = rotatedImageRect.width();
625 mPictureHeight = rotatedImageRect.height();
630 QSizeF currentPictureSize = pictureSize();
631 QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
638 tr.rotate( mPictureRotation );
639 QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
642 newRect.moveCenter( oldRect.center() );
654 disconnectMap( mRotationMap );
659 mRotationMap =
nullptr;
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 ) );
737 elem.setAttribute( QStringLiteral(
"pictureRotation" ), QString::number( mPictureRotation ) );
740 elem.setAttribute( QStringLiteral(
"mapUuid" ), QString() );
744 elem.setAttribute( QStringLiteral(
"mapUuid" ), mRotationMap->uuid() );
746 elem.setAttribute( QStringLiteral(
"northMode" ), mNorthMode );
747 elem.setAttribute( QStringLiteral(
"northOffset" ), mNorthOffset );
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();
763 QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral(
"ComposerItem" ) );
764 if ( !composerItemList.isEmpty() )
766 QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
768 if ( !
qgsDoubleNear( composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
771 mPictureRotation = composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble();
775 mDefaultSvgSize = QSize( 0, 0 );
777 if ( itemElem.hasAttribute( QStringLiteral(
"sourceExpression" ) ) )
780 QString sourceExpression = itemElem.attribute( QStringLiteral(
"sourceExpression" ), QString() );
781 QString useExpression = itemElem.attribute( QStringLiteral(
"useExpression" ) );
782 bool expressionActive;
783 expressionActive = ( useExpression.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 );
788 QString imagePath = itemElem.attribute( QStringLiteral(
"file" ) );
792 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
795 imagePath = pathResolver.
readPath( imagePath );
797 mSourcePath = imagePath;
800 if ( !
qgsDoubleNear( itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
802 mPictureRotation = itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble();
806 mNorthMode =
static_cast< NorthMode >( itemElem.attribute( QStringLiteral(
"northMode" ), QStringLiteral(
"0" ) ).toInt() );
807 mNorthOffset = itemElem.attribute( QStringLiteral(
"northOffset" ), QStringLiteral(
"0" ) ).toDouble();
809 disconnectMap( mRotationMap );
810 mRotationMap =
nullptr;
811 mRotationMapUuid = itemElem.attribute( QStringLiteral(
"mapUuid" ) );
829 mNorthOffset = offset;
835 mPictureAnchor = anchor;
841 mSvgFillColor = color;
847 mSvgStrokeColor = color;
853 mSvgStrokeWidth = width;
859 if ( !
mLayout || mRotationMapUuid.isEmpty() )
861 mRotationMap =
nullptr;
867 disconnectMap( mRotationMap );
869 if ( ( mRotationMap = qobject_cast< QgsLayoutItemMap * >(
mLayout->itemByUuid( mRotationMapUuid,
true ) ) ) )
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
The class is used as a container of context for various read/write operations on other objects...
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QSizeF applyItemSizeConstraint(QSizeF targetSize) override
Applies any item-specific size constraint handling to a given targetSize in layout units...
QString picturePath() const
Returns the path of the source image.
static Q_INVOKABLE double bearingTrueNorth(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &transformContext, const QgsPointXY &point)
Returns the direction to true north from a specified point and for a specified coordinate reference s...
Base class for graphical items within a QgsLayout.
Lower left corner of item.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
A class to represent a 2D point.
void changed()
Emitted certain settings in the context is changed, e.g.
void extentChanged()
Emitted when the map's extent changes.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Stretches image to fit frame, ignores aspect ratio.
int type() const override
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
A layout item subclass that displays SVG files or raster format images (jpg, png, ...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void setNorthOffset(double offset)
Sets the offset added to the picture's rotation from a map's North.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Format mode() const
Returns the current picture mode (image format).
void setSvgFillColor(const QColor &color)
Sets the fill color used for parametrized SVG files.
QNetworkReply * reply()
Returns a reference to the network reply.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsLayoutItemPicture * create(QgsLayout *layout)
Returns a new picture item for the specified layout.
QString evaluatedPath() const
Returns the current evaluated picture path, which includes the result of data defined path overrides...
void setPictureAnchor(QgsLayoutItem::ReferencePoint anchor)
Sets the picture's anchor point, which controls how it is placed within the picture item's frame...
Lower right corner of item.
const QgsCoordinateReferenceSystem & crs
QgsLayoutItemPicture(QgsLayout *layout)
Constructor for QgsLayoutItemPicture, with the specified parent layout.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false)
Gets SVG content.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property's value and redrawing the...
void setPictureRotation(double rotation)
Sets the picture rotation within the item bounds, in degrees clockwise.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void setSvgStrokeColor(const QColor &color)
Sets the stroke color used for parametrized SVG files.
void dpiChanged()
Emitted when the context's DPI is changed.
static QString encodeColor(const QColor &color)
ReferencePoint
Fixed position reference point.
void sizePositionChanged()
Emitted when the item's size or position changes.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
Invalid or unknown image type.
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const override
Prepares the collection against a specified expression context.
Layout graphical items for displaying a map.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
Upper right corner of item.
HTTP network content fetcher.
#define QgsDebugMsgLevel(str, level)
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates...
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QPointer< QgsLayout > mLayout
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map object for rotation.
void finished()
Emitted when content has loaded.
void pictureRotationChanged(double newRotation)
Emitted on picture rotation change.
void setPicturePath(const QString &path)
Sets the source path of the image (may be svg or a raster format).
Upper left corner of item.
Contains information about the context in which a coordinate transform is executed.
A store for object properties.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
void fetchContent(const QUrl &url, const QString &authcfg=QString())
Fetches content from a remote URL and handles redirects.
void recalculateSize()
Forces a recalculation of the picture's frame size.
void setSvgStrokeWidth(double width)
Sets the stroke width (in layout units) used for parametrized SVG files.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Enlarges image to fit frame, then resizes frame to fit resultant image.
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
NorthMode
Method for syncing rotation to a map's North direction.
QPainter * painter()
Returns the destination QPainter for the render operation.
bool isMissingImage() const
Returns true if the source image is missing and the picture cannot be rendered.
static QRectF largestRotatedRectWithinBounds(const QRectF &originalRect, const QRectF &boundsRect, double rotation)
Calculates the largest scaled version of originalRect which fits within boundsRect, when it is rotated by the a specified rotation amount.
ResizeMode
Controls how pictures are scaled within the item's frame.
void setResizeMode(QgsLayoutItemPicture::ResizeMode mode)
Sets the resize mode used for drawing the picture within the item bounds.
This class represents a coordinate reference system (CRS).
Sets size of frame to match original size of image without scaling.
Draws image at original size and clips any portion which falls outside frame.
void setNorthMode(NorthMode mode)
Sets the mode used to align the picture to a map's North.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void refreshPicture(const QgsExpressionContext *context=nullptr)
Recalculates the source image (if using an expression for picture's source) and reloads and redraws t...
QIcon icon() const override
Returns the item's icon.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
Enlarges image to fit frame while maintaining aspect ratio of picture.
Resolves relative paths into absolute paths and vice versa.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
void rotationChanged(double newRotation)
Emitted on item rotation change.
void changed()
Emitted when the object's properties change.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double height() const
Returns the height of the size.
QgsLayoutItemMap * linkedMap() const
Returns the linked rotation map, if set.
DataDefinedProperty
Data defined properties for different item types.
Defines a QGIS exception class.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
static QColor decodeColor(const QString &str)
double width() const
Returns the width of the size.