QGIS API Documentation  2.99.0-Master (8ec3eaf)
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 <QPainter>
22 
23 #define FONT_WORKAROUND_SCALE 10 //scale factor for upscaling fontsize and downscaling painter
24 
25 #ifndef M_DEG2RAD
26 #define M_DEG2RAD 0.0174532925
27 #endif
28 
29 void QgsComposerUtils::drawArrowHead( QPainter *p, const double x, const double y, const double angle, const double arrowHeadWidth )
30 {
31  if ( !p )
32  {
33  return;
34  }
35 
36  double angleRad = angle / 180.0 * M_PI;
37  QPointF middlePoint( x, y );
38  //rotate both arrow points
39  QPointF p1 = QPointF( -arrowHeadWidth / 2.0, arrowHeadWidth );
40  QPointF p2 = QPointF( arrowHeadWidth / 2.0, arrowHeadWidth );
41 
42  QPointF p1Rotated, p2Rotated;
43  p1Rotated.setX( p1.x() * cos( angleRad ) + p1.y() * -sin( angleRad ) );
44  p1Rotated.setY( p1.x() * sin( angleRad ) + p1.y() * cos( angleRad ) );
45  p2Rotated.setX( p2.x() * cos( angleRad ) + p2.y() * -sin( angleRad ) );
46  p2Rotated.setY( p2.x() * sin( angleRad ) + p2.y() * cos( angleRad ) );
47 
48  QPolygonF arrowHeadPoly;
49  arrowHeadPoly << middlePoint;
50  arrowHeadPoly << QPointF( middlePoint.x() + p1Rotated.x(), middlePoint.y() + p1Rotated.y() );
51  arrowHeadPoly << QPointF( middlePoint.x() + p2Rotated.x(), middlePoint.y() + p2Rotated.y() );
52 
53  p->save();
54 
55  QPen arrowPen = p->pen();
56  arrowPen.setJoinStyle( Qt::RoundJoin );
57  QBrush arrowBrush = p->brush();
58  arrowBrush.setStyle( Qt::SolidPattern );
59  p->setPen( arrowPen );
60  p->setBrush( arrowBrush );
61  arrowBrush.setStyle( Qt::SolidPattern );
62  p->drawPolygon( arrowHeadPoly );
63 
64  p->restore();
65 }
66 
67 double QgsComposerUtils::angle( QPointF p1, QPointF p2 )
68 {
69  double xDiff = p2.x() - p1.x();
70  double yDiff = p2.y() - p1.y();
71  double length = sqrt( xDiff * xDiff + yDiff * yDiff );
72  if ( length <= 0 )
73  {
74  return 0;
75  }
76 
77  double angle = acos(( -yDiff * length ) / ( length * length ) ) * 180 / M_PI;
78  if ( xDiff < 0 )
79  {
80  return ( 360 - angle );
81  }
82  return angle;
83 }
84 
85 void QgsComposerUtils::rotate( const double angle, double &x, double &y )
86 {
87  double rotToRad = angle * M_PI / 180.0;
88  double xRot, yRot;
89  xRot = x * cos( rotToRad ) - y * sin( rotToRad );
90  yRot = x * sin( rotToRad ) + y * cos( rotToRad );
91  x = xRot;
92  y = yRot;
93 }
94 
96 {
97  double clippedAngle = angle;
98  if ( clippedAngle >= 360.0 || clippedAngle <= -360.0 )
99  {
100  clippedAngle = fmod( clippedAngle, 360.0 );
101  }
102  if ( clippedAngle < 0.0 )
103  {
104  clippedAngle += 360.0;
105  }
106  return clippedAngle;
107 }
108 
109 double QgsComposerUtils::snappedAngle( const double angle )
110 {
111  //normalize angle to 0-360 degrees
112  double clippedAngle = normalizedAngle( angle );
113 
114  //snap angle to 45 degree
115  if ( clippedAngle >= 22.5 && clippedAngle < 67.5 )
116  {
117  return 45.0;
118  }
119  else if ( clippedAngle >= 67.5 && clippedAngle < 112.5 )
120  {
121  return 90.0;
122  }
123  else if ( clippedAngle >= 112.5 && clippedAngle < 157.5 )
124  {
125  return 135.0;
126  }
127  else if ( clippedAngle >= 157.5 && clippedAngle < 202.5 )
128  {
129  return 180.0;
130  }
131  else if ( clippedAngle >= 202.5 && clippedAngle < 247.5 )
132  {
133  return 225.0;
134  }
135  else if ( clippedAngle >= 247.5 && clippedAngle < 292.5 )
136  {
137  return 270.0;
138  }
139  else if ( clippedAngle >= 292.5 && clippedAngle < 337.5 )
140  {
141  return 315.0;
142  }
143  else
144  {
145  return 0.0;
146  }
147 }
148 
149 QRectF QgsComposerUtils::largestRotatedRectWithinBounds( const QRectF &originalRect, const QRectF &boundsRect, const double rotation )
150 {
151  double originalWidth = originalRect.width();
152  double originalHeight = originalRect.height();
153  double boundsWidth = boundsRect.width();
154  double boundsHeight = boundsRect.height();
155  double ratioBoundsRect = boundsWidth / boundsHeight;
156 
157  double clippedRotation = normalizedAngle( rotation );
158 
159  //shortcut for some rotation values
160  if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 90.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) || qgsDoubleNear( clippedRotation, 270.0 ) )
161  {
162  double rectScale;
163  if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) )
164  {
165  rectScale = (( originalWidth / originalHeight ) > ratioBoundsRect ) ? boundsWidth / originalWidth : boundsHeight / originalHeight;
166  }
167  else
168  {
169  rectScale = (( originalHeight / originalWidth ) > ratioBoundsRect ) ? boundsWidth / originalHeight : boundsHeight / originalWidth;
170  }
171  double rectScaledWidth = rectScale * originalWidth;
172  double rectScaledHeight = rectScale * originalHeight;
173 
174  if ( qgsDoubleNear( clippedRotation, 0.0 ) || qgsDoubleNear( clippedRotation, 180.0 ) )
175  {
176  return QRectF(( boundsWidth - rectScaledWidth ) / 2.0, ( boundsHeight - rectScaledHeight ) / 2.0, rectScaledWidth, rectScaledHeight );
177  }
178  else
179  {
180  return QRectF(( boundsWidth - rectScaledHeight ) / 2.0, ( boundsHeight - rectScaledWidth ) / 2.0, rectScaledWidth, rectScaledHeight );
181  }
182  }
183 
184  //convert angle to radians and flip
185  double angleRad = -clippedRotation * M_DEG2RAD;
186  double cosAngle = cos( angleRad );
187  double sinAngle = sin( angleRad );
188 
189  //calculate size of bounds of rotated rectangle
190  double widthBoundsRotatedRect = originalWidth * fabs( cosAngle ) + originalHeight * fabs( sinAngle );
191  double heightBoundsRotatedRect = originalHeight * fabs( cosAngle ) + originalWidth * fabs( sinAngle );
192 
193  //compare ratio of rotated rect with bounds rect and calculate scaling of rotated
194  //rect to fit within bounds
195  double ratioBoundsRotatedRect = widthBoundsRotatedRect / heightBoundsRotatedRect;
196  double rectScale = ratioBoundsRotatedRect > ratioBoundsRect ? boundsWidth / widthBoundsRotatedRect : boundsHeight / heightBoundsRotatedRect;
197  double rectScaledWidth = rectScale * originalWidth;
198  double rectScaledHeight = rectScale * originalHeight;
199 
200  //now calculate offset so that rotated rectangle is centered within bounds
201  //first calculate min x and y coordinates
202  double currentCornerX = 0;
203  double minX = 0;
204  currentCornerX += rectScaledWidth * cosAngle;
205  minX = minX < currentCornerX ? minX : currentCornerX;
206  currentCornerX += rectScaledHeight * sinAngle;
207  minX = minX < currentCornerX ? minX : currentCornerX;
208  currentCornerX -= rectScaledWidth * cosAngle;
209  minX = minX < currentCornerX ? minX : currentCornerX;
210 
211  double currentCornerY = 0;
212  double minY = 0;
213  currentCornerY -= rectScaledWidth * sinAngle;
214  minY = minY < currentCornerY ? minY : currentCornerY;
215  currentCornerY += rectScaledHeight * cosAngle;
216  minY = minY < currentCornerY ? minY : currentCornerY;
217  currentCornerY += rectScaledWidth * sinAngle;
218  minY = minY < currentCornerY ? minY : currentCornerY;
219 
220  //now calculate offset position of rotated rectangle
221  double offsetX = ratioBoundsRotatedRect > ratioBoundsRect ? 0 : ( boundsWidth - rectScale * widthBoundsRotatedRect ) / 2.0;
222  offsetX += fabs( minX );
223  double offsetY = ratioBoundsRotatedRect > ratioBoundsRect ? ( boundsHeight - rectScale * heightBoundsRotatedRect ) / 2.0 : 0;
224  offsetY += fabs( minY );
225 
226  return QRectF( offsetX, offsetY, rectScaledWidth, rectScaledHeight );
227 }
228 
229 double QgsComposerUtils::pointsToMM( const double pointSize )
230 {
231  //conversion to mm based on 1 point = 1/72 inch
232  return ( pointSize * 0.3527 );
233 }
234 
235 double QgsComposerUtils::mmToPoints( const double mmSize )
236 {
237  //conversion to points based on 1 point = 1/72 inch
238  return ( mmSize / 0.3527 );
239 }
240 
241 void QgsComposerUtils::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
242 {
243  //linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
244  double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
245  double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
246  double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
247  double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
248 
249  rectToResize.setRect( left, top, right - left, bottom - top );
250 }
251 
252 double QgsComposerUtils::relativePosition( const double position, const double beforeMin, const double beforeMax, const double afterMin, const double afterMax )
253 {
254  //calculate parameters for linear scale between before and after ranges
255  double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
256  double c = afterMin - ( beforeMin * m );
257 
258  //return linearly scaled position
259  return m * position + c;
260 }
261 
263 {
264  if ( orientationString.compare( QLatin1String( "Portrait" ), Qt::CaseInsensitive ) == 0 )
265  {
266  ok = true;
268  }
269  if ( orientationString.compare( QLatin1String( "Landscape" ), Qt::CaseInsensitive ) == 0 )
270  {
271  ok = true;
273  }
274  ok = false;
275  return QgsComposition::Landscape; // default to landscape
276 }
277 
278 bool QgsComposerUtils::decodePresetPaperSize( const QString& presetString, double &width, double &height )
279 {
280  QList< QPair< QString, QSizeF > > presets;
281  presets << qMakePair( QStringLiteral( "A5" ), QSizeF( 148, 210 ) );
282  presets << qMakePair( QStringLiteral( "A4" ), QSizeF( 210, 297 ) );
283  presets << qMakePair( QStringLiteral( "A3" ), QSizeF( 297, 420 ) );
284  presets << qMakePair( QStringLiteral( "A2" ), QSizeF( 420, 594 ) );
285  presets << qMakePair( QStringLiteral( "A1" ), QSizeF( 594, 841 ) );
286  presets << qMakePair( QStringLiteral( "A0" ), QSizeF( 841, 1189 ) );
287  presets << qMakePair( QStringLiteral( "B5" ), QSizeF( 176, 250 ) );
288  presets << qMakePair( QStringLiteral( "B4" ), QSizeF( 250, 353 ) );
289  presets << qMakePair( QStringLiteral( "B3" ), QSizeF( 353, 500 ) );
290  presets << qMakePair( QStringLiteral( "B2" ), QSizeF( 500, 707 ) );
291  presets << qMakePair( QStringLiteral( "B1" ), QSizeF( 707, 1000 ) );
292  presets << qMakePair( QStringLiteral( "B0" ), QSizeF( 1000, 1414 ) );
293  // North american formats
294  presets << qMakePair( QStringLiteral( "Legal" ), QSizeF( 215.9, 355.6 ) );
295  presets << qMakePair( QStringLiteral( "Letter" ), QSizeF( 215.9, 279.4 ) );
296  presets << qMakePair( QStringLiteral( "ANSI A" ), QSizeF( 215.9, 279.4 ) );
297  presets << qMakePair( QStringLiteral( "ANSI B" ), QSizeF( 279.4, 431.8 ) );
298  presets << qMakePair( QStringLiteral( "ANSI C" ), QSizeF( 431.8, 558.8 ) );
299  presets << qMakePair( QStringLiteral( "ANSI D" ), QSizeF( 558.8, 863.6 ) );
300  presets << qMakePair( QStringLiteral( "ANSI E" ), QSizeF( 863.6, 1117.6 ) );
301  presets << qMakePair( QStringLiteral( "Arch A" ), QSizeF( 228.6, 304.8 ) );
302  presets << qMakePair( QStringLiteral( "Arch B" ), QSizeF( 304.8, 457.2 ) );
303  presets << qMakePair( QStringLiteral( "Arch C" ), QSizeF( 457.2, 609.6 ) );
304  presets << qMakePair( QStringLiteral( "Arch D" ), QSizeF( 609.6, 914.4 ) );
305  presets << qMakePair( QStringLiteral( "Arch E" ), QSizeF( 914.4, 1219.2 ) );
306  presets << qMakePair( QStringLiteral( "Arch E1" ), QSizeF( 762, 1066.8 ) );
307 
308  QList< QPair< QString, QSizeF > >::const_iterator presetIt = presets.constBegin();
309  for ( ;presetIt != presets.constEnd(); ++presetIt )
310  {
311  if ( presetString.compare(( *presetIt ).first, Qt::CaseInsensitive ) == 0 )
312  {
313  width = ( *presetIt ).second.width();
314  height = ( *presetIt ).second.height();
315  return true;
316  }
317  }
318  return false;
319 }
320 
321 void QgsComposerUtils::readDataDefinedPropertyMap( const QDomElement &itemElem, QMap<QgsComposerObject::DataDefinedProperty, QString> *dataDefinedNames, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
322 {
323  QMap<QgsComposerObject::DataDefinedProperty, QString>::const_iterator i = dataDefinedNames->constBegin();
324  for ( ; i != dataDefinedNames->constEnd(); ++i )
325  {
326  QString elemName = i.value();
327  QDomNodeList ddNodeList = itemElem.elementsByTagName( elemName );
328  if ( !ddNodeList.isEmpty() )
329  {
330  QDomElement ddElem = ddNodeList.at( 0 ).toElement();
331  readDataDefinedProperty( i.key(), ddElem, dataDefinedProperties );
332  }
333  }
334 }
335 
336 void QgsComposerUtils::readDataDefinedProperty( const QgsComposerObject::DataDefinedProperty property, const QDomElement &ddElem, QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
337 {
338  if ( property == QgsComposerObject::AllProperties || property == QgsComposerObject::NoProperty )
339  {
340  //invalid property
341  return;
342  }
343 
344  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->constFind( property );
345 
346  QgsDataDefined* dd = nullptr;
347  if ( it != dataDefinedProperties->constEnd() )
348  {
349  dd = it.value();
350  }
351  else
352  {
353  //QgsDataDefined for property doesn't currently exist, need to add new
354  dd = new QgsDataDefined();
355  dataDefinedProperties->insert( property, dd );
356  }
357 
358  //set values for QgsDataDefined
359  QString active = ddElem.attribute( QStringLiteral( "active" ) );
360  if ( active.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
361  {
362  dd->setActive( true );
363  }
364  else
365  {
366  dd->setActive( false );
367  }
368  dd->setField( ddElem.attribute( QStringLiteral( "field" ) ) );
369  dd->setExpressionString( ddElem.attribute( QStringLiteral( "expr" ) ) );
370  QString useExpr = ddElem.attribute( QStringLiteral( "useExpr" ) );
371  if ( useExpr.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
372  {
373  dd->setUseExpression( true );
374  }
375  else
376  {
377  dd->setUseExpression( false );
378  }
379 }
380 
381 void QgsComposerUtils::writeDataDefinedPropertyMap( QDomElement &itemElem, QDomDocument &doc, const QMap<QgsComposerObject::DataDefinedProperty, QString> *dataDefinedNames, const QMap<QgsComposerObject::DataDefinedProperty, QgsDataDefined *> *dataDefinedProperties )
382 {
383  QMap<QgsComposerObject::DataDefinedProperty, QString >::const_iterator i = dataDefinedNames->constBegin();
384  for ( ; i != dataDefinedNames->constEnd(); ++i )
385  {
386  QString newElemName = i.value();
387 
388  QMap< QgsComposerObject::DataDefinedProperty, QgsDataDefined* >::const_iterator it = dataDefinedProperties->find( i.key() );
389  if ( it != dataDefinedProperties->constEnd() )
390  {
391  QgsDataDefined* dd = it.value();
392  if ( dd )
393  {
394  bool active = dd->isActive();
395  bool useExpr = dd->useExpression();
396  QString expr = dd->expressionString();
397  QString field = dd->field();
398 
399  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
400 
401  if ( !defaultVals )
402  {
403  QDomElement ddElem = doc.createElement( newElemName );
404  if ( active )
405  {
406  ddElem.setAttribute( QStringLiteral( "active" ), QStringLiteral( "true" ) );
407  }
408  else
409  {
410  ddElem.setAttribute( QStringLiteral( "active" ), QStringLiteral( "false" ) );
411  }
412  if ( useExpr )
413  {
414  ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "true" ) );
415  }
416  else
417  {
418  ddElem.setAttribute( QStringLiteral( "useExpr" ), QStringLiteral( "false" ) );
419  }
420  ddElem.setAttribute( QStringLiteral( "expr" ), expr );
421  ddElem.setAttribute( QStringLiteral( "field" ), field );
422  itemElem.appendChild( ddElem );
423  }
424  }
425  }
426  }
427 }
428 
429 QFont QgsComposerUtils::scaledFontPixelSize( const QFont &font )
430 {
431  //upscale using FONT_WORKAROUND_SCALE
432  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
433  QFont scaledFont = font;
434  double pixelSize = pointsToMM( scaledFont.pointSizeF() ) * FONT_WORKAROUND_SCALE + 0.5;
435  scaledFont.setPixelSize( pixelSize );
436  return scaledFont;
437 }
438 
439 double QgsComposerUtils::fontAscentMM( const QFont &font )
440 {
441  //upscale using FONT_WORKAROUND_SCALE
442  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
443  QFont metricsFont = scaledFontPixelSize( font );
444  QFontMetricsF fontMetrics( metricsFont );
445  return ( fontMetrics.ascent() / FONT_WORKAROUND_SCALE );
446 }
447 
448 double QgsComposerUtils::fontDescentMM( const QFont &font )
449 {
450  //upscale using FONT_WORKAROUND_SCALE
451  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
452  QFont metricsFont = scaledFontPixelSize( font );
453  QFontMetricsF fontMetrics( metricsFont );
454  return ( fontMetrics.descent() / FONT_WORKAROUND_SCALE );
455 }
456 
457 double QgsComposerUtils::fontHeightMM( const QFont &font )
458 {
459  //upscale using FONT_WORKAROUND_SCALE
460  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
461  QFont metricsFont = scaledFontPixelSize( font );
462  QFontMetricsF fontMetrics( metricsFont );
463  return ( fontMetrics.height() / FONT_WORKAROUND_SCALE );
464 }
465 
466 double QgsComposerUtils::fontHeightCharacterMM( const QFont &font, QChar character )
467 {
468  //upscale using FONT_WORKAROUND_SCALE
469  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
470  QFont metricsFont = scaledFontPixelSize( font );
471  QFontMetricsF fontMetrics( metricsFont );
472  return ( fontMetrics.boundingRect( character ).height() / FONT_WORKAROUND_SCALE );
473 }
474 
475 double QgsComposerUtils::textWidthMM( const QFont &font, const QString &text )
476 {
477  //upscale using FONT_WORKAROUND_SCALE
478  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
479  QFont metricsFont = scaledFontPixelSize( font );
480  QFontMetricsF fontMetrics( metricsFont );
481  return ( fontMetrics.width( text ) / FONT_WORKAROUND_SCALE );
482 }
483 
484 double QgsComposerUtils::textHeightMM( const QFont &font, const QString &text, double multiLineHeight )
485 {
486  QStringList multiLineSplit = text.split( '\n' );
487  int lines = multiLineSplit.size();
488 
489  //upscale using FONT_WORKAROUND_SCALE
490  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
491  QFont metricsFont = scaledFontPixelSize( font );
492  QFontMetricsF fontMetrics( metricsFont );
493 
494  double fontHeight = fontMetrics.ascent() + fontMetrics.descent(); // ignore +1 for baseline
495  double textHeight = fontMetrics.ascent() + static_cast< double >(( lines - 1 ) * fontHeight * multiLineHeight );
496 
497  return textHeight / FONT_WORKAROUND_SCALE;
498 }
499 
500 void QgsComposerUtils::drawText( QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color )
501 {
502  if ( !painter )
503  {
504  return;
505  }
506 
507  //upscale using FONT_WORKAROUND_SCALE
508  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
509  QFont textFont = scaledFontPixelSize( font );
510 
511  painter->save();
512  painter->setFont( textFont );
513  if ( color.isValid() )
514  {
515  painter->setPen( color );
516  }
517  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
518  painter->scale( scaleFactor, scaleFactor );
519  painter->drawText( pos * FONT_WORKAROUND_SCALE, text );
520  painter->restore();
521 }
522 
523 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 )
524 {
525  if ( !painter )
526  {
527  return;
528  }
529 
530  //upscale using FONT_WORKAROUND_SCALE
531  //ref: http://osgeo-org.1560.x6.nabble.com/Multi-line-labels-and-font-bug-td4157152.html
532  QFont textFont = scaledFontPixelSize( font );
533 
534  QRectF scaledRect( rect.x() * FONT_WORKAROUND_SCALE, rect.y() * FONT_WORKAROUND_SCALE,
535  rect.width() * FONT_WORKAROUND_SCALE, rect.height() * FONT_WORKAROUND_SCALE );
536 
537  painter->save();
538  painter->setFont( textFont );
539  if ( color.isValid() )
540  {
541  painter->setPen( color );
542  }
543  double scaleFactor = 1.0 / FONT_WORKAROUND_SCALE;
544  painter->scale( scaleFactor, scaleFactor );
545  painter->drawText( scaledRect, halignment | valignment | flags, text );
546  painter->restore();
547 }
static void relativeResizeRect(QRectF &rectToResize, const QRectF &boundsBefore, const QRectF &boundsAfter)
Resizes a QRectF relative to a resized bounding rectangle.
void setActive(bool active)
#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:196
DataDefinedProperty
Data defined properties for different item types.
bool useExpression() const
Returns if the field or the expression part is active.
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.
QString expressionString() const
Returns the expression string of this QgsDataDefined.
#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.
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...
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.
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.