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