QGIS API Documentation  2.99.0-Master (7fe5405)
qgscomposerutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerutils.cpp
3  -------------------
4  begin : July 2014
5  copyright : (C) 2014 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscomposerutils.h"
19 #include "qgscomposition.h"
20 #include "qgsdatadefined.h"
21 #include "qgsmapsettings.h"
22 #include "qgscomposermap.h"
23 #include <QPainter>
24 
25 #define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
26 
27 #ifndef M_DEG2RAD
28 #define M_DEG2RAD 0.0174532925
29 #endif
30 
31 void QgsComposerUtils::drawArrowHead( QPainter *p, const double x, const double y, const double angle, const double arrowHeadWidth )
32 {
33  if ( !p )
34  {
35  return;
36  }
37 
38  double angleRad = angle / 180.0 * M_PI;
39  QPointF middlePoint( x, y );
40  //rotate both arrow points
41  QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
42  QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
43 
44  QPointF p1Rotated, p2Rotated;
45  p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) );
46  p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) );
47  p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) );
48  p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) );
49 
50  QPolygonF arrowHeadPoly;
51  arrowHeadPoly << middlePoint;
52  arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
53  arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
54 
55  p->save();
56 
57  QPen arrowPen = p->pen();
58  arrowPen.setJoinStyle( Qt::RoundJoin );
59  QBrush arrowBrush = p->brush();
60  arrowBrush.setStyle( Qt::SolidPattern );
61  p->setPen( arrowPen );
62  p->setBrush( arrowBrush );
63  arrowBrush.setStyle( Qt::SolidPattern );
64  p->drawPolygon( arrowHeadPoly );
65 
66  p->restore();
67 }
68 
69 double QgsComposerUtils::angle( QPointF p1, QPointF p2 )
70 {
71  double xDiff = p2.x() - p1.x();
72  double yDiff = p2.y() - p1.y();
73  double length = sqrt( xDiff * xDiff + yDiff * yDiff );
74  if ( length <= 0 )
75  {
76  return 0;
77  }
78 
79  double angle = acos(( -yDiff * length ) / ( length * length ) ) * 180 / M_PI;
80  if ( xDiff < 0 )
81  {
82  return ( 360 - angle );
83  }
84  return angle;
85 }
86 
87 void QgsComposerUtils::rotate( const double angle, double &x, double &y )
88 {
89  double rotToRad = angle * M_PI / 180.0;
90  double xRot, yRot;
91  xRot = x * cos( rotToRad ) - y * sin( rotToRad );
92  yRot = x * sin( rotToRad ) + y * cos( rotToRad );
93  x = xRot;
94  y = yRot;
95 }
96 
98 {
99  double clippedAngle = angle;
100  if ( clippedAngle >= 360.0 || clippedAngle <= -360.0 )
101  {
102  clippedAngle = fmod( clippedAngle, 360.0 );
103  }
104  if ( clippedAngle < 0.0 )
105  {
106  clippedAngle += 360.0;
107  }
108  return clippedAngle;
109 }
110 
111 double QgsComposerUtils::snappedAngle( const double angle )
112 {
113  //normalize angle to 0-360 degrees
114  double clippedAngle = normalizedAngle( angle );
115 
116  //snap angle to 45 degree
117  if ( clippedAngle >= 22.5 && clippedAngle < 67.5 )
118  {
119  return 45.0;
120  }
121  else if ( clippedAngle >= 67.5 && clippedAngle < 112.5 )
122  {
123  return 90.0;
124  }
125  else if ( clippedAngle >= 112.5 && clippedAngle < 157.5 )
126  {
127  return 135.0;
128  }
129  else if ( clippedAngle >= 157.5 && clippedAngle < 202.5 )
130  {
131  return 180.0;
132  }
133  else if ( clippedAngle >= 202.5 && clippedAngle < 247.5 )
134  {
135  return 225.0;
136  }
137  else if ( clippedAngle >= 247.5 && clippedAngle < 292.5 )
138  {
139  return 270.0;
140  }
141  else if ( clippedAngle >= 292.5 && clippedAngle < 337.5 )
142  {
143  return 315.0;
144  }
145  else
146  {
147  return 0.0;
148  }
149 }
150 
151 QRectF QgsComposerUtils::largestRotatedRectWithinBounds( const QRectF &originalRect, const QRectF &boundsRect, const double rotation )
152 {
153  double originalWidth = originalRect.width();
154  double originalHeight = originalRect.height();
155  double boundsWidth = boundsRect.width();
156  double boundsHeight = boundsRect.height();
157  double ratioBoundsRect = boundsWidth / boundsHeight;
158 
159  double clippedRotation = normalizedAngle( rotation );
160 
161  //shortcut for some rotation values
162  if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 90.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) || qgsDoubleNear( clippedRotation, 270.0 ) )
163  {
164  double rectScale;
165  if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) )
166  {
167  rectScale = (( originalWidth / originalHeight ) > ratioBoundsRect ) ? boundsWidth / originalWidth : boundsHeight / originalHeight;
168  }
169  else
170  {
171  rectScale = (( originalHeight / originalWidth ) > ratioBoundsRect ) ? boundsWidth / originalHeight : boundsHeight / originalWidth;
172  }
173  double rectScaledWidth = rectScale * originalWidth;
174  double rectScaledHeight = rectScale * originalHeight;
175 
176  if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) )
177  {
178  return QRectF(( boundsWidth - rectScaledWidth ) / 2.0, ( boundsHeight - rectScaledHeight ) / 2.0, rectScaledWidth, rectScaledHeight );
179  }
180  else
181  {
182  return QRectF(( boundsWidth - rectScaledHeight ) / 2.0, ( boundsHeight - rectScaledWidth ) / 2.0, rectScaledWidth, rectScaledHeight );
183  }
184  }
185 
186  //convert angle to radians and flip
187  double angleRad = -clippedRotation * M_DEG2RAD;
188  double cosAngle = cos( angleRad );
189  double sinAngle = sin( angleRad );
190 
191  //calculate size of bounds of rotated rectangle
192  double widthBoundsRotatedRect = originalWidth * fabs( cosAngle ) + originalHeight * fabs( sinAngle );
193  double heightBoundsRotatedRect = originalHeight * fabs( cosAngle ) + originalWidth * fabs( sinAngle );
194 
195  //compare ratio of rotated rect with bounds rect and calculate scaling of rotated
196  //rect to fit within bounds
197  double ratioBoundsRotatedRect = widthBoundsRotatedRect / heightBoundsRotatedRect;
198  double rectScale = ratioBoundsRotatedRect > ratioBoundsRect ? boundsWidth / widthBoundsRotatedRect : boundsHeight / heightBoundsRotatedRect;
199  double rectScaledWidth = rectScale * originalWidth;
200  double rectScaledHeight = rectScale * originalHeight;
201 
202  //now calculate offset so that rotated rectangle is centered within bounds
203  //first calculate min x and y coordinates
204  double currentCornerX = 0;
205  double minX = 0;
206  currentCornerX += rectScaledWidth * cosAngle;
207  minX = minX < currentCornerX ? minX : currentCornerX;
208  currentCornerX += rectScaledHeight * sinAngle;
209  minX = minX < currentCornerX ? minX : currentCornerX;
210  currentCornerX -= rectScaledWidth * cosAngle;
211  minX = minX < currentCornerX ? minX : currentCornerX;
212 
213  double currentCornerY = 0;
214  double minY = 0;
215  currentCornerY -= rectScaledWidth * sinAngle;
216  minY = minY < currentCornerY ? minY : currentCornerY;
217  currentCornerY += rectScaledHeight * cosAngle;
218  minY = minY < currentCornerY ? minY : currentCornerY;
219  currentCornerY += rectScaledWidth * sinAngle;
220  minY = minY < currentCornerY ? minY : currentCornerY;
221 
222  //now calculate offset position of rotated rectangle
223  double offsetX = ratioBoundsRotatedRect > ratioBoundsRect ? 0 : ( boundsWidth - rectScale * widthBoundsRotatedRect ) / 2.0;
224  offsetX += fabs( minX );
225  double offsetY = ratioBoundsRotatedRect > ratioBoundsRect ? ( boundsHeight - rectScale * heightBoundsRotatedRect ) / 2.0 : 0;
226  offsetY += fabs( minY );
227 
228  return QRectF( offsetX, offsetY, rectScaledWidth, rectScaledHeight );
229 }
230 
231 double QgsComposerUtils::pointsToMM( const double pointSize )
232 {
233  //conversion to mm based on 1 point = 1/72 inch
234  return ( pointSize * 0.3527 );
235 }
236 
237 double QgsComposerUtils::mmToPoints( const double mmSize )
238 {
239  //conversion to points based on 1 point = 1/72 inch
240  return ( mmSize / 0.3527 );
241 }
242 
243 void QgsComposerUtils::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
244 {
245  //linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
246  double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
247  double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
248  double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
249  double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
250 
251  rectToResize.setRect( left, top, right - left, bottom - top );
252 }
253 
254 double QgsComposerUtils::relativePosition( const double position, const double beforeMin, const double beforeMax, const double afterMin, const double afterMax )
255 {
256  //calculate parameters for linear scale between before and after ranges
257  double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
258  double c = afterMin - ( beforeMin * m );
259 
260  //return linearly scaled position
261  return m * position + c;
262 }
263 
265 {
266  if ( orientationString.compare( QLatin1String( "Portrait" ), Qt::CaseInsensitive ) == 0 )
267  {
268  ok = true;
270  }
271  if ( orientationString.compare( QLatin1String( "Landscape" ), Qt::CaseInsensitive ) == 0 )
272  {
273  ok = true;
275  }
276  ok = false;
277  return QgsComposition::Landscape; // default to landscape
278 }
279 
280 bool QgsComposerUtils::decodePresetPaperSize( const QString& presetString, double &width, double &height )
281 {
282  QList< QPair< QString, QSizeF > > presets;
283  presets << qMakePair( QStringLiteral( "A5" ), QSizeF( 148, 210 ) );
284  presets << qMakePair( QStringLiteral( "A4" ), QSizeF( 210, 297 ) );
285  presets << qMakePair( QStringLiteral( "A3" ), QSizeF( 297, 420 ) );
286  presets << qMakePair( QStringLiteral( "A2" ), QSizeF( 420, 594 ) );
287  presets << qMakePair( QStringLiteral( "A1" ), QSizeF( 594, 841 ) );
288  presets << qMakePair( QStringLiteral( "A0" ), QSizeF( 841, 1189 ) );
289  presets << qMakePair( QStringLiteral( "B5" ), QSizeF( 176, 250 ) );
290  presets << qMakePair( QStringLiteral( "B4" ), QSizeF( 250, 353 ) );
291  presets << qMakePair( QStringLiteral( "B3" ), QSizeF( 353, 500 ) );
292  presets << qMakePair( QStringLiteral( "B2" ), QSizeF( 500, 707 ) );
293  presets << qMakePair( QStringLiteral( "B1" ), QSizeF( 707, 1000 ) );
294  presets << qMakePair( QStringLiteral( "B0" ), QSizeF( 1000, 1414 ) );
295  // North american formats
296  presets << qMakePair( QStringLiteral( "Legal" ), QSizeF( 215.9, 355.6 ) );
297  presets << qMakePair( QStringLiteral( "Letter" ), QSizeF( 215.9, 279.4 ) );
298  presets << qMakePair( QStringLiteral( "ANSI A" ), QSizeF( 215.9, 279.4 ) );
299  presets << qMakePair( QStringLiteral( "ANSI B" ), QSizeF( 279.4, 431.8 ) );
300  presets << qMakePair( QStringLiteral( "ANSI C" ), QSizeF( 431.8, 558.8 ) );
301  presets << qMakePair( QStringLiteral( "ANSI D" ), QSizeF( 558.8, 863.6 ) );
302  presets << qMakePair( QStringLiteral( "ANSI E" ), QSizeF( 863.6, 1117.6 ) );
303  presets << qMakePair( QStringLiteral( "Arch A" ), QSizeF( 228.6, 304.8 ) );
304  presets << qMakePair( QStringLiteral( "Arch B" ), QSizeF( 304.8, 457.2 ) );
305  presets << qMakePair( QStringLiteral( "Arch C" ), QSizeF( 457.2, 609.6 ) );
306  presets << qMakePair( QStringLiteral( "Arch D" ), QSizeF( 609.6, 914.4 ) );
307  presets << qMakePair( QStringLiteral( "Arch E" ), QSizeF( 914.4, 1219.2 ) );
308  presets << qMakePair( QStringLiteral( "Arch E1" ), QSizeF( 762, 1066.8 ) );
309 
310  QList< QPair< QString, QSizeF > >::const_iterator presetIt = presets.constBegin();
311  for ( ;presetIt != presets.constEnd(); ++presetIt )
312  {
313  if ( presetString.compare(( *presetIt ).first, Qt::CaseInsensitive ) == 0 )
314  {
315  width = ( *presetIt ).second.width();
316  height = ( *presetIt ).second.height();
317  return true;
318  }
319  }
320  return false;
321 }
322 
323 void QgsComposerUtils::readDataDefinedPropertyMap( const QDomElement &itemElem, QMap<QgsComposerObject::DataDefinedProperty, QString> *dataDefinedNames, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
324 {
325  QMap<QgsComposerObject::DataDefinedProperty, QString>::const_iterator i = dataDefinedNames->constBegin();
326  for ( ; i != dataDefinedNames->constEnd(); ++i )
327  {
328  QString elemName = i.value();
329  QDomNodeList ddNodeList = itemElem.elementsByTagName( elemName );
330  if ( !ddNodeList.isEmpty() )
331  {
332  QDomElement ddElem = ddNodeList.at( 0 ).toElement();
333  readDataDefinedProperty( i.key(), ddElem, dataDefinedProperties );
334  }
335  }
336 }
337 
338 void QgsComposerUtils::readDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property, const QDomElement &ddElem, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
339 {
340  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
341  {
342  //invalid property
343  return;
344  }
345 
346  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->constFind( property );
347 
348  QgsDataDefined* dd = nullptr;
349  if ( it != dataDefinedProperties->constEnd() )
350  {
351  dd = it.value();
352  }
353  else
354  {
355  //QgsDataDefined for property doesn't currently exist, need to add new
356  dd = new QgsDataDefined();
357  dataDefinedProperties->insert( property, dd );
358  }
359 
360  //set values for QgsDataDefined
361  QString active = ddElem.attribute( QStringLiteral( "active" ) );
362  if ( active.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
363  {
364  dd->setActive( true );
365  }
366  else
367  {
368  dd->setActive( false );
369  }
370  dd->setField( ddElem.attribute( QStringLiteral( "field" ) ) );
371  dd->setExpressionString( ddElem.attribute( QStringLiteral( "expr" ) ) );
372  QString useExpr = ddElem.attribute( QStringLiteral( "useExpr" ) );
373  if ( useExpr.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
374  {
375  dd->setUseExpression( true );
376  }
377  else
378  {
379  dd->setUseExpression( false );
380  }
381 }
382 
383 void QgsComposerUtils::writeDataDefinedPropertyMap( QDomElement &itemElem, QDomDocument &doc, const QMap<QgsComposerObject::DataDefinedProperty, QString> *dataDefinedNames, const QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
384 {
385  QMap<QgsComposerObject::DataDefinedProperty, QString >::const_iterator i = dataDefinedNames->constBegin();
386  for ( ; i != dataDefinedNames->constEnd(); ++i )
387  {
388  QString newElemName = i.value();
389 
390  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->find( i.key() );
391  if ( it != dataDefinedProperties->constEnd() )
392  {
393  QgsDataDefined* dd = it.value();
394  if ( dd )
395  {
396  bool active = dd->isActive();
397  bool useExpr = dd->useExpression();
398  QString expr = dd->expressionString();
399  QString field = dd->field();
400 
401  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
402 
403  if ( !defaultVals )
404  {
405  QDomElement ddElem = doc.createElement( newElemName );
406  if ( active )
407  {
408  ddElem.setAttribute( QStringLiteral( "active" ), QStringLiteral( "true" ) );
409  }
410  else
411  {
412  ddElem.setAttribute( QStringLiteral( "active" ), QStringLiteral( "false" ) );
413  }
414  if ( useExpr )
415  {
416  ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "true" ) );
417  }
418  else
419  {
420  ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "false" ) );
421  }
422  ddElem.setAttribute( QStringLiteral( "expr" ), expr );
423  ddElem.setAttribute( QStringLiteral( "field" ), field );
424  itemElem.appendChild( ddElem );
425  }
426  }
427  }
428  }
429 }
430 
431 QFont QgsComposerUtils::scaledFontPixelSize( const QFont &font )
432 {
433  //upscale using FONT_WORKAROUND_SCALE
434  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
435  QFont scaledFont = font;
436  double pixelSize = pointsToMM( scaledFont.pointSizeF() ) * FONT_WORKAROUND_SCALE + 0.5;
437  scaledFont.setPixelSize( pixelSize );
438  return scaledFont;
439 }
440 
441 double QgsComposerUtils::fontAscentMM( const QFont &font )
442 {
443  //upscale using FONT_WORKAROUND_SCALE
444  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
445  QFont metricsFont = scaledFontPixelSize( font );
446  QFontMetricsF fontMetrics( metricsFont );
447  return ( fontMetrics.ascent() / FONT_WORKAROUND_SCALE );
448 }
449 
450 double QgsComposerUtils::fontDescentMM( const QFont &font )
451 {
452  //upscale using FONT_WORKAROUND_SCALE
453  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
454  QFont metricsFont = scaledFontPixelSize( font );
455  QFontMetricsF fontMetrics( metricsFont );
456  return ( fontMetrics.descent() / FONT_WORKAROUND_SCALE );
457 }
458 
459 double QgsComposerUtils::fontHeightMM( const QFont &font )
460 {
461  //upscale using FONT_WORKAROUND_SCALE
462  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
463  QFont metricsFont = scaledFontPixelSize( font );
464  QFontMetricsF fontMetrics( metricsFont );
465  return ( fontMetrics.height() / FONT_WORKAROUND_SCALE );
466 }
467 
468 double QgsComposerUtils::fontHeightCharacterMM( const QFont &font, QChar character )
469 {
470  //upscale using FONT_WORKAROUND_SCALE
471  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
472  QFont metricsFont = scaledFontPixelSize( font );
473  QFontMetricsF fontMetrics( metricsFont );
474  return ( fontMetrics.boundingRect( character ).height() / FONT_WORKAROUND_SCALE );
475 }
476 
477 double QgsComposerUtils::textWidthMM( const QFont &font, const QString &text )
478 {
479  //upscale using FONT_WORKAROUND_SCALE
480  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
481  QFont metricsFont = scaledFontPixelSize( font );
482  QFontMetricsF fontMetrics( metricsFont );
483  return ( fontMetrics.width( text ) / FONT_WORKAROUND_SCALE );
484 }
485 
486 double QgsComposerUtils::textHeightMM( const QFont &font, const QString &text, double multiLineHeight )
487 {
488  QStringList multiLineSplit = text.split( '\n' );
489  int lines = multiLineSplit.size();
490 
491  //upscale using FONT_WORKAROUND_SCALE
492  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
493  QFont metricsFont = scaledFontPixelSize( font );
494  QFontMetricsF fontMetrics( metricsFont );
495 
496  double fontHeight = fontMetrics.ascent() + fontMetrics.descent(); // ignore +1 for baseline
497  double textHeight = fontMetrics.ascent() + static_cast< double >(( lines - 1 ) * fontHeight * multiLineHeight );
498 
499  return textHeight / FONT_WORKAROUND_SCALE;
500 }
501 
502 void QgsComposerUtils::drawText( QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color )
503 {
504  if ( !painter )
505  {
506  return;
507  }
508 
509  //upscale using FONT_WORKAROUND_SCALE
510  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
511  QFont textFont = scaledFontPixelSize( font );
512 
513  painter->save();
514  painter->setFont( textFont );
515  if ( color.isValid() )
516  {
517  painter->setPen( color );
518  }
519  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
520  painter->scale( scaleFactor, scaleFactor );
521  painter->drawText( pos * FONT_WORKAROUND_SCALE, text );
522  painter->restore();
523 }
524 
525 void QgsComposerUtils::drawText( QPainter *painter, const QRectF &rect, const QString &text, const QFont &font, const QColor &color, const Qt::AlignmentFlag halignment, const Qt::AlignmentFlag valignment, const int flags )
526 {
527  if ( !painter )
528  {
529  return;
530  }
531 
532  //upscale using FONT_WORKAROUND_SCALE
533  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
534  QFont textFont = scaledFontPixelSize( font );
535 
536  QRectF scaledRect( rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
537  rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE );
538 
539  painter->save();
540  painter->setFont( textFont );
541  if ( color.isValid() )
542  {
543  painter->setPen( color );
544  }
545  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
546  painter->scale( scaleFactor, scaleFactor );
547  painter->drawText( scaledRect, halignment | valignment | flags, text );
548  painter->restore();
549 }
550 
552 {
553  QgsComposerMap* referenceMap = composition ? composition->referenceMap() : nullptr;
554  if ( !referenceMap )
555  {
556  return QgsRenderContext::fromQPainter( painter );
557  }
558 
559  // default to 88 dpi if no painter specified
560  int dpi = ( painter && painter->device() ) ? painter->device()->logicalDpiX() : 88;
561  double dotsPerMM = dpi / 25.4;
562 
563  // get map settings from reference map
564  QgsRectangle extent = *( referenceMap->currentMapExtent() );
565  QSizeF mapSizeMM = referenceMap->rect().size();
566  QgsMapSettings ms = referenceMap->mapSettings( extent, mapSizeMM * dotsPerMM, dpi );
567 
569  if ( painter )
570  context.setPainter( painter );
571  return context;
572 }
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, int dpi) const
Return map settings that would be used for drawing of the map.
void setActive(bool active)
A rectangle specified with double values.
Definition: qgsrectangle.h:36
#define M_DEG2RAD
A container class for data source field mapping or expression.
static double normalizedAngle(const double angle)
Ensures that an angle is in the range 0 <= angle < 360.
static double angle(QPointF p1, QPointF p2)
Calculates the angle of the line from p1 to p2 (counter clockwise, starting from a line from north to...
static double relativePosition(const double position, const double beforeMin, const double beforeMax, const double afterMin, const double afterMax)
Returns a scaled position given a before and after range.
static void readDataDefinedPropertyMap(const QDomElement &itemElem, QMap< QgsComposerObject::DataDefinedProperty, QString > *dataDefinedNames, QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined * > *dataDefinedProperties)
Reads all data defined properties from xml.
static void drawText(QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color=QColor())
Draws text on a painter at a specific position, taking care of composer specific issues (calculation ...
static double fontAscentMM(const QFont &font)
Calculate font ascent in millimeters, including workarounds for QT font rendering issues...
static QFont scaledFontPixelSize(const QFont &font)
Returns a font where size is set in pixels and the size has been upscaled with FONT_WORKAROUND_SCALE ...
static void writeDataDefinedPropertyMap(QDomElement &itemElem, QDomDocument &doc, const QMap< QgsComposerObject::DataDefinedProperty, QString > *dataDefinedNames, const QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined * > *dataDefinedProperties)
Writes data defined properties to xml.
static double fontDescentMM(const QFont &font)
Calculate font descent in millimeters, including workarounds for QT font rendering issues...
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:193
DataDefinedProperty
Data defined properties for different item types.
bool useExpression() const
Returns if the field or the expression part is active.
The QgsMapSettings class contains configuration for rendering of the map.
static void readDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property, const QDomElement &ddElem, QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined * > *dataDefinedProperties)
Reads a single data defined property from xml DOM element.
void setUseExpression(bool use)
Controls if the field or the expression part is active.
static void drawArrowHead(QPainter *p, const double x, const double y, const double angle, const double arrowHeadWidth)
Draws an arrow head on to a QPainter.
static double fontHeightCharacterMM(const QFont &font, QChar character)
Calculate font height in millimeters of a single character, including workarounds for QT font renderi...
void setField(const QString &field)
Set the field name which this QgsDataDefined represents.
QgsComposerMap * referenceMap() const
Returns the map item which will be used to generate corresponding world files when the composition is...
QString expressionString() const
Returns the expression string of this QgsDataDefined.
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
#define M_PI
static QRectF largestRotatedRectWithinBounds(const QRectF &originalRect, const QRectF &boundsRect, const double rotation)
Calculates the largest scaled version of originalRect which fits within boundsRect, when it is rotated by a specified amount.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
static QgsRenderContext createRenderContext(QgsComposition *composition, QPainter *painter)
Creates a render context suitable for the specified composition and QPainter destination.
Graphics scene for map printing.
Object representing map window.
static void rotate(const double angle, double &x, double &y)
Rotates a point / vector around the origin.
QString field() const
Get the field which this QgsDataDefined represents.
static bool decodePresetPaperSize(const QString &presetString, double &width, double &height)
Decodes a string representing a preset page size.
static double textWidthMM(const QFont &font, const QString &text)
Calculate font width in millimeters for a string, including workarounds for QT font rendering issues...
static double fontHeightMM(const QFont &font)
Calculate font height in millimeters, including workarounds for QT font rendering issues The font hei...
Contains information about the context of a rendering operation.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
static double textHeightMM(const QFont &font, const QString &text, double multiLineHeight=1.0)
Calculate font height in millimeters for a string, including workarounds for QT font rendering issues...
static double pointsToMM(const double pointSize)
Returns the size in mm corresponding to a font point size.
static double snappedAngle(const double angle)
Snaps an angle to its closest 45 degree angle.
#define FONT_WORKAROUND_SCALE
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cc:857
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
const QgsRectangle * currentMapExtent() const
Returns a pointer to the current map extent, which is either the original user specified extent or th...
bool isActive() const
static QgsComposition::PaperOrientation decodePaperOrientation(const QString &orientationString, bool &ok)
Decodes a string representing a paper orientation.
All properties for item.
static double mmToPoints(const double mmSize)
Returns the size in mm corresponding to a font point size.