Skip to content

Commit 74307d6

Browse files
committed
Fix incorrect annotation scaling when exporting layouts
Previously, annotation size and position always used pixel units. This did not work well when exporting layouts, resulting in tiny annotations (it also caused issues when moving projects between hidpi/non hidpi displays). Instead, use millimeters for annotation size and position so that the appearance is consistent across displays and works correctly in layout exports. Add lots of unit tests covering this. Fixes #18373
1 parent 8f02861 commit 74307d6

File tree

43 files changed

+456
-143
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+456
-143
lines changed

python/core/auto_generated/annotations/qgsannotation.sip.in

+54-8
Original file line numberDiff line numberDiff line change
@@ -143,34 +143,80 @@ the relative percentage for the position compared to the map width and height.
143143
.. seealso:: :py:func:`relativePosition`
144144
%End
145145

146-
void setFrameOffsetFromReferencePoint( QPointF offset );
146+
void setFrameOffsetFromReferencePoint( QPointF offset ) /Deprecated/;
147147
%Docstring
148-
Sets the annotation's frame's offset from the mapPosition() reference point.
148+
Sets the annotation's frame's offset (in pixels) from the mapPosition() reference point.
149149

150150
.. seealso:: :py:func:`frameOffsetFromReferencePoint`
151+
152+
.. deprecated:: use setFrameOffsetFromReferencePointMm() instead
151153
%End
152154

153-
QPointF frameOffsetFromReferencePoint() const;
155+
QPointF frameOffsetFromReferencePoint() const /Deprecated/;
154156
%Docstring
155-
Returns the annotation's frame's offset from the mapPosition() reference point.
157+
Returns the annotation's frame's offset (in pixels) from the mapPosition() reference point.
156158

157159
.. seealso:: :py:func:`setFrameOffsetFromReferencePoint`
160+
161+
.. deprecated:: use frameOffsetFromReferencePointMm() instead
162+
%End
163+
164+
void setFrameOffsetFromReferencePointMm( QPointF offset );
165+
%Docstring
166+
Sets the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
167+
168+
.. seealso:: :py:func:`frameOffsetFromReferencePointMm`
169+
170+
.. versionadded:: 3.4.8
158171
%End
159172

160-
void setFrameSize( QSizeF size );
173+
QPointF frameOffsetFromReferencePointMm() const;
161174
%Docstring
162-
Sets the size of the annotation's frame (the main area in which
175+
Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
176+
177+
.. seealso:: :py:func:`setFrameOffsetFromReferencePointMm`
178+
179+
.. versionadded:: 3.4.8
180+
%End
181+
182+
void setFrameSize( QSizeF size ) /Deprecated/;
183+
%Docstring
184+
Sets the size (in pixels) of the annotation's frame (the main area in which
163185
the annotation's content is drawn).
164186

165187
.. seealso:: :py:func:`frameSize`
188+
189+
.. deprecated:: use setFrameSizeMm() instead
166190
%End
167191

168-
QSizeF frameSize() const;
192+
QSizeF frameSize() const /Deprecated/;
169193
%Docstring
170-
Returns the size of the annotation's frame (the main area in which
194+
Returns the size (in pixels) of the annotation's frame (the main area in which
171195
the annotation's content is drawn).
172196

173197
.. seealso:: :py:func:`setFrameSize`
198+
199+
.. deprecated:: use frameSizeMm() instead
200+
%End
201+
202+
void setFrameSizeMm( QSizeF size );
203+
%Docstring
204+
Sets the size (in millimeters) of the annotation's frame (the main area in which
205+
the annotation's content is drawn).
206+
207+
.. seealso:: :py:func:`frameSizeMm`
208+
209+
.. versionadded:: 3.4.8
210+
%End
211+
212+
QSizeF frameSizeMm() const;
213+
%Docstring
214+
Returns the size (in millimeters) of the annotation's frame (the main area in which
215+
the annotation's content is drawn).
216+
217+
.. seealso:: :py:func:`setFrameSizeMm`
218+
219+
.. versionadded:: 3.4.8
174220
%End
175221

176222
void setContentsMargin( const QgsMargins &margins );

src/app/qgsmaptoolannotation.cpp

