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