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 );
339 QString source = mSourcePath;
342 mHasExpressionError =
false;
349 source = source.trimmed();
350 QgsDebugMsg( QString(
"exprVal PictureSource:%1" ).arg( source ) );
354 mHasExpressionError =
true;
360 loadPicture( source );
363 void QgsLayoutItemPicture::loadRemotePicture(
const QString &url )
373 loop.exec( QEventLoop::ExcludeUserInputEvents );
375 QNetworkReply *reply = fetcher.
reply();
378 QImageReader imageReader( reply );
379 mImage = imageReader.read();
388 void QgsLayoutItemPicture::loadLocalPicture(
const QString &path )
391 pic.setFileName( path );
399 QFileInfo sourceFileInfo( pic );
400 QString sourceFileSuffix = sourceFileInfo.suffix();
401 if ( sourceFileSuffix.compare( QLatin1String(
"svg" ), Qt::CaseInsensitive ) == 0 )
410 mSVG.load( svgContent );
411 if ( mSVG.isValid() )
414 QRect viewBox = mSVG.viewBox();
415 mDefaultSvgSize.setWidth( viewBox.width() );
416 mDefaultSvgSize.setHeight( viewBox.height() );
426 QImageReader imageReader( pic.fileName() );
427 if ( imageReader.read( &mImage ) )
448 void QgsLayoutItemPicture::updateMapRotation()
454 double rotation = mRotationMap->mapRotation();
457 switch ( mNorthMode )
464 QgsPointXY center = mRotationMap->extent().center();
482 rotation += mNorthOffset;
486 void QgsLayoutItemPicture::loadPicture(
const QString &path )
488 if ( path.startsWith( QLatin1String(
"http" ) ) )
491 loadRemotePicture( path );
496 loadLocalPicture( path );
502 else if ( mHasExpressionError || !( path.isEmpty() ) )
506 QString badFile( QStringLiteral(
":/images/composer/missing_image.svg" ) );
507 mSVG.load( badFile );
508 if ( mSVG.isValid() )
511 QRect viewBox = mSVG.viewBox();
512 mDefaultSvgSize.setWidth( viewBox.width() );
513 mDefaultSvgSize.setHeight( viewBox.height() );
522 QRectF QgsLayoutItemPicture::boundedImageRect(
double deviceWidth,
double deviceHeight )
524 double imageToDeviceRatio;
525 if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
527 imageToDeviceRatio = deviceWidth / mImage.width();
528 double height = imageToDeviceRatio * mImage.height();
529 return QRectF( 0, 0, deviceWidth, height );
533 imageToDeviceRatio = deviceHeight / mImage.height();
534 double width = imageToDeviceRatio * mImage.width();
535 return QRectF( 0, 0, width, deviceHeight );
539 QRectF QgsLayoutItemPicture::boundedSVGRect(
double deviceWidth,
double deviceHeight )
541 double imageToSvgRatio;
542 if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
544 imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
545 double width = mDefaultSvgSize.width() * imageToSvgRatio;
546 return QRectF( 0, 0, width, deviceHeight );
550 imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
551 double height = mDefaultSvgSize.height() * imageToSvgRatio;
552 return QRectF( 0, 0, deviceWidth, height );
556 QSizeF QgsLayoutItemPicture::pictureSize()
560 return mDefaultSvgSize;
564 return QSizeF( mImage.width(), mImage.height() );
568 return QSizeF( 0, 0 );
572 void QgsLayoutItemPicture::shapeChanged()
574 if ( mMode ==
FormatSVG && !mLoadingSvg )
584 double oldRotation = mPictureRotation;
585 mPictureRotation = rotation;
587 if ( mResizeMode ==
Zoom )
590 QSizeF currentPictureSize = pictureSize();
592 mPictureWidth = rotatedImageRect.width();
593 mPictureHeight = rotatedImageRect.height();
598 QSizeF currentPictureSize = pictureSize();
599 QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
606 tr.rotate( mPictureRotation );
607 QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
610 newRect.moveCenter( oldRect.center() );
622 disconnectMap( mRotationMap );
627 mRotationMap =
nullptr;
693 QString imagePath = mSourcePath;
697 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
700 imagePath = pathResolver.
writePath( imagePath );
702 elem.setAttribute( QStringLiteral(
"file" ), imagePath );
703 elem.setAttribute( QStringLiteral(
"pictureWidth" ), QString::number( mPictureWidth ) );
704 elem.setAttribute( QStringLiteral(
"pictureHeight" ), QString::number( mPictureHeight ) );
705 elem.setAttribute( QStringLiteral(
"resizeMode" ), QString::number( static_cast< int >( mResizeMode ) ) );
706 elem.setAttribute( QStringLiteral(
"anchorPoint" ), QString::number( static_cast< int >( mPictureAnchor ) ) );
709 elem.setAttribute( QStringLiteral(
"svgBorderWidth" ), QString::number( mSvgStrokeWidth ) );
712 elem.setAttribute( QStringLiteral(
"pictureRotation" ), QString::number( mPictureRotation ) );
715 elem.setAttribute( QStringLiteral(
"mapUuid" ), QString() );
719 elem.setAttribute( QStringLiteral(
"mapUuid" ), mRotationMap->uuid() );
721 elem.setAttribute( QStringLiteral(
"northMode" ), mNorthMode );
722 elem.setAttribute( QStringLiteral(
"northOffset" ), mNorthOffset );
728 mPictureWidth = itemElem.attribute( QStringLiteral(
"pictureWidth" ), QStringLiteral(
"10" ) ).toDouble();
729 mPictureHeight = itemElem.attribute( QStringLiteral(
"pictureHeight" ), QStringLiteral(
"10" ) ).toDouble();
736 mSvgStrokeWidth = itemElem.attribute( QStringLiteral(
"svgBorderWidth" ), QStringLiteral(
"0.2" ) ).toDouble();
738 QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral(
"ComposerItem" ) );
739 if ( !composerItemList.isEmpty() )
741 QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
743 if ( !
qgsDoubleNear( composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
746 mPictureRotation = composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble();
750 mDefaultSvgSize = QSize( 0, 0 );
752 if ( itemElem.hasAttribute( QStringLiteral(
"sourceExpression" ) ) )
755 QString sourceExpression = itemElem.attribute( QStringLiteral(
"sourceExpression" ), QLatin1String(
"" ) );
756 QString useExpression = itemElem.attribute( QStringLiteral(
"useExpression" ) );
757 bool expressionActive;
758 expressionActive = ( useExpression.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 );
763 QString imagePath = itemElem.attribute( QStringLiteral(
"file" ) );
767 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
770 imagePath = pathResolver.
readPath( imagePath );
772 mSourcePath = imagePath;
775 if ( !
qgsDoubleNear( itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
777 mPictureRotation = itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble();
781 mNorthMode =
static_cast< NorthMode >( itemElem.attribute( QStringLiteral(
"northMode" ), QStringLiteral(
"0" ) ).toInt() );
782 mNorthOffset = itemElem.attribute( QStringLiteral(
"northOffset" ), QStringLiteral(
"0" ) ).toDouble();
784 disconnectMap( mRotationMap );
785 mRotationMap =
nullptr;
786 mRotationMapUuid = itemElem.attribute( QStringLiteral(
"mapUuid" ) );
804 mNorthOffset = offset;
810 mPictureAnchor = anchor;
816 mSvgFillColor = color;
822 mSvgStrokeColor = color;
828 mSvgStrokeWidth = width;
834 if ( !
mLayout || mRotationMapUuid.isEmpty() )
836 mRotationMap =
nullptr;
842 disconnectMap( mRotationMap );
844 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...
QString picturePath() const
Returns the path of the source image.
static 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.
virtual bool containsAdvancedEffects() const
Returns true if the item contains contents with blend modes or transparency effects which can only be...
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()
Is 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.
static QgsLayoutItemPicture * create(QgsLayout *layout)
Returns a new picture item for the specified layout.
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.
QgsLayoutItemPicture(QgsLayout *layout)
Constructor for QgsLayoutItemPicture, with the specified parent layout.
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)
bool containsAdvancedEffects() const override
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.
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.
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.
double itemOpacity() const
Returns the item's opacity.
void pictureRotationChanged(double newRotation)
Is 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.
QSizeF applyItemSizeConstraint(const QSizeF &targetSize) override
Applies any item-specific size constraint handling to a given targetSize in layout units...
Contains information about the context in which a coordinate transform is executed.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
void mapRotationChanged(double newRotation)
Is emitted when the map's rotation changes.
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 ...
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
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.
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.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Gets SVG content.
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 fetchContent(const QUrl &url)
Fetches content from a remote URL and handles redirects.
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.
static QColor decodeColor(const QString &str)
double width() const
Returns the width of the size.