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