1 /***************************************************************************
2 qgstextbuffersettings.cpp
3 -----------------
4 begin : May 2020
5 copyright : (C) Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgstextbuffersettings.h"
18 #include "qgstextrenderer_p.h"
19 #include "qgsvectorlayer.h"
20 #include "qgspallabeling.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgspainting.h"
23 #include "qgspainteffectregistry.h"
24 #include "qgstextrendererutils.h"
25
QgsTextBufferSettings()26 QgsTextBufferSettings::QgsTextBufferSettings()
27 {
28 d = new QgsTextBufferSettingsPrivate();
29 }
30
QgsTextBufferSettings(const QgsTextBufferSettings & other)31 QgsTextBufferSettings::QgsTextBufferSettings( const QgsTextBufferSettings &other ) //NOLINT
32 : d( other.d )
33 {
34 }
35
operator =(const QgsTextBufferSettings & other)36 QgsTextBufferSettings &QgsTextBufferSettings::operator=( const QgsTextBufferSettings &other ) //NOLINT
37 {
38 d = other.d;
39 return *this;
40 }
41
~QgsTextBufferSettings()42 QgsTextBufferSettings::~QgsTextBufferSettings() //NOLINT
43 {
44
45 }
46
operator ==(const QgsTextBufferSettings & other) const47 bool QgsTextBufferSettings::operator==( const QgsTextBufferSettings &other ) const
48 {
49 if ( d->enabled != other.enabled()
50 || d->size != other.size()
51 || d->sizeUnit != other.sizeUnit()
52 || d->sizeMapUnitScale != other.sizeMapUnitScale()
53 || d->color != other.color()
54 || d->opacity != other.opacity()
55 || d->fillBufferInterior != other.fillBufferInterior()
56 || d->joinStyle != other.joinStyle()
57 || d->blendMode != other.blendMode() )
58 return false;
59
60 if ( static_cast< bool >( d->paintEffect ) != static_cast< bool >( other.paintEffect() )
61 || ( d->paintEffect && d->paintEffect->properties() != other.paintEffect()->properties() ) )
62 return false;
63
64 return true;
65 }
66
operator !=(const QgsTextBufferSettings & other) const67 bool QgsTextBufferSettings::operator!=( const QgsTextBufferSettings &other ) const
68 {
69 return !( *this == other );
70 }
71
enabled() const72 bool QgsTextBufferSettings::enabled() const
73 {
74 return d->enabled;
75 }
76
setEnabled(bool enabled)77 void QgsTextBufferSettings::setEnabled( bool enabled )
78 {
79 d->enabled = enabled;
80 }
81
size() const82 double QgsTextBufferSettings::size() const
83 {
84 return d->size;
85 }
86
setSize(double size)87 void QgsTextBufferSettings::setSize( double size )
88 {
89 d->size = size;
90 }
91
sizeUnit() const92 QgsUnitTypes::RenderUnit QgsTextBufferSettings::sizeUnit() const
93 {
94 return d->sizeUnit;
95 }
96
setSizeUnit(QgsUnitTypes::RenderUnit unit)97 void QgsTextBufferSettings::setSizeUnit( QgsUnitTypes::RenderUnit unit )
98 {
99 d->sizeUnit = unit;
100 }
101
sizeMapUnitScale() const102 QgsMapUnitScale QgsTextBufferSettings::sizeMapUnitScale() const
103 {
104 return d->sizeMapUnitScale;
105 }
106
setSizeMapUnitScale(const QgsMapUnitScale & scale)107 void QgsTextBufferSettings::setSizeMapUnitScale( const QgsMapUnitScale &scale )
108 {
109 d->sizeMapUnitScale = scale;
110 }
111
color() const112 QColor QgsTextBufferSettings::color() const
113 {
114 return d->color;
115 }
116
setColor(const QColor & color)117 void QgsTextBufferSettings::setColor( const QColor &color )
118 {
119 d->color = color;
120 }
121
fillBufferInterior() const122 bool QgsTextBufferSettings::fillBufferInterior() const
123 {
124 return d->fillBufferInterior;
125 }
126
setFillBufferInterior(bool fill)127 void QgsTextBufferSettings::setFillBufferInterior( bool fill )
128 {
129 d->fillBufferInterior = fill;
130 }
131
opacity() const132 double QgsTextBufferSettings::opacity() const
133 {
134 return d->opacity;
135 }
136
setOpacity(double opacity)137 void QgsTextBufferSettings::setOpacity( double opacity )
138 {
139 d->opacity = opacity;
140 }
141
joinStyle() const142 Qt::PenJoinStyle QgsTextBufferSettings::joinStyle() const
143 {
144 return d->joinStyle;
145 }
146
setJoinStyle(Qt::PenJoinStyle style)147 void QgsTextBufferSettings::setJoinStyle( Qt::PenJoinStyle style )
148 {
149 d->joinStyle = style;
150 }
151
blendMode() const152 QPainter::CompositionMode QgsTextBufferSettings::blendMode() const
153 {
154 return d->blendMode;
155 }
156
setBlendMode(QPainter::CompositionMode mode)157 void QgsTextBufferSettings::setBlendMode( QPainter::CompositionMode mode )
158 {
159 d->blendMode = mode;
160 }
161
paintEffect() const162 const QgsPaintEffect *QgsTextBufferSettings::paintEffect() const
163 {
164 return d->paintEffect.get();
165 }
166
setPaintEffect(QgsPaintEffect * effect)167 void QgsTextBufferSettings::setPaintEffect( QgsPaintEffect *effect )
168 {
169 d->paintEffect.reset( effect );
170 }
171
updateDataDefinedProperties(QgsRenderContext & context,const QgsPropertyCollection & properties)172 void QgsTextBufferSettings::updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties )
173 {
174 if ( properties.isActive( QgsPalLayerSettings::BufferDraw ) )
175 {
176 context.expressionContext().setOriginalValueVariable( d->enabled );
177 d->enabled = properties.valueAsBool( QgsPalLayerSettings::BufferDraw, context.expressionContext(), d->enabled );
178 }
179
180 if ( properties.isActive( QgsPalLayerSettings::BufferSize ) )
181 {
182 context.expressionContext().setOriginalValueVariable( d->size );
183 d->size = properties.valueAsDouble( QgsPalLayerSettings::BufferSize, context.expressionContext(), d->size );
184 }
185
186 QVariant exprVal = properties.value( QgsPalLayerSettings::BufferUnit, context.expressionContext() );
187 if ( !exprVal.isNull() )
188 {
189 const QString units = exprVal.toString();
190 if ( !units.isEmpty() )
191 {
192 bool ok;
193 const QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
194 if ( ok )
195 d->sizeUnit = res;
196 }
197 }
198
199 if ( properties.isActive( QgsPalLayerSettings::BufferOpacity ) )
200 {
201 context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
202 const QVariant val = properties.value( QgsPalLayerSettings::BufferOpacity, context.expressionContext(), d->opacity * 100 );
203 if ( !val.isNull() )
204 {
205 d->opacity = val.toDouble() / 100.0;
206 }
207 }
208
209 if ( properties.isActive( QgsPalLayerSettings::BufferColor ) )
210 {
211 context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->color ) );
212 d->color = properties.valueAsColor( QgsPalLayerSettings::BufferColor, context.expressionContext(), d->color );
213 }
214
215 if ( properties.isActive( QgsPalLayerSettings::BufferBlendMode ) )
216 {
217 exprVal = properties.value( QgsPalLayerSettings::BufferBlendMode, context.expressionContext() );
218 const QString blendstr = exprVal.toString().trimmed();
219 if ( !blendstr.isEmpty() )
220 d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
221 }
222
223 if ( properties.isActive( QgsPalLayerSettings::BufferJoinStyle ) )
224 {
225 exprVal = properties.value( QgsPalLayerSettings::BufferJoinStyle, context.expressionContext() );
226 const QString joinstr = exprVal.toString().trimmed();
227 if ( !joinstr.isEmpty() )
228 {
229 d->joinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( joinstr );
230 }
231 }
232 }
233
referencedFields(const QgsRenderContext &) const234 QSet<QString> QgsTextBufferSettings::referencedFields( const QgsRenderContext & ) const
235 {
236 return QSet< QString >(); // nothing for now
237 }
238
readFromLayer(QgsVectorLayer * layer)239 void QgsTextBufferSettings::readFromLayer( QgsVectorLayer *layer )
240 {
241 // text buffer
242 const double bufSize = layer->customProperty( QStringLiteral( "labeling/bufferSize" ), QVariant( 0.0 ) ).toDouble();
243
244 // fix for buffer being keyed off of just its size in the past (<2.0)
245 const QVariant drawBuffer = layer->customProperty( QStringLiteral( "labeling/bufferDraw" ), QVariant() );
246 if ( drawBuffer.isValid() )
247 {
248 d->enabled = drawBuffer.toBool();
249 d->size = bufSize;
250 }
251 else if ( bufSize != 0.0 )
252 {
253 d->enabled = true;
254 d->size = bufSize;
255 }
256 else
257 {
258 // keep bufferSize at new 1.0 default
259 d->enabled = false;
260 }
261
262 if ( layer->customProperty( QStringLiteral( "labeling/bufferSizeUnits" ) ).toString().isEmpty() )
263 {
264 const bool bufferSizeInMapUnits = layer->customProperty( QStringLiteral( "labeling/bufferSizeInMapUnits" ) ).toBool();
265 d->sizeUnit = bufferSizeInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
266 }
267 else
268 {
269 d->sizeUnit = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/bufferSizeUnits" ) ).toString() );
270 }
271
272 if ( layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitScale" ) ).toString().isEmpty() )
273 {
274 //fallback to older property
275 const double oldMin = layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitMinScale" ), 0.0 ).toDouble();
276 d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
277 const double oldMax = layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitMaxScale" ), 0.0 ).toDouble();
278 d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
279 }
280 else
281 {
282 d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitScale" ) ).toString() );
283 }
284 d->color = QgsTextRendererUtils::readColor( layer, QStringLiteral( "labeling/bufferColor" ), Qt::white, false );
285 if ( layer->customProperty( QStringLiteral( "labeling/bufferOpacity" ) ).toString().isEmpty() )
286 {
287 d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/bufferTransp" ) ).toInt() / 100.0 ); //0 -100
288 }
289 else
290 {
291 d->opacity = ( layer->customProperty( QStringLiteral( "labeling/bufferOpacity" ) ).toDouble() );
292 }
293 d->blendMode = QgsPainting::getCompositionMode(
294 static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/bufferBlendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
295 d->joinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( QStringLiteral( "labeling/bufferJoinStyle" ), QVariant( Qt::RoundJoin ) ).toUInt() );
296
297 d->fillBufferInterior = !layer->customProperty( QStringLiteral( "labeling/bufferNoFill" ), QVariant( false ) ).toBool();
298
299 if ( layer->customProperty( QStringLiteral( "labeling/bufferEffect" ) ).isValid() )
300 {
301 QDomDocument doc( QStringLiteral( "effect" ) );
302 doc.setContent( layer->customProperty( QStringLiteral( "labeling/bufferEffect" ) ).toString() );
303 const QDomElement effectElem = doc.firstChildElement( QStringLiteral( "effect" ) ).firstChildElement( QStringLiteral( "effect" ) );
304 setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
305 }
306 else
307 setPaintEffect( nullptr );
308 }
309
readXml(const QDomElement & elem)310 void QgsTextBufferSettings::readXml( const QDomElement &elem )
311 {
312 const QDomElement textBufferElem = elem.firstChildElement( QStringLiteral( "text-buffer" ) );
313 const double bufSize = textBufferElem.attribute( QStringLiteral( "bufferSize" ), QStringLiteral( "0" ) ).toDouble();
314
315 // fix for buffer being keyed off of just its size in the past (<2.0)
316 const QVariant drawBuffer = textBufferElem.attribute( QStringLiteral( "bufferDraw" ) );
317 if ( drawBuffer.isValid() )
318 {
319 d->enabled = drawBuffer.toBool();
320 d->size = bufSize;
321 }
322 else if ( bufSize != 0.0 )
323 {
324 d->enabled = true;
325 d->size = bufSize;
326 }
327 else
328 {
329 // keep bufferSize at new 1.0 default
330 d->enabled = false;
331 }
332
333 if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferSizeUnits" ) ) )
334 {
335 const bool bufferSizeInMapUnits = textBufferElem.attribute( QStringLiteral( "bufferSizeInMapUnits" ) ).toInt();
336 d->sizeUnit = bufferSizeInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
337 }
338 else
339 {
340 d->sizeUnit = QgsUnitTypes::decodeRenderUnit( textBufferElem.attribute( QStringLiteral( "bufferSizeUnits" ) ) );
341 }
342
343 if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferSizeMapUnitScale" ) ) )
344 {
345 //fallback to older property
346 const double oldMin = textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
347 d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
348 const double oldMax = textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
349 d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
350 }
351 else
352 {
353 d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitScale" ) ) );
354 }
355 d->color = QgsSymbolLayerUtils::decodeColor( textBufferElem.attribute( QStringLiteral( "bufferColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) );
356
357 if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferOpacity" ) ) )
358 {
359 d->opacity = ( 1 - textBufferElem.attribute( QStringLiteral( "bufferTransp" ) ).toInt() / 100.0 ); //0 -100
360 }
361 else
362 {
363 d->opacity = ( textBufferElem.attribute( QStringLiteral( "bufferOpacity" ) ).toDouble() );
364 }
365
366 d->blendMode = QgsPainting::getCompositionMode(
367 static_cast< QgsPainting::BlendMode >( textBufferElem.attribute( QStringLiteral( "bufferBlendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
368 d->joinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( QStringLiteral( "bufferJoinStyle" ), QString::number( Qt::RoundJoin ) ).toUInt() );
369 d->fillBufferInterior = !textBufferElem.attribute( QStringLiteral( "bufferNoFill" ), QStringLiteral( "0" ) ).toInt();
370 const QDomElement effectElem = textBufferElem.firstChildElement( QStringLiteral( "effect" ) );
371 if ( !effectElem.isNull() )
372 setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
373 else
374 setPaintEffect( nullptr );
375 }
376
writeXml(QDomDocument & doc) const377 QDomElement QgsTextBufferSettings::writeXml( QDomDocument &doc ) const
378 {
379 // text buffer
380 QDomElement textBufferElem = doc.createElement( QStringLiteral( "text-buffer" ) );
381 textBufferElem.setAttribute( QStringLiteral( "bufferDraw" ), d->enabled );
382 textBufferElem.setAttribute( QStringLiteral( "bufferSize" ), d->size );
383 textBufferElem.setAttribute( QStringLiteral( "bufferSizeUnits" ), QgsUnitTypes::encodeUnit( d->sizeUnit ) );
384 textBufferElem.setAttribute( QStringLiteral( "bufferSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->sizeMapUnitScale ) );
385 textBufferElem.setAttribute( QStringLiteral( "bufferColor" ), QgsSymbolLayerUtils::encodeColor( d->color ) );
386 textBufferElem.setAttribute( QStringLiteral( "bufferNoFill" ), !d->fillBufferInterior );
387 textBufferElem.setAttribute( QStringLiteral( "bufferOpacity" ), d->opacity );
388 textBufferElem.setAttribute( QStringLiteral( "bufferJoinStyle" ), static_cast< unsigned int >( d->joinStyle ) );
389 textBufferElem.setAttribute( QStringLiteral( "bufferBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
390 if ( d->paintEffect && !QgsPaintEffectRegistry::isDefaultStack( d->paintEffect.get() ) )
391 d->paintEffect->saveProperties( doc, textBufferElem );
392 return textBufferElem;
393 }
394