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( QStringLiteral(
"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();
476 QgsDebugMsg( QStringLiteral(
"Caught exception %1" ).arg( e.
what() ) );
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;
685 QString imagePath = mSourcePath;
689 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
692 imagePath = pathResolver.
writePath( imagePath );
694 elem.setAttribute( QStringLiteral(
"file" ), imagePath );
695 elem.setAttribute( QStringLiteral(
"pictureWidth" ), QString::number( mPictureWidth ) );
696 elem.setAttribute( QStringLiteral(
"pictureHeight" ), QString::number( mPictureHeight ) );
697 elem.setAttribute( QStringLiteral(
"resizeMode" ), QString::number( static_cast< int >( mResizeMode ) ) );
698 elem.setAttribute( QStringLiteral(
"anchorPoint" ), QString::number( static_cast< int >( mPictureAnchor ) ) );
701 elem.setAttribute( QStringLiteral(
"svgBorderWidth" ), QString::number( mSvgStrokeWidth ) );
704 elem.setAttribute( QStringLiteral(
"pictureRotation" ), QString::number( mPictureRotation ) );
707 elem.setAttribute( QStringLiteral(
"mapUuid" ), QString() );
711 elem.setAttribute( QStringLiteral(
"mapUuid" ), mRotationMap->uuid() );
713 elem.setAttribute( QStringLiteral(
"northMode" ), mNorthMode );
714 elem.setAttribute( QStringLiteral(
"northOffset" ), mNorthOffset );
720 mPictureWidth = itemElem.attribute( QStringLiteral(
"pictureWidth" ), QStringLiteral(
"10" ) ).toDouble();
721 mPictureHeight = itemElem.attribute( QStringLiteral(
"pictureHeight" ), QStringLiteral(
"10" ) ).toDouble();
728 mSvgStrokeWidth = itemElem.attribute( QStringLiteral(
"svgBorderWidth" ), QStringLiteral(
"0.2" ) ).toDouble();
730 QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral(
"ComposerItem" ) );
731 if ( !composerItemList.isEmpty() )
733 QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
735 if ( !
qgsDoubleNear( composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
738 mPictureRotation = composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble();
742 mDefaultSvgSize = QSize( 0, 0 );
744 if ( itemElem.hasAttribute( QStringLiteral(
"sourceExpression" ) ) )
747 QString sourceExpression = itemElem.attribute( QStringLiteral(
"sourceExpression" ), QString() );
748 QString useExpression = itemElem.attribute( QStringLiteral(
"useExpression" ) );
749 bool expressionActive;
750 expressionActive = ( useExpression.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 );
755 QString imagePath = itemElem.attribute( QStringLiteral(
"file" ) );
759 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
762 imagePath = pathResolver.
readPath( imagePath );
764 mSourcePath = imagePath;
767 if ( !
qgsDoubleNear( itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
769 mPictureRotation = itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble();
773 mNorthMode =
static_cast< NorthMode >( itemElem.attribute( QStringLiteral(
"northMode" ), QStringLiteral(
"0" ) ).toInt() );
774 mNorthOffset = itemElem.attribute( QStringLiteral(
"northOffset" ), QStringLiteral(
"0" ) ).toDouble();
776 disconnectMap( mRotationMap );
777 mRotationMap =
nullptr;
778 mRotationMapUuid = itemElem.attribute( QStringLiteral(
"mapUuid" ) );
796 mNorthOffset = offset;
802 mPictureAnchor = anchor;
808 mSvgFillColor = color;
814 mSvgStrokeColor = color;
820 mSvgStrokeWidth = width;
826 if ( !
mLayout || mRotationMapUuid.isEmpty() )
828 mRotationMap =
nullptr;
834 disconnectMap( mRotationMap );
836 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...
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...
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.
Lower left corner of item.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
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...
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
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
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.
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.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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.
double height() const
Returns the height of the size.
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.
double width() const
Returns the width of the size.
Layout graphical items for displaying a map.
QgsPropertyCollection mDataDefinedProperties
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).
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
QPointer< QgsLayout > mLayout
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map object for rotation.
void finished()
Emitted when content has loaded.
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.
Contains information about the context in which a coordinate transform is executed.
QString picturePath() const
Returns the path of the source image.
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.
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...
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 ...
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...
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.
QgsLayoutItemMap * linkedMap() const
Returns the linked rotation map, if set.
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.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
Format mode() const
Returns the current picture mode (image format).
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.
const QgsLayout * layout() const
Returns the layout the object is attached to.
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.
DataDefinedProperty
Data defined properties for different item types.
Defines a QGIS exception class.
static QColor decodeColor(const QString &str)