QGIS API Documentation 3.41.0-Master (25ec5511245)
Loading...
Searching...
No Matches
qgstextformat.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstextformat.cpp
3 ---------------
4 begin : May 2020
5 copyright : (C) Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgstextformat.h"
17#include "qgstextrenderer_p.h"
18#include "qgstextrenderer.h"
19#include "qgsvectorlayer.h"
20#include "qgsfontutils.h"
21#include "qgssymbollayerutils.h"
22#include "qgspainting.h"
24#include "qgspallabeling.h"
25#include "qgsconfig.h"
26#include "qgsfontmanager.h"
27#include "qgsapplication.h"
28#include "qgsunittypes.h"
29#include "qgscolorutils.h"
30
31#include <QFontDatabase>
32#include <QMimeData>
33#include <QWidget>
34#include <QScreen>
35
37{
38 d = new QgsTextSettingsPrivate();
39}
40
42 : mBufferSettings( other.mBufferSettings )
43 , mBackgroundSettings( other.mBackgroundSettings )
44 , mShadowSettings( other.mShadowSettings )
45 , mMaskSettings( other.mMaskSettings )
46 , mTextFontFamily( other.mTextFontFamily )
47 , mTextFontFound( other.mTextFontFound )
48 , d( other.d )
49{
50
51}
52
54{
55 d = other.d;
56 mBufferSettings = other.mBufferSettings;
57 mBackgroundSettings = other.mBackgroundSettings;
58 mShadowSettings = other.mShadowSettings;
59 mMaskSettings = other.mMaskSettings;
60 mTextFontFamily = other.mTextFontFamily;
61 mTextFontFound = other.mTextFontFound;
62 return *this;
63}
64
66{
67
68}
69
70bool QgsTextFormat::operator==( const QgsTextFormat &other ) const
71{
72 if ( d->isValid != other.isValid()
73 || d->textFont != other.font()
74 || namedStyle() != other.namedStyle()
75 || d->fontSizeUnits != other.sizeUnit()
76 || d->fontSizeMapUnitScale != other.sizeMapUnitScale()
77 || d->fontSize != other.size()
78 || d->textColor != other.color()
79 || d->opacity != other.opacity()
80 || d->blendMode != other.blendMode()
81 || d->multilineHeight != other.lineHeight()
82 || d->multilineHeightUnits != other.lineHeightUnit()
83 || d->orientation != other.orientation()
84 || d->previewBackgroundColor != other.previewBackgroundColor()
85 || d->allowHtmlFormatting != other.allowHtmlFormatting()
86 || d->forcedBold != other.forcedBold()
87 || d->forcedItalic != other.forcedItalic()
88 || d->capitalization != other.capitalization()
89 || d->tabStopDistance != other.tabStopDistance()
90 || d->tabPositions != other.tabPositions()
91 || d->tabStopDistanceUnits != other.tabStopDistanceUnit()
92 || d->tabStopDistanceMapUnitScale != other.tabStopDistanceMapUnitScale()
93 || mBufferSettings != other.mBufferSettings
94 || mBackgroundSettings != other.mBackgroundSettings
95 || mShadowSettings != other.mShadowSettings
96 || mMaskSettings != other.mMaskSettings
97 || d->families != other.families()
98 || d->mDataDefinedProperties != other.dataDefinedProperties() )
99 return false;
100
101 return true;
102}
103
104bool QgsTextFormat::operator!=( const QgsTextFormat &other ) const
105{
106 return !( *this == other );
107}
108
110{
111 return d->isValid;
112}
113
115{
116 d->isValid = true;
117}
118
120{
121 d->isValid = true;
122 return mBufferSettings;
123}
124
126{
127 d->isValid = true;
128 mBufferSettings = bufferSettings;
129}
130
132{
133 d->isValid = true;
134 return mBackgroundSettings;
135}
136
138{
139 d->isValid = true;
140 mBackgroundSettings = backgroundSettings;
141}
142
144{
145 d->isValid = true;
146 return mShadowSettings;
147}
148
150{
151 d->isValid = true;
152 mShadowSettings = shadowSettings;
153}
154
156{
157 d->isValid = true;
158 return mMaskSettings;
159}
160
162{
163 d->isValid = true;
164 mMaskSettings = maskSettings;
165}
166
168{
169 return d->textFont;
170}
171
172QFont QgsTextFormat::scaledFont( const QgsRenderContext &context, double scaleFactor, bool *isZeroSize ) const
173{
174 if ( isZeroSize )
175 *isZeroSize = false;
176
177 QFont font = d->textFont;
178 if ( scaleFactor == 1 )
179 {
180 int fontPixelSize = QgsTextRenderer::sizeToPixel( d->fontSize, context, d->fontSizeUnits,
181 d->fontSizeMapUnitScale );
182 if ( fontPixelSize == 0 )
183 {
184 if ( isZeroSize )
185 *isZeroSize = true;
186 return QFont();
187 }
188
189 font.setPixelSize( fontPixelSize );
190 }
191 else
192 {
193 double fontPixelSize = context.convertToPainterUnits( d->fontSize, d->fontSizeUnits, d->fontSizeMapUnitScale );
194 if ( qgsDoubleNear( fontPixelSize, 0 ) )
195 {
196 if ( isZeroSize )
197 *isZeroSize = true;
198 return QFont();
199 }
200 const int roundedPixelSize = static_cast< int >( std::round( scaleFactor * fontPixelSize + 0.5 ) );
201 font.setPixelSize( roundedPixelSize );
202 }
203
204 font.setLetterSpacing( QFont::AbsoluteSpacing, context.convertToPainterUnits( d->textFont.letterSpacing(), d->fontSizeUnits, d->fontSizeMapUnitScale ) * scaleFactor );
205 font.setWordSpacing( context.convertToPainterUnits( d->textFont.wordSpacing(), d->fontSizeUnits, d->fontSizeMapUnitScale ) * scaleFactor * scaleFactor );
206
207 if ( d->capitalization == Qgis::Capitalization::SmallCaps
208 || d->capitalization == Qgis::Capitalization::AllSmallCaps )
209 font.setCapitalization( QFont::SmallCaps );
210
211 return font;
212}
213
214void QgsTextFormat::setFont( const QFont &font )
215{
216 d->isValid = true;
217 d->textFont = font;
218 d->originalFontFamily.clear();
219}
220
222{
223 if ( !d->textNamedStyle.isEmpty() )
224 return d->textNamedStyle;
225
226 QFontDatabase db;
227 return db.styleString( d->textFont );
228}
229
230void QgsTextFormat::setNamedStyle( const QString &style )
231{
232 d->isValid = true;
233 QgsFontUtils::updateFontViaStyle( d->textFont, style );
234 d->textNamedStyle = style;
235}
236
238{
239 return d->forcedBold;
240}
241
243{
244 d->isValid = true;
245 d->textFont.setBold( forced );
246 d->forcedBold = true;
247}
248
250{
251 return d->forcedItalic;
252}
253
255{
256 d->isValid = true;
257 d->textFont.setItalic( forced );
258 d->forcedItalic = true;
259}
260
261QStringList QgsTextFormat::families() const
262{
263 return d->families;
264}
265
266void QgsTextFormat::setFamilies( const QStringList &families )
267{
268 d->isValid = true;
269 d->families = families;
270}
271
273{
274 return d->fontSizeUnits;
275}
276
278{
279 d->isValid = true;
280 d->fontSizeUnits = unit;
281}
282
284{
285 return d->fontSizeMapUnitScale;
286}
287
289{
290 d->isValid = true;
291 d->fontSizeMapUnitScale = scale;
292}
293
295{
296 return d->fontSize;
297}
298
299void QgsTextFormat::setSize( double size )
300{
301 d->isValid = true;
302 d->fontSize = size;
303}
304
306{
307 return d->textColor;
308}
309
310void QgsTextFormat::setColor( const QColor &color )
311{
312 d->isValid = true;
313 d->textColor = color;
314}
315
317{
318 return d->opacity;
319}
320
321void QgsTextFormat::multiplyOpacity( double opacityFactor )
322{
323 if ( qgsDoubleNear( opacityFactor, 1.0 ) )
324 return;
325 d->opacity *= opacityFactor;
326 mBufferSettings.setOpacity( mBufferSettings.opacity() * opacityFactor );
327 mShadowSettings.setOpacity( mShadowSettings.opacity() * opacityFactor );
328 mBackgroundSettings.setOpacity( mBackgroundSettings.opacity() * opacityFactor );
329 mMaskSettings.setOpacity( mMaskSettings.opacity() * opacityFactor );
330}
331
332void QgsTextFormat::setOpacity( double opacity )
333{
334 d->isValid = true;
335 d->opacity = opacity;
336}
337
339{
340 return d->textFont.stretch() > 0 ? d->textFont.stretch() : 100;
341}
342
344{
345 d->isValid = true;
346 d->textFont.setStretch( factor );
347}
348
349QPainter::CompositionMode QgsTextFormat::blendMode() const
350{
351 return d->blendMode;
352}
353
354void QgsTextFormat::setBlendMode( QPainter::CompositionMode mode )
355{
356 d->isValid = true;
357 d->blendMode = mode;
358}
359
361{
362 return d->multilineHeight;
363}
364
365void QgsTextFormat::setLineHeight( double height )
366{
367 d->isValid = true;
368 d->multilineHeight = height;
369}
370
372{
373 return d->multilineHeightUnits;
374}
375
377{
378 d->isValid = true;
379 d->multilineHeightUnits = unit;
380}
381
383{
384 return d->tabStopDistance;
385}
386
388{
389 d->isValid = true;
390 d->tabStopDistance = distance;
391}
392
393QList<QgsTextFormat::Tab> QgsTextFormat::tabPositions() const
394{
395 return d->tabPositions;
396}
397
398void QgsTextFormat::setTabPositions( const QList<Tab> &positions )
399{
400 d->isValid = true;
401 d->tabPositions = positions;
402}
403
405{
406 return d->tabStopDistanceUnits;
407}
408
410{
411 d->isValid = true;
412 d->tabStopDistanceUnits = unit;
413}
414
416{
417 return d->tabStopDistanceMapUnitScale;
418}
419
421{
422 d->isValid = true;
423 d->tabStopDistanceMapUnitScale = scale;
424}
425
427{
428 return d->orientation;
429}
430
432{
433 d->isValid = true;
434 d->orientation = orientation;
435}
436
438{
439 // bit of complexity here to maintain API..
440 return d->capitalization == Qgis::Capitalization::MixedCase && d->textFont.capitalization() != QFont::MixedCase
441 ? static_cast< Qgis::Capitalization >( d->textFont.capitalization() )
442 : d->capitalization ;
443}
444
446{
447 d->isValid = true;
448 d->capitalization = capitalization;
449#if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
450 d->textFont.setCapitalization( capitalization == Qgis::Capitalization::SmallCaps || capitalization == Qgis::Capitalization::AllSmallCaps ? QFont::SmallCaps : QFont::MixedCase );
451#else
452 d->textFont.setCapitalization( QFont::MixedCase );
453#endif
454}
455
457{
458 return d->allowHtmlFormatting;
459}
460
462{
463 d->isValid = true;
464 d->allowHtmlFormatting = allow;
465}
466
468{
469 return d->previewBackgroundColor;
470}
471
473{
474 d->isValid = true;
475 d->previewBackgroundColor = color;
476}
477
479{
480 d->isValid = true;
481 QFont appFont = QApplication::font();
482 d->originalFontFamily = QgsApplication::fontManager()->processFontFamilyName( layer->customProperty( QStringLiteral( "labeling/fontFamily" ), QVariant( appFont.family() ) ).toString() );
483 mTextFontFamily = d->originalFontFamily;
484 QString fontFamily = mTextFontFamily;
485 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
486 {
487 // trigger to notify about font family substitution
488 mTextFontFound = false;
489
490 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
491 // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
492
493 // for now, do not use matching algorithm for substitution if family not found, substitute default instead
494 fontFamily = appFont.family();
495 }
496 else
497 {
498 mTextFontFound = true;
499 }
500
501 if ( !layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).isValid() )
502 {
503 d->fontSize = appFont.pointSizeF();
504 }
505 else
506 {
507 d->fontSize = layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).toDouble();
508 }
509
510 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString().isEmpty() )
511 {
512 d->fontSizeUnits = layer->customProperty( QStringLiteral( "labeling/fontSizeInMapUnits" ), QVariant( false ) ).toBool() ?
514 }
515 else
516 {
517 bool ok = false;
518 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString(), &ok );
519 if ( !ok )
520 d->fontSizeUnits = Qgis::RenderUnit::Points;
521 }
522 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString().isEmpty() )
523 {
524 //fallback to older property
525 double oldMin = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMinScale" ), 0.0 ).toDouble();
526 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
527 double oldMax = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMaxScale" ), 0.0 ).toDouble();
528 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
529 }
530 else
531 {
532 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString() );
533 }
534 int fontWeight = layer->customProperty( QStringLiteral( "labeling/fontWeight" ) ).toInt();
535 bool fontItalic = layer->customProperty( QStringLiteral( "labeling/fontItalic" ) ).toBool();
536 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
537 d->textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( QStringLiteral( "labeling/namedStyle" ), QVariant( "" ) ).toString() );
538 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
539 d->capitalization = static_cast< Qgis::Capitalization >( layer->customProperty( QStringLiteral( "labeling/fontCapitals" ), QVariant( 0 ) ).toUInt() );
540 d->textFont.setUnderline( layer->customProperty( QStringLiteral( "labeling/fontUnderline" ) ).toBool() );
541 d->textFont.setStrikeOut( layer->customProperty( QStringLiteral( "labeling/fontStrikeout" ) ).toBool() );
542 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( QStringLiteral( "labeling/fontLetterSpacing" ), QVariant( 0.0 ) ).toDouble() );
543 d->textFont.setWordSpacing( layer->customProperty( QStringLiteral( "labeling/fontWordSpacing" ), QVariant( 0.0 ) ).toDouble() );
544 d->textColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/textColor" ), Qt::black, false );
545 if ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toString().isEmpty() )
546 {
547 d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/textTransp" ) ).toInt() / 100.0 ); //0 -100
548 }
549 else
550 {
551 d->opacity = ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toDouble() );
552 }
553 d->blendMode = QgsPainting::getCompositionMode(
554 static_cast< Qgis::BlendMode >( layer->customProperty( QStringLiteral( "labeling/blendMode" ), QVariant( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
555 d->multilineHeight = layer->customProperty( QStringLiteral( "labeling/multilineHeight" ), QVariant( 1.0 ) ).toDouble();
556 d->previewBackgroundColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/previewBkgrdColor" ), QColor( 255, 255, 255 ), false );
557
558 mBufferSettings.readFromLayer( layer );
559 mShadowSettings.readFromLayer( layer );
560 mBackgroundSettings.readFromLayer( layer );
561}
562
563void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
564{
565 d->isValid = true;
566 QDomElement textStyleElem;
567 if ( elem.nodeName() == QLatin1String( "text-style" ) )
568 textStyleElem = elem;
569 else
570 textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
571 QFont appFont = QApplication::font();
572 d->originalFontFamily = QgsApplication::fontManager()->processFontFamilyName( textStyleElem.attribute( QStringLiteral( "fontFamily" ), appFont.family() ) );
573 mTextFontFamily = d->originalFontFamily;
574 QString fontFamily = mTextFontFamily;
575
576 const QDomElement familiesElem = textStyleElem.firstChildElement( QStringLiteral( "families" ) );
577 const QDomNodeList familyNodes = familiesElem.childNodes();
578 QStringList families;
579 families.reserve( familyNodes.size() );
580 for ( int i = 0; i < familyNodes.count(); ++i )
581 {
582 const QDomElement familyElem = familyNodes.at( i ).toElement();
583 families << familyElem.attribute( QStringLiteral( "name" ) );
584 }
585 d->families = families;
586
587 mTextFontFound = false;
588 QString matched;
589 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
590 {
591 if ( QgsApplication::fontManager()->tryToDownloadFontFamily( mTextFontFamily, matched ) )
592 {
593 mTextFontFound = true;
594 }
595 else
596 {
597 for ( const QString &family : std::as_const( families ) )
598 {
599 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( family );
600 if ( QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) ||
601 QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
602 {
603 mTextFontFound = true;
604 fontFamily = processedFamily;
605 break;
606 }
607 }
608
609 if ( !mTextFontFound )
610 {
611 // couldn't even find a matching font in the backup list -- substitute default instead
612 fontFamily = appFont.family();
613 }
614 }
615 }
616 else
617 {
618 mTextFontFound = true;
619 }
620
621 if ( !mTextFontFound )
622 {
623 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( mTextFontFamily ) );
624 }
625
626 if ( textStyleElem.hasAttribute( QStringLiteral( "fontSize" ) ) )
627 {
628 d->fontSize = textStyleElem.attribute( QStringLiteral( "fontSize" ) ).toDouble();
629 }
630 else
631 {
632 d->fontSize = appFont.pointSizeF();
633 }
634
635 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeUnit" ) ) )
636 {
637 d->fontSizeUnits = textStyleElem.attribute( QStringLiteral( "fontSizeInMapUnits" ) ).toUInt() == 0 ? Qgis::RenderUnit::Points
639 }
640 else
641 {
642 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "fontSizeUnit" ) ) );
643 }
644
645 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeMapUnitScale" ) ) )
646 {
647 //fallback to older property
648 double oldMin = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
649 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
650 double oldMax = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
651 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
652 }
653 else
654 {
655 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitScale" ) ) );
656 }
657 int fontWeight = textStyleElem.attribute( QStringLiteral( "fontWeight" ) ).toInt();
658 bool fontItalic = textStyleElem.attribute( QStringLiteral( "fontItalic" ) ).toInt();
659 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
660 d->textFont.setPointSizeF( d->fontSize ); //double precision needed because of map units
661 d->textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( QStringLiteral( "namedStyle" ) ) );
662 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
663 d->forcedBold = textStyleElem.attribute( QStringLiteral( "forcedBold" ) ).toInt();
664 d->forcedItalic = textStyleElem.attribute( QStringLiteral( "forcedItalic" ) ).toInt();
665 d->textFont.setUnderline( textStyleElem.attribute( QStringLiteral( "fontUnderline" ) ).toInt() );
666 d->textFont.setStrikeOut( textStyleElem.attribute( QStringLiteral( "fontStrikeout" ) ).toInt() );
667 d->textFont.setKerning( textStyleElem.attribute( QStringLiteral( "fontKerning" ), QStringLiteral( "1" ) ).toInt() );
668 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( QStringLiteral( "fontLetterSpacing" ), QStringLiteral( "0" ) ).toDouble() );
669 d->textFont.setWordSpacing( textStyleElem.attribute( QStringLiteral( "fontWordSpacing" ), QStringLiteral( "0" ) ).toDouble() );
670 d->textColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( Qt::black ) ) );
671 if ( !textStyleElem.hasAttribute( QStringLiteral( "textOpacity" ) ) )
672 {
673 d->opacity = ( 1 - textStyleElem.attribute( QStringLiteral( "textTransp" ) ).toInt() / 100.0 ); //0 -100
674 }
675 else
676 {
677 d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() );
678 }
679#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
680 d->textFont.setStretch( textStyleElem.attribute( QStringLiteral( "stretchFactor" ), QStringLiteral( "100" ) ).toInt() );
681#endif
682 d->orientation = QgsTextRendererUtils::decodeTextOrientation( textStyleElem.attribute( QStringLiteral( "textOrientation" ) ) );
683 d->previewBackgroundColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( Qt::white ) ) );
684
685 d->blendMode = QgsPainting::getCompositionMode(
686 static_cast< Qgis::BlendMode >( textStyleElem.attribute( QStringLiteral( "blendMode" ), QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
687
688 if ( !textStyleElem.hasAttribute( QStringLiteral( "multilineHeight" ) ) )
689 {
690 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
691 d->multilineHeight = textFormatElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
692 }
693 else
694 {
695 d->multilineHeight = textStyleElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
696 }
697 bool ok = false;
698 d->multilineHeightUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "multilineHeightUnit" ), QStringLiteral( "percent" ) ), &ok );
699
700 d->tabStopDistance = textStyleElem.attribute( QStringLiteral( "tabStopDistance" ), QStringLiteral( "80" ) ).toDouble();
701 d->tabStopDistanceUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "tabStopDistanceUnit" ), QStringLiteral( "Point" ) ), &ok );
702 d->tabStopDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "tabStopDistanceMapUnitScale" ) ) );
703
704 QList< Tab > tabPositions;
705 {
706 const QDomElement tabPositionsElem = textStyleElem.firstChildElement( QStringLiteral( "tabPositions" ) );
707 const QDomNodeList tabNodes = tabPositionsElem.childNodes();
708 tabPositions.reserve( tabNodes.size() );
709 for ( int i = 0; i < tabNodes.count(); ++i )
710 {
711 const QDomElement tabElem = tabNodes.at( i ).toElement();
712 tabPositions << Tab( tabElem.attribute( QStringLiteral( "position" ) ).toDouble() );
713 }
714 }
715 d->tabPositions = tabPositions;
716
717 if ( textStyleElem.hasAttribute( QStringLiteral( "capitalization" ) ) )
718 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( Qgis::Capitalization::MixedCase ) ) ).toInt() );
719 else
720 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "fontCapitals" ), QStringLiteral( "0" ) ).toUInt() );
721
722 if ( d->capitalization == Qgis::Capitalization::SmallCaps || d->capitalization == Qgis::Capitalization::AllSmallCaps )
723 d->textFont.setCapitalization( QFont::SmallCaps );
724
725 d->allowHtmlFormatting = textStyleElem.attribute( QStringLiteral( "allowHtml" ), QStringLiteral( "0" ) ).toInt();
726
727 if ( textStyleElem.firstChildElement( QStringLiteral( "text-buffer" ) ).isNull() )
728 {
729 mBufferSettings.readXml( elem );
730 }
731 else
732 {
733 mBufferSettings.readXml( textStyleElem );
734 }
735 if ( textStyleElem.firstChildElement( QStringLiteral( "text-mask" ) ).isNull() )
736 {
737 mMaskSettings.readXml( elem );
738 }
739 else
740 {
741 mMaskSettings.readXml( textStyleElem );
742 }
743 if ( textStyleElem.firstChildElement( QStringLiteral( "shadow" ) ).isNull() )
744 {
745 mShadowSettings.readXml( elem );
746 }
747 else
748 {
749 mShadowSettings.readXml( textStyleElem );
750 }
751 if ( textStyleElem.firstChildElement( QStringLiteral( "background" ) ).isNull() )
752 {
753 mBackgroundSettings.readXml( elem, context );
754 }
755 else
756 {
757 mBackgroundSettings.readXml( textStyleElem, context );
758 }
759
760 QDomElement ddElem = textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) );
761 if ( ddElem.isNull() )
762 {
763 ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
764 }
765 if ( !ddElem.isNull() )
766 {
767 d->mDataDefinedProperties.readXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
768 mBackgroundSettings.upgradeDataDefinedProperties( d->mDataDefinedProperties );
769 }
770 else
771 {
772 d->mDataDefinedProperties.clear();
773 }
774}
775
776QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
777{
778 // text style
779 QDomElement textStyleElem = doc.createElement( QStringLiteral( "text-style" ) );
780 textStyleElem.setAttribute( QStringLiteral( "fontFamily" ), !d->originalFontFamily.isEmpty() ? d->originalFontFamily : d->textFont.family() );
781
782 QDomElement familiesElem = doc.createElement( QStringLiteral( "families" ) );
783 for ( const QString &family : std::as_const( d->families ) )
784 {
785 QDomElement familyElem = doc.createElement( QStringLiteral( "family" ) );
786 familyElem.setAttribute( QStringLiteral( "name" ), family );
787 familiesElem.appendChild( familyElem );
788 }
789 textStyleElem.appendChild( familiesElem );
790
791 textStyleElem.setAttribute( QStringLiteral( "namedStyle" ), QgsFontUtils::untranslateNamedStyle( d->textNamedStyle ) );
792 textStyleElem.setAttribute( QStringLiteral( "fontSize" ), d->fontSize );
793 textStyleElem.setAttribute( QStringLiteral( "fontSizeUnit" ), QgsUnitTypes::encodeUnit( d->fontSizeUnits ) );
794 textStyleElem.setAttribute( QStringLiteral( "fontSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->fontSizeMapUnitScale ) );
795 textStyleElem.setAttribute( QStringLiteral( "fontWeight" ), d->textFont.weight() );
796 textStyleElem.setAttribute( QStringLiteral( "fontItalic" ), d->textFont.italic() );
797 textStyleElem.setAttribute( QStringLiteral( "fontStrikeout" ), d->textFont.strikeOut() );
798 textStyleElem.setAttribute( QStringLiteral( "fontUnderline" ), d->textFont.underline() );
799 textStyleElem.setAttribute( QStringLiteral( "forcedBold" ), d->forcedBold );
800 textStyleElem.setAttribute( QStringLiteral( "forcedItalic" ), d->forcedItalic );
801 textStyleElem.setAttribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( d->textColor ) );
802 textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( d->previewBackgroundColor ) );
803 textStyleElem.setAttribute( QStringLiteral( "fontLetterSpacing" ), d->textFont.letterSpacing() );
804 textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() );
805 textStyleElem.setAttribute( QStringLiteral( "fontKerning" ), d->textFont.kerning() );
806 textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity );
807#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
808 if ( d->textFont.stretch() > 0 )
809 textStyleElem.setAttribute( QStringLiteral( "stretchFactor" ), d->textFont.stretch() );
810#endif
811 textStyleElem.setAttribute( QStringLiteral( "textOrientation" ), QgsTextRendererUtils::encodeTextOrientation( d->orientation ) );
812 textStyleElem.setAttribute( QStringLiteral( "blendMode" ), static_cast< int >( QgsPainting::getBlendModeEnum( d->blendMode ) ) );
813 textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
814 textStyleElem.setAttribute( QStringLiteral( "multilineHeightUnit" ), QgsUnitTypes::encodeUnit( d->multilineHeightUnits ) );
815
816 textStyleElem.setAttribute( QStringLiteral( "tabStopDistance" ), d->tabStopDistance );
817 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceUnit" ), QgsUnitTypes::encodeUnit( d->tabStopDistanceUnits ) );
818 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->tabStopDistanceMapUnitScale ) );
819
820 if ( !d->tabPositions.empty() )
821 {
822 QDomElement tabPositionsElem = doc.createElement( QStringLiteral( "tabPositions" ) );
823 for ( const Tab &tab : std::as_const( d->tabPositions ) )
824 {
825 QDomElement tabElem = doc.createElement( QStringLiteral( "tab" ) );
826 tabElem.setAttribute( QStringLiteral( "position" ), tab.position() );
827 tabPositionsElem.appendChild( tabElem );
828 }
829 textStyleElem.appendChild( tabPositionsElem );
830 }
831
832 textStyleElem.setAttribute( QStringLiteral( "allowHtml" ), d->allowHtmlFormatting ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
833 textStyleElem.setAttribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( d->capitalization ) ) );
834
835 QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
836 d->mDataDefinedProperties.writeXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
837
838 textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
839 textStyleElem.appendChild( mMaskSettings.writeXml( doc ) );
840 textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
841 textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
842 textStyleElem.appendChild( ddElem );
843
844 return textStyleElem;
845}
846
848{
849 //set both the mime color data, and the text (format settings).
850
851 QMimeData *mimeData = new QMimeData;
852 mimeData->setColorData( QVariant( color() ) );
853
854 QgsReadWriteContext rwContext;
855 QDomDocument textDoc;
856 QDomElement textElem = writeXml( textDoc, rwContext );
857 textDoc.appendChild( textElem );
858 mimeData->setText( textDoc.toString() );
859
860 return mimeData;
861}
862
864{
865 QgsTextFormat format;
866 format.setFont( font );
867 if ( font.pointSizeF() > 0 )
868 {
869 format.setSize( font.pointSizeF() );
871 }
872 else if ( font.pixelSize() > 0 )
873 {
874 format.setSize( font.pixelSize() );
876 }
877
878 return format;
879}
880
882{
883 QFont f = font();
884 switch ( sizeUnit() )
885 {
887 f.setPointSizeF( size() );
888 break;
889
891 f.setPointSizeF( size() * 2.83464567 );
892 break;
893
895 f.setPointSizeF( size() * 72 );
896 break;
897
899 f.setPixelSize( static_cast< int >( std::round( size() ) ) );
900 break;
901
906 // no meaning here
907 break;
908 }
909 return f;
910}
911
912QgsTextFormat QgsTextFormat::fromMimeData( const QMimeData *data, bool *ok )
913{
914 if ( ok )
915 *ok = false;
916 QgsTextFormat format;
917 if ( !data )
918 return format;
919
920 QString text = data->text();
921 if ( !text.isEmpty() )
922 {
923 QDomDocument doc;
924 QDomElement elem;
925 QgsReadWriteContext rwContext;
926
927 if ( doc.setContent( text ) )
928 {
929 elem = doc.documentElement();
930
931 format.readXml( elem, rwContext );
932 if ( ok )
933 *ok = true;
934 return format;
935 }
936 }
937 return format;
938}
939
941{
942 if ( d->blendMode != QPainter::CompositionMode_SourceOver )
943 return true;
944
945 if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
946 return true;
947
948 if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
949 return true;
950
951 if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
952 return true;
953
954 return false;
955}
956
958{
959 d->isValid = true;
960 return d->mDataDefinedProperties;
961}
962
964{
965 return d->mDataDefinedProperties;
966}
967
968QSet<QString> QgsTextFormat::referencedFields( const QgsRenderContext &context ) const
969{
970 QSet< QString > fields = d->mDataDefinedProperties.referencedFields( context.expressionContext(), true );
971 fields.unite( mBufferSettings.referencedFields( context ) );
972 fields.unite( mBackgroundSettings.referencedFields( context ) );
973 fields.unite( mShadowSettings.referencedFields( context ) );
974 fields.unite( mMaskSettings.referencedFields( context ) );
975 return fields;
976}
977
979{
980 d->isValid = true;
981 d->mDataDefinedProperties = collection;
982}
983
985{
986 d->isValid = true;
987 if ( !d->mDataDefinedProperties.hasActiveProperties() )
988 return;
989
990 QString ddFontFamily;
991 context.expressionContext().setOriginalValueVariable( d->textFont.family() );
992 QVariant exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::Family, context.expressionContext() );
993 if ( !QgsVariantUtils::isNull( exprVal ) )
994 {
995 QString family = exprVal.toString().trimmed();
997 if ( d->textFont.family() != family )
998 {
999 // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
1000 // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
1001 if ( QgsFontUtils::fontFamilyOnSystem( family ) )
1002 {
1003 ddFontFamily = family;
1004 }
1005 }
1006 }
1007
1008 // data defined named font style?
1009 QString ddFontStyle;
1010 context.expressionContext().setOriginalValueVariable( d->textNamedStyle );
1011 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStyle, context.expressionContext() );
1012 if ( !QgsVariantUtils::isNull( exprVal ) )
1013 {
1014 QString fontstyle = exprVal.toString().trimmed();
1015 ddFontStyle = fontstyle;
1016 }
1017
1018 bool ddBold = false;
1019 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Bold ) )
1020 {
1021 context.expressionContext().setOriginalValueVariable( d->textFont.bold() );
1022 ddBold = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Bold, context.expressionContext(), false ) ;
1023 }
1024
1025 bool ddItalic = false;
1026 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Italic ) )
1027 {
1028 context.expressionContext().setOriginalValueVariable( d->textFont.italic() );
1029 ddItalic = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Italic, context.expressionContext(), false );
1030 }
1031
1032 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1033 // (currently defaults to what has been read in from layer settings)
1034 QFont newFont;
1035 QFontDatabase fontDb;
1036 QFont appFont = QApplication::font();
1037 bool newFontBuilt = false;
1038 if ( ddBold || ddItalic )
1039 {
1040 // new font needs built, since existing style needs removed
1041 newFont = QgsFontUtils::createFont( !ddFontFamily.isEmpty() ? ddFontFamily : d->textFont.family() );
1042 newFontBuilt = true;
1043 newFont.setBold( ddBold );
1044 newFont.setItalic( ddItalic );
1045 }
1046 else if ( !ddFontStyle.isEmpty()
1047 && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1048 {
1049 if ( !ddFontFamily.isEmpty() )
1050 {
1051 // both family and style are different, build font from database
1052 QFont styledfont = fontDb.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
1053 if ( appFont != styledfont )
1054 {
1055 newFont = styledfont;
1056 newFontBuilt = true;
1057 }
1058 }
1059
1060 // update the font face style
1061 QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : d->textFont, ddFontStyle );
1062 }
1063 else if ( !ddFontFamily.isEmpty() )
1064 {
1065 if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1066 {
1067 // just family is different, build font from database
1068 QFont styledfont = fontDb.font( ddFontFamily, d->textNamedStyle, appFont.pointSize() );
1069 if ( appFont != styledfont )
1070 {
1071 newFont = styledfont;
1072 newFontBuilt = true;
1073 }
1074 }
1075 else
1076 {
1077 newFont = QgsFontUtils::createFont( ddFontFamily );
1078 newFontBuilt = true;
1079 }
1080 }
1081
1082 if ( newFontBuilt )
1083 {
1084 // copy over existing font settings
1085 newFont.setUnderline( d->textFont.underline() );
1086 newFont.setStrikeOut( d->textFont.strikeOut() );
1087 newFont.setWordSpacing( d->textFont.wordSpacing() );
1088 newFont.setLetterSpacing( QFont::AbsoluteSpacing, d->textFont.letterSpacing() );
1089 d->textFont = newFont;
1090 }
1091
1092 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Underline ) )
1093 {
1094 context.expressionContext().setOriginalValueVariable( d->textFont.underline() );
1095 d->textFont.setUnderline( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Underline, context.expressionContext(), d->textFont.underline() ) );
1096 }
1097
1098 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Strikeout ) )
1099 {
1100 context.expressionContext().setOriginalValueVariable( d->textFont.strikeOut() );
1101 d->textFont.setStrikeOut( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Strikeout, context.expressionContext(), d->textFont.strikeOut() ) );
1102 }
1103
1104 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Color ) )
1105 {
1107 d->textColor = d->mDataDefinedProperties.valueAsColor( QgsPalLayerSettings::Property::Color, context.expressionContext(), d->textColor );
1108 }
1109
1110 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Size ) )
1111 {
1113 d->fontSize = d->mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Property::Size, context.expressionContext(), d->fontSize );
1114 }
1115
1116 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontSizeUnit, context.expressionContext() );
1117 if ( !QgsVariantUtils::isNull( exprVal ) )
1118 {
1119 QString units = exprVal.toString();
1120 if ( !units.isEmpty() )
1121 {
1122 bool ok;
1124 if ( ok )
1125 d->fontSizeUnits = res;
1126 }
1127 }
1128
1129 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontOpacity ) )
1130 {
1131 context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1132 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontOpacity, context.expressionContext(), d->opacity * 100 );
1133 if ( !QgsVariantUtils::isNull( val ) )
1134 {
1135 d->opacity = val.toDouble() / 100.0;
1136 }
1137 }
1138
1139#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
1140 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontStretchFactor ) )
1141 {
1142 context.expressionContext().setOriginalValueVariable( d->textFont.stretch() );
1143 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStretchFactor, context.expressionContext(), d->textFont.stretch() );
1144 if ( !QgsVariantUtils::isNull( val ) )
1145 {
1146 d->textFont.setStretch( val.toInt() );
1147 }
1148 }
1149#endif
1150
1151 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TextOrientation ) )
1152 {
1153 const QString encoded = QgsTextRendererUtils::encodeTextOrientation( d->orientation );
1154 context.expressionContext().setOriginalValueVariable( encoded );
1155 d->orientation = QgsTextRendererUtils::decodeTextOrientation( d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TextOrientation, context.expressionContext(), encoded ).toString() );
1156 }
1157
1158 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontLetterSpacing ) )
1159 {
1160 context.expressionContext().setOriginalValueVariable( d->textFont.letterSpacing() );
1161 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontLetterSpacing, context.expressionContext(), d->textFont.letterSpacing() );
1162 if ( !QgsVariantUtils::isNull( val ) )
1163 {
1164 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, val.toDouble() );
1165 }
1166 }
1167
1168 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontWordSpacing ) )
1169 {
1170 context.expressionContext().setOriginalValueVariable( d->textFont.wordSpacing() );
1171 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontWordSpacing, context.expressionContext(), d->textFont.wordSpacing() );
1172 if ( !QgsVariantUtils::isNull( val ) )
1173 {
1174 d->textFont.setWordSpacing( val.toDouble() );
1175 }
1176 }
1177
1178 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TabStopDistance ) )
1179 {
1180 context.expressionContext().setOriginalValueVariable( d->tabStopDistance );
1181 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TabStopDistance, context.expressionContext(), d->tabStopDistance );
1182 if ( !QgsVariantUtils::isNull( val ) )
1183 {
1184 if ( val.userType() == QMetaType::Type::QVariantList )
1185 {
1186 const QVariantList parts = val.toList();
1187 d->tabPositions.clear();
1188 d->tabPositions.reserve( parts.size() );
1189 for ( const QVariant &part : parts )
1190 {
1191 d->tabPositions.append( Tab( part.toDouble() ) );
1192 }
1193 }
1194 else if ( val.userType() == QMetaType::Type::QStringList )
1195 {
1196 const QStringList parts = val.toStringList();
1197 d->tabPositions.clear();
1198 d->tabPositions.reserve( parts.size() );
1199 for ( const QString &part : parts )
1200 {
1201 d->tabPositions.append( Tab( part.toDouble() ) );
1202 }
1203 }
1204 else
1205 {
1206 d->tabPositions.clear();
1207 d->tabStopDistance = val.toDouble();
1208 }
1209 }
1210 }
1211
1212 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontBlendMode ) )
1213 {
1214 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontBlendMode, context.expressionContext() );
1215 QString blendstr = exprVal.toString().trimmed();
1216 if ( !blendstr.isEmpty() )
1217 d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1218 }
1219
1220 mShadowSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1221 mBackgroundSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1222 mBufferSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1223 mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1224}
1225
1226QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen )
1227{
1228 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
1229 QgsTextFormat tempFormat = format;
1230 QPixmap pixmap( size * devicePixelRatio );
1231 pixmap.fill( Qt::transparent );
1232 pixmap.setDevicePixelRatio( devicePixelRatio );
1233
1234 QPainter painter;
1235 painter.begin( &pixmap );
1236
1237 painter.setRenderHint( QPainter::Antialiasing );
1238
1239 const QRectF rect( 0, 0, size.width(), size.height() );
1240
1241 // shameless eye candy - use a subtle gradient when drawing background
1242 painter.setPen( Qt::NoPen );
1243 QColor background1 = tempFormat.previewBackgroundColor();
1244 if ( ( background1.lightnessF() < 0.7 ) )
1245 {
1246 background1 = background1.darker( 125 );
1247 }
1248 else
1249 {
1250 background1 = background1.lighter( 125 );
1251 }
1252 QColor background2 = tempFormat.previewBackgroundColor();
1253 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1254 linearGrad.setColorAt( 0, background1 );
1255 linearGrad.setColorAt( 1, background2 );
1256 painter.setBrush( QBrush( linearGrad ) );
1257 if ( size.width() > 30 )
1258 {
1259 painter.drawRoundedRect( rect, 6, 6 );
1260 }
1261 else
1262 {
1263 // don't use rounded rect for small previews
1264 painter.drawRect( rect );
1265 }
1266 painter.setBrush( Qt::NoBrush );
1267 painter.setPen( Qt::NoPen );
1268 padding += 1; // move text away from background border
1269
1270 QgsRenderContext context;
1271 QgsMapToPixel newCoordXForm;
1272 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
1273 context.setMapToPixel( newCoordXForm );
1274
1275 if ( screen.isValid() )
1276 {
1277 screen.updateRenderContextForScreen( context );
1278 }
1279 else
1280 {
1281 QWidget *activeWindow = QApplication::activeWindow();
1282 if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr )
1283 {
1284 context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 );
1285 context.setDevicePixelRatio( screen->devicePixelRatio() );
1286 }
1287 else
1288 {
1289 context.setScaleFactor( 96.0 / 25.4 );
1290 context.setDevicePixelRatio( 1.0 );
1291 }
1292 }
1293
1294 context.setUseAdvancedEffects( true );
1296 context.setPainter( &painter );
1298
1299 // slightly inset text to account for buffer/background
1300 const double fontSize = context.convertToPainterUnits( tempFormat.size(), tempFormat.sizeUnit(), tempFormat.sizeMapUnitScale() );
1301 double xtrans = 0;
1302 if ( tempFormat.buffer().enabled() )
1303 xtrans = tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1304 ? fontSize * tempFormat.buffer().size() / 100
1305 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
1306 if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
1307 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1308
1309 double ytrans = 0.0;
1310 if ( tempFormat.buffer().enabled() )
1311 ytrans = std::max( ytrans, tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1312 ? fontSize * tempFormat.buffer().size() / 100
1313 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
1314 if ( tempFormat.background().enabled() )
1315 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1316
1317 const QStringList text = QStringList() << ( previewText.isEmpty() ? QObject::tr( "Aa" ) : previewText );
1318 const double textHeight = QgsTextRenderer::textHeight( context, tempFormat, text, Qgis::TextLayoutMode::Rectangle );
1319 QRectF textRect = rect;
1320 textRect.setLeft( xtrans + padding );
1321 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1322
1323 if ( textRect.width() > 2000 )
1324 textRect.setWidth( 2000 - 2 * padding );
1325
1326 const double bottom = textRect.height() / 2 + textHeight / 2;
1327 textRect.setTop( bottom - textHeight );
1328 textRect.setBottom( bottom );
1329
1330 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Center, text, context, tempFormat );
1331
1332 // draw border on top of text
1333 painter.setBrush( Qt::NoBrush );
1334 painter.setPen( QPen( tempFormat.previewBackgroundColor().darker( 150 ), 0 ) );
1335 if ( size.width() > 30 )
1336 {
1337 painter.drawRoundedRect( rect, 6, 6 );
1338 }
1339 else
1340 {
1341 // don't use rounded rect for small previews
1342 painter.drawRect( rect );
1343 }
1344 painter.end();
1345 return pixmap;
1346}
1347
1348QString QgsTextFormat::asCSS( double pointToPixelMultiplier ) const
1349{
1350 QString css;
1351
1352 switch ( lineHeightUnit() )
1353 {
1355 css += QStringLiteral( "line-height: %1%;" ).arg( lineHeight() * 100 );
1356 break;
1358 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() );
1359 break;
1361 // While the Qt documentation states pt unit type is supported, it's ignored, convert to px
1362 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * pointToPixelMultiplier );
1363 break;
1365 // While the Qt documentation states cm unit type is supported, it's ignored, convert to px
1366 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * 2.83464567 * pointToPixelMultiplier );
1367 break;
1372 break;
1373 }
1374 css += QStringLiteral( "color: rgba(%1,%2,%3,%4);" ).arg( color().red() ).arg( color().green() ).arg( color().blue() ).arg( QString::number( color().alphaF(), 'f', 4 ) );
1375 QFont f = toQFont();
1377 {
1378 f.setPointSizeF( size() / 0.352778 );
1379 }
1380 css += QgsFontUtils::asCSS( toQFont(), pointToPixelMultiplier );
1381
1382 return css;
1383}
1384
1385QgsTextFormat::Tab::Tab( double position )
1386 : mPosition( position )
1387{}
@ Rectangle
Text within rectangle layout mode.
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4586
Capitalization
String capitalization options.
Definition qgis.h:3132
@ AllSmallCaps
Force all characters to small caps.
@ MixedCase
Mixed case, ie no change.
@ SmallCaps
Mixed case small caps.
TextOrientation
Text orientations.
Definition qgis.h:2684
RenderUnit
Rendering size units.
Definition qgis.h:4839
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Points
Points (e.g., for font sizes)
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ Antialiasing
Use antialiasing while drawing.
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
static QString asCSS(const QFont &font, double pointToPixelMultiplier=1.0)
Returns a CSS string representing the specified font as closely as possible.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
Perform transforms between map coordinates and device coordinates.
void setParameters(double mapUnitsPerPixel, double centerX, double centerY, int widthPixels, int heightPixels, double rotation)
Sets parameters for use in transforming coordinates.
Struct for storing maximum and minimum scales for measurements in map units.
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
@ FontSizeUnit
Font size units.
@ FontStyle
Font style name.
@ Italic
Use italic style.
@ FontStretchFactor
Font stretch factor, since QGIS 3.24.
@ FontBlendMode
Text blend mode.
@ FontLetterSpacing
Letter spacing.
@ TabStopDistance
Tab stop distance, since QGIS 3.38.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the labeling property definitions.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
void setDevicePixelRatio(float ratio)
Sets the device pixel ratio.
void setUseAdvancedEffects(bool enabled)
Used to enable or disable advanced effects such as blend modes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QgsExpressionContext & expressionContext()
Gets the expression context.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Stores properties relating to a screen.
double devicePixelRatio() const
Returns the ratio between physical pixels and device-independent pixels for the screen.
bool isValid() const
Returns true if the properties are valid.
void updateRenderContextForScreen(QgsRenderContext &context) const
Updates the settings in a render context to match the screen settings.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QPainter::CompositionMode decodeBlendMode(const QString &s)
static QString encodeColor(const QColor &color)
Container for settings relating to a text background object.
QSizeF size() const
Returns the size of the background shape.
QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns all field names referenced by the configuration (e.g.
void upgradeDataDefinedProperties(QgsPropertyCollection &properties)
Upgrade data defined properties when reading a project file saved in QGIS prior to version 3....
void setOpacity(double opacity)
Sets the background shape's opacity.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the background shape.
bool enabled() const
Returns whether the background is enabled.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
double opacity() const
Returns the background shape's opacity.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer's custom properties (for QGIS 2.x projects).
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
Qgis::RenderUnit sizeUnit() const
Returns the units used for the shape's size.
Container for settings relating to a text buffer.
Qgis::RenderUnit sizeUnit() const
Returns the units for the buffer size.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer's custom properties (for QGIS 2.x projects).
QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns all field names referenced by the configuration (e.g.
double size() const
Returns the size of the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
bool enabled() const
Returns whether the buffer is enabled.
double opacity() const
Returns the buffer opacity.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the buffer.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
void readXml(const QDomElement &elem)
Read settings from a DOM element.
Defines a tab position for a text format.
Tab(double position)
Constructor for a Tab at the specified position.
Container for all settings relating to text rendering.
QgsTextFormat()
Default constructor for QgsTextFormat.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the text.
void setSize(double size)
Sets the size for rendered text.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
void setCapitalization(Qgis::Capitalization capitalization)
Sets the text capitalization style.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the format's property collection, used for data defined overrides.
QStringList families() const
Returns the list of font families to use when restoring the text format, in order of precedence.
void setOrientation(Qgis::TextOrientation orientation)
Sets the orientation for the text.
void setFont(const QFont &font)
Sets the font used for rendering text.
QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns all field names referenced by the configuration (e.g.
QList< QgsTextFormat::Tab > tabPositions() const
Returns the list of tab positions for tab stops.
void setFamilies(const QStringList &families)
Sets a list of font families to use for the text format, in order of precedence.
void setForcedItalic(bool forced)
Sets whether the format is set to force an italic style.
static QgsTextFormat fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsTextFormat.
double lineHeight() const
Returns the line height for text.
double tabStopDistance() const
Returns the distance for tab stops.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
int stretchFactor() const
Returns the text's stretch factor.
void updateDataDefinedProperties(QgsRenderContext &context)
Updates the format by evaluating current values of data defined properties.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the format's property collection, used for data defined overrides.
void setStretchFactor(int factor)
Sets the text's stretch factor.
void setShadow(const QgsTextShadowSettings &shadowSettings)
Sets the text's drop shadow settings.
void setMask(const QgsTextMaskSettings &maskSettings)
Sets the text's masking settings.
void setPreviewBackgroundColor(const QColor &color)
Sets the background color that text will be rendered on for previews.
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0, bool *isZeroSize=nullptr) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
void setTabStopDistanceUnit(Qgis::RenderUnit unit)
Sets the unit used for the tab stop distance.
void multiplyOpacity(double opacityFactor)
Multiply opacity by opacityFactor.
void setOpacity(double opacity)
Sets the text's opacity.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
bool operator==(const QgsTextFormat &other) const
void setAllowHtmlFormatting(bool allow)
Sets whether text should be treated as a HTML document and HTML tags should be used for formatting th...
void setLineHeightUnit(Qgis::RenderUnit unit)
Sets the unit for the line height for text.
Qgis::RenderUnit lineHeightUnit() const
Returns the units for the line height for text.
Qgis::Capitalization capitalization() const
Returns the text capitalization style.
bool forcedItalic() const
Returns true if the format is set to force an italic style.
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
void setForcedBold(bool forced)
Sets whether the format is set to force a bold style.
bool isValid() const
Returns true if the format is valid.
void setTabStopDistanceMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the tab stop distance.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
Qgis::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
static QgsTextFormat fromQFont(const QFont &font)
Returns a text format matching the settings from an input font.
void setValid()
Sets the format to a valid state, without changing any of the default format settings.
bool allowHtmlFormatting() const
Returns true if text should be treated as a HTML document and HTML tags should be used for formatting...
QFont toQFont() const
Returns a QFont matching the relevant settings from this text format.
bool operator!=(const QgsTextFormat &other) const
double opacity() const
Returns the text's opacity.
void setTabStopDistance(double distance)
Sets the distance for tab stops.
Qgis::TextOrientation orientation() const
Returns the orientation of the text.
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
double size() const
Returns the size for rendered text.
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
QgsTextFormat & operator=(const QgsTextFormat &other)
void setTabPositions(const QList< QgsTextFormat::Tab > &positions)
Sets the list of tab positions for tab stops.
static QPixmap textFormatPreviewPixmap(const QgsTextFormat &format, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a text format.
Qgis::RenderUnit tabStopDistanceUnit() const
Returns the units for the tab stop distance.
bool forcedBold() const
Returns true if the format is set to force a bold style.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
QString asCSS(double pointToPixelMultiplier=1.0) const
Returns a CSS string representing the specified text format as closely as possible.
QMimeData * toMimeData() const
Returns new mime data representing the text format settings.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the size.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QColor color() const
Returns the color that text will be rendered in.
QFont font() const
Returns the font used for rendering text.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer's custom properties (for QGIS 2.x projects).
QgsMapUnitScale tabStopDistanceMapUnitScale() const
Returns the map unit scale object for the tab stop distance.
QColor previewBackgroundColor() const
Returns the background color for text previews.
bool containsAdvancedEffects() const
Returns true if any component of the font format requires advanced effects such as blend modes,...
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
void setLineHeight(double height)
Sets the line height for text.
Container for settings relating to a selective masking around a text.
QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns all field names referenced by the configuration (e.g.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
void readXml(const QDomElement &elem)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
double opacity() const
Returns the mask's opacity.
void setOpacity(double opacity)
Sets the mask's opacity.
static Qgis::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
static QColor readColor(QgsVectorLayer *layer, const QString &property, const QColor &defaultColor=Qt::black, bool withAlpha=true)
Converts an encoded color value from a layer property.
static QString encodeTextOrientation(Qgis::TextOrientation orientation)
Encodes a text orientation.
static int sizeToPixel(double size, const QgsRenderContext &c, Qgis::RenderUnit unit, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
Container for settings relating to a text shadow.
bool enabled() const
Returns whether the shadow is enabled.
double opacity() const
Returns the shadow's opacity.
void readXml(const QDomElement &elem)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the drop shadow.
void setOpacity(double opacity)
Sets the shadow's opacity.
QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns all field names referenced by the configuration (e.g.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer's custom properties (for QGIS 2.x projects).
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based data sets.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5949