+23-23
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,19 @@ QDialog *QgsMapToolAnnotation::createItemEditor( QgsMapCanvasAnnotationItem *ite
5050

5151
QgsAnnotation *annotation = item->annotation();
5252

53-
QgsTextAnnotation *tItem = dynamic_cast<QgsTextAnnotation *>( annotation );
54-
if ( tItem )
53+
if ( qobject_cast<QgsTextAnnotation *>( annotation ) )
5554
{
5655
return new QgsTextAnnotationDialog( item );
5756
}
58-
59-
QgsFormAnnotation *fItem = dynamic_cast<QgsFormAnnotation *>( annotation );
60-
if ( fItem )
57+
else if ( qobject_cast<QgsFormAnnotation *>( annotation ) )
6158
{
6259
return new QgsFormAnnotationDialog( item );
6360
}
64-
65-
QgsHtmlAnnotation *hItem = dynamic_cast<QgsHtmlAnnotation *>( annotation );
66-
if ( hItem )
61+
else if ( qobject_cast<QgsHtmlAnnotation *>( annotation ) )
6762
{
6863
return new QgsHtmlAnnotationDialog( item );
6964
}
70-
71-
QgsSvgAnnotation *sItem = dynamic_cast<QgsSvgAnnotation *>( annotation );
72-
if ( sItem )
65+
else if ( qobject_cast<QgsSvgAnnotation *>( annotation ) )
7366
{
7467
return new QgsSvgAnnotationDialog( item );
7568
}
@@ -124,7 +117,7 @@ void QgsMapToolAnnotation::canvasPressEvent( QgsMapMouseEvent *e )
124117
annotation->setMapPositionCrs( mCanvas->mapSettings().destinationCrs() );
125118
annotation->setRelativePosition( QPointF( e->pos().x() / mCanvas->width(),
126119
e->pos().y() / mCanvas->height() ) );
127-
annotation->setFrameSize( QSizeF( 200, 100 ) );
120+
annotation->setFrameSizeMm( QSizeF( 50, 25 ) );
128121

129122
QgsProject::instance()->annotationManager()->addAnnotation( annotation );
130123

@@ -192,7 +185,11 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
192185
QPointF newCanvasPos = item->pos() + ( e->pos() - mLastMousePosition );
193186
if ( annotation->hasFixedMapPosition() )
194187
{
195-
annotation->setFrameOffsetFromReferencePoint( annotation->frameOffsetFromReferencePoint() + ( e->pos() - mLastMousePosition ) );
188+
const double pixelToMmScale = 25.4 / mCanvas->logicalDpiX();
189+
const double deltaX = pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
190+
const double deltaY = pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
191+
annotation->setFrameOffsetFromReferencePointMm( QPointF( annotation->frameOffsetFromReferencePointMm().x() + deltaX,
192+
annotation->frameOffsetFromReferencePointMm().y() + deltaY ) );
196193
annotation->setRelativePosition( QPointF( newCanvasPos.x() / mCanvas->width(),
197194
newCanvasPos.y() / mCanvas->height() ) );
198195
}
@@ -209,9 +206,12 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
209206
else if ( mCurrentMoveAction != QgsMapCanvasAnnotationItem::NoAction )
210207
{
211208
//handle the frame resize actions
212-
QSizeF size = annotation->frameSize();
213-
double xmin = annotation->frameOffsetFromReferencePoint().x();
214-
double ymin = annotation->frameOffsetFromReferencePoint().y();
209+
210+
const double pixelToMmScale = 25.4 / mCanvas->logicalDpiX();
211+
212+
QSizeF size = annotation->frameSizeMm();
213+
double xmin = annotation->frameOffsetFromReferencePointMm().x();
214+
double ymin = annotation->frameOffsetFromReferencePointMm().y();
215215
double xmax = xmin + size.width();
216216
double ymax = ymin + size.height();
217217
double relPosX = annotation->relativePosition().x();
@@ -221,27 +221,27 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
221221
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown ||
222222
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp )
223223
{
224-
xmax += e->pos().x() - mLastMousePosition.x();
224+
xmax += pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
225225
}
226226
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeft ||
227227
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown ||
228228
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp )
229229
{
230-
xmin += e->pos().x() - mLastMousePosition.x();
230+
xmin += pixelToMmScale * ( e->pos().x() - mLastMousePosition.x() );
231231
relPosX = ( relPosX * mCanvas->width() + e->pos().x() - mLastMousePosition.x() ) / static_cast<double>( mCanvas->width() );
232232
}
233233
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameUp ||
234234
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftUp ||
235235
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightUp )
236236
{
237-
ymin += e->pos().y() - mLastMousePosition.y();
237+
ymin += pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
238238
relPosY = ( relPosY * mCanvas->height() + e->pos().y() - mLastMousePosition.y() ) / static_cast<double>( mCanvas->height() );
239239
}
240240
if ( mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameDown ||
241241
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameLeftDown ||
242242
mCurrentMoveAction == QgsMapCanvasAnnotationItem::ResizeFrameRightDown )
243243
{
244-
ymax += e->pos().y() - mLastMousePosition.y();
244+
ymax += pixelToMmScale * ( e->pos().y() - mLastMousePosition.y() );
245245
}
246246

247247
//switch min / max if necessary
@@ -259,8 +259,8 @@ void QgsMapToolAnnotation::canvasMoveEvent( QgsMapMouseEvent *e )
259259
ymin = tmp;
260260
}
261261

262-
annotation->setFrameOffsetFromReferencePoint( QPointF( xmin, ymin ) );
263-
annotation->setFrameSize( QSizeF( xmax - xmin, ymax - ymin ) );
262+
annotation->setFrameOffsetFromReferencePointMm( QPointF( xmin, ymin ) );
263+
annotation->setFrameSizeMm( QSizeF( xmax - xmin, ymax - ymin ) );
264264
annotation->setRelativePosition( QPointF( relPosX, relPosY ) );
265265
item->update();
266266
QgsProject::instance()->setDirty( true );
@@ -349,7 +349,7 @@ void QgsMapToolAnnotation::toggleTextItemVisibilities()
349349
QList<QgsMapCanvasAnnotationItem *> itemList = annotationItems();
350350
Q_FOREACH ( QgsMapCanvasAnnotationItem *item, itemList )
351351
{
352-
QgsTextAnnotation *textItem = dynamic_cast<QgsTextAnnotation *>( item->annotation() );
352+
QgsTextAnnotation *textItem = qobject_cast<QgsTextAnnotation *>( item->annotation() );
353353
if ( textItem )
354354
{
355355
textItem->setVisible( !textItem->isVisible() );

0 commit comments

Comments
 (0)