Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport queued_ltr_backports] Don't restore corrupted sizes/positions when restoring layout items #59103

Merged
3 changes: 2 additions & 1 deletion src/core/layout/qgslayoutitemgroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ void QgsLayoutItemGroup::attemptResize( const QgsLayoutSize &size, bool includes
command->saveBeforeState();
}

QRectF itemRect = mapRectFromItem( item, item->rect() );
const QRectF originalItemRect = item->rect();
QRectF itemRect = mapRectFromItem( item, originalItemRect );
QgsLayoutUtils::relativeResizeRect( itemRect, oldRect, newRect );

itemRect = itemRect.normalized();
Expand Down
14 changes: 8 additions & 6 deletions src/core/layout/qgslayoutitemmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,14 @@ void QgsLayoutItemMap::setExtent( const QgsRectangle &extent )
//recalculate data defined scale and extents, since that may override extent
refreshMapExtents();

//adjust height
QRectF currentRect = rect();

double newHeight = currentRect.width() * mExtent.height() / mExtent.width();

attemptSetSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
//adjust height, if possible
if ( mExtent.isFinite() && !mExtent.isEmpty() )
{
const QRectF currentRect = rect();
const double newHeight = mExtent.width() == 0 ? 0
: currentRect.width() * mExtent.height() / mExtent.width();
attemptSetSceneRect( QRectF( pos().x(), pos().y(), currentRect.width(), newHeight ) );
}
update();
}

Expand Down
15 changes: 13 additions & 2 deletions src/core/layout/qgslayoutpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ QgsLayoutPoint::QgsLayoutPoint( const double x, const double y, const Qgis::Layo
, mY( y )
, mUnits( units )
{

#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( mX ) && !std::isnan( mY ), "QgsLayoutPoint", "Layout point with NaN coordinates created" );
#endif
}

QgsLayoutPoint::QgsLayoutPoint( const QPointF point, const Qgis::LayoutUnit units )
Expand Down Expand Up @@ -65,7 +67,16 @@ QgsLayoutPoint QgsLayoutPoint::decodePoint( const QString &string )
{
return QgsLayoutPoint();
}
return QgsLayoutPoint( parts[0].toDouble(), parts[1].toDouble(), QgsUnitTypes::decodeLayoutUnit( parts[2] ) );
const double x = parts[0].toDouble();
const double y = parts[1].toDouble();

// don't restore corrupted coordinates from xml. This can happen when eg a broken item size causes a nan position,
// which breaks the layout size calculation and results in nan or massive x/y values. Restoring these leads to a broken
// layout which cannot be interacted with.
if ( std::isnan( x ) || std::isnan( y ) || x > 9.99998e+06 || y > 9.99998e+06 )
return QgsLayoutPoint();

return QgsLayoutPoint( x, y, QgsUnitTypes::decodeLayoutUnit( parts[2] ) );
}

bool QgsLayoutPoint::operator==( const QgsLayoutPoint &other ) const
Expand Down
17 changes: 15 additions & 2 deletions src/core/layout/qgslayoutpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "qgis_core.h"
#include "qgis.h"
#include "qgsconfig.h"
#include <QPointF>

/**
Expand Down Expand Up @@ -76,7 +77,13 @@ class CORE_EXPORT QgsLayoutPoint
* \see x()
* \see setY()
*/
void setX( const double x ) { mX = x; }
void setX( const double x )
{
#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( x ), "QgsLayoutPoint", "Layout point with NaN coordinates created" );
#endif
mX = x;
}

/**
* Returns y coordinate of point.
Expand All @@ -90,7 +97,13 @@ class CORE_EXPORT QgsLayoutPoint
* \see y()
* \see setX()
*/
void setY( const double y ) { mY = y; }
void setY( const double y )
{
#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( y ), "QgsLayoutPoint", "Layout point with NaN coordinates created" );
#endif
mY = y;
}

/**
* Returns the units for the point.
Expand Down
10 changes: 9 additions & 1 deletion src/core/layout/qgslayoutsize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ QgsLayoutSize::QgsLayoutSize( const double width, const double height, const Qgi
, mHeight( height )
, mUnits( units )
{
#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( width ) && !std::isnan( height ), "QgsLayoutSize", "Layout size with NaN dimensions created" );
#endif
}

QgsLayoutSize::QgsLayoutSize( const QSizeF size, const Qgis::LayoutUnit units )
Expand Down Expand Up @@ -62,8 +65,13 @@ QgsLayoutSize QgsLayoutSize::decodeSize( const QString &string )
{
return QgsLayoutSize();
}
return QgsLayoutSize( parts[0].toDouble(), parts[1].toDouble(), QgsUnitTypes::decodeLayoutUnit( parts[2] ) );

const double width = parts[0].toDouble();
const double height = parts[1].toDouble();
if ( std::isnan( width ) || std::isnan( height ) )
return QgsLayoutSize();

return QgsLayoutSize( width, height, QgsUnitTypes::decodeLayoutUnit( parts[2] ) );
}

bool QgsLayoutSize::operator==( const QgsLayoutSize &other ) const
Expand Down
26 changes: 23 additions & 3 deletions src/core/layout/qgslayoutsize.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "qgis_core.h"
#include "qgis.h"
#include "qgsconfig.h"
#include <QSizeF>


Expand Down Expand Up @@ -66,7 +67,14 @@ class CORE_EXPORT QgsLayoutSize
* \see setHeight()
* \see setUnits()
*/
void setSize( const double width, const double height ) { mWidth = width; mHeight = height; }
void setSize( const double width, const double height )
{
mWidth = width;
mHeight = height;
#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( width ) && !std::isnan( height ), "QgsLayoutSize", "Layout size with NaN dimensions created" );
#endif
}

