Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsdatadefinedsizelegend.cpp
3 : : --------------------------------------
4 : : Date : June 2017
5 : : Copyright : (C) 2017 by Martin Dobias
6 : : Email : wonder dot sk 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 "qgsdatadefinedsizelegend.h"
17 : :
18 : : #include "qgsproperty.h"
19 : : #include "qgspropertytransformer.h"
20 : : #include "qgssymbollayerutils.h"
21 : : #include "qgsxmlutils.h"
22 : : #include "qgslinesymbollayer.h"
23 : : #include "qgstextformat.h"
24 : : #include "qgstextrenderer.h"
25 : :
26 : 0 : QgsDataDefinedSizeLegend::QgsDataDefinedSizeLegend()
27 : : {
28 : 0 : std::unique_ptr< QgsSimpleLineSymbolLayer > lineSymbolLayer = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 0, 0, 0 ), 0.2 );
29 : 0 : mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << lineSymbolLayer.release() );
30 : 0 : }
31 : :
32 : 0 : QgsDataDefinedSizeLegend::~QgsDataDefinedSizeLegend() = default;
33 : :
34 : 0 : QgsDataDefinedSizeLegend::QgsDataDefinedSizeLegend( const QgsDataDefinedSizeLegend &other )
35 : 0 : : mType( other.mType )
36 : 0 : , mTitleLabel( other.mTitleLabel )
37 : 0 : , mSizeClasses( other.mSizeClasses )
38 : 0 : , mSymbol( other.mSymbol.get() ? other.mSymbol->clone() : nullptr )
39 : 0 : , mLineSymbol( other.mLineSymbol.get() ? other.mLineSymbol->clone() : nullptr )
40 : 0 : , mSizeScaleTransformer( other.mSizeScaleTransformer.get() ? new QgsSizeScaleTransformer( *other.mSizeScaleTransformer ) : nullptr )
41 : 0 : , mVAlign( other.mVAlign )
42 : 0 : , mFont( other.mFont )
43 : 0 : , mTextColor( other.mTextColor )
44 : 0 : , mTextAlignment( other.mTextAlignment )
45 : : {
46 : 0 : }
47 : :
48 : 0 : QgsDataDefinedSizeLegend &QgsDataDefinedSizeLegend::operator=( const QgsDataDefinedSizeLegend &other )
49 : : {
50 : 0 : if ( this != &other )
51 : : {
52 : 0 : mType = other.mType;
53 : 0 : mTitleLabel = other.mTitleLabel;
54 : 0 : mSizeClasses = other.mSizeClasses;
55 : 0 : mSymbol.reset( other.mSymbol.get() ? other.mSymbol->clone() : nullptr );
56 : 0 : mLineSymbol.reset( other.mLineSymbol.get() ? other.mLineSymbol->clone() : nullptr );
57 : 0 : mSizeScaleTransformer.reset( other.mSizeScaleTransformer.get() ? new QgsSizeScaleTransformer( *other.mSizeScaleTransformer ) : nullptr );
58 : 0 : mVAlign = other.mVAlign;
59 : 0 : mFont = other.mFont;
60 : 0 : mTextColor = other.mTextColor;
61 : 0 : mTextAlignment = other.mTextAlignment;
62 : 0 : }
63 : 0 : return *this;
64 : 0 : }
65 : :
66 : 0 : void QgsDataDefinedSizeLegend::setSymbol( QgsMarkerSymbol *symbol )
67 : : {
68 : 0 : mSymbol.reset( symbol );
69 : 0 : }
70 : :
71 : 0 : QgsMarkerSymbol *QgsDataDefinedSizeLegend::symbol() const
72 : : {
73 : 0 : return mSymbol.get();
74 : : }
75 : :
76 : 0 : void QgsDataDefinedSizeLegend::setLineSymbol( QgsLineSymbol *symbol )
77 : : {
78 : 0 : mLineSymbol.reset( symbol );
79 : 0 : }
80 : :
81 : 0 : QgsLineSymbol *QgsDataDefinedSizeLegend::lineSymbol() const
82 : : {
83 : 0 : return mLineSymbol.get();
84 : : }
85 : :
86 : 0 : void QgsDataDefinedSizeLegend::setSizeScaleTransformer( QgsSizeScaleTransformer *transformer )
87 : : {
88 : 0 : mSizeScaleTransformer.reset( transformer );
89 : 0 : }
90 : :
91 : 0 : QgsSizeScaleTransformer *QgsDataDefinedSizeLegend::sizeScaleTransformer() const
92 : : {
93 : 0 : return mSizeScaleTransformer.get();
94 : : }
95 : :
96 : :
97 : 0 : void QgsDataDefinedSizeLegend::updateFromSymbolAndProperty( const QgsMarkerSymbol *symbol, const QgsProperty &ddSize )
98 : : {
99 : 0 : mSymbol.reset( symbol->clone() );
100 : 0 : mSymbol->setDataDefinedSize( QgsProperty() ); // original symbol may have had data-defined size associated
101 : :
102 : 0 : const QgsSizeScaleTransformer *sizeTransformer = dynamic_cast< const QgsSizeScaleTransformer * >( ddSize.transformer() );
103 : 0 : mSizeScaleTransformer.reset( sizeTransformer ? sizeTransformer->clone() : nullptr );
104 : :
105 : 0 : if ( mTitleLabel.isEmpty() )
106 : 0 : mTitleLabel = ddSize.propertyType() == QgsProperty::ExpressionBasedProperty ? ddSize.expressionString() : ddSize.field();
107 : :
108 : : // automatically generate classes if no classes are defined
109 : 0 : if ( sizeTransformer && mSizeClasses.isEmpty() )
110 : : {
111 : 0 : mSizeClasses.clear();
112 : 0 : const auto prettyBreaks { QgsSymbolLayerUtils::prettyBreaks( sizeTransformer->minValue(), sizeTransformer->maxValue(), 4 ) };
113 : 0 : for ( double v : prettyBreaks )
114 : : {
115 : 0 : mSizeClasses << SizeClass( v, QString::number( v ) );
116 : : }
117 : 0 : }
118 : 0 : }
119 : :
120 : 0 : QgsLegendSymbolList QgsDataDefinedSizeLegend::legendSymbolList() const
121 : : {
122 : 0 : QgsLegendSymbolList lst;
123 : 0 : if ( !mTitleLabel.isEmpty() )
124 : : {
125 : 0 : QgsLegendSymbolItem title( nullptr, mTitleLabel, QString() );
126 : 0 : lst << title;
127 : 0 : }
128 : :
129 : 0 : switch ( mType )
130 : : {
131 : : case LegendCollapsed:
132 : : {
133 : 0 : QgsLegendSymbolItem i;
134 : 0 : i.setDataDefinedSizeLegendSettings( new QgsDataDefinedSizeLegend( *this ) );
135 : 0 : lst << i;
136 : : break;
137 : 0 : }
138 : :
139 : : case LegendSeparated:
140 : : {
141 : 0 : lst.reserve( mSizeClasses.size() );
142 : 0 : for ( const SizeClass &cl : mSizeClasses )
143 : : {
144 : 0 : QgsLegendSymbolItem si( mSymbol.get(), cl.label, QString() );
145 : 0 : QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( si.symbol() );
146 : 0 : double size = cl.size;
147 : 0 : if ( mSizeScaleTransformer )
148 : : {
149 : 0 : size = mSizeScaleTransformer->size( size );
150 : 0 : }
151 : :
152 : 0 : s->setSize( size );
153 : 0 : lst << si;
154 : 0 : }
155 : 0 : break;
156 : : }
157 : : }
158 : 0 : return lst;
159 : 0 : }
160 : :
161 : :
162 : 0 : void QgsDataDefinedSizeLegend::drawCollapsedLegend( QgsRenderContext &context, QSizeF *outputSize, double *labelXOffset ) const
163 : : {
164 : : // this assumes the context's painter has been scaled to pixels in advance!
165 : :
166 : 0 : if ( mType != LegendCollapsed || mSizeClasses.isEmpty() || !mSymbol )
167 : 0 : {
168 : 0 : if ( outputSize )
169 : 0 : *outputSize = QSizeF();
170 : 0 : if ( labelXOffset )
171 : 0 : *labelXOffset = 0;
172 : 0 : return;
173 : 0 : }
174 : :
175 : 0 : // parameters that could be configurable
176 : 0 : double hLengthLineMM = 2; // extra horizontal space to be occupied by callout line
177 : 0 : double hSpaceLineTextMM = 1; // horizontal space between end of the line and start of the text
178 : :
179 : 0 : std::unique_ptr<QgsMarkerSymbol> s( mSymbol->clone() );
180 : :
181 : 0 : QList<SizeClass> classes = mSizeClasses;
182 : :
183 : : // optionally scale size values if transformer is defined
184 : 0 : if ( mSizeScaleTransformer )
185 : : {
186 : 0 : for ( SizeClass &cls : classes )
187 : 0 : cls.size = mSizeScaleTransformer->size( cls.size );
188 : 0 : }
189 : :
190 : : // make sure we draw bigger symbols first
191 : 0 : std::sort( classes.begin(), classes.end(), []( const SizeClass & a, const SizeClass & b ) { return a.size > b.size; } );
192 : :
193 : 0 : double hLengthLine = context.convertToPainterUnits( hLengthLineMM, QgsUnitTypes::RenderMillimeters );
194 : 0 : double hSpaceLineText = context.convertToPainterUnits( hSpaceLineTextMM, QgsUnitTypes::RenderMillimeters );
195 : 0 : int dpm = std::round( context.scaleFactor() * 1000 ); // scale factor = dots per millimeter
196 : :
197 : : // get font metrics - we need a temporary image just to get the metrics right for the given DPI
198 : 0 : QImage tmpImg( QSize( 1, 1 ), QImage::Format_ARGB32_Premultiplied );
199 : 0 : tmpImg.setDotsPerMeterX( dpm );
200 : 0 : tmpImg.setDotsPerMeterY( dpm );
201 : 0 : QFontMetricsF fm( mFont, &tmpImg );
202 : 0 : double textHeight = fm.height();
203 : 0 : double leading = fm.leading();
204 : 0 : double minTextDistY = textHeight + leading;
205 : :
206 : : //
207 : : // determine layout of the rendered elements
208 : : //
209 : :
210 : : // find out how wide the text will be
211 : 0 : double maxTextWidth = 0;
212 : 0 : for ( const SizeClass &c : std::as_const( classes ) )
213 : : {
214 : 0 : maxTextWidth = std::max( maxTextWidth, fm.boundingRect( c.label ).width() );
215 : : }
216 : : // add extra width needed to handle varying rendering of font weight
217 : 0 : maxTextWidth += 1;
218 : :
219 : : // find out size of the largest symbol
220 : 0 : double largestSize = classes.at( 0 ).size;
221 : 0 : double outputLargestSize = context.convertToPainterUnits( largestSize, s->sizeUnit(), s->sizeMapUnitScale() );
222 : :
223 : : // find out top Y coordinate for individual symbol sizes
224 : 0 : QList<double> symbolTopY;
225 : 0 : for ( const SizeClass &c : std::as_const( classes ) )
226 : : {
227 : 0 : double outputSymbolSize = context.convertToPainterUnits( c.size, s->sizeUnit(), s->sizeMapUnitScale() );
228 : 0 : switch ( mVAlign )
229 : : {
230 : : case AlignCenter:
231 : 0 : symbolTopY << outputLargestSize / 2 - outputSymbolSize / 2;
232 : 0 : break;
233 : : case AlignBottom:
234 : 0 : symbolTopY << outputLargestSize - outputSymbolSize;
235 : 0 : break;
236 : : }
237 : : }
238 : :
239 : : // determine Y coordinate of texts: ideally they should be at the same level as symbolTopY
240 : : // but we need to avoid overlapping texts, so adjust the vertical positions
241 : 0 : double middleIndex = 0; // classes.count() / 2; // will get the ideal position
242 : 0 : QList<double> textCenterY;
243 : 0 : double lastY = middleIndex < symbolTopY.size() ? symbolTopY[middleIndex] : 0;
244 : 0 : textCenterY << lastY;
245 : 0 : for ( int i = middleIndex + 1; i < classes.count(); ++i )
246 : : {
247 : 0 : double symbolY = symbolTopY[i];
248 : 0 : if ( symbolY - lastY < minTextDistY )
249 : 0 : symbolY = lastY + minTextDistY;
250 : 0 : textCenterY << symbolY;
251 : 0 : lastY = symbolY;
252 : 0 : }
253 : :
254 : 0 : double textTopY = textCenterY.first() - textHeight / 2;
255 : 0 : double textBottomY = textCenterY.last() + textHeight / 2;
256 : 0 : double totalTextHeight = textBottomY - textTopY;
257 : :
258 : 0 : double fullWidth = outputLargestSize + hLengthLine + hSpaceLineText + maxTextWidth;
259 : 0 : double fullHeight = std::max( outputLargestSize - textTopY, totalTextHeight );
260 : :
261 : 0 : if ( outputSize )
262 : 0 : *outputSize = QSizeF( fullWidth, fullHeight );
263 : 0 : if ( labelXOffset )
264 : 0 : *labelXOffset = outputLargestSize + hLengthLine + hSpaceLineText;
265 : :
266 : 0 : if ( !context.painter() )
267 : 0 : return; // only layout
268 : :
269 : : //
270 : : // drawing
271 : : //
272 : :
273 : 0 : QPainter *p = context.painter();
274 : 0 : QgsScopedQPainterState painterState( p );
275 : 0 : p->translate( 0, -textTopY );
276 : :
277 : : // draw symbols first so that they do not cover
278 : 0 : for ( const SizeClass &c : std::as_const( classes ) )
279 : : {
280 : 0 : s->setSize( c.size );
281 : :
282 : 0 : double outputSymbolSize = context.convertToPainterUnits( c.size, s->sizeUnit(), s->sizeMapUnitScale() );
283 : 0 : double tx = ( outputLargestSize - outputSymbolSize ) / 2;
284 : :
285 : 0 : QgsScopedQPainterState symbolPainterState( p );
286 : 0 : switch ( mVAlign )
287 : : {
288 : : case AlignCenter:
289 : 0 : p->translate( tx, ( outputLargestSize - outputSymbolSize ) / 2 );
290 : 0 : break;
291 : : case AlignBottom:
292 : 0 : p->translate( tx, outputLargestSize - outputSymbolSize );
293 : 0 : break;
294 : : }
295 : 0 : s->drawPreviewIcon( nullptr, QSize( outputSymbolSize, outputSymbolSize ), &context );
296 : 0 : }
297 : :
298 : 0 : QgsTextFormat format = QgsTextFormat::fromQFont( mFont );
299 : 0 : format.setColor( mTextColor );
300 : :
301 : 0 : if ( mLineSymbol )
302 : : {
303 : 0 : mLineSymbol->startRender( context );
304 : 0 : }
305 : :
306 : 0 : int i = 0;
307 : 0 : for ( const SizeClass &c : std::as_const( classes ) )
308 : : {
309 : : // line from symbol to the text
310 : 0 : if ( mLineSymbol )
311 : : {
312 : 0 : mLineSymbol->renderPolyline( QPolygonF() << QPointF( outputLargestSize / 2, symbolTopY[i] )
313 : 0 : << QPointF( outputLargestSize + hLengthLine, textCenterY[i] ), nullptr, context );
314 : 0 : }
315 : :
316 : : // draw label
317 : 0 : QRect rect( outputLargestSize + hLengthLine + hSpaceLineText, textCenterY[i] - textHeight / 2,
318 : 0 : maxTextWidth, textHeight );
319 : :
320 : 0 : QgsTextRenderer::drawText( rect, 0, QgsTextRenderer::convertQtHAlignment( mTextAlignment ),
321 : 0 : QStringList() << c.label, context, format );
322 : 0 : i++;
323 : : }
324 : :
325 : 0 : if ( mLineSymbol )
326 : 0 : mLineSymbol->stopRender( context );
327 : 0 : }
328 : :
329 : :
330 : 0 : QImage QgsDataDefinedSizeLegend::collapsedLegendImage( QgsRenderContext &context, const QColor &backgroundColor, double paddingMM ) const
331 : : {
332 : 0 : if ( mType != LegendCollapsed || mSizeClasses.isEmpty() || !mSymbol )
333 : 0 : return QImage();
334 : :
335 : : // find out the size first
336 : 0 : QSizeF contentSize;
337 : 0 : drawCollapsedLegend( context, &contentSize );
338 : :
339 : 0 : double padding = context.convertToPainterUnits( paddingMM, QgsUnitTypes::RenderMillimeters );
340 : 0 : int dpm = std::round( context.scaleFactor() * 1000 ); // scale factor = dots per millimeter
341 : :
342 : 0 : QImage img( contentSize.width() + padding * 2, contentSize.height() + padding * 2, QImage::Format_ARGB32_Premultiplied );
343 : 0 : img.setDotsPerMeterX( dpm );
344 : 0 : img.setDotsPerMeterY( dpm );
345 : 0 : img.fill( backgroundColor );
346 : :
347 : 0 : QPainter painter( &img );
348 : 0 : painter.setRenderHint( QPainter::Antialiasing, true );
349 : :
350 : 0 : painter.translate( padding, padding ); // so we do not need to care about padding at all
351 : :
352 : : // now do the rendering
353 : 0 : QPainter *oldPainter = context.painter();
354 : 0 : context.setPainter( &painter );
355 : 0 : drawCollapsedLegend( context );
356 : 0 : context.setPainter( oldPainter );
357 : :
358 : 0 : painter.end();
359 : 0 : return img;
360 : 0 : }
361 : :
362 : 0 : QgsDataDefinedSizeLegend *QgsDataDefinedSizeLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
363 : : {
364 : 0 : if ( elem.isNull() )
365 : 0 : return nullptr;
366 : 0 : QgsDataDefinedSizeLegend *ddsLegend = new QgsDataDefinedSizeLegend;
367 : 0 : ddsLegend->setLegendType( elem.attribute( QStringLiteral( "type" ) ) == QLatin1String( "collapsed" ) ? LegendCollapsed : LegendSeparated );
368 : 0 : ddsLegend->setVerticalAlignment( elem.attribute( QStringLiteral( "valign" ) ) == QLatin1String( "center" ) ? AlignCenter : AlignBottom );
369 : 0 : ddsLegend->setTitle( elem.attribute( QStringLiteral( "title" ) ) );
370 : :
371 : 0 : QDomElement elemSymbol = elem.firstChildElement( QStringLiteral( "symbol" ) );
372 : 0 : if ( !elemSymbol.isNull() )
373 : : {
374 : 0 : ddsLegend->setSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( elemSymbol, context ) );
375 : 0 : }
376 : :
377 : 0 : const QDomElement lineSymbolElem = elem.firstChildElement( QStringLiteral( "lineSymbol" ) );
378 : 0 : if ( !lineSymbolElem.isNull() )
379 : : {
380 : 0 : ddsLegend->setLineSymbol( QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( lineSymbolElem.firstChildElement(), context ) );
381 : 0 : }
382 : :
383 : 0 : QgsSizeScaleTransformer *transformer = nullptr;
384 : 0 : QDomElement elemTransformer = elem.firstChildElement( QStringLiteral( "transformer" ) );
385 : 0 : if ( !elemTransformer.isNull() )
386 : : {
387 : 0 : transformer = new QgsSizeScaleTransformer;
388 : 0 : transformer->loadVariant( QgsXmlUtils::readVariant( elemTransformer ) );
389 : 0 : }
390 : 0 : ddsLegend->setSizeScaleTransformer( transformer );
391 : :
392 : 0 : QDomElement elemTextStyle = elem.firstChildElement( QStringLiteral( "text-style" ) );
393 : 0 : if ( !elemTextStyle.isNull() )
394 : : {
395 : 0 : QDomElement elemFont = elemTextStyle.firstChildElement( QStringLiteral( "font" ) );
396 : 0 : if ( !elemFont.isNull() )
397 : : {
398 : 0 : ddsLegend->setFont( QFont( elemFont.attribute( QStringLiteral( "family" ) ), elemFont.attribute( QStringLiteral( "size" ) ).toInt(),
399 : 0 : elemFont.attribute( QStringLiteral( "weight" ) ).toInt(), elemFont.attribute( QStringLiteral( "italic" ) ).toInt() ) );
400 : 0 : }
401 : 0 : ddsLegend->setTextColor( QgsSymbolLayerUtils::decodeColor( elemTextStyle.attribute( QStringLiteral( "color" ) ) ) );
402 : 0 : ddsLegend->setTextAlignment( static_cast<Qt::AlignmentFlag>( elemTextStyle.attribute( QStringLiteral( "align" ) ).toInt() ) );
403 : 0 : }
404 : :
405 : 0 : QDomElement elemClasses = elem.firstChildElement( QStringLiteral( "classes" ) );
406 : 0 : if ( !elemClasses.isNull() )
407 : : {
408 : 0 : QList<SizeClass> classes;
409 : 0 : QDomElement elemClass = elemClasses.firstChildElement( QStringLiteral( "class" ) );
410 : 0 : while ( !elemClass.isNull() )
411 : : {
412 : 0 : classes << SizeClass( elemClass.attribute( QStringLiteral( "size" ) ).toDouble(), elemClass.attribute( QStringLiteral( "label" ) ) );
413 : 0 : elemClass = elemClass.nextSiblingElement();
414 : : }
415 : 0 : ddsLegend->setClasses( classes );
416 : 0 : }
417 : :
418 : 0 : return ddsLegend;
419 : 0 : }
420 : :
421 : 0 : void QgsDataDefinedSizeLegend::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
422 : : {
423 : 0 : QDomDocument doc = elem.ownerDocument();
424 : :
425 : 0 : elem.setAttribute( QStringLiteral( "type" ), mType == LegendCollapsed ? "collapsed" : "separated" );
426 : 0 : elem.setAttribute( QStringLiteral( "valign" ), mVAlign == AlignCenter ? "center" : "bottom" );
427 : 0 : elem.setAttribute( QStringLiteral( "title" ), mTitleLabel );
428 : :
429 : 0 : if ( mSymbol )
430 : : {
431 : 0 : QDomElement elemSymbol = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "source" ), mSymbol.get(), doc, context );
432 : 0 : elem.appendChild( elemSymbol );
433 : 0 : }
434 : :
435 : 0 : if ( mLineSymbol )
436 : : {
437 : 0 : QDomElement lineSymbolElem = doc.createElement( QStringLiteral( "lineSymbol" ) );
438 : 0 : lineSymbolElem.appendChild( QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "lineSymbol" ), mLineSymbol.get(), doc, context ) );
439 : 0 : elem.appendChild( lineSymbolElem );
440 : 0 : }
441 : :
442 : 0 : if ( mSizeScaleTransformer )
443 : : {
444 : 0 : QDomElement elemTransformer = QgsXmlUtils::writeVariant( mSizeScaleTransformer->toVariant(), doc );
445 : 0 : elemTransformer.setTagName( QStringLiteral( "transformer" ) );
446 : 0 : elem.appendChild( elemTransformer );
447 : 0 : }
448 : :
449 : 0 : QDomElement elemFont = doc.createElement( QStringLiteral( "font" ) );
450 : 0 : elemFont.setAttribute( QStringLiteral( "family" ), mFont.family() );
451 : 0 : elemFont.setAttribute( QStringLiteral( "size" ), mFont.pointSize() );
452 : 0 : elemFont.setAttribute( QStringLiteral( "weight" ), mFont.weight() );
453 : 0 : elemFont.setAttribute( QStringLiteral( "italic" ), mFont.italic() );
454 : :
455 : 0 : QDomElement elemTextStyle = doc.createElement( QStringLiteral( "text-style" ) );
456 : 0 : elemTextStyle.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mTextColor ) );
457 : 0 : elemTextStyle.setAttribute( QStringLiteral( "align" ), static_cast<int>( mTextAlignment ) );
458 : 0 : elemTextStyle.appendChild( elemFont );
459 : 0 : elem.appendChild( elemTextStyle );
460 : :
461 : 0 : if ( !mSizeClasses.isEmpty() )
462 : : {
463 : 0 : QDomElement elemClasses = doc.createElement( QStringLiteral( "classes" ) );
464 : 0 : for ( const SizeClass &sc : std::as_const( mSizeClasses ) )
465 : : {
466 : 0 : QDomElement elemClass = doc.createElement( QStringLiteral( "class" ) );
467 : 0 : elemClass.setAttribute( QStringLiteral( "size" ), sc.size );
468 : 0 : elemClass.setAttribute( QStringLiteral( "label" ), sc.label );
469 : 0 : elemClasses.appendChild( elemClass );
470 : 0 : }
471 : 0 : elem.appendChild( elemClasses );
472 : 0 : }
473 : 0 : }
|