QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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}
218
220{
221 if ( !d->textNamedStyle.isEmpty() )
222 return d->textNamedStyle;
223
224 QFontDatabase db;
225 return db.styleString( d->textFont );
226}
227
228void QgsTextFormat::setNamedStyle( const QString &style )
229{
230 d->isValid = true;
231 QgsFontUtils::updateFontViaStyle( d->textFont, style );
232 d->textNamedStyle = style;
233}
234
236{
237 return d->forcedBold;
238}
239
241{
242 d->isValid = true;
243 d->textFont.setBold( forced );
244 d->forcedBold = true;
245}
246
248{
249 return d->forcedItalic;
250}
251
253{
254 d->isValid = true;
255 d->textFont.setItalic( forced );
256 d->forcedItalic = true;
257}
258
259QStringList QgsTextFormat::families() const
260{
261 return d->families;
262}
263
264void QgsTextFormat::setFamilies( const QStringList &families )
265{
266 d->isValid = true;
267 d->families = families;
268}
269
271{
272 return d->fontSizeUnits;
273}
274
276{
277 d->isValid = true;
278 d->fontSizeUnits = unit;
279}
280
282{
283 return d->fontSizeMapUnitScale;
284}
285
287{
288 d->isValid = true;
289 d->fontSizeMapUnitScale = scale;
290}
291
293{
294 return d->fontSize;
295}
296
297void QgsTextFormat::setSize( double size )
298{
299 d->isValid = true;
300 d->fontSize = size;
301}
302
304{
305 return d->textColor;
306}
307
308void QgsTextFormat::setColor( const QColor &color )
309{
310 d->isValid = true;
311 d->textColor = color;
312}
313
315{
316 return d->opacity;
317}
318
319void QgsTextFormat::multiplyOpacity( double opacityFactor )
320{
321 if ( qgsDoubleNear( opacityFactor, 1.0 ) )
322 return;
323 d->opacity *= opacityFactor;
324 mBufferSettings.setOpacity( mBufferSettings.opacity() * opacityFactor );
325 mShadowSettings.setOpacity( mShadowSettings.opacity() * opacityFactor );
326 mBackgroundSettings.setOpacity( mBackgroundSettings.opacity() * opacityFactor );
327 mMaskSettings.setOpacity( mMaskSettings.opacity() * opacityFactor );
328}
329
330void QgsTextFormat::setOpacity( double opacity )
331{
332 d->isValid = true;
333 d->opacity = opacity;
334}
335
337{
338 return d->textFont.stretch() > 0 ? d->textFont.stretch() : 100;
339}
340
342{
343 d->isValid = true;
344 d->textFont.setStretch( factor );
345}
346
347QPainter::CompositionMode QgsTextFormat::blendMode() const
348{
349 return d->blendMode;
350}
351
352void QgsTextFormat::setBlendMode( QPainter::CompositionMode mode )
353{
354 d->isValid = true;
355 d->blendMode = mode;
356}
357
359{
360 return d->multilineHeight;
361}
362
363void QgsTextFormat::setLineHeight( double height )
364{
365 d->isValid = true;
366 d->multilineHeight = height;
367}
368
370{
371 return d->multilineHeightUnits;
372}
373
375{
376 d->isValid = true;
377 d->multilineHeightUnits = unit;
378}
379
381{
382 return d->tabStopDistance;
383}
384
386{
387 d->isValid = true;
388 d->tabStopDistance = distance;
389}
390
392{
393 return d->tabStopDistanceUnits;
394}
395
397{
398 d->isValid = true;
399 d->tabStopDistanceUnits = unit;
400}
401
403{
404 return d->tabStopDistanceMapUnitScale;
405}
406
408{
409 d->isValid = true;
410 d->tabStopDistanceMapUnitScale = scale;
411}
412
414{
415 return d->orientation;
416}
417
419{
420 d->isValid = true;
421 d->orientation = orientation;
422}
423
425{
426 // bit of complexity here to maintain API..
427 return d->capitalization == Qgis::Capitalization::MixedCase && d->textFont.capitalization() != QFont::MixedCase
428 ? static_cast< Qgis::Capitalization >( d->textFont.capitalization() )
429 : d->capitalization ;
430}
431
433{
434 d->isValid = true;
435 d->capitalization = capitalization;
436#if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
437 d->textFont.setCapitalization( capitalization == Qgis::Capitalization::SmallCaps || capitalization == Qgis::Capitalization::AllSmallCaps ? QFont::SmallCaps : QFont::MixedCase );
438#else
439 d->textFont.setCapitalization( QFont::MixedCase );
440#endif
441}
442
444{
445 return d->allowHtmlFormatting;
446}
447
449{
450 d->isValid = true;
451 d->allowHtmlFormatting = allow;
452}
453
455{
456 return d->previewBackgroundColor;
457}
458
460{
461 d->isValid = true;
462 d->previewBackgroundColor = color;
463}
464
466{
467 d->isValid = true;
468 QFont appFont = QApplication::font();
469 mTextFontFamily = QgsApplication::fontManager()->processFontFamilyName( layer->customProperty( QStringLiteral( "labeling/fontFamily" ), QVariant( appFont.family() ) ).toString() );
470 QString fontFamily = mTextFontFamily;
471 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
472 {
473 // trigger to notify about font family substitution
474 mTextFontFound = false;
475
476 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
477 // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
478
479 // for now, do not use matching algorithm for substitution if family not found, substitute default instead
480 fontFamily = appFont.family();
481 }
482 else
483 {
484 mTextFontFound = true;
485 }
486
487 if ( !layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).isValid() )
488 {
489 d->fontSize = appFont.pointSizeF();
490 }
491 else
492 {
493 d->fontSize = layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).toDouble();
494 }
495
496 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString().isEmpty() )
497 {
498 d->fontSizeUnits = layer->customProperty( QStringLiteral( "labeling/fontSizeInMapUnits" ), QVariant( false ) ).toBool() ?
500 }
501 else
502 {
503 bool ok = false;
504 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString(), &ok );
505 if ( !ok )
506 d->fontSizeUnits = Qgis::RenderUnit::Points;
507 }
508 if ( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString().isEmpty() )
509 {
510 //fallback to older property
511 double oldMin = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMinScale" ), 0.0 ).toDouble();
512 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
513 double oldMax = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMaxScale" ), 0.0 ).toDouble();
514 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
515 }
516 else
517 {
518 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString() );
519 }
520 int fontWeight = layer->customProperty( QStringLiteral( "labeling/fontWeight" ) ).toInt();
521 bool fontItalic = layer->customProperty( QStringLiteral( "labeling/fontItalic" ) ).toBool();
522 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
523 d->textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( QStringLiteral( "labeling/namedStyle" ), QVariant( "" ) ).toString() );
524 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
525 d->capitalization = static_cast< Qgis::Capitalization >( layer->customProperty( QStringLiteral( "labeling/fontCapitals" ), QVariant( 0 ) ).toUInt() );
526 d->textFont.setUnderline( layer->customProperty( QStringLiteral( "labeling/fontUnderline" ) ).toBool() );
527 d->textFont.setStrikeOut( layer->customProperty( QStringLiteral( "labeling/fontStrikeout" ) ).toBool() );
528 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( QStringLiteral( "labeling/fontLetterSpacing" ), QVariant( 0.0 ) ).toDouble() );
529 d->textFont.setWordSpacing( layer->customProperty( QStringLiteral( "labeling/fontWordSpacing" ), QVariant( 0.0 ) ).toDouble() );
530 d->textColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/textColor" ), Qt::black, false );
531 if ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toString().isEmpty() )
532 {
533 d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/textTransp" ) ).toInt() / 100.0 ); //0 -100
534 }
535 else
536 {
537 d->opacity = ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toDouble() );
538 }
539 d->blendMode = QgsPainting::getCompositionMode(
540 static_cast< Qgis::BlendMode >( layer->customProperty( QStringLiteral( "labeling/blendMode" ), QVariant( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
541 d->multilineHeight = layer->customProperty( QStringLiteral( "labeling/multilineHeight" ), QVariant( 1.0 ) ).toDouble();
542 d->previewBackgroundColor = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/previewBkgrdColor" ), QColor( 255, 255, 255 ), false );
543
544 mBufferSettings.readFromLayer( layer );
545 mShadowSettings.readFromLayer( layer );
546 mBackgroundSettings.readFromLayer( layer );
547}
548
549void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
550{
551 d->isValid = true;
552 QDomElement textStyleElem;
553 if ( elem.nodeName() == QLatin1String( "text-style" ) )
554 textStyleElem = elem;
555 else
556 textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
557 QFont appFont = QApplication::font();
558 mTextFontFamily = QgsApplication::fontManager()->processFontFamilyName( textStyleElem.attribute( QStringLiteral( "fontFamily" ), appFont.family() ) );
559 QString fontFamily = mTextFontFamily;
560
561 const QDomElement familiesElem = textStyleElem.firstChildElement( QStringLiteral( "families" ) );
562 const QDomNodeList familyNodes = familiesElem.childNodes();
563 QStringList families;
564 families.reserve( familyNodes.size() );
565 for ( int i = 0; i < familyNodes.count(); ++i )
566 {
567 const QDomElement familyElem = familyNodes.at( i ).toElement();
568 families << familyElem.attribute( QStringLiteral( "name" ) );
569 }
570 d->families = families;
571
572 mTextFontFound = false;
573 QString matched;
574 if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
575 {
576 if ( QgsApplication::fontManager()->tryToDownloadFontFamily( mTextFontFamily, matched ) )
577 {
578 mTextFontFound = true;
579 }
580 else
581 {
582 for ( const QString &family : std::as_const( families ) )
583 {
584 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( family );
585 if ( QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) ||
586 QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
587 {
588 mTextFontFound = true;
589 fontFamily = processedFamily;
590 break;
591 }
592 }
593
594 if ( !mTextFontFound )
595 {
596 // couldn't even find a matching font in the backup list -- substitute default instead
597 fontFamily = appFont.family();
598 }
599 }
600 }
601 else
602 {
603 mTextFontFound = true;
604 }
605
606 if ( !mTextFontFound )
607 {
608 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( mTextFontFamily ) );
609 }
610
611 if ( textStyleElem.hasAttribute( QStringLiteral( "fontSize" ) ) )
612 {
613 d->fontSize = textStyleElem.attribute( QStringLiteral( "fontSize" ) ).toDouble();
614 }
615 else
616 {
617 d->fontSize = appFont.pointSizeF();
618 }
619
620 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeUnit" ) ) )
621 {
622 d->fontSizeUnits = textStyleElem.attribute( QStringLiteral( "fontSizeInMapUnits" ) ).toUInt() == 0 ? Qgis::RenderUnit::Points
624 }
625 else
626 {
627 d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "fontSizeUnit" ) ) );
628 }
629
630 if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeMapUnitScale" ) ) )
631 {
632 //fallback to older property
633 double oldMin = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
634 d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
635 double oldMax = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
636 d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
637 }
638 else
639 {
640 d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitScale" ) ) );
641 }
642 int fontWeight = textStyleElem.attribute( QStringLiteral( "fontWeight" ) ).toInt();
643 bool fontItalic = textStyleElem.attribute( QStringLiteral( "fontItalic" ) ).toInt();
644 d->textFont = QgsFontUtils::createFont( fontFamily, d->fontSize, fontWeight, fontItalic );
645 d->textFont.setPointSizeF( d->fontSize ); //double precision needed because of map units
646 d->textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( QStringLiteral( "namedStyle" ) ) );
647 QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
648 d->forcedBold = textStyleElem.attribute( QStringLiteral( "forcedBold" ) ).toInt();
649 d->forcedItalic = textStyleElem.attribute( QStringLiteral( "forcedItalic" ) ).toInt();
650 d->textFont.setUnderline( textStyleElem.attribute( QStringLiteral( "fontUnderline" ) ).toInt() );
651 d->textFont.setStrikeOut( textStyleElem.attribute( QStringLiteral( "fontStrikeout" ) ).toInt() );
652 d->textFont.setKerning( textStyleElem.attribute( QStringLiteral( "fontKerning" ), QStringLiteral( "1" ) ).toInt() );
653 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( QStringLiteral( "fontLetterSpacing" ), QStringLiteral( "0" ) ).toDouble() );
654 d->textFont.setWordSpacing( textStyleElem.attribute( QStringLiteral( "fontWordSpacing" ), QStringLiteral( "0" ) ).toDouble() );
655 d->textColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( Qt::black ) ) );
656 if ( !textStyleElem.hasAttribute( QStringLiteral( "textOpacity" ) ) )
657 {
658 d->opacity = ( 1 - textStyleElem.attribute( QStringLiteral( "textTransp" ) ).toInt() / 100.0 ); //0 -100
659 }
660 else
661 {
662 d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() );
663 }
664#ifdef HAS_KDE_QT5_FONT_STRETCH_FIX
665 d->textFont.setStretch( textStyleElem.attribute( QStringLiteral( "stretchFactor" ), QStringLiteral( "100" ) ).toInt() );
666#endif
667 d->orientation = QgsTextRendererUtils::decodeTextOrientation( textStyleElem.attribute( QStringLiteral( "textOrientation" ) ) );
668 d->previewBackgroundColor = QgsColorUtils::colorFromString( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( Qt::white ) ) );
669
670 d->blendMode = QgsPainting::getCompositionMode(
671 static_cast< Qgis::BlendMode >( textStyleElem.attribute( QStringLiteral( "blendMode" ), QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
672
673 if ( !textStyleElem.hasAttribute( QStringLiteral( "multilineHeight" ) ) )
674 {
675 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
676 d->multilineHeight = textFormatElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
677 }
678 else
679 {
680 d->multilineHeight = textStyleElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
681 }
682 bool ok = false;
683 d->multilineHeightUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "multilineHeightUnit" ), QStringLiteral( "percent" ) ), &ok );
684
685 d->tabStopDistance = textStyleElem.attribute( QStringLiteral( "tabStopDistance" ), QStringLiteral( "80" ) ).toDouble();
686 d->tabStopDistanceUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "tabStopDistanceUnit" ), QStringLiteral( "Point" ) ), &ok );
687 d->tabStopDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "tabStopDistanceMapUnitScale" ) ) );
688
689 if ( textStyleElem.hasAttribute( QStringLiteral( "capitalization" ) ) )
690 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( Qgis::Capitalization::MixedCase ) ) ).toInt() );
691 else
692 d->capitalization = static_cast< Qgis::Capitalization >( textStyleElem.attribute( QStringLiteral( "fontCapitals" ), QStringLiteral( "0" ) ).toUInt() );
693
694 if ( d->capitalization == Qgis::Capitalization::SmallCaps || d->capitalization == Qgis::Capitalization::AllSmallCaps )
695 d->textFont.setCapitalization( QFont::SmallCaps );
696
697 d->allowHtmlFormatting = textStyleElem.attribute( QStringLiteral( "allowHtml" ), QStringLiteral( "0" ) ).toInt();
698
699 if ( textStyleElem.firstChildElement( QStringLiteral( "text-buffer" ) ).isNull() )
700 {
701 mBufferSettings.readXml( elem );
702 }
703 else
704 {
705 mBufferSettings.readXml( textStyleElem );
706 }
707 if ( textStyleElem.firstChildElement( QStringLiteral( "text-mask" ) ).isNull() )
708 {
709 mMaskSettings.readXml( elem );
710 }
711 else
712 {
713 mMaskSettings.readXml( textStyleElem );
714 }
715 if ( textStyleElem.firstChildElement( QStringLiteral( "shadow" ) ).isNull() )
716 {
717 mShadowSettings.readXml( elem );
718 }
719 else
720 {
721 mShadowSettings.readXml( textStyleElem );
722 }
723 if ( textStyleElem.firstChildElement( QStringLiteral( "background" ) ).isNull() )
724 {
725 mBackgroundSettings.readXml( elem, context );
726 }
727 else
728 {
729 mBackgroundSettings.readXml( textStyleElem, context );
730 }
731
732 QDomElement ddElem = textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) );
733 if ( ddElem.isNull() )
734 {
735 ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
736 }
737 if ( !ddElem.isNull() )
738 {
739 d->mDataDefinedProperties.readXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
740 mBackgroundSettings.upgradeDataDefinedProperties( d->mDataDefinedProperties );
741 }
742 else
743 {
744 d->mDataDefinedProperties.clear();
745 }
746}
747
748QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
749{
750 // text style
751 QDomElement textStyleElem = doc.createElement( QStringLiteral( "text-style" ) );
752 textStyleElem.setAttribute( QStringLiteral( "fontFamily" ), d->textFont.family() );
753
754 QDomElement familiesElem = doc.createElement( QStringLiteral( "families" ) );
755 for ( const QString &family : std::as_const( d->families ) )
756 {
757 QDomElement familyElem = doc.createElement( QStringLiteral( "family" ) );
758 familyElem.setAttribute( QStringLiteral( "name" ), family );
759 familiesElem.appendChild( familyElem );
760 }
761 textStyleElem.appendChild( familiesElem );
762
763 textStyleElem.setAttribute( QStringLiteral( "namedStyle" ), QgsFontUtils::untranslateNamedStyle( d->textNamedStyle ) );
764 textStyleElem.setAttribute( QStringLiteral( "fontSize" ), d->fontSize );
765 textStyleElem.setAttribute( QStringLiteral( "fontSizeUnit" ), QgsUnitTypes::encodeUnit( d->fontSizeUnits ) );
766 textStyleElem.setAttribute( QStringLiteral( "fontSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->fontSizeMapUnitScale ) );
767 textStyleElem.setAttribute( QStringLiteral( "fontWeight" ), d->textFont.weight() );
768 textStyleElem.setAttribute( QStringLiteral( "fontItalic" ), d->textFont.italic() );
769 textStyleElem.setAttribute( QStringLiteral( "fontStrikeout" ), d->textFont.strikeOut() );
770 textStyleElem.setAttribute( QStringLiteral( "fontUnderline" ), d->textFont.underline() );
771 textStyleElem.setAttribute( QStringLiteral( "forcedBold" ), d->forcedBold );
772 textStyleElem.setAttribute( QStringLiteral( "forcedItalic" ), d->forcedItalic );
773 textStyleElem.setAttribute( QStringLiteral( "textColor" ), QgsColorUtils::colorToString( d->textColor ) );
774 textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), QgsColorUtils::colorToString( d->previewBackgroundColor ) );
775 textStyleElem.setAttribute( QStringLiteral( "fontLetterSpacing" ), d->textFont.letterSpacing() );
776 textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() );
777 textStyleElem.setAttribute( QStringLiteral( "fontKerning" ), d->textFont.kerning() );
778 textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity );
779#ifdef HAS_KDE_QT5_FONT_STRETCH_FIX
780 if ( d->textFont.stretch() > 0 )
781 textStyleElem.setAttribute( QStringLiteral( "stretchFactor" ), d->textFont.stretch() );
782#endif
783 textStyleElem.setAttribute( QStringLiteral( "textOrientation" ), QgsTextRendererUtils::encodeTextOrientation( d->orientation ) );
784 textStyleElem.setAttribute( QStringLiteral( "blendMode" ), static_cast< int >( QgsPainting::getBlendModeEnum( d->blendMode ) ) );
785 textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
786 textStyleElem.setAttribute( QStringLiteral( "multilineHeightUnit" ), QgsUnitTypes::encodeUnit( d->multilineHeightUnits ) );
787
788 textStyleElem.setAttribute( QStringLiteral( "tabStopDistance" ), d->tabStopDistance );
789 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceUnit" ), QgsUnitTypes::encodeUnit( d->tabStopDistanceUnits ) );
790 textStyleElem.setAttribute( QStringLiteral( "tabStopDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->tabStopDistanceMapUnitScale ) );
791
792 textStyleElem.setAttribute( QStringLiteral( "allowHtml" ), d->allowHtmlFormatting ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
793 textStyleElem.setAttribute( QStringLiteral( "capitalization" ), QString::number( static_cast< int >( d->capitalization ) ) );
794
795 QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
796 d->mDataDefinedProperties.writeXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
797
798 textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
799 textStyleElem.appendChild( mMaskSettings.writeXml( doc ) );
800 textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
801 textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
802 textStyleElem.appendChild( ddElem );
803
804 return textStyleElem;
805}
806
808{
809 //set both the mime color data, and the text (format settings).
810
811 QMimeData *mimeData = new QMimeData;
812 mimeData->setColorData( QVariant( color() ) );
813
814 QgsReadWriteContext rwContext;
815 QDomDocument textDoc;
816 QDomElement textElem = writeXml( textDoc, rwContext );
817 textDoc.appendChild( textElem );
818 mimeData->setText( textDoc.toString() );
819
820 return mimeData;
821}
822
824{
825 QgsTextFormat format;
826 format.setFont( font );
827 if ( font.pointSizeF() > 0 )
828 {
829 format.setSize( font.pointSizeF() );
831 }
832 else if ( font.pixelSize() > 0 )
833 {
834 format.setSize( font.pixelSize() );
836 }
837
838 return format;
839}
840
842{
843 QFont f = font();
844 switch ( sizeUnit() )
845 {
847 f.setPointSizeF( size() );
848 break;
849
851 f.setPointSizeF( size() * 2.83464567 );
852 break;
853
855 f.setPointSizeF( size() * 72 );
856 break;
857
859 f.setPixelSize( static_cast< int >( std::round( size() ) ) );
860 break;
861
866 // no meaning here
867 break;
868 }
869 return f;
870}
871
872QgsTextFormat QgsTextFormat::fromMimeData( const QMimeData *data, bool *ok )
873{
874 if ( ok )
875 *ok = false;
876 QgsTextFormat format;
877 if ( !data )
878 return format;
879
880 QString text = data->text();
881 if ( !text.isEmpty() )
882 {
883 QDomDocument doc;
884 QDomElement elem;
885 QgsReadWriteContext rwContext;
886
887 if ( doc.setContent( text ) )
888 {
889 elem = doc.documentElement();
890
891 format.readXml( elem, rwContext );
892 if ( ok )
893 *ok = true;
894 return format;
895 }
896 }
897 return format;
898}
899
901{
902 if ( d->blendMode != QPainter::CompositionMode_SourceOver )
903 return true;
904
905 if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
906 return true;
907
908 if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
909 return true;
910
911 if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
912 return true;
913
914 return false;
915}
916
918{
919 d->isValid = true;
920 return d->mDataDefinedProperties;
921}
922
924{
925 return d->mDataDefinedProperties;
926}
927
928QSet<QString> QgsTextFormat::referencedFields( const QgsRenderContext &context ) const
929{
930 QSet< QString > fields = d->mDataDefinedProperties.referencedFields( context.expressionContext(), true );
931 fields.unite( mBufferSettings.referencedFields( context ) );
932 fields.unite( mBackgroundSettings.referencedFields( context ) );
933 fields.unite( mShadowSettings.referencedFields( context ) );
934 fields.unite( mMaskSettings.referencedFields( context ) );
935 return fields;
936}
937
939{
940 d->isValid = true;
941 d->mDataDefinedProperties = collection;
942}
943
945{
946 d->isValid = true;
947 if ( !d->mDataDefinedProperties.hasActiveProperties() )
948 return;
949
950 QString ddFontFamily;
951 context.expressionContext().setOriginalValueVariable( d->textFont.family() );
952 QVariant exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::Family, context.expressionContext() );
953 if ( !QgsVariantUtils::isNull( exprVal ) )
954 {
955 QString family = exprVal.toString().trimmed();
957 if ( d->textFont.family() != family )
958 {
959 // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
960 // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
961 if ( QgsFontUtils::fontFamilyOnSystem( family ) )
962 {
963 ddFontFamily = family;
964 }
965 }
966 }
967
968 // data defined named font style?
969 QString ddFontStyle;
970 context.expressionContext().setOriginalValueVariable( d->textNamedStyle );
971 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStyle, context.expressionContext() );
972 if ( !QgsVariantUtils::isNull( exprVal ) )
973 {
974 QString fontstyle = exprVal.toString().trimmed();
975 ddFontStyle = fontstyle;
976 }
977
978 bool ddBold = false;
979 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Bold ) )
980 {
981 context.expressionContext().setOriginalValueVariable( d->textFont.bold() );
982 ddBold = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Bold, context.expressionContext(), false ) ;
983 }
984
985 bool ddItalic = false;
986 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Italic ) )
987 {
988 context.expressionContext().setOriginalValueVariable( d->textFont.italic() );
989 ddItalic = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Italic, context.expressionContext(), false );
990 }
991
992 // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
993 // (currently defaults to what has been read in from layer settings)
994 QFont newFont;
995 QFontDatabase fontDb;
996 QFont appFont = QApplication::font();
997 bool newFontBuilt = false;
998 if ( ddBold || ddItalic )
999 {
1000 // new font needs built, since existing style needs removed
1001 newFont = QgsFontUtils::createFont( !ddFontFamily.isEmpty() ? ddFontFamily : d->textFont.family() );
1002 newFontBuilt = true;
1003 newFont.setBold( ddBold );
1004 newFont.setItalic( ddItalic );
1005 }
1006 else if ( !ddFontStyle.isEmpty()
1007 && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1008 {
1009 if ( !ddFontFamily.isEmpty() )
1010 {
1011 // both family and style are different, build font from database
1012 QFont styledfont = fontDb.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
1013 if ( appFont != styledfont )
1014 {
1015 newFont = styledfont;
1016 newFontBuilt = true;
1017 }
1018 }
1019
1020 // update the font face style
1021 QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : d->textFont, ddFontStyle );
1022 }
1023 else if ( !ddFontFamily.isEmpty() )
1024 {
1025 if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
1026 {
1027 // just family is different, build font from database
1028 QFont styledfont = fontDb.font( ddFontFamily, d->textNamedStyle, appFont.pointSize() );
1029 if ( appFont != styledfont )
1030 {
1031 newFont = styledfont;
1032 newFontBuilt = true;
1033 }
1034 }
1035 else
1036 {
1037 newFont = QgsFontUtils::createFont( ddFontFamily );
1038 newFontBuilt = true;
1039 }
1040 }
1041
1042 if ( newFontBuilt )
1043 {
1044 // copy over existing font settings
1045 newFont.setUnderline( d->textFont.underline() );
1046 newFont.setStrikeOut( d->textFont.strikeOut() );
1047 newFont.setWordSpacing( d->textFont.wordSpacing() );
1048 newFont.setLetterSpacing( QFont::AbsoluteSpacing, d->textFont.letterSpacing() );
1049 d->textFont = newFont;
1050 }
1051
1052 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Underline ) )
1053 {
1054 context.expressionContext().setOriginalValueVariable( d->textFont.underline() );
1055 d->textFont.setUnderline( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Underline, context.expressionContext(), d->textFont.underline() ) );
1056 }
1057
1058 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Strikeout ) )
1059 {
1060 context.expressionContext().setOriginalValueVariable( d->textFont.strikeOut() );
1061 d->textFont.setStrikeOut( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Property::Strikeout, context.expressionContext(), d->textFont.strikeOut() ) );
1062 }
1063
1064 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Color ) )
1065 {
1067 d->textColor = d->mDataDefinedProperties.valueAsColor( QgsPalLayerSettings::Property::Color, context.expressionContext(), d->textColor );
1068 }
1069
1070 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::Size ) )
1071 {
1073 d->fontSize = d->mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Property::Size, context.expressionContext(), d->fontSize );
1074 }
1075
1076 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontSizeUnit, context.expressionContext() );
1077 if ( !QgsVariantUtils::isNull( exprVal ) )
1078 {
1079 QString units = exprVal.toString();
1080 if ( !units.isEmpty() )
1081 {
1082 bool ok;
1084 if ( ok )
1085 d->fontSizeUnits = res;
1086 }
1087 }
1088
1089 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontOpacity ) )
1090 {
1091 context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1092 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontOpacity, context.expressionContext(), d->opacity * 100 );
1093 if ( !QgsVariantUtils::isNull( val ) )
1094 {
1095 d->opacity = val.toDouble() / 100.0;
1096 }
1097 }
1098
1099#ifdef HAS_KDE_QT5_FONT_STRETCH_FIX
1100 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontStretchFactor ) )
1101 {
1102 context.expressionContext().setOriginalValueVariable( d->textFont.stretch() );
1103 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontStretchFactor, context.expressionContext(), d->textFont.stretch() );
1104 if ( !QgsVariantUtils::isNull( val ) )
1105 {
1106 d->textFont.setStretch( val.toInt() );
1107 }
1108 }
1109#endif
1110
1111 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TextOrientation ) )
1112 {
1113 const QString encoded = QgsTextRendererUtils::encodeTextOrientation( d->orientation );
1114 context.expressionContext().setOriginalValueVariable( encoded );
1115 d->orientation = QgsTextRendererUtils::decodeTextOrientation( d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TextOrientation, context.expressionContext(), encoded ).toString() );
1116 }
1117
1118 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontLetterSpacing ) )
1119 {
1120 context.expressionContext().setOriginalValueVariable( d->textFont.letterSpacing() );
1121 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontLetterSpacing, context.expressionContext(), d->textFont.letterSpacing() );
1122 if ( !QgsVariantUtils::isNull( val ) )
1123 {
1124 d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, val.toDouble() );
1125 }
1126 }
1127
1128 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontWordSpacing ) )
1129 {
1130 context.expressionContext().setOriginalValueVariable( d->textFont.wordSpacing() );
1131 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontWordSpacing, context.expressionContext(), d->textFont.wordSpacing() );
1132 if ( !QgsVariantUtils::isNull( val ) )
1133 {
1134 d->textFont.setWordSpacing( val.toDouble() );
1135 }
1136 }
1137
1138 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::TabStopDistance ) )
1139 {
1140 context.expressionContext().setOriginalValueVariable( d->tabStopDistance );
1141 const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::TabStopDistance, context.expressionContext(), d->tabStopDistance );
1142 if ( !QgsVariantUtils::isNull( val ) )
1143 {
1144 d->tabStopDistance = val.toDouble();
1145 }
1146 }
1147
1148 if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Property::FontBlendMode ) )
1149 {
1150 exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Property::FontBlendMode, context.expressionContext() );
1151 QString blendstr = exprVal.toString().trimmed();
1152 if ( !blendstr.isEmpty() )
1153 d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1154 }
1155
1156 mShadowSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1157 mBackgroundSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1158 mBufferSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1159 mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
1160}
1161
1162QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen )
1163{
1164 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
1165 QgsTextFormat tempFormat = format;
1166 QPixmap pixmap( size * devicePixelRatio );
1167 pixmap.fill( Qt::transparent );
1168 pixmap.setDevicePixelRatio( devicePixelRatio );
1169
1170 QPainter painter;
1171 painter.begin( &pixmap );
1172
1173 painter.setRenderHint( QPainter::Antialiasing );
1174
1175 const QRectF rect( 0, 0, size.width(), size.height() );
1176
1177 // shameless eye candy - use a subtle gradient when drawing background
1178 painter.setPen( Qt::NoPen );
1179 QColor background1 = tempFormat.previewBackgroundColor();
1180 if ( ( background1.lightnessF() < 0.7 ) )
1181 {
1182 background1 = background1.darker( 125 );
1183 }
1184 else
1185 {
1186 background1 = background1.lighter( 125 );
1187 }
1188 QColor background2 = tempFormat.previewBackgroundColor();
1189 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1190 linearGrad.setColorAt( 0, background1 );
1191 linearGrad.setColorAt( 1, background2 );
1192 painter.setBrush( QBrush( linearGrad ) );
1193 if ( size.width() > 30 )
1194 {
1195 painter.drawRoundedRect( rect, 6, 6 );
1196 }
1197 else
1198 {
1199 // don't use rounded rect for small previews
1200 painter.drawRect( rect );
1201 }
1202 painter.setBrush( Qt::NoBrush );
1203 painter.setPen( Qt::NoPen );
1204 padding += 1; // move text away from background border
1205
1206 QgsRenderContext context;
1207 QgsMapToPixel newCoordXForm;
1208 newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
1209 context.setMapToPixel( newCoordXForm );
1210
1211 if ( screen.isValid() )
1212 {
1213 screen.updateRenderContextForScreen( context );
1214 }
1215 else
1216 {
1217 QWidget *activeWindow = QApplication::activeWindow();
1218 if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr )
1219 {
1220 context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 );
1221 context.setDevicePixelRatio( screen->devicePixelRatio() );
1222 }
1223 else
1224 {
1225 context.setScaleFactor( 96.0 / 25.4 );
1226 context.setDevicePixelRatio( 1.0 );
1227 }
1228 }
1229
1230 context.setUseAdvancedEffects( true );
1232 context.setPainter( &painter );
1234
1235 // slightly inset text to account for buffer/background
1236 const double fontSize = context.convertToPainterUnits( tempFormat.size(), tempFormat.sizeUnit(), tempFormat.sizeMapUnitScale() );
1237 double xtrans = 0;
1238 if ( tempFormat.buffer().enabled() )
1239 xtrans = tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1240 ? fontSize * tempFormat.buffer().size() / 100
1241 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
1242 if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
1243 xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1244
1245 double ytrans = 0.0;
1246 if ( tempFormat.buffer().enabled() )
1247 ytrans = std::max( ytrans, tempFormat.buffer().sizeUnit() == Qgis::RenderUnit::Percentage
1248 ? fontSize * tempFormat.buffer().size() / 100
1249 : context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
1250 if ( tempFormat.background().enabled() )
1251 ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1252
1253 const QStringList text = QStringList() << ( previewText.isEmpty() ? QObject::tr( "Aa" ) : previewText );
1254 const double textHeight = QgsTextRenderer::textHeight( context, tempFormat, text, Qgis::TextLayoutMode::Rectangle );
1255 QRectF textRect = rect;
1256 textRect.setLeft( xtrans + padding );
1257 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1258
1259 if ( textRect.width() > 2000 )
1260 textRect.setWidth( 2000 - 2 * padding );
1261
1262 const double bottom = textRect.height() / 2 + textHeight / 2;
1263 textRect.setTop( bottom - textHeight );
1264 textRect.setBottom( bottom );
1265
1266 QgsTextRenderer::drawText( textRect, 0, Qgis::TextHorizontalAlignment::Center, text, context, tempFormat );
1267
1268 // draw border on top of text
1269 painter.setBrush( Qt::NoBrush );
1270 painter.setPen( QPen( tempFormat.previewBackgroundColor().darker( 150 ), 0 ) );
1271 if ( size.width() > 30 )
1272 {
1273 painter.drawRoundedRect( rect, 6, 6 );
1274 }
1275 else
1276 {
1277 // don't use rounded rect for small previews
1278 painter.drawRect( rect );
1279 }
1280 painter.end();
1281 return pixmap;
1282}
1283
1284QString QgsTextFormat::asCSS( double pointToPixelMultiplier ) const
1285{
1286 QString css;
1287
1288 switch ( lineHeightUnit() )
1289 {
1291 css += QStringLiteral( "line-height: %1%;" ).arg( lineHeight() * 100 );
1292 break;
1294 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() );
1295 break;
1297 // While the Qt documentation states pt unit type is supported, it's ignored, convert to px
1298 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * pointToPixelMultiplier );
1299 break;
1301 // While the Qt documentation states cm unit type is supported, it's ignored, convert to px
1302 css += QStringLiteral( "line-height: %1px;" ).arg( lineHeight() * 2.83464567 * pointToPixelMultiplier );
1303 break;
1308 break;
1309 }
1310 css += QStringLiteral( "color: rgba(%1,%2,%3,%4);" ).arg( color().red() ).arg( color().green() ).arg( color().blue() ).arg( QString::number( color().alphaF(), 'f', 4 ) );
1311 QFont f = toQFont();
1313 {
1314 f.setPointSizeF( size() / 0.352778 );
1315 }
1316 css += QgsFontUtils::asCSS( toQFont(), pointToPixelMultiplier );
1317
1318 return css;
1319}
@ Rectangle
Text within rectangle layout mode.
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4280
Capitalization
String capitalization options.
Definition qgis.h:2889
@ 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:2468
RenderUnit
Rendering size units.
Definition qgis.h:4494
@ 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:5465