Skip to content

Commit 67271cf

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 5c79b74 commit 67271cf

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
@@ -142,34 +142,80 @@ the relative percentage for the position compared to the map width and height.
142142
.. seealso:: :py:func:`relativePosition`
143143
%End
144144

145-
void setFrameOffsetFromReferencePoint( QPointF offset );
145+
void setFrameOffsetFromReferencePoint( QPointF offset ) /Deprecated/;
146146
%Docstring
147-
Sets the annotation's frame's offset from the mapPosition() reference point.
147+
Sets the annotation's frame's offset (in pixels) from the mapPosition() reference point.
148148

149149
.. seealso:: :py:func:`frameOffsetFromReferencePoint`
150+
151+
.. deprecated:: use setFrameOffsetFromReferencePointMm() instead
150152
%End
151153

152-
QPointF frameOffsetFromReferencePoint() const;
154+
QPointF frameOffsetFromReferencePoint() const /Deprecated/;
153155
%Docstring
154-
Returns the annotation's frame's offset from the mapPosition() reference point.
156+
Returns the annotation's frame's offset (in pixels) from the mapPosition() reference point.
155157

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

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

164186
.. seealso:: :py:func:`frameSize`
187+
188+
.. deprecated:: use setFrameSizeMm() instead
165189
%End
166190

167-
QSizeF frameSize() const;
191+
QSizeF frameSize() const /Deprecated/;
168192
%Docstring
169-
Returns the size of the annotation's frame (the main area in which
193+
Returns the size (in pixels) of the annotation's frame (the main area in which
170194
the annotation's content is drawn).
171195

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

175221
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)