QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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#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 || d->tabStopDistance != other.tabStopDistance()
90 || d->tabStopDistanceUnits != other.tabStopDistanceUnit()
91 || d->tabStopDistanceMapUnitScale != other.tabStopDistanceMapUnitScale()
92 || mBufferSettings != other.mBufferSettings
93 || mBackgroundSettings != other.mBackgroundSettings
94 || mShadowSettings != other.mShadowSettings
95 || mMaskSettings != other.mMaskSettings
96 || d->families != other.families()
97 || d->mDataDefinedProperties != other.dataDefinedProperties() )
98 return false;
99
100 return true;
101}
102
103bool QgsTextFormat::operator!=( const QgsTextFormat &other ) const
104{
105 return !( *this == other );
106}
107
109{
110 return d->isValid;
111}
112
114{
115 d->isValid = true;
116}
117
119{
120 d->isValid = true;
121 return mBufferSettings;
122}
123
125{
126 d->isValid = true;
127 mBufferSettings = bufferSettings;
128}
129
131{
132 d->isValid = true;
133 return mBackgroundSettings;
134}
135
137{
138 d->isValid = true;
139 mBackgroundSettings = backgroundSettings;
140}
141
143{
144 d->isValid = true;
145 return mShadowSettings;
146}
147
149{
150 d->isValid = true;
151 mShadowSettings = shadowSettings;
152}
153
155{
156 d->isValid = true;
157 return mMaskSettings;
158}
159
161{
162 d->isValid = true;
163 mMaskSettings = maskSettings;
164}
165
167{
168 return d->textFont;
169}
170
171QFont QgsTextFormat::scaledFont( const QgsRenderContext &context, double scaleFactor, bool *isZeroSize ) const
172{
173 if ( isZeroSize )
174 *isZeroSize = false;
175
176 QFont font = d->textFont;
177 if ( scaleFactor == 1 )
178 {
179 int fontPixelSize = QgsTextRenderer::sizeToPixel( d->fontSize, context, d->fontSizeUnits,
180 d->fontSizeMapUnitScale );
181 if ( fontPixelSize == 0 )
182 {
183 if ( isZeroSize )
184 *isZeroSize = true;
185 return QFont();
186 }
187
188 font.setPixelSize( fontPixelSize );
189 }
190 else
191 {
192 double fontPixelSize = context.convertToPainterUnits( d->fontSize, d->fontSizeUnits, d->fontSizeMapUnitScale );
193 if ( qgsDoubleNear( fontPixelSize, 0 ) )
194 {
195 if ( isZeroSize )
196 *isZeroSize = true;
197 return QFont();
198 }
199 const int roundedPixelSize = static_cast< int >( std::round( scaleFactor * fontPixelSize + 0.5 ) );
200 font.setPixelSize( roundedPixelSize );
201 }
202
203 font.setLetterSpacing( QFont::AbsoluteSpacing, context.convertToPainterUnits( d->textFont.letterSpacing(), d->fontSizeUnits, d->fontSizeMapUnitScale ) * scaleFactor );
204 font.setWordSpacing( context.convertToPainterUnits( d->textFont.wordSpacing(), d->fontSizeUnits, d->fontSizeMapUnitScale ) * scaleFactor * scaleFactor );
205
206 if ( d->capitalization == Qgis::Capitalization::SmallCaps
207 || d->capitalization == Qgis::Capitalization::AllSmallCaps )
208 font.setCapitalization( QFont::SmallCaps );
209
210 return font;
211}
212
213void QgsTextFormat::setFont( const QFont &font )
214{
215 d->isValid = true;
216 d->textFont = font;
217 d->originalFontFamily.clear();
218}
219
221{
222 if ( !d->textNamedStyle.isEmpty() )
223 return d->textNamedStyle;
224
225 QFontDatabase db;
226 return db.styleString( d->textFont );
227}
228
229void QgsTextFormat::setNamedStyle( const QString &style )
230{
231 d->isValid = true;
232 QgsFontUtils::updateFontViaStyle( d->textFont, style );
233 d->textNamedStyle = style;
234}
235
237{
238 return d->forcedBold;
239}
240
242{
243 d->isValid = true;
244 d->textFont.setBold( forced );
245 d->forcedBold = true;
246}
247
249{
250 return d->forcedItalic;
251}
252
254{
255 d->isValid = true;
256 d->textFont.setItalic( forced );
257 d->forcedItalic = true;
258}
259
260QStringList QgsTextFormat::families() const
261{
262 return d->families;
263}
264
265void QgsTextFormat::setFamilies( const QStringList &families )
266{
267 d->isValid = true;
268 d->families = families;
269}
270
272{
273 return d->fontSizeUnits;
274}
275
277{
278 d->isValid = true;
279 d->fontSizeUnits = unit;
280}
281
283{
284 return d->fontSizeMapUnitScale;
285}
286
288{
289 d->isValid = true;
290 d->fontSizeMapUnitScale = scale;
291}
292
294{
295 return d->fontSize;
296}
297
298void QgsTextFormat::setSize( double size )
299{
300 d->isValid = true;
301 d->fontSize = size;
302}
303
305{
306 return d->textColor;
307}
308
309void QgsTextFormat::setColor( const QColor &color )
310{
311 d->isValid = true;
312 d->textColor = color;
313}
314
316{
317 return d->opacity;
318}
319
320void QgsTextFormat::multiplyOpacity( double opacityFactor )
321{
322 if ( qgsDoubleNear( opacityFactor, 1.0 ) )
323 return;
324 d->opacity *= opacityFactor;
325 mBufferSettings.setOpacity( mBufferSettings.opacity() * opacityFactor );
326 mShadowSettings.setOpacity( mShadowSettings.opacity() * opacityFactor );
327 mBackgroundSettings.setOpacity( mBackgroundSettings.opacity() * opacityFactor );
328 mMaskSettings.setOpacity( mMaskSettings.opacity() * opacityFactor );
329}
330
331void QgsTextFormat::setOpacity( double opacity )
332{
333 d->isValid = true;
334 d->opacity = opacity;
335}
336
338{
339 return d->textFont.stretch() > 0 ? d->textFont.stretch() : 100;
340}
341
343{
344 d->isValid = true;
345 d->textFont.setStretch( factor );
346}
347
348QPainter::CompositionMode QgsTextFormat::blendMode() const
349{
350 return d->blendMode;
351}
352
353void QgsTextFormat::setBlendMode( QPainter::CompositionMode mode )
354{
355 d->isValid = true;
356 d->blendMode = mode;
357}
358
360{
361 return d->multilineHeight;
362}
363
364void QgsTextFormat::setLineHeight( double height )
365{
366 d->isValid = true;
367 d->multilineHeight = height;
368}
369
371{
372 return d->multilineHeightUnits;
373}
374
376{
377 d->isValid = true;
378 d->multilineHeightUnits = unit;
379}
380
382{
383 return d->tabStopDistance;
384}
385
387{
388 d->isValid = true;
389 d->tabStopDistance = distance;
390}
391
393{
394 return d->tabStopDistanceUnits;
395}
396
398{
399 d->isValid = true;
400 d->tabStopDistanceUnits = unit;
401}
402
404{
405 return d->tabStopDistanceMapUnitScale;
406}
407
409{
410 d->isValid = true;
411 d->tabStopDistanceMapUnitScale = scale;
412}
413
415{
416 return d->orientation;
417}
418
420{
421 d->isValid = true;
422 d->orientation = orientation;
423}
424
426{
427 // bit of complexity here to maintain API..
428 return d->capitalization == Qgis::Capitalization::MixedCase && d->textFont.capitalization() != QFont::MixedCase
429 ? static_cast< Qgis::Capitalization >( d->textFont.capitalization() )
430 : d->capitalization ;
431}
432
434{
435 d->isValid = true;
436 d->capitalization = capitalization;
437#if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
438 d->textFont.setCapitalization( capitalization == Qgis::Capitalization::SmallCaps || capitalization == Qgis::Capitalization::AllSmallCaps ? QFont::SmallCaps : QFont::MixedCase );
439#else
440 d->textFont.setCapitalization( QFont::MixedCase );
441#endif
442}
443
445{
446 return d->allowHtmlFormatting;
447}
448
450{
451 d->isValid = true;
452 d->allowHtmlFormatting = allow;
453}
454
456{
457 return d->previewBackgroundColor;
458}
459
461{
462 d->isValid = true;
463 d->previewBackgroundColor = color;
464}
465
467{
468 d->isValid = true;
469 QFont appFont = QApplication::font();
470 d->originalFontFamily = QgsApplication::fontManager()->processFontFamilyName( layer->customProperty( QStringLiteral( "labeling/fontFamily" ), QVariant( appFont.family() ) ).toString() );
471 mTextFontFamily = d->originalFontFamily;
472 QString fontFamily = mTextFontFamily;
473 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
474 {
475 // trigger to notify about font family substitution
476 mTextFontFound = false;
477
478 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
479 // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
480
481 // for now, do not use matching algorithm for substitution if family not found, substitute default instead
482 fontFamily = appFont.family();
483 }
484 else
485 {
486 mTextFontFound = true;
487 }
488
489 if ( !layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).isValid() )
490 {
491 d->fontSize = appFont.pointSizeF();
492 }
493 else
494 {
495 d->fontSize = layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).toDouble();
496 }
497
498 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString().isEmpty() )
499 {
500 d->fontSizeUnits = layer->customProperty( QStringLiteral( "labeling/fontSizeInMapUnits" ), QVariant( false ) ).toBool() ?
502 }
503 else
504 {
505 bool ok = false;
506 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString(), &ok );
507 if ( !ok )
508 d->fontSizeUnits = Qgis::RenderUnit::Points;
509 }
510 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString().isEmpty() )
511 {
512 //fallback to older property
513 double oldMin = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMinScale" ), 0.0 ).toDouble();
514 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
515 double oldMax = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMaxScale" ), 0.0 ).toDouble();
516 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
517 }
518 else
519 {
520 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString() );
521 }
522 int fontWeight = layer->customProperty( QStringLiteral( "labeling/fontWeight" ) ).toInt();
523 bool fontItalic = layer->customProperty( QStringLiteral( "labeling/fontItalic" ) ).toBool();
524 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
525 d->textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( QStringLiteral( "labeling/namedStyle" ), QVariant( "" ) ).toString() );
526 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
527 d->capitalization = static_cast< Qgis::Capitalization >( layer->customProperty( QStringLiteral( "labeling/fontCapitals" ), QVariant( 0 ) ).toUInt() );
528 d->textFont.setUnderline( layer->customProperty( QStringLiteral( "labeling/fontUnderline" ) ).toBool() );
529 d->textFont.setStrikeOut( layer->customProperty( QStringLiteral( "labeling/fontStrikeout" ) ).toBool() );
530 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( QStringLiteral( "labeling/fontLetterSpacing" ), QVariant( 0.0 ) ).toDouble() );
531 d->textFont.setWordSpacing( layer->customProperty( QStringLiteral( "labeling/fontWordSpacing" ), QVariant( 0.0 ) ).toDouble() );
532 d->textColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/textColor" ), Qt::black, false );
533 if ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toString().isEmpty() )
534 {
535 d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/textTransp" ) ).toInt() / 100.0 ); //0 -100
536 }
537 else
538 {
539 d->opacity = ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toDouble() );
540 }
541 d->blendMode = QgsPainting::getCompositionMode(
542 static_cast< Qgis::BlendMode >( layer->customProperty( QStringLiteral( "labeling/blendMode" ), QVariant( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
543 d->multilineHeight = layer->customProperty( QStringLiteral( "labeling/multilineHeight" ), QVariant( 1.0 ) ).toDouble();
544 d->previewBackgroundColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/previewBkgrdColor" ), QColor( 255, 255, 255 ), false );
545
546 mBufferSettings.readFromLayer( layer );
547 mShadowSettings.readFromLayer( layer );
548 mBackgroundSettings.readFromLayer( layer );
549}
550
551void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
552{
553 d->isValid = true;
554 QDomElement textStyleElem;
555 if ( elem.nodeName() == QLatin1String( "text-style" ) )
556 textStyleElem = elem;
557 else
558 textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
559 QFont appFont = QApplication::font();
560 d->originalFontFamily = QgsApplication::fontManager()->processFontFamilyName( textStyleElem.attribute( QStringLiteral( "fontFamily" ), appFont.family() ) );
561 mTextFontFamily = d->originalFontFamily;
562 QString fontFamily = mTextFontFamily;
563
564 const QDomElement familiesElem = textStyleElem.firstChildElement( QStringLiteral( "families" ) );
565 const QDomNodeList familyNodes = familiesElem.childNodes();
566 QStringList families;
567 families.reserve( familyNodes.size() );
568 for ( int i = 0; i < familyNodes.count(); ++i )
569 {
570 const QDomElement familyElem = familyNodes.at( i ).toElement();
571 families << familyElem.attribute( QStringLiteral( "name" ) );
572 }
573 d->families = families;
574
575 mTextFontFound = false;
576 QString matched;
577 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
578 {
579 if ( QgsApplication::fontManager()->tryToDownloadFontFamily( mTextFontFamily, matched ) )
580 {
581 mTextFontFound = true;
582 }
583 else
584 {
585 for ( const QString &family : std::as_const( families ) )
586 {
587 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( family );
588 if ( QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) ||
589 QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
590 {
591 mTextFontFound = true;
592 fontFamily = processedFamily;
593 break;
594 }
595 }
596
597 if ( !mTextFontFound )
598 {
599 // couldn't even find a matching font in the backup list -- substitute default instead
600 fontFamily = appFont.family();
601 }
602 }
603 }
604 else
605 {
606 mTextFontFound = true;
607 }
608
609 if ( !mTextFontFound )
610 {
611 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( mTextFontFamily ) );
612 }
613
614 if ( textStyleElem.hasAttribute( QStringLiteral( "fontSize" ) ) )
615 {
616 d->fontSize = textStyleElem.attribute( QStringLiteral( "fontSize" ) ).toDouble();
617 }
618 else
619 {
620 d->fontSize = appFont.pointSizeF();
621 }
622
623 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeUnit" ) ) )
624 {
625 d->fontSizeUnits = textStyleElem.attribute( QStringLiteral( "fontSizeInMapUnits" ) ).toUInt() == 0 ? Qgis::RenderUnit::Points
627 }
628 else
629 {
630 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "fontSizeUnit" ) ) );
631 }
632
633 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeMapUnitScale" ) ) )
634 {
635 //fallback to older property
636 double oldMin = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
637 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
638 double oldMax = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
639 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
640 }
641 else
642 {
643 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitScale" ) ) );
644 }
645 int fontWeight = textStyleElem.attribute( QStringLiteral( "fontWeight" ) ).toInt();
646 bool fontItalic = textStyleElem.attribute( QStringLiteral( "fontItalic" ) ).toInt();
647 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
648 d->textFont.setPointSizeF( d->fontSize ); //double precision needed because of map units
649 d->textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( QStringLiteral( "namedStyle" ) ) );
650 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
651 d->forcedBold = textStyleElem.attribute( QStringLiteral( "forcedBold" ) ).toInt();
652 d->forcedItalic = textStyleElem.attribute( QStringLiteral( "forcedItalic" ) ).toInt();
653 d->textFont.setUnderline( textStyleElem.attribute( QStringLiteral( "fontUnderline" ) ).toInt() );
654 d->textFont.setStrikeOut( textStyleElem.attribute( QStringLiteral( "fontStrikeout" ) ).toInt() );
655 d->textFont.setKerning( textStyleElem.attribute( QStringLiteral( "fontKerning" ), QStringLiteral( "1" ) ).toInt() );
656 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( QStringLiteral( "fontLetterSpacing" ), QStringLiteral( "0" ) ).toDouble() );
657 d->textFont.setWordSpacing( textStyleElem.attribute( QStringLiteral( "fontWordSpacing" ), QStringLiteral( "0" ) ).toDouble() );
658 d->textColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( Qt::black ) ) );
659 if ( !textStyleElem.hasAttribute( QStringLiteral( "textOpacity" ) ) )
660 {
661 d->opacity = ( 1 - textStyleElem.attribute( QStringLiteral( "textTransp" ) ).toInt() / 100.0 ); //0 -100
662 }
663 else
664 {
665 d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() );
666 }
667#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
668 d->textFont.setStretch( textStyleElem.attribute( QStringLiteral( "stretchFactor" ), QStringLiteral( "100" ) ).toInt() );
669#endif
670 d->orientation = QgsTextRendererUtils::decodeTextOrientation( textStyleElem.attribute( QStringLiteral( "textOrientation" ) ) );
671 d->previewBackgroundColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( Qt::white ) ) );
672
673 d->blendMode = QgsPainting::getCompositionMode(
674 static_cast< Qgis::BlendMode >( textStyleElem.attribute( QStringLiteral( "blendMode" ), QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
675
676 if ( !textStyleElem.hasAttribute( QStringLiteral( "multilineHeight" ) ) )
677 {
678 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
679 d->multilineHeight = textFormatElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
680 }
681 else
682 {
683 d->multilineHeight = textStyleElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
684 }
685 bool ok = false;
686 d->multilineHeightUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "multilineHeightUnit" ), QStringLiteral( "percent" ) ), &ok );
687
688 d->tabStopDistance = textStyleElem.attribute( QStringLiteral( "tabStopDistance" ), QStringLiteral( "80" ) ).toDouble();
689 d->tabStopDistanceUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "tabStopDistanceUnit" ), QStringLiteral( "Point" ) ), &ok );
690 d->tabStopDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "tabStopDistanceMapUnitScale" ) ) );
691
692 if ( textStyleElem.hasAttribute( QStringLiteral( "capitalization" ) ) )
693 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( Qgis::Capitalization::MixedCase ) ) ).toInt() );
694 else
695 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "fontCapitals" ), QStringLiteral( "0" ) ).toUInt() );
696
697 if ( d->capitalization == Qgis::Capitalization::SmallCaps || d->capitalization == Qgis::Capitalization::AllSmallCaps )
698 d->textFont.setCapitalization( QFont::SmallCaps );
699
700 d->allowHtmlFormatting = textStyleElem.attribute( QStringLiteral( "allowHtml" ), QStringLiteral( "0" ) ).toInt();
701
702 if ( textStyleElem.firstChildElement( QStringLiteral( "text-buffer" ) ).isNull() )
703 {
704 mBufferSettings.readXml( elem );
705 }
706 else
707 {
708 mBufferSettings.readXml( textStyleElem );
709 }
710 if ( textStyleElem.firstChildElement( QStringLiteral( "text-mask" ) ).isNull() )
711 {
712 mMaskSettings.readXml( elem );
713 }
714 else
715 {
716 mMaskSettings.readXml( textStyleElem );
717 }
718 if ( textStyleElem.firstChildElement( QStringLiteral( "shadow" ) ).isNull() )
719 {
720 mShadowSettings.readXml( elem );
721 }
722 else
723 {
724 mShadowSettings.readXml( textStyleElem );
725 }
726 if ( textStyleElem.firstChildElement( QStringLiteral( "background" ) ).isNull() )
727 {
728 mBackgroundSettings.readXml( elem, context );
729 }
730 else
731 {
732 mBackgroundSettings.readXml( textStyleElem, context );
733 }
734
735 QDomElement ddElem = textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) );
736 if ( ddElem.isNull() )
737 {
738 ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
739 }
740 if ( !ddElem.isNull() )
741 {
742 d->mDataDefinedProperties.readXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
743 mBackgroundSettings.upgradeDataDefinedProperties( d->mDataDefinedProperties );
744 }
745 else
746 {
747 d->mDataDefinedProperties.clear();
748 }
749}
750
751QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
752{
753 // text style
754 QDomElement textStyleElem = doc.createElement( QStringLiteral( "text-style" ) );
755 textStyleElem.setAttribute( QStringLiteral( "fontFamily" ), !d->originalFontFamily.isEmpty() ? d->originalFontFamily : d->textFont.family() );
756
757 QDomElement familiesElem = doc.createElement( QStringLiteral( "families" ) );
758 for ( const QString &family : std::as_const( d->families ) )
759 {
760 QDomElement familyElem = doc.createElement( QStringLiteral( "family" ) );
761 familyElem.setAttribute( QStringLiteral( "name" ), family );
762 familiesElem.appendChild( familyElem );
763 }
764 textStyleElem.appendChild( familiesElem );
765
766 textStyleElem.setAttribute( QStringLiteral( "namedStyle" ), QgsFontUtils::untranslateNamedStyle( d->textNamedStyle ) );
767 textStyleElem.setAttribute( QStringLiteral( "fontSize" ), d->fontSize );
768 textStyleElem.setAttribute( QStringLiteral( "fontSizeUnit" ), QgsUnitTypes::encodeUnit( d->fontSizeUnits ) );
769 textStyleElem.setAttribute( QStringLiteral( "fontSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->fontSizeMapUnitScale ) );
770 textStyleElem.setAttribute( QStringLiteral( "fontWeight" ), d->textFont.weight() );
771 textStyleElem.setAttribute( QStringLiteral( "fontItalic" ), d->textFont.italic() );
772 textStyleElem.setAttribute( QStringLiteral( "fontStrikeout" ), d->textFont.strikeOut() );
773 textStyleElem.setAttribute( QStringLiteral( "fontUnderline" ), d->textFont.underline() );
774 textStyleElem.setAttribute( QStringLiteral( "forcedBold" ), d->forcedBold );
775 textStyleElem.setAttribute( QStringLiteral( "forcedItalic" ), d->forcedItalic );
776 textStyleElem.setAttribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( d->textColor ) );
777 textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( d->previewBackgroundColor ) );
778 textStyleElem.setAttribute( QStringLiteral( "fontLetterSpacing" ), d->textFont.letterSpacing() );
779 textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() );
780 textStyleElem.setAttribute( QStringLiteral( "fontKerning" ), d->textFont.kerning() );
781 textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity );
782#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
783 if ( d->textFont.stretch() > 0 )
784 textStyleElem.setAttribute( QStringLiteral( "stretchFactor" ), d->textFont.stretch() );
785#endif
786 textStyleElem.setAttribute( QStringLiteral( "textOrientation" ), QgsTextRendererUtils::encodeTextOrientation( d->orientation ) );
787 textStyleElem.setAttribute( QStringLiteral( "blendMode" ), static_cast< int >( QgsPainting::getBlendModeEnum( d->blendMode ) ) );
788 textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
789 textStyleElem.setAttribute( QStringLiteral( "multilineHeightUnit" ), QgsUnitTypes::encodeUnit( d->multilineHeightUnits ) );
790
791 textStyleElem.setAttribute( QStringLiteral( "tabStopDistance" ), d->tabStopDistance );
792 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceUnit" ), QgsUnitTypes::encodeUnit( d->tabStopDistanceUnits ) );
793 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->tabStopDistanceMapUnitScale ) );
794
795 textStyleElem.setAttribute( QStringLiteral( "allowHtml" ), d->allowHtmlFormatting ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
796 textStyleElem.setAttribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( d->capitalization ) ) );
797
798 QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
799 d->mDataDefinedProperties.writeXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
800
801 textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
802 textStyleElem.appendChild( mMaskSettings.writeXml( doc ) );
803 textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
804 textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
805 textStyleElem.appendChild( ddElem );
806
807 return textStyleElem;
808}
809
811{
812 //set both the mime color data, and the text (format settings).
813
814 QMimeData *mimeData = new QMimeData;
815 mimeData->setColorData( QVariant( color() ) );
816
817 QgsReadWriteContext rwContext;
818 QDomDocument textDoc;
819 QDomElement textElem = writeXml( textDoc, rwContext );
820 textDoc.appendChild( textElem );
821 mimeData->setText( textDoc.toString() );
822
823 return mimeData;
824}
825
827{
828 QgsTextFormat format;
829 format.setFont( font );
830 if ( font.pointSizeF() > 0 )
831 {
832 format.setSize( font.pointSizeF() );
834 }
835 else if ( font.pixelSize() > 0 )
836 {
837 format.setSize( font.pixelSize() );
839 }
840
841 return format;
842}
843
845{
846 QFont f = font();
847 switch ( sizeUnit() )
848 {
850 f.setPointSizeF( size() );
851 break;
852
854 f.setPointSizeF( size() * 2.83464567 );
855 break;
856
858 f.setPointSizeF( size() * 72 );
859 break;
860
862 f.setPixelSize( static_cast< int >( std::round( size() ) ) );
863 break;
864
869 // no meaning here
870 break;
871 }
872 return f;
873}
874
875QgsTextFormat QgsTextFormat::fromMimeData( const QMimeData *data, bool *ok )
876{
877 if ( ok )
878 *ok = false;
879 QgsTextFormat format;
880 if ( !data )
881 return format;
882
883 QString text = data->text();
884 if ( !text.isEmpty() )
885 {
886 QDomDocument doc;
887 QDomElement elem;
888 QgsReadWriteContext rwContext;
889
890 if ( doc.setContent( text ) )
891 {
892 elem = doc.documentElement();
893
894 format.readXml( elem, rwContext );
895 if ( ok )
896 *ok = true;
897 return format;
898 }
899 }
900 return format;
901}
902
904{
905 if ( d->blendMode != QPainter::CompositionMode_SourceOver )
906 return true;
907
908 if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
909 return true;
910
911 if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
912 return true;
913
914 if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
915 return true;
916
917 return false;
918}
919
921{
922 d->isValid = true;
923 return d->mDataDefinedProperties;
924}
925
927{
928 return d->mDataDefinedProperties;
929}
930
931QSet<QString> QgsTextFormat::referencedFields( const QgsRenderContext &context ) const
932{
933 QSet< QString > fields = d->mDataDefinedProperties.referencedFields( context.expressionContext(), true );
934 fields.unite( mBufferSettings.referencedFields( context ) );
935 fields.unite( mBackgroundSettings.referencedFields( context ) );
936 fields.unite( mShadowSettings.referencedFields( context ) );
937 fields.unite( mMaskSettings.referencedFields( context ) );
938 return fields;
939}
940
942{
943 d->isValid = true;
944 d->mDataDefinedProperties = collection;
945}
946
948{
949 d->isValid = true;
950 if ( !d->mDataDefinedProperties.hasActiveProperties() )
951 return;
952
953 QString ddFontFamily;
954 context.expressionContext().setOriginalValueVariable( d->textFont.family() );
955 QVariant exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::Family, context.expressionContext() );
956 if ( !QgsVariantUtils::isNull( exprVal ) )
957 {
958 QString family = exprVal.toString().trimmed();
960 if ( d->textFont.family() != family )
961 {
962 // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
963 // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
964 if ( QgsFontUtils::fontFamilyOnSystem( family ) )
965 {
966 ddFontFamily = family;
967 }
968 }
969 }
970
971 // data defined named font style?
972 QString ddFontStyle;
973 context.expressionContext().setOriginalValueVariable( d->textNamedStyle );
974 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStyle, context.expressionContext() );
975 if ( !QgsVariantUtils::isNull( exprVal ) )
976 {
977 QString fontstyle = exprVal.toString().trimmed();
978 ddFontStyle = fontstyle;
979 }
980
981 bool ddBold = false;
982 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Bold ) )
983 {
984 context.expressionContext().setOriginalValueVariable( d->textFont.bold() );
985 ddBold = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Bold, context.expressionContext(), false ) ;
986 }
987
988 bool ddItalic = false;
989 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Italic ) )
990 {
991 context.expressionContext().setOriginalValueVariable( d->textFont.italic() );
992 ddItalic = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Italic, context.expressionContext(), false );
993 }
994
995 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
996 // (currently defaults to what has been read in from layer settings)
997 QFont newFont;
998 QFontDatabase fontDb;
999 QFont appFont = QApplication::font();
1000 bool newFontBuilt = false;
1001 if ( ddBold || ddItalic )
1002 {
1003 // new font needs built, since existing style needs removed
1004 newFont = QgsFontUtils::createFont( !ddFontFamily.isEmpty() ? ddFontFamily : d->textFont.family() );
1005 newFontBuilt = true;
1006 newFont.setBold( ddBold );
1007 newFont.setItalic( ddItalic );
1008 }
1009 else if ( !ddFontStyle.isEmpty()
1010 && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1011 {
1012 if ( !ddFontFamily.isEmpty() )
1013 {
1014 // both family and style are different, build font from database
1015 QFont styledfont = fontDb.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
1016 if ( appFont != styledfont )
1017 {
1018 newFont = styledfont;
1019 newFontBuilt = true;
1020 }
1021 }
1022
1023 // update the font face style
1024 QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : d->textFont, ddFontStyle );
1025 }
1026 else if ( !ddFontFamily.isEmpty() )
1027 {
1028 if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1029 {
1030 // just family is different, build font from database
1031 QFont styledfont = fontDb.font( ddFontFamily, d->textNamedStyle, appFont.pointSize() );
1032 if ( appFont != styledfont )
1033 {
1034 newFont = styledfont;
1035 newFontBuilt = true;
1036 }
1037 }
1038 else
1039 {
1040 newFont = QgsFontUtils::createFont( ddFontFamily );
1041 newFontBuilt = true;
1042 }
1043 }
1044
1045 if ( newFontBuilt )
1046 {
1047 // copy over existing font settings
1048 newFont.setUnderline( d->textFont.underline() );
1049 newFont.setStrikeOut( d->textFont.strikeOut() );
1050 newFont.setWordSpacing( d->textFont.wordSpacing() );
1051 newFont.setLetterSpacing( QFont::AbsoluteSpacing, d->textFont.letterSpacing() );
1052 d->textFont = newFont;
1053 }
1054
1055 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Underline ) )
1056 {
1057 context.expressionContext().setOriginalValueVariable( d->textFont.underline() );
1058 d->textFont.setUnderline( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Underline, context.expressionContext(), d->textFont.underline() ) );
1059 }
1060
1061 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Strikeout ) )
1062 {
1063 context.expressionContext().setOriginalValueVariable( d->textFont.strikeOut() );
1064 d->textFont.setStrikeOut( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Strikeout, context.expressionContext(), d->textFont.strikeOut() ) );
1065 }
1066
1067 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Color ) )
1068 {
1070 d->textColor = d->mDataDefinedProperties.valueAsColor( QgsPalLayerSettings::Property::Color, context.expressionContext(), d->textColor );
1071 }
1072
1073 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Size ) )
1074 {
1076 d->fontSize = d->mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Property::Size, context.expressionContext(), d->fontSize );
1077 }
1078
1079 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontSizeUnit, context.expressionContext() );
1080 if ( !QgsVariantUtils::isNull( exprVal ) )
1081 {
1082 QString units = exprVal.toString();
1083 if ( !units.isEmpty() )
1084 {
1085 bool ok;
1087 if ( ok )
1088 d->fontSizeUnits = res;
1089 }
1090 }
1091
1092 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontOpacity ) )
1093 {
1094 context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1095 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontOpacity, context.expressionContext(), d->opacity * 100 );
1096 if ( !QgsVariantUtils::isNull( val ) )
1097 {
1098 d->opacity = val.toDouble() / 100.0;
1099 }
1100 }
1101
1102#if defined(HAS_KDE_QT5_FONT_STRETCH_FIX) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
1103 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontStretchFactor ) )
1104 {
1105 context.expressionContext().setOriginalValueVariable( d->textFont.stretch() );
1106 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStretchFactor, context.expressionContext(), d->textFont.stretch() );
1107 if ( !QgsVariantUtils::isNull( val ) )
1108 {
1109 d->textFont.setStretch( val.toInt() );
1110 }
1111 }
1112#endif
1113
1114 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TextOrientation ) )
1115 {
1116 const QString encoded = QgsTextRendererUtils::encodeTextOrientation( d->orientation );
1117 context.expressionContext().setOriginalValueVariable( encoded );
1118 d->orientation = QgsTextRendererUtils::decodeTextOrientation( d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TextOrientation, context.expressionContext(), encoded ).toString() );
1119 }
1120
1121 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontLetterSpacing ) )
1122 {
1123 context.expressionContext().setOriginalValueVariable( d->textFont.letterSpacing() );
1124 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontLetterSpacing, context.expressionContext(), d->textFont.letterSpacing() );
1125 if ( !QgsVariantUtils::isNull( val ) )
1126 {
1127 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, val.toDouble() );
1128 }
1129 }
1130
1131 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontWordSpacing ) )
1132 {
1133 context.expressionContext().setOriginalValueVariable( d->textFont.wordSpacing() );
1134 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontWordSpacing, context.expressionContext(), d->textFont.wordSpacing() );
1135 if ( !QgsVariantUtils::isNull( val ) )
1136 {
1137 d->textFont.setWordSpacing( val.toDouble() );
1138 }
1139 }
1140
1141 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TabStopDistance ) )
1142 {
1143 context.expressionContext().setOriginalValueVariable( d->tabStopDistance );
1144 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TabStopDistance, context.expressionContext(), d->tabStopDistance );
1145 if ( !QgsVariantUtils::isNull( val ) )
1146 {
1147 d->tabStopDistance = val.toDouble();
1148 }
1149 }
1150
1151 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontBlendMode ) )
1152 {
1153 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontBlendMode, context.expressionContext() );
1154 QString blendstr = exprVal.toString().trimmed();
1155 if ( !blendstr.isEmpty() )
1156 d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1157 }
1158
1159 mShadowSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1160 mBackgroundSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1161 mBufferSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1162 mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1163}
1164
1165QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen )
1166{
1167 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
1168 QgsTextFormat tempFormat = format;
1169 QPixmap pixmap( size * devicePixelRatio );
1170 pixmap.fill( Qt::transparent );
1171 pixmap.setDevicePixelRatio( devicePixelRatio );
1172
1173 QPainter painter;
1174 painter.begin( &pixmap );
1175
1176 painter.setRenderHint( QPainter::Antialiasing );
1177
1178 const QRectF rect( 0, 0, size.width(), size.height() );
1179
1180 // shameless eye candy - use a subtle gradient when drawing background
1181 painter.setPen( Qt::NoPen );
1182 QColor background1 = tempFormat.previewBackgroundColor();
1183 if ( ( background1.lightnessF() < 0.7 ) )
1184 {
1185 background1 = background1.darker( 125 );
1186 }
1187 else
1188 {
1189 background1 = background1.lighter( 125 );
1190 }
1191 QColor background2 = tempFormat.previewBackgroundColor();
1192 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1193 linearGrad.setColorAt( 0, background1 );
1194 linearGrad.setColorAt( 1, background2 );
1195 painter.setBrush( QBrush( linearGrad ) );
1196 if ( size.width() > 30 )
1197 {
1198 painter.drawRoundedRect( rect, 6, 6 );
1199 }
1200 else
1201 {
1202 // don't use rounded rect for small previews
1203 painter.drawRect( rect );
1204 }
1205 painter.setBrush( Qt::NoBrush );
1206 painter.setPen( Qt::NoPen );
1207 padding += 1; // move text away from background border
1208
1209 QgsRenderContext context;
1210 QgsMapToPixel newCoordXForm;
1211 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
1212 context.setMapToPixel( newCoordXForm );
1213
1214 if ( screen.isValid() )
1215 {
1216 screen.updateRenderContextForScreen( context );
1217 }
1218 else
1219 {
1220 QWidget *activeWindow = QApplication::activeWindow();
1221 if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr )
1222 {
1223 context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 );
1224 context.setDevicePixelRatio( screen->devicePixelRatio() );
1225 }
1226 else
1227 {
1228 context.setScaleFactor( 96.0 / 25.4 );
1229 context.setDevicePixelRatio( 1.0 );
1230 }
1231 }
1232
1233 context.setUseAdvancedEffects( true );
1235 context.setPainter( &painter );
1237
1238 // slightly inset text to account for buffer/background
1239 const double fontSize = context.convertToPainterUnits( tempFormat.size(), tempFormat.sizeUnit(), tempFormat.sizeMapUnitScale() );
1240 double xtrans = 0;
1241 if ( tempFormat.buffer().enabled() )
1242 xtrans = tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1243 ? fontSize * tempFormat.buffer().size() / 100
1244 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
1245 if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
1246 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1247
1248 double ytrans = 0.0;
1249 if ( tempFormat.buffer().enabled() )
1250 ytrans = std::max( ytrans, tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1251 ? fontSize * tempFormat.buffer().size() / 100
1252 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
1253 if ( tempFormat.background().enabled() )
1254 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1255
1256 const QStringList text = QStringList() << ( previewText.isEmpty() ? QObject::tr( "Aa" ) : previewText );
1257 const double textHeight = QgsTextRenderer::textHeight( context, tempFormat, text, Qgis::TextLayoutMode::Rectangle );
1258 QRectF textRect = rect;
1259 textRect.setLeft( xtrans + padding );
1260 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1261
1262 if ( textRect.width() > 2000 )
1263 textRect.setWidth( 2000 - 2 * padding );
1264
1265 const double bottom = textRect.height() / 2 + textHeight / 2;
1266 textRect.setTop( bottom - textHeight );
1267 textRect.setBottom( bottom );
1268
1269 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Center, text, context, tempFormat );
1270
1271 // draw border on top of text
1272 painter.setBrush( Qt::NoBrush );
1273 painter.setPen( QPen( tempFormat.previewBackgroundColor().darker( 150 ), 0 ) );
1274 if ( size.width() > 30 )
1275 {
1276 painter.drawRoundedRect( rect, 6, 6 );
1277 }
1278 else
1279 {
1280 // don't use rounded rect for small previews
1281 painter.drawRect( rect );
1282 }
1283 painter.end();
1284 return pixmap;
1285}
1286
1287QString QgsTextFormat::asCSS( double pointToPixelMultiplier ) const
1288{
1289 QString css;
1290
1291 switch ( lineHeightUnit() )
1292 {
1294 css += QStringLiteral( "line-height: %1%;" ).arg( lineHeight() * 100 );
1295 break;
1297 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() );
1298 break;
1300 // While the Qt documentation states pt unit type is supported, it's ignored, convert to px
1301 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * pointToPixelMultiplier );
1302 break;
1304 // While the Qt documentation states cm unit type is supported, it's ignored, convert to px
1305 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * 2.83464567 * pointToPixelMultiplier );
1306 break;
1311 break;
1312 }
1313 css += QStringLiteral( "color: rgba(%1,%2,%3,%4);" ).arg( color().red() ).arg( color().green() ).arg( color().blue() ).arg( QString::number( color().alphaF(), 'f', 4 ) );
1314 QFont f = toQFont();
1316 {
1317 f.setPointSizeF( size() / 0.352778 );
1318 }
1319 css += QgsFontUtils::asCSS( toQFont(), pointToPixelMultiplier );
1320
1321 return css;
1322}
@ Rectangle
Text within rectangle layout mode.
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4586
Capitalization
String capitalization options.
Definition qgis.h:3132
@ AllSmallCaps
Force all characters to small caps.
@ MixedCase
Mixed case, ie no change.
@ SmallCaps
Mixed case small caps.
TextOrientation
Text orientations.
Definition qgis.h:2684
RenderUnit
Rendering size units.
Definition qgis.h:4839
@ 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.
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.
@ 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.
@ TabStopDistance
Tab stop distance, since QGIS 3.38.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the labeling property definitions.
A grouped map of multiple QgsProperty objects, each referenced by 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.
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.
double tabStopDistance() const
Returns the distance for tab stops.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
int stretchFactor() const
Returns the text's stretch factor.
void updateDataDefinedProperties(QgsRenderContext &context)
Updates the format by evaluating current values of data defined properties.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the format's property collection, used for data defined overrides.
void setStretchFactor(int factor)
Sets the text's stretch factor.
void setShadow(const QgsTextShadowSettings &shadowSettings)
Sets the text's drop shadow settings.
void setMask(const QgsTextMaskSettings &maskSettings)
Sets the text's masking settings.
void setPreviewBackgroundColor(const QColor &color)
Sets the background color that text will be rendered on for previews.
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0, bool *isZeroSize=nullptr) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
void setTabStopDistanceUnit(Qgis::RenderUnit unit)
Sets the unit used for the tab stop distance.
void multiplyOpacity(double opacityFactor)
Multiply opacity by opacityFactor.
void setOpacity(double opacity)
Sets the text's opacity.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
bool operator==(const QgsTextFormat &other) const
void setAllowHtmlFormatting(bool allow)
Sets whether text should be treated as a HTML document and HTML tags should be used for formatting th...
void setLineHeightUnit(Qgis::RenderUnit unit)
Sets the unit for the line height for text.
Qgis::RenderUnit lineHeightUnit() const
Returns the units for the line height for text.
Qgis::Capitalization capitalization() const
Returns the text capitalization style.
bool forcedItalic() const
Returns true if the format is set to force an italic style.
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
void setForcedBold(bool forced)
Sets whether the format is set to force a bold style.
bool isValid() const
Returns true if the format is valid.
void setTabStopDistanceMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the tab stop distance.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text's buffer settings.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
Qgis::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
static QgsTextFormat fromQFont(const QFont &font)
Returns a text format matching the settings from an input font.
void setValid()
Sets the format to a valid state, without changing any of the default format settings.
bool allowHtmlFormatting() const
Returns true if text should be treated as a HTML document and HTML tags should be used for formatting...
QFont toQFont() const
Returns a QFont matching the relevant settings from this text format.
bool operator!=(const QgsTextFormat &other) const
double opacity() const
Returns the text's opacity.
void setTabStopDistance(double distance)
Sets the distance for tab stops.
Qgis::TextOrientation orientation() const
Returns the orientation of the text.
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
double size() const
Returns the size for rendered text.
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
QgsTextFormat & operator=(const QgsTextFormat &other)
static QPixmap textFormatPreviewPixmap(const QgsTextFormat &format, QSize size, const QString &previewText=QString(), int padding=0, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a pixmap preview for a text format.
Qgis::RenderUnit tabStopDistanceUnit() const
Returns the units for the tab stop distance.
bool forcedBold() const
Returns true if the format is set to force a bold style.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
QString asCSS(double pointToPixelMultiplier=1.0) const
Returns a CSS string representing the specified text format as closely as possible.
QMimeData * toMimeData() const
Returns new mime data representing the text format settings.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the size.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QColor color() const
Returns the color that text will be rendered in.
QFont font() const
Returns the font used for rendering text.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer's custom properties (for QGIS 2.x projects).
QgsMapUnitScale tabStopDistanceMapUnitScale() const
Returns the map unit scale object for the tab stop distance.
QColor previewBackgroundColor() const
Returns the background color for text previews.
bool containsAdvancedEffects() const
Returns true if any component of the font format requires advanced effects such as blend modes,...
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
void setLineHeight(double height)
Sets the line height for text.
Container for settings relating to a selective masking around a text.
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:5917