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 mIsMissingImage =
false;
489 mEvaluatedPath = path;
490 if ( path.startsWith( QLatin1String(
"http" ) ) )
493 loadRemotePicture( path );
498 loadLocalPicture( path );
504 else if ( mHasExpressionError || !( path.isEmpty() ) )
508 mIsMissingImage =
true;
509 QString badFile( QStringLiteral(
":/images/composer/missing_image.svg" ) );
510 mSVG.load( badFile );
511 if ( mSVG.isValid() )
514 QRect viewBox = mSVG.viewBox();
515 mDefaultSvgSize.setWidth( viewBox.width() );
516 mDefaultSvgSize.setHeight( viewBox.height() );
525 QRectF QgsLayoutItemPicture::boundedImageRect(
double deviceWidth,
double deviceHeight )
527 double imageToDeviceRatio;
528 if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
530 imageToDeviceRatio = deviceWidth / mImage.width();
531 double height = imageToDeviceRatio * mImage.height();
532 return QRectF( 0, 0, deviceWidth, height );
536 imageToDeviceRatio = deviceHeight / mImage.height();
537 double width = imageToDeviceRatio * mImage.width();
538 return QRectF( 0, 0, width, deviceHeight );
542 QRectF QgsLayoutItemPicture::boundedSVGRect(
double deviceWidth,
double deviceHeight )
544 double imageToSvgRatio;
545 if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
547 imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
548 double width = mDefaultSvgSize.width() * imageToSvgRatio;
549 return QRectF( 0, 0, width, deviceHeight );
553 imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
554 double height = mDefaultSvgSize.height() * imageToSvgRatio;
555 return QRectF( 0, 0, deviceWidth, height );
559 QSizeF QgsLayoutItemPicture::pictureSize()
563 return mDefaultSvgSize;
567 return QSizeF( mImage.width(), mImage.height() );
571 return QSizeF( 0, 0 );
577 return mIsMissingImage;
582 return mEvaluatedPath;
585 void QgsLayoutItemPicture::shapeChanged()
587 if ( mMode ==
FormatSVG && !mLoadingSvg )
597 double oldRotation = mPictureRotation;
598 mPictureRotation = rotation;
600 if ( mResizeMode ==
Zoom )
603 QSizeF currentPictureSize = pictureSize();
605 mPictureWidth = rotatedImageRect.width();
606 mPictureHeight = rotatedImageRect.height();
611 QSizeF currentPictureSize = pictureSize();
612 QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
619 tr.rotate( mPictureRotation );
620 QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
623 newRect.moveCenter( oldRect.center() );
635 disconnectMap( mRotationMap );
640 mRotationMap =
nullptr;
698 QString imagePath = mSourcePath;
702 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
705 imagePath = pathResolver.
writePath( imagePath );
707 elem.setAttribute( QStringLiteral(
"file" ), imagePath );
708 elem.setAttribute( QStringLiteral(
"pictureWidth" ), QString::number( mPictureWidth ) );
709 elem.setAttribute( QStringLiteral(
"pictureHeight" ), QString::number( mPictureHeight ) );
710 elem.setAttribute( QStringLiteral(
"resizeMode" ), QString::number( static_cast< int >( mResizeMode ) ) );
711 elem.setAttribute( QStringLiteral(
"anchorPoint" ), QString::number( static_cast< int >( mPictureAnchor ) ) );
714 elem.setAttribute( QStringLiteral(
"svgBorderWidth" ), QString::number( mSvgStrokeWidth ) );
717 elem.setAttribute( QStringLiteral(
"pictureRotation" ), QString::number( mPictureRotation ) );
720 elem.setAttribute( QStringLiteral(
"mapUuid" ), QString() );
724 elem.setAttribute( QStringLiteral(
"mapUuid" ), mRotationMap->uuid() );
726 elem.setAttribute( QStringLiteral(
"northMode" ), mNorthMode );
727 elem.setAttribute( QStringLiteral(
"northOffset" ), mNorthOffset );
733 mPictureWidth = itemElem.attribute( QStringLiteral(
"pictureWidth" ), QStringLiteral(
"10" ) ).toDouble();
734 mPictureHeight = itemElem.attribute( QStringLiteral(
"pictureHeight" ), QStringLiteral(
"10" ) ).toDouble();
741 mSvgStrokeWidth = itemElem.attribute( QStringLiteral(
"svgBorderWidth" ), QStringLiteral(
"0.2" ) ).toDouble();
743 QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral(
"ComposerItem" ) );
744 if ( !composerItemList.isEmpty() )
746 QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
748 if ( !
qgsDoubleNear( composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
751 mPictureRotation = composerItemElem.attribute( QStringLiteral(
"rotation" ), QStringLiteral(
"0" ) ).toDouble();
755 mDefaultSvgSize = QSize( 0, 0 );
757 if ( itemElem.hasAttribute( QStringLiteral(
"sourceExpression" ) ) )
760 QString sourceExpression = itemElem.attribute( QStringLiteral(
"sourceExpression" ), QString() );
761 QString useExpression = itemElem.attribute( QStringLiteral(
"useExpression" ) );
762 bool expressionActive;
763 expressionActive = ( useExpression.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 );
768 QString imagePath = itemElem.attribute( QStringLiteral(
"file" ) );
772 if ( imagePath.endsWith( QLatin1String(
".svg" ), Qt::CaseInsensitive ) )
775 imagePath = pathResolver.
readPath( imagePath );
777 mSourcePath = imagePath;
780 if ( !
qgsDoubleNear( itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble(), 0.0 ) )
782 mPictureRotation = itemElem.attribute( QStringLiteral(
"pictureRotation" ), QStringLiteral(
"0" ) ).toDouble();
786 mNorthMode =
static_cast< NorthMode >( itemElem.attribute( QStringLiteral(
"northMode" ), QStringLiteral(
"0" ) ).toInt() );
787 mNorthOffset = itemElem.attribute( QStringLiteral(
"northOffset" ), QStringLiteral(
"0" ) ).toDouble();
789 disconnectMap( mRotationMap );
790 mRotationMap =
nullptr;
791 mRotationMapUuid = itemElem.attribute( QStringLiteral(
"mapUuid" ) );
809 mNorthOffset = offset;
815 mPictureAnchor = anchor;
821 mSvgFillColor = color;
827 mSvgStrokeColor = color;
833 mSvgStrokeWidth = width;
839 if ( !
mLayout || mRotationMapUuid.isEmpty() )
841 mRotationMap =
nullptr;
847 disconnectMap( mRotationMap );
849 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 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()
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.
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.
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.
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.
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.
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.
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.
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.