/**
* Returns the width of the size.
Expand All @@ -80,7 +88,13 @@ class CORE_EXPORT QgsLayoutSize
* \see width()
* \see setHeight()
*/
void setWidth( const double width ) { mWidth = width; }
void setWidth( const double width )
{
mWidth = width;
#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( width ), "QgsLayoutSize", "Layout size with NaN dimensions created" );
#endif
}

/**
* Returns the height of the size.
Expand All @@ -94,7 +108,13 @@ class CORE_EXPORT QgsLayoutSize
* \see height()
* \see setWidth()
*/
void setHeight( const double height ) { mHeight = height; }
void setHeight( const double height )
{
mHeight = height;
#ifdef QGISDEBUG
Q_ASSERT_X( !std::isnan( height ), "QgsLayoutSize", "Layout size with NaN dimensions created" );
#endif
}

/**
* Returns the units for the size.
Expand Down
16 changes: 12 additions & 4 deletions src/core/layout/qgslayoututils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,18 @@ QgsRenderContext QgsLayoutUtils::createRenderContextForLayout( QgsLayout *layout
void QgsLayoutUtils::relativeResizeRect( QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter )
{
//linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
const double left = !qgsDoubleNear( boundsBefore.left(), boundsBefore.right() )
? relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() )
: boundsAfter.left();
const double right = !qgsDoubleNear( boundsBefore.left(), boundsBefore.right() )
? relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() )
: boundsAfter.right();
const double top = !qgsDoubleNear( boundsBefore.top(), boundsBefore.bottom() )
? relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() )
: boundsAfter.top();
const double bottom = !qgsDoubleNear( boundsBefore.top(), boundsBefore.bottom() )
? relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() )
: boundsAfter.bottom();

rectToResize.setRect( left, top, right - left, bottom - top );
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/scalebar/qgsscalebarrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ QSizeF QgsScaleBarRenderer::calculateBoxSize( QgsRenderContext &context, const Q

//consider last number and label
const double largestLabelNumber = settings.numberOfSegments() * settings.unitsPerSegment() / settings.mapUnitsPerScaleBarUnit();
const QString largestNumberLabel = settings.numericFormat()->formatDouble( largestLabelNumber, QgsNumericFormatContext() );
const QString largestNumberLabel = std::isnan( largestLabelNumber ) ? QString() : settings.numericFormat()->formatDouble( largestLabelNumber, QgsNumericFormatContext() );
const QString largestLabel = largestNumberLabel + ' ' + settings.unitLabel();
double largestLabelWidth;
if ( settings.labelHorizontalPlacement() == QgsScaleBarSettings::LabelCenteredSegment )
Expand Down
14 changes: 14 additions & 0 deletions tests/src/core/testqgslayoutunits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ void TestQgsLayoutUnits::sizeEncodeDecode()
//test with bad string
result = QgsLayoutSize::decodeSize( QStringLiteral( "1,2" ) );
QCOMPARE( result, QgsLayoutSize() );

//test with nan values
result = QgsLayoutSize::decodeSize( QStringLiteral( "nan,2,mm" ) );
QCOMPARE( result, QgsLayoutSize() );
result = QgsLayoutSize::decodeSize( QStringLiteral( "2,nan,mm" ) );
QCOMPARE( result, QgsLayoutSize() );
}

void TestQgsLayoutUnits::createPoint()
Expand Down Expand Up @@ -520,6 +526,14 @@ void TestQgsLayoutUnits::pointEncodeDecode()
//test with bad string
result = QgsLayoutPoint::decodePoint( QStringLiteral( "1,2" ) );
QCOMPARE( result, QgsLayoutPoint() );

//test with nan values
result = QgsLayoutPoint::decodePoint( QStringLiteral( "nan,2,mm" ) );
QCOMPARE( result, QgsLayoutPoint() );
result = QgsLayoutPoint::decodePoint( QStringLiteral( "2,nan,mm" ) );
QCOMPARE( result, QgsLayoutPoint() );
result = QgsLayoutPoint::decodePoint( QStringLiteral( "9.99999e+06,1e+07,mm" ) );
QCOMPARE( result, QgsLayoutPoint() );
}

void TestQgsLayoutUnits::converterCreate()
Expand Down
Loading