QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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 "qgsconfig.h"
17#include "qgstextformat.h"
18
19#include "qgsapplication.h"
20#include "qgscolorutils.h"
21#include "qgsfontmanager.h"
22#include "qgsfontutils.h"
23#include "qgspainting.h"
24#include "qgspallabeling.h"
25#include "qgssymbollayerutils.h"
26#include "qgstextrenderer.h"
27#include "qgstextrenderer_p.h"
29#include "qgsunittypes.h"
30#include "qgsvectorlayer.h"
31
32#include <QFontDatabase>
33#include <QMimeData>
34#include <QScreen>
35#include <QWidget>
36
38{
39 d = new QgsTextSettingsPrivate();
40}
41
43//****** IMPORTANT! editing this? make sure you update the move constructor too! *****
44 : mBufferSettings( other.mBufferSettings )
45 , mBackgroundSettings( other.mBackgroundSettings )
46 , mShadowSettings( other.mShadowSettings )
47 , mMaskSettings( other.mMaskSettings )
48 , mTextFontFamily( other.mTextFontFamily )
49 , mTextFontFound( other.mTextFontFound )
50 , d( other.d )
51 //****** IMPORTANT! editing this? make sure you update the move constructor too! *****
52{
53
54}
55
57 : mBufferSettings( std::move( other.mBufferSettings ) )
58 , mBackgroundSettings( std::move( other.mBackgroundSettings ) )
59 , mShadowSettings( std::move( other.mShadowSettings ) )
60 , mMaskSettings( std::move( other.mMaskSettings ) )
61 , mTextFontFamily( std::move( other.mTextFontFamily ) )
62 , mTextFontFound( other.mTextFontFound )
63 , d( std::move( other.d ) )
64{
65
66}
67
69{
70 if ( &other == this )
71 return *this;
72
73 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
74 d = other.d;
75 mBufferSettings = other.mBufferSettings;
76 mBackgroundSettings = other.mBackgroundSettings;
77 mShadowSettings = other.mShadowSettings;
78 mMaskSettings = other.mMaskSettings;
79 mTextFontFamily = other.mTextFontFamily;
80 mTextFontFound = other.mTextFontFound;
81 return *this;
82 //****** IMPORTANT! editing this? make sure you update the move assignment operator too! *****
83}
84
86{
87 if ( &other == this )
88 return *this;
89
90 d = std::move( other.d );
91 mBufferSettings = std::move( other.mBufferSettings );
92 mBackgroundSettings = std::move( other.mBackgroundSettings );
93 mShadowSettings = std::move( other.mShadowSettings );
94 mMaskSettings = std::move( other.mMaskSettings );
95 mTextFontFamily = std::move( other.mTextFontFamily );
96 mTextFontFound = other.mTextFontFound;
97 return *this;
98}
99
101{
102
103}
104
105bool QgsTextFormat::operator==( const QgsTextFormat &other ) const
106{
107 if ( d->isValid != other.isValid()
108 || d->textFont != other.font()
109 || namedStyle() != other.namedStyle()
110 || d->fontSizeUnits != other.sizeUnit()
111 || d->fontSizeMapUnitScale != other.sizeMapUnitScale()
112 || d->fontSize != other.size()
113 || d->textColor != other.color()
114 || d->opacity != other.opacity()
115 || d->blendMode != other.blendMode()
116 || d->multilineHeight != other.lineHeight()
117 || d->multilineHeightUnits != other.lineHeightUnit()
118 || d->orientation != other.orientation()
119 || d->previewBackgroundColor != other.previewBackgroundColor()
120 || d->allowHtmlFormatting != other.allowHtmlFormatting()
121 || d->forcedBold != other.forcedBold()
122 || d->forcedItalic != other.forcedItalic()
123 || d->capitalization != other.capitalization()
124 || d->tabStopDistance != other.tabStopDistance()
125 || d->tabPositions != other.tabPositions()
126 || d->tabStopDistanceUnits != other.tabStopDistanceUnit()
127 || d->tabStopDistanceMapUnitScale != other.tabStopDistanceMapUnitScale()
128 || mBufferSettings != other.mBufferSettings
129 || mBackgroundSettings != other.mBackgroundSettings
130 || mShadowSettings != other.mShadowSettings
131 || mMaskSettings != other.mMaskSettings
132 || d->families != other.families()
133 || d->mDataDefinedProperties != other.dataDefinedProperties() )
134 return false;
135
136 return true;
137}
138
139bool QgsTextFormat::operator!=( const QgsTextFormat &other ) const
140{
141 return !( *this == other );
142}
143
145{
146 return d->isValid;
147}
148
150{
151 d->isValid = true;
152}
153
155{
156 d->isValid = true;
157 return mBufferSettings;
158}
159
161{
162 d->isValid = true;
163 mBufferSettings = bufferSettings;
164}
165
167{
168 d->isValid = true;
169 return mBackgroundSettings;
170}
171
173{
174 d->isValid = true;
175 mBackgroundSettings = backgroundSettings;
176}
177
179{
180 d->isValid = true;
181 return mShadowSettings;
182}
183
185{
186 d->isValid = true;
187 mShadowSettings = shadowSettings;
188}
189
191{
192 d->isValid = true;
193 return mMaskSettings;
194}
195
197{
198 d->isValid = true;
199 mMaskSettings = maskSettings;
200}
201
203{
204 return d->textFont;
205}
206
207QFont QgsTextFormat::scaledFont( const QgsRenderContext &context, double scaleFactor, bool *isZeroSize ) const
208{
209 if ( isZeroSize )
210 *isZeroSize = false;
211
212 QFont font = d->textFont;
213 if ( scaleFactor == 1 )
214 {
215 int fontPixelSize = QgsTextRenderer::sizeToPixel( d->fontSize, context, d->fontSizeUnits,
216 d->fontSizeMapUnitScale );
217 if ( fontPixelSize == 0 )
218 {
219 if ( isZeroSize )
220 *isZeroSize = true;
221 return QFont();
222 }
223
224 font.setPixelSize( fontPixelSize );
225 }
226 else
227 {
228 double fontPixelSize = context.convertToPainterUnits( d->fontSize, d->fontSizeUnits, d->fontSizeMapUnitScale );
229 if ( qgsDoubleNear( fontPixelSize, 0 ) )
230 {
231 if ( isZeroSize )
232 *isZeroSize = true;
233 return QFont();
234 }
235 const int roundedPixelSize = static_cast< int >( std::round( scaleFactor * fontPixelSize + 0.5 ) );
236 font.setPixelSize( roundedPixelSize );
237 }
238
239 font.setLetterSpacing( QFont::AbsoluteSpacing, context.convertToPainterUnits( d->textFont.letterSpacing(), d->fontSizeUnits, d->fontSizeMapUnitScale ) * scaleFactor );
240 font.setWordSpacing( context.convertToPainterUnits( d->textFont.wordSpacing(), d->fontSizeUnits, d->fontSizeMapUnitScale ) * scaleFactor * scaleFactor );
241
242 if ( d->capitalization == Qgis::Capitalization::SmallCaps
243 || d->capitalization == Qgis::Capitalization::AllSmallCaps )
244 font.setCapitalization( QFont::SmallCaps );
245
246 return font;
247}
248
249void QgsTextFormat::setFont( const QFont &font )
250{
251 d->isValid = true;
252 d->textFont = font;
253 d->originalFontFamily.clear();
254}
255
257{
258 if ( !d->textNamedStyle.isEmpty() )
259 return d->textNamedStyle;
260
261 QFontDatabase db;
262 return db.styleString( d->textFont );
263}
264
265void QgsTextFormat::setNamedStyle( const QString &style )
266{
267 d->isValid = true;
268 QgsFontUtils::updateFontViaStyle( d->textFont, style );
269 d->textNamedStyle = style;
270}
271
273{
274 return d->forcedBold;
275}
276
278{
279 d->isValid = true;
280 d->textFont.setBold( forced );
281 d->forcedBold = true;
282}
283
285{
286 return d->forcedItalic;
287}
288
290{
291 d->isValid = true;
292 d->textFont.setItalic( forced );
293 d->forcedItalic = true;
294}
295
296QStringList QgsTextFormat::families() const
297{
298 return d->families;
299}
300
301void QgsTextFormat::setFamilies( const QStringList &families )
302{
303 d->isValid = true;
304 d->families = families;
305}
306
308{
309 return d->fontSizeUnits;
310}
311
313{
314 d->isValid = true;
315 d->fontSizeUnits = unit;
316}
317
319{
320 return d->fontSizeMapUnitScale;
321}
322
324{
325 d->isValid = true;
326 d->fontSizeMapUnitScale = scale;
327}
328
330{
331 return d->fontSize;
332}
333
335{
336 d->isValid = true;
337 d->fontSize = size;
338}
339
341{
342 return d->textColor;
343}
344
345void QgsTextFormat::setColor( const QColor &color )
346{
347 d->isValid = true;
348 d->textColor = color;
349}
350
352{
353 return d->opacity;
354}
355
356void QgsTextFormat::multiplyOpacity( double opacityFactor )
357{
358 if ( qgsDoubleNear( opacityFactor, 1.0 ) )
359 return;
360 d->opacity *= opacityFactor;
361 mBufferSettings.setOpacity( mBufferSettings.opacity() * opacityFactor );
362 mShadowSettings.setOpacity( mShadowSettings.opacity() * opacityFactor );
363 mBackgroundSettings.setOpacity( mBackgroundSettings.opacity() * opacityFactor );
364 mMaskSettings.setOpacity( mMaskSettings.opacity() * opacityFactor );
365}
366
368{
369 d->isValid = true;
370 d->opacity = opacity;
371}
372
374{
375 return d->textFont.stretch() > 0 ? d->textFont.stretch() : 100;
376}
377
379{
380 d->isValid = true;
381 d->textFont.setStretch( factor );
382}
383
384QPainter::CompositionMode QgsTextFormat::blendMode() const
385{
386 return d->blendMode;
387}
388
389void QgsTextFormat::setBlendMode( QPainter::CompositionMode mode )
390{
391 d->isValid = true;
392 d->blendMode = mode;
393}
394
396{
397 return d->multilineHeight;
398}
399
400void QgsTextFormat::setLineHeight( double height )
401{
402 d->isValid = true;
403 d->multilineHeight = height;
404}
405
407{
408 return d->multilineHeightUnits;
409}
410
412{
413 d->isValid = true;
414 d->multilineHeightUnits = unit;
415}
416
418{
419 return d->tabStopDistance;
420}
421
423{
424 d->isValid = true;
425 d->tabStopDistance = distance;
426}
427
428QList<QgsTextFormat::Tab> QgsTextFormat::tabPositions() const
429{
430 return d->tabPositions;
431}
432
433void QgsTextFormat::setTabPositions( const QList<Tab> &positions )
434{
435 d->isValid = true;
436 d->tabPositions = positions;
437}
438
440{
441 return d->tabStopDistanceUnits;
442}
443
445{
446 d->isValid = true;
447 d->tabStopDistanceUnits = unit;
448}
449
451{
452 return d->tabStopDistanceMapUnitScale;
453}
454
456{
457 d->isValid = true;
458 d->tabStopDistanceMapUnitScale = scale;
459}
460
462{
463 return d->orientation;
464}
465
467{
468 d->isValid = true;
469 d->orientation = orientation;
470}
471
473{
474 // bit of complexity here to maintain API..
475 return d->capitalization == Qgis::Capitalization::MixedCase && d->textFont.capitalization() != QFont::MixedCase
476 ? static_cast< Qgis::Capitalization >( d->textFont.capitalization() )
477 : d->capitalization ;
478}
479
481{
482 d->isValid = true;
483 d->capitalization = capitalization;
484#if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
485 d->textFont.setCapitalization( capitalization == Qgis::Capitalization::SmallCaps || capitalization == Qgis::Capitalization::AllSmallCaps ? QFont::SmallCaps : QFont::MixedCase );
486#else
487 d->textFont.setCapitalization( QFont::MixedCase );
488#endif
489}
490
492{
493 return d->allowHtmlFormatting;
494}
495
497{
498 d->isValid = true;
499 d->allowHtmlFormatting = allow;
500}
501
503{
504 return d->previewBackgroundColor;
505}
506
508{
509 d->isValid = true;
510 d->previewBackgroundColor = color;
511}
512
514{
515 d->isValid = true;
516 QFont appFont = QApplication::font();
517 d->originalFontFamily = QgsApplication::fontManager()->processFontFamilyName( layer->customProperty( QStringLiteral( "labeling/fontFamily" ), QVariant( appFont.family() ) ).toString() );
518 mTextFontFamily = d->originalFontFamily;
519 QString fontFamily = mTextFontFamily;
520 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
521 {
522 // trigger to notify about font family substitution
523 mTextFontFound = false;
524
525 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
526 // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
527
528 // for now, do not use matching algorithm for substitution if family not found, substitute default instead
529 fontFamily = appFont.family();
530 }
531 else
532 {
533 mTextFontFound = true;
534 }
535
536 if ( !layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).isValid() )
537 {
538 d->fontSize = appFont.pointSizeF();
539 }
540 else
541 {
542 d->fontSize = layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).toDouble();
543 }
544
545 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString().isEmpty() )
546 {
547 d->fontSizeUnits = layer->customProperty( QStringLiteral( "labeling/fontSizeInMapUnits" ), QVariant( false ) ).toBool() ?
549 }
550 else
551 {
552 bool ok = false;
553 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString(), &ok );
554 if ( !ok )
555 d->fontSizeUnits = Qgis::RenderUnit::Points;
556 }
557 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString().isEmpty() )
558 {
559 //fallback to older property
560 double oldMin = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMinScale" ), 0.0 ).toDouble();
561 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
562 double oldMax = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMaxScale" ), 0.0 ).toDouble();
563 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
564 }
565 else
566 {
567 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString() );
568 }
569 int fontWeight = layer->customProperty( QStringLiteral( "labeling/fontWeight" ) ).toInt();
570 bool fontItalic = layer->customProperty( QStringLiteral( "labeling/fontItalic" ) ).toBool();
571 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
572 d->textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( QStringLiteral( "labeling/namedStyle" ), QVariant( "" ) ).toString() );
573 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
574 d->capitalization = static_cast< Qgis::Capitalization >( layer->customProperty( QStringLiteral( "labeling/fontCapitals" ), QVariant( 0 ) ).toUInt() );
575 d->textFont.setUnderline( layer->customProperty( QStringLiteral( "labeling/fontUnderline" ) ).toBool() );
576 d->textFont.setStrikeOut( layer->customProperty( QStringLiteral( "labeling/fontStrikeout" ) ).toBool() );
577 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( QStringLiteral( "labeling/fontLetterSpacing" ), QVariant( 0.0 ) ).toDouble() );
578 d->textFont.setWordSpacing( layer->customProperty( QStringLiteral( "labeling/fontWordSpacing" ), QVariant( 0.0 ) ).toDouble() );
579 d->textColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/textColor" ), Qt::black, false );
580 if ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toString().isEmpty() )
581 {
582 d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/textTransp" ) ).toInt() / 100.0 ); //0 -100
583 }
584 else
585 {
586 d->opacity = ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toDouble() );
587 }
588 d->blendMode = QgsPainting::getCompositionMode(
589 static_cast< Qgis::BlendMode >( layer->customProperty( QStringLiteral( "labeling/blendMode" ), QVariant( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
590 d->multilineHeight = layer->customProperty( QStringLiteral( "labeling/multilineHeight" ), QVariant( 1.0 ) ).toDouble();
591 d->previewBackgroundColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/previewBkgrdColor" ), QColor( 255, 255, 255 ), false );
592
593 mBufferSettings.readFromLayer( layer );
594 mShadowSettings.readFromLayer( layer );
595 mBackgroundSettings.readFromLayer( layer );
596}
597
598void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
599{
600 d->isValid = true;
601 QDomElement textStyleElem;
602 if ( elem.nodeName() == QLatin1String( "text-style" ) )
603 textStyleElem = elem;
604 else
605 textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
606 QFont appFont = QApplication::font();
607 d->originalFontFamily = QgsApplication::fontManager()->processFontFamilyName( textStyleElem.attribute( QStringLiteral( "fontFamily" ), appFont.family() ) );
608 mTextFontFamily = d->originalFontFamily;
609 QString fontFamily = mTextFontFamily;
610
611 const QDomElement familiesElem = textStyleElem.firstChildElement( QStringLiteral( "families" ) );
612 const QDomNodeList familyNodes = familiesElem.childNodes();
613 QStringList families;
614 families.reserve( familyNodes.size() );
615 for ( int i = 0; i < familyNodes.count(); ++i )
616 {
617 const QDomElement familyElem = familyNodes.at( i ).toElement();
618 families << familyElem.attribute( QStringLiteral( "name" ) );
619 }
620 d->families = families;
621
622 mTextFontFound = false;
623 QString matched;
624 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
625 {
626 if ( QgsApplication::fontManager()->tryToDownloadFontFamily( mTextFontFamily, matched ) )
627 {
628 mTextFontFound = true;
629 }
630 else
631 {
632 for ( const QString &family : std::as_const( families ) )
633 {
634 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( family );
635 if ( QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) ||
636 QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
637 {
638 mTextFontFound = true;
639 fontFamily = processedFamily;
640 break;
641 }
642 }
643
644 if ( !mTextFontFound )
645 {
646 // couldn't even find a matching font in the backup list -- substitute default instead
647 fontFamily = appFont.family();
648 }
649 }
650 }
651 else
652 {
653 mTextFontFound = true;
654 }
655
656 if ( !mTextFontFound )
657 {
658 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( mTextFontFamily ) );
659 }
660
661 if ( textStyleElem.hasAttribute( QStringLiteral( "fontSize" ) ) )
662 {
663 d->fontSize = textStyleElem.attribute( QStringLiteral( "fontSize" ) ).toDouble();
664 }
665 else
666 {
667 d->fontSize = appFont.pointSizeF();
668 }
669
670 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeUnit" ) ) )
671 {
672 d->fontSizeUnits = textStyleElem.attribute( QStringLiteral( "fontSizeInMapUnits" ) ).toUInt() == 0 ? Qgis::RenderUnit::Points
674 }
675 else
676 {
677 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "fontSizeUnit" ) ) );
678 }
679
680 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeMapUnitScale" ) ) )
681 {
682 //fallback to older property
683 double oldMin = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
684 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
685 double oldMax = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
686 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
687 }
688 else
689 {
690 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitScale" ) ) );
691 }
692 int fontWeight = textStyleElem.attribute( QStringLiteral( "fontWeight" ) ).toInt();
693 bool fontItalic = textStyleElem.attribute( QStringLiteral( "fontItalic" ) ).toInt();
694 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
695 d->textFont.setPointSizeF( d->fontSize ); //double precision needed because of map units
696 d->textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( QStringLiteral( "namedStyle" ) ) );
697 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
698 d->forcedBold = textStyleElem.attribute( QStringLiteral( "forcedBold" ) ).toInt();
699 d->forcedItalic = textStyleElem.attribute( QStringLiteral( "forcedItalic" ) ).toInt();
700 d->textFont.setUnderline( textStyleElem.attribute( QStringLiteral( "fontUnderline" ) ).toInt() );
701 d->textFont.setStrikeOut( textStyleElem.attribute( QStringLiteral( "fontStrikeout" ) ).toInt() );
702 d->textFont.setKerning( textStyleElem.attribute( QStringLiteral( "fontKerning" ), QStringLiteral( "1" ) ).toInt() );
703 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( QStringLiteral( "fontLetterSpacing" ), QStringLiteral( "0" ) ).toDouble() );
704 d->textFont.setWordSpacing( textStyleElem.attribute( QStringLiteral( "fontWordSpacing" ), QStringLiteral( "0" ) ).toDouble() );
705 d->textColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( Qt::black ) ) );
706 if ( !textStyleElem.hasAttribute( QStringLiteral( "textOpacity" ) ) )
707 {
708 d->opacity = ( 1 - textStyleElem.attribute( QStringLiteral( "textTransp" ) ).toInt() / 100.0 ); //0 -100
709 }
710 else
711 {
712 d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() );
713 }
714#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
715 d->textFont.setStretch( textStyleElem.attribute( QStringLiteral( "stretchFactor" ), QStringLiteral( "100" ) ).toInt() );
716#endif
717 d->orientation = QgsTextRendererUtils::decodeTextOrientation( textStyleElem.attribute( QStringLiteral( "textOrientation" ) ) );
718 d->previewBackgroundColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( Qt::white ) ) );
719
720 d->blendMode = QgsPainting::getCompositionMode(
721 static_cast< Qgis::BlendMode >( textStyleElem.attribute( QStringLiteral( "blendMode" ), QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
722
723 if ( !textStyleElem.hasAttribute( QStringLiteral( "multilineHeight" ) ) )
724 {
725 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
726 d->multilineHeight = textFormatElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
727 }
728 else
729 {
730 d->multilineHeight = textStyleElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
731 }
732 bool ok = false;
733 d->multilineHeightUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "multilineHeightUnit" ), QStringLiteral( "percent" ) ), &ok );
734
735 d->tabStopDistance = textStyleElem.attribute( QStringLiteral( "tabStopDistance" ), QStringLiteral( "80" ) ).toDouble();
736 d->tabStopDistanceUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "tabStopDistanceUnit" ), QStringLiteral( "Point" ) ), &ok );
737 d->tabStopDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "tabStopDistanceMapUnitScale" ) ) );
738
739 QList< Tab > tabPositions;
740 {
741 const QDomElement tabPositionsElem = textStyleElem.firstChildElement( QStringLiteral( "tabPositions" ) );
742 const QDomNodeList tabNodes = tabPositionsElem.childNodes();
743 tabPositions.reserve( tabNodes.size() );
744 for ( int i = 0; i < tabNodes.count(); ++i )
745 {
746 const QDomElement tabElem = tabNodes.at( i ).toElement();
747 tabPositions << Tab( tabElem.attribute( QStringLiteral( "position" ) ).toDouble() );
748 }
749 }
750 d->tabPositions = tabPositions;
751
752 if ( textStyleElem.hasAttribute( QStringLiteral( "capitalization" ) ) )
753 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( Qgis::Capitalization::MixedCase ) ) ).toInt() );
754 else
755 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "fontCapitals" ), QStringLiteral( "0" ) ).toUInt() );
756
757 if ( d->capitalization == Qgis::Capitalization::SmallCaps || d->capitalization == Qgis::Capitalization::AllSmallCaps )
758 d->textFont.setCapitalization( QFont::SmallCaps );
759
760 d->allowHtmlFormatting = textStyleElem.attribute( QStringLiteral( "allowHtml" ), QStringLiteral( "0" ) ).toInt();
761
762 if ( textStyleElem.firstChildElement( QStringLiteral( "text-buffer" ) ).isNull() )
763 {
764 mBufferSettings.readXml( elem );
765 }
766 else
767 {
768 mBufferSettings.readXml( textStyleElem );
769 }
770 if ( textStyleElem.firstChildElement( QStringLiteral( "text-mask" ) ).isNull() )
771 {
772 mMaskSettings.readXml( elem );
773 }
774 else
775 {
776 mMaskSettings.readXml( textStyleElem );
777 }
778 if ( textStyleElem.firstChildElement( QStringLiteral( "shadow" ) ).isNull() )
779 {
780 mShadowSettings.readXml( elem );
781 }
782 else
783 {
784 mShadowSettings.readXml( textStyleElem );
785 }
786 if ( textStyleElem.firstChildElement( QStringLiteral( "background" ) ).isNull() )
787 {
788 mBackgroundSettings.readXml( elem, context );
789 }
790 else
791 {
792 mBackgroundSettings.readXml( textStyleElem, context );
793 }
794
795 QDomElement ddElem = textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) );
796 if ( ddElem.isNull() )
797 {
798 ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
799 }
800 if ( !ddElem.isNull() )
801 {
802 d->mDataDefinedProperties.readXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
803 mBackgroundSettings.upgradeDataDefinedProperties( d->mDataDefinedProperties );
804 }
805 else
806 {
807 d->mDataDefinedProperties.clear();
808 }
809}
810
811QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
812{
813 // text style
814 QDomElement textStyleElem = doc.createElement( QStringLiteral( "text-style" ) );
815 textStyleElem.setAttribute( QStringLiteral( "fontFamily" ), !d->originalFontFamily.isEmpty() ? d->originalFontFamily : d->textFont.family() );
816
817 QDomElement familiesElem = doc.createElement( QStringLiteral( "families" ) );
818 for ( const QString &family : std::as_const( d->families ) )
819 {
820 QDomElement familyElem = doc.createElement( QStringLiteral( "family" ) );
821 familyElem.setAttribute( QStringLiteral( "name" ), family );
822 familiesElem.appendChild( familyElem );
823 }
824 textStyleElem.appendChild( familiesElem );
825
826 textStyleElem.setAttribute( QStringLiteral( "namedStyle" ), QgsFontUtils::untranslateNamedStyle( d->textNamedStyle ) );
827 textStyleElem.setAttribute( QStringLiteral( "fontSize" ), d->fontSize );
828 textStyleElem.setAttribute( QStringLiteral( "fontSizeUnit" ), QgsUnitTypes::encodeUnit( d->fontSizeUnits ) );
829 textStyleElem.setAttribute( QStringLiteral( "fontSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->fontSizeMapUnitScale ) );
830 textStyleElem.setAttribute( QStringLiteral( "fontWeight" ), d->textFont.weight() );
831 textStyleElem.setAttribute( QStringLiteral( "fontItalic" ), d->textFont.italic() );
832 textStyleElem.setAttribute( QStringLiteral( "fontStrikeout" ), d->textFont.strikeOut() );
833 textStyleElem.setAttribute( QStringLiteral( "fontUnderline" ), d->textFont.underline() );
834 textStyleElem.setAttribute( QStringLiteral( "forcedBold" ), d->forcedBold );
835 textStyleElem.setAttribute( QStringLiteral( "forcedItalic" ), d->forcedItalic );
836 textStyleElem.setAttribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( d->textColor ) );
837 textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( d->previewBackgroundColor ) );
838 textStyleElem.setAttribute( QStringLiteral( "fontLetterSpacing" ), d->textFont.letterSpacing() );
839 textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() );
840 textStyleElem.setAttribute( QStringLiteral( "fontKerning" ), d->textFont.kerning() );
841 textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity );
842#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
843 if ( d->textFont.stretch() > 0 )
844 textStyleElem.setAttribute( QStringLiteral( "stretchFactor" ), d->textFont.stretch() );
845#endif
846 textStyleElem.setAttribute( QStringLiteral( "textOrientation" ), QgsTextRendererUtils::encodeTextOrientation( d->orientation ) );
847 textStyleElem.setAttribute( QStringLiteral( "blendMode" ), static_cast< int >( QgsPainting::getBlendModeEnum( d->blendMode ) ) );
848 textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
849 textStyleElem.setAttribute( QStringLiteral( "multilineHeightUnit" ), QgsUnitTypes::encodeUnit( d->multilineHeightUnits ) );
850
851 textStyleElem.setAttribute( QStringLiteral( "tabStopDistance" ), d->tabStopDistance );
852 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceUnit" ), QgsUnitTypes::encodeUnit( d->tabStopDistanceUnits ) );
853 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->tabStopDistanceMapUnitScale ) );
854
855 if ( !d->tabPositions.empty() )
856 {
857 QDomElement tabPositionsElem = doc.createElement( QStringLiteral( "tabPositions" ) );
858 for ( const Tab &tab : std::as_const( d->tabPositions ) )
859 {
860 QDomElement tabElem = doc.createElement( QStringLiteral( "tab" ) );
861 tabElem.setAttribute( QStringLiteral( "position" ), tab.position() );
862 tabPositionsElem.appendChild( tabElem );
863 }
864 textStyleElem.appendChild( tabPositionsElem );
865 }
866
867 textStyleElem.setAttribute( QStringLiteral( "allowHtml" ), d->allowHtmlFormatting ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
868 textStyleElem.setAttribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( d->capitalization ) ) );
869
870 QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
871 d->mDataDefinedProperties.writeXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
872
873 textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
874 textStyleElem.appendChild( mMaskSettings.writeXml( doc ) );
875 textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
876 textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
877 textStyleElem.appendChild( ddElem );
878
879 return textStyleElem;
880}
881
883{
884 //set both the mime color data, and the text (format settings).
885
886 QMimeData *mimeData = new QMimeData;
887 mimeData->setColorData( QVariant( color() ) );
888
889 QgsReadWriteContext rwContext;
890 QDomDocument textDoc;
891 QDomElement textElem = writeXml( textDoc, rwContext );
892 textDoc.appendChild( textElem );
893 mimeData->setText( textDoc.toString() );
894
895 return mimeData;
896}
897
899{
900 QgsTextFormat format;
901 format.setFont( font );
902 if ( font.pointSizeF() > 0 )
903 {
904 format.setSize( font.pointSizeF() );
906 }
907 else if ( font.pixelSize() > 0 )
908 {
909 format.setSize( font.pixelSize() );
911 }
912
913 return format;
914}
915
917{
918 QFont f = font();
919 switch ( sizeUnit() )
920 {
922 f.setPointSizeF( size() );
923 break;
924
926 f.setPointSizeF( size() * 2.83464567 );
927 break;
928
930 f.setPointSizeF( size() * 72 );
931 break;
932
934 f.setPixelSize( static_cast< int >( std::round( size() ) ) );
935 break;
936
941 // no meaning here
942 break;
943 }
944 return f;
945}
946
947QgsTextFormat QgsTextFormat::fromMimeData( const QMimeData *data, bool *ok )
948{
949 if ( ok )
950 *ok = false;
951 QgsTextFormat format;
952 if ( !data )
953 return format;
954
955 QString text = data->text();
956 if ( !text.isEmpty() )
957 {
958 QDomDocument doc;
959 QDomElement elem;
960 QgsReadWriteContext rwContext;
961
962 if ( doc.setContent( text ) )
963 {
964 elem = doc.documentElement();
965
966 format.readXml( elem, rwContext );
967 if ( ok )
968 *ok = true;
969 return format;
970 }
971 }
972 return format;
973}
974
976{
977 if ( d->blendMode != QPainter::CompositionMode_SourceOver )
978 return true;
979
980 if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
981 return true;
982
983 if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
984 return true;
985
986 if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
987 return true;
988
989 return false;
990}
991
993{
994 if ( d->blendMode != QPainter::CompositionMode_SourceOver )
995 return true;
996
997 if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
998 return true;
999
1000 if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
1001 return true;
1002
1003 if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
1004 return true;
1005
1006 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontBlendMode )
1007 || d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::ShadowBlendMode )
1008 || d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::BufferBlendMode )
1009 || d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::ShapeBlendMode ) )
1010 return true;
1011
1012 return false;
1013}
1014
1016{
1017 d->isValid = true;
1018 return d->mDataDefinedProperties;
1019}
1020
1022{
1023 return d->mDataDefinedProperties;
1024}
1025
1026QSet<QString> QgsTextFormat::referencedFields( const QgsRenderContext &context ) const
1027{
1028 QSet< QString > fields = d->mDataDefinedProperties.referencedFields( context.expressionContext(), true );
1029 fields.unite( mBufferSettings.referencedFields( context ) );
1030 fields.unite( mBackgroundSettings.referencedFields( context ) );
1031 fields.unite( mShadowSettings.referencedFields( context ) );
1032 fields.unite( mMaskSettings.referencedFields( context ) );
1033 return fields;
1034}
1035
1037{
1038 d->isValid = true;
1039 d->mDataDefinedProperties = collection;
1040}
1041
1043{
1044 d->isValid = true;
1045 if ( !d->mDataDefinedProperties.hasActiveProperties() )
1046 return;
1047
1048 QString ddFontFamily;
1049 context.expressionContext().setOriginalValueVariable( d->textFont.family() );
1050 QVariant exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::Family, context.expressionContext() );
1051 if ( !QgsVariantUtils::isNull( exprVal ) )
1052 {
1053 QString family = exprVal.toString().trimmed();
1055 if ( d->textFont.family() != family )
1056 {
1057 // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
1058 // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
1059 if ( QgsFontUtils::fontFamilyOnSystem( family ) )
1060 {
1061 ddFontFamily = family;
1062 }
1063 }
1064 }
1065
1066 // data defined named font style?
1067 QString ddFontStyle;
1068 context.expressionContext().setOriginalValueVariable( d->textNamedStyle );
1069 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStyle, context.expressionContext() );
1070 if ( !QgsVariantUtils::isNull( exprVal ) )
1071 {
1072 QString fontstyle = exprVal.toString().trimmed();
1073 ddFontStyle = fontstyle;
1074 }
1075
1076 bool ddBold = false;
1077 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Bold ) )
1078 {
1079 context.expressionContext().setOriginalValueVariable( d->textFont.bold() );
1080 ddBold = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Bold, context.expressionContext(), false ) ;
1081 }
1082
1083 bool ddItalic = false;
1084 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Italic ) )
1085 {
1086 context.expressionContext().setOriginalValueVariable( d->textFont.italic() );
1087 ddItalic = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Italic, context.expressionContext(), false );
1088 }
1089
1090 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1091 // (currently defaults to what has been read in from layer settings)
1092 QFont newFont;
1093 QFontDatabase fontDb;
1094 QFont appFont = QApplication::font();
1095 bool newFontBuilt = false;
1096 if ( ddBold || ddItalic )
1097 {
1098 // new font needs built, since existing style needs removed
1099 newFont = QgsFontUtils::createFont( !ddFontFamily.isEmpty() ? ddFontFamily : d->textFont.family() );
1100 newFontBuilt = true;
1101 newFont.setBold( ddBold );
1102 newFont.setItalic( ddItalic );
1103 }
1104 else if ( !ddFontStyle.isEmpty()
1105 && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1106 {
1107 if ( !ddFontFamily.isEmpty() )
1108 {
1109 // both family and style are different, build font from database
1110 QFont styledfont = fontDb.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
1111 if ( appFont != styledfont )
1112 {
1113 newFont = styledfont;
1114 newFontBuilt = true;
1115 }
1116 }
1117
1118 // update the font face style
1119 QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : d->textFont, ddFontStyle );
1120 }
1121 else if ( !ddFontFamily.isEmpty() )
1122 {
1123 if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1124 {
1125 // just family is different, build font from database
1126 QFont styledfont = fontDb.font( ddFontFamily, d->textNamedStyle, appFont.pointSize() );
1127 if ( appFont != styledfont )
1128 {
1129 newFont = styledfont;
1130 newFontBuilt = true;
1131 }
1132 }
1133 else
1134 {
1135 newFont = QgsFontUtils::createFont( ddFontFamily );
1136 newFontBuilt = true;
1137 }
1138 }
1139
1140 if ( newFontBuilt )
1141 {
1142 // copy over existing font settings
1143 newFont.setUnderline( d->textFont.underline() );
1144 newFont.setStrikeOut( d->textFont.strikeOut() );
1145 newFont.setWordSpacing( d->textFont.wordSpacing() );
1146 newFont.setLetterSpacing( QFont::AbsoluteSpacing, d->textFont.letterSpacing() );
1147 d->textFont = newFont;
1148 }
1149
1150 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Underline ) )
1151 {
1152 context.expressionContext().setOriginalValueVariable( d->textFont.underline() );
1153 d->textFont.setUnderline( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Underline, context.expressionContext(), d->textFont.underline() ) );
1154 }
1155
1156 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Strikeout ) )
1157 {
1158 context.expressionContext().setOriginalValueVariable( d->textFont.strikeOut() );
1159 d->textFont.setStrikeOut( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Strikeout, context.expressionContext(), d->textFont.strikeOut() ) );
1160 }
1161
1162 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Color ) )
1163 {
1165 d->textColor = d->mDataDefinedProperties.valueAsColor( QgsPalLayerSettings::Property::Color, context.expressionContext(), d->textColor );
1166 }
1167
1168 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Size ) )
1169 {
1171 d->fontSize = d->mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Property::Size, context.expressionContext(), d->fontSize );
1172 }
1173
1174 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontSizeUnit, context.expressionContext() );
1175 if ( !QgsVariantUtils::isNull( exprVal ) )
1176 {
1177 QString units = exprVal.toString();
1178 if ( !units.isEmpty() )
1179 {
1180 bool ok;
1182 if ( ok )
1183 d->fontSizeUnits = res;
1184 }
1185 }
1186
1187 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontOpacity ) )
1188 {
1189 context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1190 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontOpacity, context.expressionContext(), d->opacity * 100 );
1191 if ( !QgsVariantUtils::isNull( val ) )
1192 {
1193 d->opacity = val.toDouble() / 100.0;
1194 }
1195 }
1196
1197#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
1198 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontStretchFactor ) )
1199 {
1200 context.expressionContext().setOriginalValueVariable( d->textFont.stretch() );
1201 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStretchFactor, context.expressionContext(), d->textFont.stretch() );
1202 if ( !QgsVariantUtils::isNull( val ) )
1203 {
1204 d->textFont.setStretch( val.toInt() );
1205 }
1206 }
1207#endif
1208
1209 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TextOrientation ) )
1210 {
1211 const QString encoded = QgsTextRendererUtils::encodeTextOrientation( d->orientation );
1212 context.expressionContext().setOriginalValueVariable( encoded );
1213 d->orientation = QgsTextRendererUtils::decodeTextOrientation( d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TextOrientation, context.expressionContext(), encoded ).toString() );
1214 }
1215
1216 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontLetterSpacing ) )
1217 {
1218 context.expressionContext().setOriginalValueVariable( d->textFont.letterSpacing() );
1219 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontLetterSpacing, context.expressionContext(), d->textFont.letterSpacing() );
1220 if ( !QgsVariantUtils::isNull( val ) )
1221 {
1222 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, val.toDouble() );
1223 }
1224 }
1225
1226 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontWordSpacing ) )
1227 {
1228 context.expressionContext().setOriginalValueVariable( d->textFont.wordSpacing() );
1229 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontWordSpacing, context.expressionContext(), d->textFont.wordSpacing() );
1230 if ( !QgsVariantUtils::isNull( val ) )
1231 {
1232 d->textFont.setWordSpacing( val.toDouble() );
1233 }
1234 }
1235
1236 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TabStopDistance ) )
1237 {
1238 context.expressionContext().setOriginalValueVariable( d->tabStopDistance );
1239 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TabStopDistance, context.expressionContext(), d->tabStopDistance );
1240 if ( !QgsVariantUtils::isNull( val ) )
1241 {
1242 if ( val.userType() == QMetaType::Type::QVariantList )
1243 {
1244 const QVariantList parts = val.toList();
1245 d->tabPositions.clear();
1246 d->tabPositions.reserve( parts.size() );
1247 for ( const QVariant &part : parts )
1248 {
1249 d->tabPositions.append( Tab( part.toDouble() ) );
1250 }
1251 }
1252 else if ( val.userType() == QMetaType::Type::QStringList )
1253 {
1254 const QStringList parts = val.toStringList();
1255 d->tabPositions.clear();
1256 d->tabPositions.reserve( parts.size() );
1257 for ( const QString &part : parts )
1258 {
1259 d->tabPositions.append( Tab( part.toDouble() ) );
1260 }
1261 }
1262 else
1263 {
1264 d->tabPositions.clear();
1265 d->tabStopDistance = val.toDouble();
1266 }
1267 }
1268 }
1269
1270 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontBlendMode ) )
1271 {
1272 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontBlendMode, context.expressionContext() );
1273 QString blendstr = exprVal.toString().trimmed();
1274 if ( !blendstr.isEmpty() )
1275 d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1276 }
1277
1278 mShadowSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1279 mBackgroundSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1280 mBufferSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1281 mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1282}
1283
1284QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen )
1285{
1286 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
1287 QgsTextFormat tempFormat = format;
1288 QPixmap pixmap( size * devicePixelRatio );
1289 pixmap.fill( Qt::transparent );
1290 pixmap.setDevicePixelRatio( devicePixelRatio );
1291
1292 QPainter painter;
1293 painter.begin( &pixmap );
1294
1295 painter.setRenderHint( QPainter::Antialiasing );
1296
1297 const QRectF rect( 0, 0, size.width(), size.height() );
1298
1299 // shameless eye candy - use a subtle gradient when drawing background
1300 painter.setPen( Qt::NoPen );
1301 QColor background1 = tempFormat.previewBackgroundColor();
1302 if ( ( background1.lightnessF() < 0.7 ) )
1303 {
1304 background1 = background1.darker( 125 );
1305 }
1306 else
1307 {
1308 background1 = background1.lighter( 125 );
1309 }
1310 QColor background2 = tempFormat.previewBackgroundColor();
1311 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1312 linearGrad.setColorAt( 0, background1 );
1313 linearGrad.setColorAt( 1, background2 );
1314 painter.setBrush( QBrush( linearGrad ) );
1315 if ( size.width() > 30 )
1316 {
1317 painter.drawRoundedRect( rect, 6, 6 );
1318 }
1319 else
1320 {
1321 // don't use rounded rect for small previews
1322 painter.drawRect( rect );
1323 }
1324 painter.setBrush( Qt::NoBrush );
1325 painter.setPen( Qt::NoPen );
1326 padding += 1; // move text away from background border
1327
1328 QgsRenderContext context;
1329 QgsMapToPixel newCoordXForm;
1330 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
1331 context.setMapToPixel( newCoordXForm );
1332
1333 if ( screen.isValid() )
1334 {
1335 screen.updateRenderContextForScreen( context );
1336 }
1337 else
1338 {
1339 QWidget *activeWindow = QApplication::activeWindow();
1340 if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr )
1341 {
1342 context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 );
1343 context.setDevicePixelRatio( screen->devicePixelRatio() );
1344 }
1345 else
1346 {
1347 context.setScaleFactor( 96.0 / 25.4 );
1348 context.setDevicePixelRatio( 1.0 );
1349 }
1350 }
1351
1354 context.setPainter( &painter );
1356
1357 // slightly inset text to account for buffer/background
1358 const double fontSize = context.convertToPainterUnits( tempFormat.size(), tempFormat.sizeUnit(), tempFormat.sizeMapUnitScale() );
1359 double xtrans = 0;
1360 if ( tempFormat.buffer().enabled() )
1361 xtrans = tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1362 ? fontSize * tempFormat.buffer().size() / 100
1363 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
1364 if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
1365 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1366
1367 double ytrans = 0.0;
1368 if ( tempFormat.buffer().enabled() )
1369 ytrans = std::max( ytrans, tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1370 ? fontSize * tempFormat.buffer().size() / 100
1371 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
1372 if ( tempFormat.background().enabled() )
1373 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1374
1375 const QStringList text = QStringList() << ( previewText.isEmpty() ? QObject::tr( "Aa" ) : previewText );
1376 const double textHeight = QgsTextRenderer::textHeight( context, tempFormat, text, Qgis::TextLayoutMode::Rectangle );
1377 QRectF textRect = rect;
1378 textRect.setLeft( xtrans + padding );
1379 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1380
1381 if ( textRect.width() > 2000 )
1382 textRect.setWidth( 2000 - 2 * padding );
1383
1384 const double bottom = textRect.height() / 2 + textHeight / 2;
1385 textRect.setTop( bottom - textHeight );
1386 textRect.setBottom( bottom );
1387
1388 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Center, text, context, tempFormat );
1389
1390 // draw border on top of text
1391 painter.setBrush( Qt::NoBrush );
1392 painter.setPen( QPen( tempFormat.previewBackgroundColor().darker( 150 ), 0 ) );
1393 if ( size.width() > 30 )
1394 {
1395 painter.drawRoundedRect( rect, 6, 6 );
1396 }
1397 else
1398 {
1399 // don't use rounded rect for small previews
1400 painter.drawRect( rect );
1401 }
1402 painter.end();
1403 return pixmap;
1404}
1405
1406QString QgsTextFormat::asCSS( double pointToPixelMultiplier ) const
1407{
1408 QString css;
1409
1410 switch ( lineHeightUnit() )
1411 {
1413 css += QStringLiteral( "line-height: %1%;" ).arg( lineHeight() * 100 );
1414 break;
1416 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() );
1417 break;
1419 // While the Qt documentation states pt unit type is supported, it's ignored, convert to px
1420 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * pointToPixelMultiplier );
1421 break;
1423 // While the Qt documentation states cm unit type is supported, it's ignored, convert to px
1424 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * 2.83464567 * pointToPixelMultiplier );
1425 break;
1430 break;
1431 }
1432 css += QStringLiteral( "color: rgba(%1,%2,%3,%4);" ).arg( color().red() ).arg( color().green() ).arg( color().blue() ).arg( QString::number( color().alphaF(), 'f', 4 ) );
1433 QFont f = toQFont();
1435 {
1436 f.setPointSizeF( size() / 0.352778 );
1437 }
1438 css += QgsFontUtils::asCSS( toQFont(), pointToPixelMultiplier );
1439
1440 return css;
1441}
1442
1444 : mPosition( position )
1445{}
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2704
@ Rectangle
Text within rectangle layout mode.
Definition qgis.h:2902
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4930
@ Normal
Normal.
Definition qgis.h:4931
Capitalization
String capitalization options.
Definition qgis.h:3389
@ AllSmallCaps
Force all characters to small caps.
Definition qgis.h:3397
@ MixedCase
Mixed case, ie no change.
Definition qgis.h:3390
@ SmallCaps
Mixed case small caps.
Definition qgis.h:3394
TextOrientation
Text orientations.
Definition qgis.h:2886
RenderUnit
Rendering size units.
Definition qgis.h:5183
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5187
@ Millimeters
Millimeters.
Definition qgis.h:5184
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5188
@ Unknown
Mixed or unknown units.
Definition qgis.h:5190
@ MapUnits
Map units.
Definition qgis.h:5185
@ Pixels
Pixels.
Definition qgis.h:5186
@ Inches
Inches.
Definition qgis.h:5189
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5191
@ Antialiasing
Use antialiasing while drawing.
Definition qgis.h:2756
@ Center
Center align.
Definition qgis.h:2944
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.
@ FontStretchFactor
Font stretch factor, since QGIS 3.24.
@ 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 an integer key value.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const final
Returns the set of any fields referenced by the active properties from the collection.
A container for the context for various read/write operations on 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.
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 setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterisation of content during renders is permitted.
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.
bool enabled() const
Returns whether the background is enabled.
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
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.
double size() const
Returns the size of the buffer.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
bool enabled() const
Returns whether the buffer is enabled.
Defines a tab position for a text format.
double position() const
Returns the tab position.
Tab(double position)
Constructor for a Tab at the specified position.
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.
bool hasNonDefaultCompositionMode() const
Returns true if any component of the font format requires a non-default composition mode.
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.
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.
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 dataset.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607