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