QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgscompositionconverter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscompositionconverter.cpp - QgsCompositionConverter
3 
4  ---------------------
5  begin : 13.12.2017
6  copyright : (C) 2017 by Alessandro Pasotti
7  email : elpaso at itopen dot it
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 <QObject>
18 #include <QUuid>
19 
21 #include "qgsreadwritecontext.h"
22 #include "qgslayertree.h"
23 #include "qgslayoutmodel.h"
24 #include "qgslayoutitemgroup.h"
25 #include "qgslayoutobject.h"
26 #include "qgsfontutils.h"
27 #include "qgspainting.h"
28 #include "qgsproperty.h"
29 #include "qgssymbollayerutils.h"
30 #include "qgssymbollayer.h"
31 #include "qgsproject.h"
33 #include "qgsvectorlayer.h"
34 #include "qgslinesymbollayer.h"
35 #include "qgslinesymbol.h"
36 #include "qgsfillsymbol.h"
37 #include "qgsmarkersymbol.h"
38 
39 #include "qgsprintlayout.h"
40 #include "qgslayoutatlas.h"
41 
42 #include "qgslayoutundostack.h"
44 #include "qgslayoutitemregistry.h"
45 #include "qgslayoutitemlabel.h"
46 #include "qgslayoutitemshape.h"
47 #include "qgslayoutitempicture.h"
48 #include "qgslayoutitempolygon.h"
49 #include "qgslayoutitempolyline.h"
50 #include "qgslayoutitemmap.h"
51 #include "qgslayoutitemmapgrid.h"
52 #include "qgslayoutitemscalebar.h"
53 #include "qgslayoutitemlegend.h"
54 #include "qgslayoutitemhtml.h"
55 #include "qgslayouttable.h"
57 #include "qgslayouttablecolumn.h"
58 #include "qgslayoutmultiframe.h"
59 #include "qgslayoutframe.h"
62 
63 QgsPropertiesDefinition QgsCompositionConverter::sPropertyDefinitions;
64 
65 void QgsCompositionConverter::initPropertyDefinitions()
66 {
67  if ( !sPropertyDefinitions.isEmpty() )
68  return;
69 
70  sPropertyDefinitions = QgsPropertiesDefinition
71  {
72  { QgsCompositionConverter::TestProperty, QgsPropertyDefinition( "dataDefinedProperty", QgsPropertyDefinition::DataTypeString, "invalid property", QString() ) },
73  {
74  QgsCompositionConverter::PresetPaperSize, QgsPropertyDefinition( "dataDefinedPaperSize", QgsPropertyDefinition::DataTypeString, QObject::tr( "Paper size" ), QObject::tr( "string " ) + QStringLiteral( "[<b>A5</b>|<b>A4</b>|<b>A3</b>|<b>A2</b>|<b>A1</b>|<b>A0</b>"
75  "|<b>B5</b>|<b>B4</b>|<b>B3</b>|<b>B2</b>|<b>B1</b>|<b>B0</b>"
76  "|<b>Legal</b>|<b>Ansi A</b>|<b>Ansi B</b>|<b>Ansi C</b>|<b>Ansi D</b>|<b>Ansi E</b>"
77  "|<b>Arch A</b>|<b>Arch B</b>|<b>Arch C</b>|<b>Arch D</b>|<b>Arch E</b>|<b>Arch E1</b>]"
78  ) )
79  },
80  { QgsCompositionConverter::PaperWidth, QgsPropertyDefinition( "dataDefinedPaperWidth", QObject::tr( "Page width" ), QgsPropertyDefinition::DoublePositive ) },
81  { QgsCompositionConverter::PaperHeight, QgsPropertyDefinition( "dataDefinedPaperHeight", QObject::tr( "Page height" ), QgsPropertyDefinition::DoublePositive ) },
82  { QgsCompositionConverter::NumPages, QgsPropertyDefinition( "dataDefinedNumPages", QObject::tr( "Number of pages" ), QgsPropertyDefinition::IntegerPositive ) },
83  { QgsCompositionConverter::PaperOrientation, QgsPropertyDefinition( "dataDefinedPaperOrientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Symbol size" ), QObject::tr( "string " ) + QStringLiteral( "[<b>portrait</b>|<b>landscape</b>]" ) ) },
84  { QgsCompositionConverter::PageNumber, QgsPropertyDefinition( "dataDefinedPageNumber", QObject::tr( "Page number" ), QgsPropertyDefinition::IntegerPositive ) },
85  { QgsCompositionConverter::PositionX, QgsPropertyDefinition( "dataDefinedPositionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double ) },
86  { QgsCompositionConverter::PositionY, QgsPropertyDefinition( "dataDefinedPositionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double ) },
88  { QgsCompositionConverter::ItemHeight, QgsPropertyDefinition( "dataDefinedHeight", QObject::tr( "Height" ), QgsPropertyDefinition::DoublePositive ) },
89  { QgsCompositionConverter::ItemRotation, QgsPropertyDefinition( "dataDefinedRotation", QObject::tr( "Rotation angle" ), QgsPropertyDefinition::Rotation ) },
90  { QgsCompositionConverter::Transparency, QgsPropertyDefinition( "dataDefinedTransparency", QObject::tr( "Transparency" ), QgsPropertyDefinition::Opacity ) },
91  { QgsCompositionConverter::Opacity, QgsPropertyDefinition( "dataDefinedOpacity", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity ) },
92  { QgsCompositionConverter::BlendMode, QgsPropertyDefinition( "dataDefinedBlendMode", QObject::tr( "Blend mode" ), QgsPropertyDefinition::BlendMode ) },
93  { QgsCompositionConverter::ExcludeFromExports, QgsPropertyDefinition( "dataDefinedExcludeExports", QObject::tr( "Exclude item from exports" ), QgsPropertyDefinition::Boolean ) },
94  { QgsCompositionConverter::FrameColor, QgsPropertyDefinition( "dataDefinedFrameColor", QObject::tr( "Frame color" ), QgsPropertyDefinition::ColorWithAlpha ) },
95  { QgsCompositionConverter::BackgroundColor, QgsPropertyDefinition( "dataDefinedBackgroundColor", QObject::tr( "Background color" ), QgsPropertyDefinition::ColorWithAlpha ) },
96  { QgsCompositionConverter::MapRotation, QgsPropertyDefinition( "dataDefinedMapRotation", QObject::tr( "Map rotation" ), QgsPropertyDefinition::Rotation ) },
97  { QgsCompositionConverter::MapScale, QgsPropertyDefinition( "dataDefinedMapScale", QObject::tr( "Map scale" ), QgsPropertyDefinition::DoublePositive ) },
98  { QgsCompositionConverter::MapXMin, QgsPropertyDefinition( "dataDefinedMapXMin", QObject::tr( "Extent minimum X" ), QgsPropertyDefinition::Double ) },
99  { QgsCompositionConverter::MapYMin, QgsPropertyDefinition( "dataDefinedMapYMin", QObject::tr( "Extent minimum Y" ), QgsPropertyDefinition::Double ) },
100  { QgsCompositionConverter::MapXMax, QgsPropertyDefinition( "dataDefinedMapXMax", QObject::tr( "Extent maximum X" ), QgsPropertyDefinition::Double ) },
101  { QgsCompositionConverter::MapYMax, QgsPropertyDefinition( "dataDefinedMapYMax", QObject::tr( "Extent maximum Y" ), QgsPropertyDefinition::Double ) },
102  { QgsCompositionConverter::MapAtlasMargin, QgsPropertyDefinition( "dataDefinedMapAtlasMargin", QObject::tr( "Atlas margin" ), QgsPropertyDefinition::DoublePositive ) },
103  { QgsCompositionConverter::MapLayers, QgsPropertyDefinition( "dataDefinedMapLayers", QgsPropertyDefinition::DataTypeString, QObject::tr( "Symbol size" ), QObject::tr( "list of map layer names separated by | characters" ) ) },
104  { QgsCompositionConverter::MapStylePreset, QgsPropertyDefinition( "dataDefinedMapStylePreset", QgsPropertyDefinition::DataTypeString, QObject::tr( "Symbol size" ), QObject::tr( "list of map layer names separated by | characters" ) ) },
105  { QgsCompositionConverter::PictureSource, QgsPropertyDefinition( "dataDefinedSource", QObject::tr( "Picture source (URL)" ), QgsPropertyDefinition::String ) },
106  { QgsCompositionConverter::SourceUrl, QgsPropertyDefinition( "dataDefinedSourceUrl", QObject::tr( "Source URL" ), QgsPropertyDefinition::String ) },
107  { QgsCompositionConverter::PictureSvgBackgroundColor, QgsPropertyDefinition( "dataDefinedSvgBackgroundColor", QObject::tr( "SVG background color" ), QgsPropertyDefinition::ColorWithAlpha ) },
108  { QgsCompositionConverter::PictureSvgStrokeColor, QgsPropertyDefinition( "dataDefinedSvgStrokeColor", QObject::tr( "SVG stroke color" ), QgsPropertyDefinition::ColorWithAlpha ) },
109  { QgsCompositionConverter::PictureSvgStrokeWidth, QgsPropertyDefinition( "dataDefinedSvgStrokeWidth", QObject::tr( "SVG stroke width" ), QgsPropertyDefinition::StrokeWidth ) },
110  { QgsCompositionConverter::LegendTitle, QgsPropertyDefinition( "dataDefinedLegendTitle", QObject::tr( "Legend title" ), QgsPropertyDefinition::String ) },
111  { QgsCompositionConverter::LegendColumnCount, QgsPropertyDefinition( "dataDefinedLegendColumns", QObject::tr( "Number of columns" ), QgsPropertyDefinition::IntegerPositiveGreaterZero ) },
112  { QgsCompositionConverter::ScalebarFillColor, QgsPropertyDefinition( "dataDefinedScalebarFill", QObject::tr( "Fill color" ), QgsPropertyDefinition::ColorWithAlpha ) },
113  { QgsCompositionConverter::ScalebarFillColor2, QgsPropertyDefinition( "dataDefinedScalebarFill2", QObject::tr( "Secondary fill color" ), QgsPropertyDefinition::ColorWithAlpha ) },
114  { QgsCompositionConverter::ScalebarLineColor, QgsPropertyDefinition( "dataDefinedScalebarLineColor", QObject::tr( "Line color" ), QgsPropertyDefinition::ColorWithAlpha ) },
115  { QgsCompositionConverter::ScalebarLineWidth, QgsPropertyDefinition( "dataDefinedScalebarLineWidth", QObject::tr( "Line width" ), QgsPropertyDefinition::StrokeWidth ) },
116  };
117 }
118 
119 QgsPropertiesDefinition QgsCompositionConverter::propertyDefinitions()
120 {
121  QgsCompositionConverter::initPropertyDefinitions();
122  return sPropertyDefinitions;
123 }
124 
125 
126 std::unique_ptr< QgsPrintLayout > QgsCompositionConverter::createLayoutFromCompositionXml( const QDomElement &composerElement, QgsProject *project )
127 {
128  initPropertyDefinitions();
129 
130  const QDomElement parentElement = composerElement.parentNode().toElement();
131 
132  std::unique_ptr< QgsPrintLayout > layout = std::make_unique< QgsPrintLayout >( project );
133  layout->undoStack()->blockCommands( true );
134 
135  layout->mCustomProperties.readXml( composerElement );
136 
137  // Guides
138  layout->guides().setVisible( composerElement.attribute( QStringLiteral( "guidesVisible" ), QStringLiteral( "1" ) ).toInt() != 0 );
139 
140  const int printResolution = composerElement.attribute( QStringLiteral( "printResolution" ), QStringLiteral( "300" ) ).toInt();
141  layout->renderContext().setDpi( printResolution );
142 
143  // Create pages
144  const int pages = composerElement.attribute( QStringLiteral( "numPages" ) ).toInt( );
145  const float paperHeight = composerElement.attribute( QStringLiteral( "paperHeight" ) ).toDouble( );
146  const float paperWidth = composerElement.attribute( QStringLiteral( "paperWidth" ) ).toDouble( );
147 
148  QString name = composerElement.attribute( QStringLiteral( "name" ) );
149  // Try title
150  if ( name.isEmpty() )
151  name = composerElement.attribute( QStringLiteral( "title" ) );
152  // Try title on parent element
153  if ( name.isEmpty() )
154  name = parentElement.attribute( QStringLiteral( "title" ) );
155  layout->setName( name );
156  const QgsLayoutSize pageSize( paperWidth, paperHeight );
157  for ( int j = 0; j < pages; j++ )
158  {
159  QgsLayoutItemPage *page = QgsLayoutItemPage::create( layout.get() );
160  page->setPageSize( pageSize );
161  layout->pageCollection()->addPage( page );
162  //custom snap lines
163  const QDomNodeList snapLineNodes = composerElement.elementsByTagName( QStringLiteral( "SnapLine" ) );
164  for ( int i = 0; i < snapLineNodes.size(); ++i )
165  {
166  const QDomElement snapLineElem = snapLineNodes.at( i ).toElement();
167  const double x1 = snapLineElem.attribute( QStringLiteral( "x1" ) ).toDouble();
168  const double y1 = snapLineElem.attribute( QStringLiteral( "y1" ) ).toDouble();
169  const double x2 = snapLineElem.attribute( QStringLiteral( "x2" ) ).toDouble();
170  // Not necessary: double y2 = snapLineElem.attribute( QStringLiteral( "y2" ) ).toDouble();
171  const Qt::Orientation orientation( x1 == x2 ? Qt::Orientation::Vertical : Qt::Orientation::Horizontal );
172  const QgsLayoutMeasurement position( x1 == x2 ? x1 : y1 );
173  std::unique_ptr< QgsLayoutGuide > guide = std::make_unique< QgsLayoutGuide >( orientation, position, page );
174  layout->guides().addGuide( guide.release() );
175  }
176  }
177 
178 
179  if ( composerElement.elementsByTagName( QStringLiteral( "symbol" ) ).size() )
180  {
181  const QDomElement symbolElement = composerElement.elementsByTagName( QStringLiteral( "symbol" ) ).at( 0 ).toElement();
182  QgsReadWriteContext context;
183  if ( project )
184  context.setPathResolver( project->pathResolver() );
185  const std::unique_ptr< QgsFillSymbol > symbol( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElement, context ) );
186  if ( symbol )
187  layout->pageCollection()->setPageStyleSymbol( symbol.get() );
188  }
189 
190  addItemsFromCompositionXml( layout.get(), composerElement );
191 
192  // Read atlas from the parent element (Composer)
193  if ( parentElement.elementsByTagName( QStringLiteral( "Atlas" ) ).size() )
194  {
195  const QDomElement atlasElement = parentElement.elementsByTagName( QStringLiteral( "Atlas" ) ).at( 0 ).toElement();
196  readAtlasXml( layout->atlas(), atlasElement, layout->project() );
197  }
198 
199  layout->undoStack()->blockCommands( false );
200  return layout;
201 }
202 
203 
204 void QgsCompositionConverter::adjustPos( QgsPrintLayout *layout, QgsLayoutItem *layoutItem, QPointF *position, bool &pasteInPlace, int zOrderOffset, QPointF &pasteShiftPos, int &pageNumber )
205 {
206  if ( position )
207  {
208  if ( pasteInPlace )
209  {
210  layoutItem->attemptMove( QgsLayoutPoint( *position ), true, false, pageNumber );
211  }
212  else
213  {
214  layoutItem->attemptMoveBy( pasteShiftPos.x(), pasteShiftPos.y() );
215  }
216  }
217 
218  if ( !layoutItem->scene() )
219  layout->addLayoutItem( layoutItem );
220  layoutItem->setZValue( layoutItem->zValue() + zOrderOffset );
221 }
222 
223 void QgsCompositionConverter::restoreGeneralComposeItemProperties( QgsLayoutItem *layoutItem, const QDomElement &itemElem )
224 {
225  //restore general composer item properties
226  const QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
227  if ( !composerItemList.isEmpty() )
228  {
229  const QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
230 
231  //rotation
232  if ( !qgsDoubleNear( composerItemElem.attribute( QStringLiteral( "rotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
233  {
234  //check for old (pre 2.1) rotation attribute
235  layoutItem->setItemRotation( composerItemElem.attribute( QStringLiteral( "rotation" ), QStringLiteral( "0" ) ).toDouble(), false );
236  }
237  QgsCompositionConverter::readXml( layoutItem, composerItemElem );
238  }
239 }
240 
241 QRectF QgsCompositionConverter::itemPosition( QgsLayoutItem *layoutItem, const QDomElement &itemElem )
242 {
243  int page;
244  double x, y, pagex, pagey, width, height;
245  bool xOk, yOk, pageOk, pagexOk, pageyOk, widthOk, heightOk, positionModeOk;
246 
247  x = itemElem.attribute( QStringLiteral( "x" ) ).toDouble( &xOk );
248  y = itemElem.attribute( QStringLiteral( "y" ) ).toDouble( &yOk );
249  page = itemElem.attribute( QStringLiteral( "page" ) ).toInt( &pageOk );
250  pagex = itemElem.attribute( QStringLiteral( "pagex" ) ).toDouble( &pagexOk );
251  pagey = itemElem.attribute( QStringLiteral( "pagey" ) ).toDouble( &pageyOk );
252  width = itemElem.attribute( QStringLiteral( "width" ) ).toDouble( &widthOk );
253  height = itemElem.attribute( QStringLiteral( "height" ) ).toDouble( &heightOk );
254 
255 
256  layoutItem->mReferencePoint = static_cast< QgsLayoutItem::ReferencePoint >( itemElem.attribute( QStringLiteral( "positionMode" ) ).toInt( &positionModeOk ) );
257  if ( !positionModeOk )
258  {
259  layoutItem->setReferencePoint( QgsLayoutItem::ReferencePoint::UpperLeft );
260  }
261 
262  if ( pageOk && pagexOk && pageyOk )
263  {
264  xOk = true;
265  yOk = true;
266  x = pagex;
267  // position in the page (1-based)
268  if ( page <= layoutItem->layout()->pageCollection()->pageCount() )
269  {
270  QgsLayoutItemPage *pageObject = layoutItem->layout()->pageCollection()->pages().at( page - 1 );
271  y = ( page - 1 )
272  * ( pageObject->sizeWithUnits().height()
273  + layoutItem->layout()->pageCollection()->spaceBetweenPages() )
274  + pagey;
275  }
276  else
277  {
278  y = pagey;
279  }
280  }
281  return QRectF( x, y, width, height );
282 }
283 
284 QPointF QgsCompositionConverter::minPointFromXml( const QDomElement &elem )
285 {
286  double minX = std::numeric_limits<double>::max();
287  double minY = std::numeric_limits<double>::max();
288  const QDomNodeList composerItemList = elem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
289  for ( int i = 0; i < composerItemList.size(); ++i )
290  {
291  const QDomElement currentComposerItemElem = composerItemList.at( i ).toElement();
292  double x, y;
293  bool xOk, yOk;
294  x = currentComposerItemElem.attribute( QStringLiteral( "x" ) ).toDouble( &xOk );
295  y = currentComposerItemElem.attribute( QStringLiteral( "y" ) ).toDouble( &yOk );
296  if ( !xOk || !yOk )
297  {
298  continue;
299  }
300  minX = std::min( minX, x );
301  minY = std::min( minY, y );
302  }
303  if ( minX < std::numeric_limits<double>::max() )
304  {
305  return QPointF( minX, minY );
306  }
307  else
308  {
309  return QPointF( 0, 0 );
310  }
311 }
312 
313 QList<QgsLayoutObject *> QgsCompositionConverter::addItemsFromCompositionXml( QgsPrintLayout *layout, const QDomElement &parentElement, QPointF *position, bool pasteInPlace )
314 {
315 
316  initPropertyDefinitions();
317 
318  QList< QgsLayoutObject * > newItems;
319 
320  //if we are adding items to a layout which already contains items, we need to make sure
321  //these items are placed at the top of the layout and that zValues are not duplicated
322  //so, calculate an offset which needs to be added to the zValue of created items
323  const int zOrderOffset = layout->mItemsModel->zOrderListSize();
324 
325  QPointF pasteShiftPos;
326  int pageNumber = -1;
327  if ( position )
328  {
329  //If we are placing items relative to a certain point, then calculate how much we need
330  //to shift the items by so that they are placed at this point
331  //First, calculate the minimum position from the xml
332  const QPointF minItemPos = minPointFromXml( parentElement );
333  //next, calculate how much each item needs to be shifted from its original position
334  //so that it's placed at the correct relative position
335  pasteShiftPos = *position - minItemPos;
336  if ( pasteInPlace )
337  {
338  pageNumber = layout->mPageCollection->pageNumberForPoint( *position );
339  }
340  }
341 
342  QgsStringMap mapIdUiidMap;
343 
344  // Map (this needs to come first to build the uuid <-> ID map for map composer items
345  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerMap" ) ).size(); i++ )
346  {
347  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerMap" ) ).at( i ) );
348  QgsLayoutItemMap *layoutItem = new QgsLayoutItemMap( layout );
349  readMapXml( layoutItem, itemNode.toElement(), layout->project(), mapIdUiidMap );
350  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
351  newItems << layoutItem ;
352  }
353 
354  // Label
355  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerLabel" ) ).size(); i++ )
356  {
357  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerLabel" ) ).at( i ) );
358  QgsLayoutItemLabel *layoutItem = new QgsLayoutItemLabel( layout );
359  readLabelXml( layoutItem, itemNode.toElement(), layout->project() );
360  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
361  newItems << layoutItem ;
362  }
363 
364  // Shape
365  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerShape" ) ).size(); i++ )
366  {
367  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerShape" ) ).at( i ) );
368  QgsLayoutItemShape *layoutItem = new QgsLayoutItemShape( layout );
369  readShapeXml( layoutItem, itemNode.toElement(), layout->project() );
370  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
371  newItems << layoutItem ;
372  }
373 
374  // Picture
375  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerPicture" ) ).size(); i++ )
376  {
377  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerPicture" ) ).at( i ) );
378  QgsLayoutItemPicture *layoutItem = new QgsLayoutItemPicture( layout );
379  readPictureXml( layoutItem, itemNode.toElement(), layout->project(), mapIdUiidMap );
380  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
381  newItems << layoutItem ;
382  }
383 
384  // Polygon
385  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerPolygon" ) ).size(); i++ )
386  {
387  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerPolygon" ) ).at( i ) );
388  QgsLayoutItemPolygon *layoutItem = new QgsLayoutItemPolygon( layout );
389  readPolyXml<QgsLayoutItemPolygon, QgsFillSymbol>( layoutItem, itemNode.toElement(), layout->project() );
390  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
391  newItems << layoutItem ;
392  }
393 
394  // Polyline
395  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerPolyline" ) ).size(); i++ )
396  {
397  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerPolyline" ) ).at( i ) );
398  QgsLayoutItemPolyline *layoutItem = new QgsLayoutItemPolyline( layout );
399  readPolyXml<QgsLayoutItemPolyline, QgsLineSymbol>( layoutItem, itemNode.toElement(), layout->project() );
400  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
401  newItems << layoutItem ;
402  }
403 
404  // Arrow
405  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerArrow" ) ).size(); i++ )
406  {
407  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerArrow" ) ).at( i ) );
408  QgsLayoutItemPolyline *layoutItem = new QgsLayoutItemPolyline( layout );
409  readArrowXml( layoutItem, itemNode.toElement(), layout->project() );
410  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
411  newItems << layoutItem ;
412  }
413 
414  // Scalebar
415  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerScaleBar" ) ).size(); i++ )
416  {
417  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerScaleBar" ) ).at( i ) );
418  QgsLayoutItemScaleBar *layoutItem = new QgsLayoutItemScaleBar( layout );
419  readScaleBarXml( layoutItem, itemNode.toElement(), layout->project(), mapIdUiidMap );
420  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
421  newItems << layoutItem ;
422  }
423 
424  // Legend
425  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerLegend" ) ).size(); i++ )
426  {
427  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerLegend" ) ).at( i ) );
428  QgsLayoutItemLegend *layoutItem = new QgsLayoutItemLegend( layout );
429  readLegendXml( layoutItem, itemNode.toElement(), layout->project(), mapIdUiidMap );
430  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
431  newItems << layoutItem ;
432  }
433 
434  // Html
435  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerHtml" ) ).size(); i++ )
436  {
437  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerHtml" ) ).at( i ) );
438  QgsLayoutItemHtml *layoutItem = new QgsLayoutItemHtml( layout );
439  readHtmlXml( layoutItem, itemNode.toElement(), layout->project() );
440  // Adjust position for frames
441  const QList<QgsLayoutFrame *> framesList( layoutItem->frames() );
442  for ( const auto &frame : framesList )
443  {
444  adjustPos( layout, frame, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
445  }
446  newItems << layoutItem ;
447  }
448 
449  // Attribute Table
450  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerAttributeTableV2" ) ).size(); i++ )
451  {
452  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerAttributeTableV2" ) ).at( i ) );
453  QgsLayoutItemAttributeTable *layoutItem = new QgsLayoutItemAttributeTable( layout );
454  readTableXml( layoutItem, itemNode.toElement(), layout->project() );
455  // Adjust position for frames
456  const QList<QgsLayoutFrame *> framesList( layoutItem->frames() );
457  for ( const auto &frame : framesList )
458  {
459  adjustPos( layout, frame, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
460  }
461  newItems << layoutItem ;
462  }
463 
464  // Group
465  for ( int i = 0; i < parentElement.elementsByTagName( QStringLiteral( "ComposerItemGroup" ) ).size(); i++ )
466  {
467  const QDomNode itemNode( parentElement.elementsByTagName( QStringLiteral( "ComposerItemGroup" ) ).at( i ) );
468  QgsLayoutItemGroup *layoutItem = new QgsLayoutItemGroup( layout );
469  readGroupXml( layoutItem, itemNode.toElement(), layout->project(), newItems );
470  adjustPos( layout, layoutItem, position, pasteInPlace, zOrderOffset, pasteShiftPos, pageNumber );
471  newItems << layoutItem ;
472  }
473 
474  return newItems;
475 }
476 
477 bool QgsCompositionConverter::isCompositionTemplate( const QDomDocument &document )
478 {
479  return document.elementsByTagName( QStringLiteral( "Composition" ) ).count() > 0;
480 }
481 
482 QDomDocument QgsCompositionConverter::convertCompositionTemplate( const QDomDocument &document, QgsProject *project )
483 {
484  QDomDocument doc;
485  QgsReadWriteContext context;
486  if ( project )
487  context.setPathResolver( project->pathResolver() );
488  if ( document.elementsByTagName( QStringLiteral( "Composition" ) ).count( ) > 0 )
489  {
490  const QDomElement composerElem = document.elementsByTagName( QStringLiteral( "Composition" ) ).at( 0 ).toElement( );
491 
492  std::unique_ptr<QgsLayout> layout = createLayoutFromCompositionXml( composerElem,
493  project );
494  const QDomElement elem = layout->writeXml( doc, context );
495  doc.appendChild( elem );
496  }
497  return doc;
498 }
499 
500 bool QgsCompositionConverter::readLabelXml( QgsLayoutItemLabel *layoutItem, const QDomElement &itemElem, const QgsProject *project )
501 {
502  Q_UNUSED( project )
503  if ( itemElem.isNull() )
504  {
505  return false;
506  }
507 
508  restoreGeneralComposeItemProperties( layoutItem, itemElem );
509 
510  //text
511  layoutItem->setText( itemElem.attribute( QStringLiteral( "labelText" ) ) );
512 
513  //html state
514  layoutItem->setMode( itemElem.attribute( QStringLiteral( "htmlState" ) ).toInt() == Qt::Checked ? QgsLayoutItemLabel::Mode::ModeHtml : QgsLayoutItemLabel::Mode::ModeFont );
515 
516  //margin
517  bool marginXOk = false;
518  bool marginYOk = false;
519  double marginX = itemElem.attribute( QStringLiteral( "marginX" ) ).toDouble( &marginXOk );
520  double marginY = itemElem.attribute( QStringLiteral( "marginY" ) ).toDouble( &marginYOk );
521  if ( !marginXOk || !marginYOk )
522  {
523  //upgrade old projects where margins where stored in a single attribute
524  const double margin = itemElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "1.0" ) ).toDouble();
525  marginX = margin;
526  marginY = margin;
527  }
528  layoutItem->setMarginX( marginX );
529  layoutItem->setMarginY( marginY );
530 
531  //Horizontal alignment
532  layoutItem->setHAlign( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "halign" ) ).toInt() ) );
533 
534  //Vertical alignment
535  layoutItem->setVAlign( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "valign" ) ).toInt() ) );
536 
537 
538  QFont font;
539  //font
540  QgsFontUtils::setFromXmlChildNode( font, itemElem, QStringLiteral( "LabelFont" ) );
541  QgsTextFormat format;
542  format.setFont( font );
543  if ( font.pointSizeF() > 0 )
544  {
545  format.setSize( font.pointSizeF() );
547  }
548  else if ( font.pixelSize() > 0 )
549  {
550  format.setSize( font.pixelSize() );
552  }
553 
554  //font color
555  const QDomNodeList fontColorList = itemElem.elementsByTagName( QStringLiteral( "FontColor" ) );
556  if ( !fontColorList.isEmpty() )
557  {
558  const QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
559  const int red = fontColorElem.attribute( QStringLiteral( "red" ), QStringLiteral( "0" ) ).toInt();
560  const int green = fontColorElem.attribute( QStringLiteral( "green" ), QStringLiteral( "0" ) ).toInt();
561  const int blue = fontColorElem.attribute( QStringLiteral( "blue" ), QStringLiteral( "0" ) ).toInt();
562  format.setColor( QColor( red, green, blue ) );
563  }
564  else
565  {
566  format.setColor( QColor( 0, 0, 0 ) );
567  }
568  layoutItem->setTextFormat( format );
569 
570  return true;
571 }
572 
573 bool QgsCompositionConverter::readShapeXml( QgsLayoutItemShape *layoutItem, const QDomElement &itemElem, const QgsProject *project )
574 {
575  Q_UNUSED( project )
576  layoutItem->setShapeType( static_cast<QgsLayoutItemShape::Shape>( itemElem.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() ) );
577  layoutItem->setCornerRadius( QgsLayoutMeasurement( itemElem.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() ) );
578 
579  restoreGeneralComposeItemProperties( layoutItem, itemElem );
580 
581  QgsReadWriteContext context;
582  if ( project )
583  context.setPathResolver( project->pathResolver() );
584 
585  if ( itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).size() )
586  {
587  const QDomElement symbolElement = itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).at( 0 ).toElement();
588  const std::unique_ptr< QgsFillSymbol > shapeStyleSymbol( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElement, context ) );
589  if ( shapeStyleSymbol )
590  layoutItem->setSymbol( shapeStyleSymbol.get() );
591  }
592  else
593  {
594  //upgrade project file from 2.0 to use symbol styling
595  QVariantMap properties;
596  properties.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( layoutItem->brush().color() ) );
597  if ( layoutItem->hasBackground() )
598  {
599  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
600  }
601  else
602  {
603  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
604  }
605  if ( layoutItem->frameEnabled() )
606  {
607  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
608  }
609  else
610  {
611  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
612  }
613  properties.insert( QStringLiteral( "color_border" ), QgsSymbolLayerUtils::encodeColor( layoutItem->pen().color() ) );
614  properties.insert( QStringLiteral( "width_border" ), QString::number( layoutItem->pen().widthF() ) );
615 
616  //for pre 2.0 projects, shape color and outline were specified in a different element...
617  const QDomNodeList outlineColorList = itemElem.elementsByTagName( QStringLiteral( "OutlineColor" ) );
618  if ( !outlineColorList.isEmpty() )
619  {
620  const QDomElement frameColorElem = outlineColorList.at( 0 ).toElement();
621  bool redOk, greenOk, blueOk, alphaOk, widthOk;
622  int penRed, penGreen, penBlue, penAlpha;
623  double penWidth;
624 
625  penWidth = itemElem.attribute( QStringLiteral( "outlineWidth" ) ).toDouble( &widthOk );
626  penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toInt( &redOk );
627  penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toInt( &greenOk );
628  penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toInt( &blueOk );
629  penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toInt( &alphaOk );
630 
631  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
632  {
633  properties.insert( QStringLiteral( "color_border" ), QgsSymbolLayerUtils::encodeColor( QColor( penRed, penGreen, penBlue, penAlpha ) ) );
634  properties.insert( QStringLiteral( "width_border" ), QString::number( penWidth ) );
635  }
636  }
637  const QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "FillColor" ) );
638  if ( !fillColorList.isEmpty() )
639  {
640  const QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
641  bool redOk, greenOk, blueOk, alphaOk;
642  int fillRed, fillGreen, fillBlue, fillAlpha;
643 
644  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toInt( &redOk );
645  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toInt( &greenOk );
646  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toInt( &blueOk );
647  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toInt( &alphaOk );
648 
649  if ( redOk && greenOk && blueOk && alphaOk )
650  {
651  properties.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) ) );
652  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
653  }
654  }
655  if ( itemElem.hasAttribute( QStringLiteral( "transparentFill" ) ) )
656  {
657  //old style (pre 2.0) of specifying that shapes had no fill
658  const bool hasOldTransparentFill = itemElem.attribute( QStringLiteral( "transparentFill" ), QStringLiteral( "0" ) ).toInt();
659  if ( hasOldTransparentFill )
660  {
661  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
662  }
663  }
664 
665  const std::unique_ptr< QgsFillSymbol > shapeStyleSymbol( QgsFillSymbol::createSimple( properties ) );
666  layoutItem->setSymbol( shapeStyleSymbol.get() );
667  }
668  // Disable frame for shapes
669  layoutItem->setFrameEnabled( false );
670  layoutItem->setBackgroundEnabled( false );
671 
672  return true;
673 }
674 
675 bool QgsCompositionConverter::readPictureXml( QgsLayoutItemPicture *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QgsStringMap &mapId2Uuid )
676 {
677  restoreGeneralComposeItemProperties( layoutItem, itemElem );
678 
679  layoutItem->mResizeMode = QgsLayoutItemPicture::ResizeMode( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() );
680  //when loading from xml, default to anchor point of middle to match pre 2.4 behavior
681  bool positionModeOk = false;
682  layoutItem->mReferencePoint = static_cast< QgsLayoutItem::ReferencePoint >( itemElem.attribute( QStringLiteral( "positionMode" ) ).toInt( &positionModeOk ) );
683  if ( !positionModeOk )
684  {
685  layoutItem->mReferencePoint = QgsLayoutItem::ReferencePoint::UpperLeft;
686  }
687  bool anchorPointOk = false;
688 
689  layoutItem->setPictureAnchor( static_cast< QgsLayoutItem::ReferencePoint >( itemElem.attribute( QStringLiteral( "anchorPoint" ), QString::number( QgsLayoutItem::ReferencePoint::Middle ) ).toInt( &anchorPointOk ) ) );
690  if ( !anchorPointOk )
691  {
692  layoutItem->mPictureAnchor = QgsLayoutItem::ReferencePoint::UpperLeft;
693  }
694  layoutItem->mSvgFillColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "svgFillColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 255, 255, 255 ) ) ) );
695  layoutItem->mSvgStrokeColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "svgBorderColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 0, 0, 0 ) ) ) );
696  layoutItem->mSvgStrokeWidth = itemElem.attribute( QStringLiteral( "svgBorderWidth" ), QStringLiteral( "0.2" ) ).toDouble();
697 
698  QString imagePath = itemElem.attribute( QStringLiteral( "file" ) );
699  if ( project )
700  {
701  // convert from relative path to absolute. For SVG we also need to consider system SVG paths
702  const QgsPathResolver pathResolver = project->pathResolver();
703  if ( imagePath.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
704  imagePath = QgsSymbolLayerUtils::svgSymbolNameToPath( imagePath, pathResolver );
705  else
706  imagePath = pathResolver.readPath( imagePath );
707  }
708  layoutItem->setPicturePath( imagePath );
709  layoutItem->mPictureHeight = itemElem.attribute( QStringLiteral( "pictureHeight" ), QStringLiteral( "10" ) ).toDouble();
710  layoutItem->mPictureWidth = itemElem.attribute( QStringLiteral( "pictureWidth" ), QStringLiteral( "10" ) ).toDouble();
711 
712  //picture rotation
713  if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "pictureRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
714  {
715  layoutItem->mPictureRotation = itemElem.attribute( QStringLiteral( "pictureRotation" ), QStringLiteral( "0" ) ).toDouble();
716  }
717 
718  //rotation map
719  layoutItem->mNorthArrowHandler->setNorthMode( static_cast< QgsLayoutNorthArrowHandler::NorthMode >( itemElem.attribute( QStringLiteral( "northMode" ), QStringLiteral( "0" ) ).toInt() ) );
720  layoutItem->mNorthArrowHandler->setNorthOffset( itemElem.attribute( QStringLiteral( "northOffset" ), QStringLiteral( "0" ) ).toDouble() );
721 
722  const QString rotationMapId = itemElem.attribute( QStringLiteral( "mapId" ), QStringLiteral( "-1" ) );
723  if ( rotationMapId != QLatin1String( "-1" ) )
724  {
725  // Find uuid for map with given id
726  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ rotationMapId ] ) );
727  if ( mapInstance )
728  {
729  layoutItem->setLinkedMap( mapInstance );
730  }
731  }
732  return true;
733 }
734 
735 bool QgsCompositionConverter::readArrowXml( QgsLayoutItemPolyline *layoutItem, const QDomElement &itemElem, const QgsProject *project )
736 {
737  readPolyXml<QgsLayoutItemPolyline, QgsLineSymbol>( layoutItem, itemElem, project );
738  QPolygonF polygon;
739  const QDomNodeList startPointList = itemElem.elementsByTagName( QStringLiteral( "StartPoint" ) );
740  if ( ! startPointList.isEmpty() )
741  {
742  const QDomElement node = startPointList.at( 0 ).toElement();
743  polygon.append( QPointF( node.attribute( QStringLiteral( "x" ) ).toDouble( ), node.attribute( QStringLiteral( "y" ) ).toDouble() ) );
744  }
745  const QDomNodeList stopPointList = itemElem.elementsByTagName( QStringLiteral( "StopPoint" ) );
746  if ( ! stopPointList.isEmpty() )
747  {
748  const QDomElement node = stopPointList.at( 0 ).toElement();
749  polygon.append( QPointF( node.attribute( QStringLiteral( "x" ) ).toDouble( ), node.attribute( QStringLiteral( "y" ) ).toDouble() ) );
750  }
751 
752  const QgsCompositionConverter::MarkerMode markerMode = static_cast< QgsCompositionConverter::MarkerMode>( itemElem.attribute( QStringLiteral( "markerMode" ), QStringLiteral( "0" ) ).toInt( ) );
753 
754  if ( markerMode == QgsCompositionConverter::MarkerMode::DefaultMarker )
755  {
756  layoutItem->setEndMarker( QgsLayoutItemPolyline::MarkerMode::ArrowHead );
757  layoutItem->setStartMarker( QgsLayoutItemPolyline::MarkerMode::NoMarker );
758  layoutItem->setArrowHeadFillColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "arrowHeadFillColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 255, 255, 255 ) ) ) ) );
759  layoutItem->setArrowHeadStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "arrowHeadOutlineColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 255, 255, 255 ) ) ) ) );
760  layoutItem->setArrowHeadStrokeWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "1.0" ) ).toDouble( ) );
761  layoutItem->setArrowHeadWidth( itemElem.attribute( QStringLiteral( "arrowHeadWidth" ), QStringLiteral( "1.0" ) ).toDouble( ) );
762  }
763  else if ( markerMode == QgsCompositionConverter::MarkerMode::SVGMarker )
764  {
765  QString endMarkerFile = itemElem.attribute( QStringLiteral( "endMarkerFile" ) );
766  QString startMarkerFile = itemElem.attribute( QStringLiteral( "endMarkerFile" ) );
767 
768  // Fix the paths
769  if ( project )
770  {
771  // convert from relative path to absolute. For SVG we also need to consider system SVG paths
772  const QgsPathResolver pathResolver = project->pathResolver();
773  if ( !endMarkerFile.isEmpty() )
774  {
775  if ( endMarkerFile.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
776  endMarkerFile = QgsSymbolLayerUtils::svgSymbolNameToPath( endMarkerFile, pathResolver );
777  else
778  endMarkerFile = pathResolver.readPath( endMarkerFile );
779  }
780  if ( !startMarkerFile.isEmpty() )
781  {
782  if ( startMarkerFile.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
783  startMarkerFile = QgsSymbolLayerUtils::svgSymbolNameToPath( startMarkerFile, pathResolver );
784  else
785  startMarkerFile = pathResolver.readPath( startMarkerFile );
786  }
787  }
788  if ( !endMarkerFile.isEmpty() )
789  {
790  layoutItem->setEndMarker( QgsLayoutItemPolyline::MarkerMode::SvgMarker );
791  layoutItem->setEndSvgMarkerPath( endMarkerFile );
792  }
793  if ( !startMarkerFile.isEmpty() )
794  {
795  layoutItem->setStartMarker( QgsLayoutItemPolyline::MarkerMode::SvgMarker );
796  layoutItem->setStartSvgMarkerPath( startMarkerFile );
797  }
798  }
799  else // NoMarker
800  {
801  layoutItem->setEndMarker( QgsLayoutItemPolyline::MarkerMode::NoMarker );
802  layoutItem->setStartMarker( QgsLayoutItemPolyline::MarkerMode::NoMarker );
803  }
804  // Calculate the margin
805  const double margin = polygon.boundingRect().left() - layoutItem->pos().x();
806  polygon.translate( - polygon.boundingRect().left() + margin, - polygon.boundingRect().top() + margin );
807  layoutItem->setNodes( polygon );
808 
809  return true;
810 }
811 
812 bool QgsCompositionConverter::readMapXml( QgsLayoutItemMap *layoutItem, const QDomElement &itemElem, const QgsProject *project, QgsStringMap &mapId2Uuid )
813 {
814  restoreGeneralComposeItemProperties( layoutItem, itemElem );
815 
816  mapId2Uuid[ itemElem.attribute( QStringLiteral( "id" ) ) ] = layoutItem->uuid();
817 
818  // TODO: Unused but all the layouts readXML require it (I'd suggest to remove it from the API)
819  const QDomDocument doc;
820 
821  QgsReadWriteContext context;
822 
823  if ( project )
824  context.setPathResolver( project->pathResolver() );
825 
826  //extent
827  const QDomNodeList extentNodeList = itemElem.elementsByTagName( QStringLiteral( "Extent" ) );
828  if ( !extentNodeList.isEmpty() )
829  {
830  const QDomElement extentElem = extentNodeList.at( 0 ).toElement();
831  double xmin, xmax, ymin, ymax;
832  xmin = extentElem.attribute( QStringLiteral( "xmin" ) ).toDouble();
833  xmax = extentElem.attribute( QStringLiteral( "xmax" ) ).toDouble();
834  ymin = extentElem.attribute( QStringLiteral( "ymin" ) ).toDouble();
835  ymax = extentElem.attribute( QStringLiteral( "ymax" ) ).toDouble();
836  layoutItem->setExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
837  }
838 
839  const QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
840  if ( !crsNodeList.isEmpty() )
841  {
842  const QDomElement crsElem = crsNodeList.at( 0 ).toElement();
843  layoutItem->crs().readXml( crsElem );
844  }
845  else
846  {
847  layoutItem->setCrs( QgsCoordinateReferenceSystem() );
848  }
849 
850  //map rotation
851  if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
852  {
853  layoutItem->setMapRotation( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble() );
854  }
855 
856  // follow map theme
857  layoutItem->setFollowVisibilityPreset( itemElem.attribute( QStringLiteral( "followPreset" ) ).compare( QLatin1String( "true" ) ) == 0 );
858  layoutItem->setFollowVisibilityPresetName( itemElem.attribute( QStringLiteral( "followPresetName" ) ) );
859 
860  //mKeepLayerSet flag
861  const QString keepLayerSetFlag = itemElem.attribute( QStringLiteral( "keepLayerSet" ) );
862  if ( keepLayerSetFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
863  {
864  layoutItem->setKeepLayerSet( true );
865  }
866  else
867  {
868  layoutItem->setKeepLayerSet( false );
869  }
870 
871  const QString drawCanvasItemsFlag = itemElem.attribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
872  if ( drawCanvasItemsFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
873  {
874  layoutItem->setDrawAnnotations( true );
875  }
876  else
877  {
878  layoutItem->setDrawAnnotations( false );
879  }
880 
881  layoutItem->mLayerStyleOverrides.clear();
882 
883  //mLayers
884  layoutItem->mLayers.clear();
885 
886  const QDomNodeList layerSetNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerSet" ) );
887  if ( !layerSetNodeList.isEmpty() )
888  {
889  const QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
890  const QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral( "Layer" ) );
891  layoutItem->mLayers.reserve( layerIdNodeList.size() );
892  for ( int i = 0; i < layerIdNodeList.size(); ++i )
893  {
894  const QDomElement layerElem = layerIdNodeList.at( i ).toElement();
895  const QString layerId = layerElem.text();
896  const QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
897  const QString layerSource = layerElem.attribute( QStringLiteral( "source" ) );
898  const QString layerProvider = layerElem.attribute( QStringLiteral( "provider" ) );
899 
900  QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
901  ref.resolveWeakly( project );
902  layoutItem->mLayers << ref;
903  }
904  }
905 
906  // override styles
907  const QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerStyles" ) );
908  layoutItem->mKeepLayerStyles = !layerStylesNodeList.isEmpty();
909  if ( layoutItem->mKeepLayerStyles )
910  {
911  const QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
912  const QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( QStringLiteral( "LayerStyle" ) );
913  for ( int i = 0; i < layerStyleNodeList.size(); ++i )
914  {
915  const QDomElement &layerStyleElement = layerStyleNodeList.at( i ).toElement();
916  const QString layerId = layerStyleElement.attribute( QStringLiteral( "layerid" ) );
917  const QString layerName = layerStyleElement.attribute( QStringLiteral( "name" ) );
918  const QString layerSource = layerStyleElement.attribute( QStringLiteral( "source" ) );
919  const QString layerProvider = layerStyleElement.attribute( QStringLiteral( "provider" ) );
920  QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
921  ref.resolveWeakly( project );
922 
923  QgsMapLayerStyle style;
924  style.readXml( layerStyleElement );
925  layoutItem->mLayerStyleOverrides.insert( ref.layerId, style.xmlData() );
926  }
927  }
928 
929  layoutItem->mDrawing = false;
930  layoutItem->mNumCachedLayers = 0;
931  layoutItem->mCacheInvalidated = true;
932 
933  //overviews
934  //read overview stack
935  const QDomNodeList mapOverviewNodeList = itemElem.elementsByTagName( QStringLiteral( "ComposerMapOverview" ) );
936  for ( int i = 0; i < mapOverviewNodeList.size(); ++i )
937  {
938  const QDomElement mapOverviewElem = mapOverviewNodeList.at( i ).toElement();
939  std::unique_ptr<QgsLayoutItemMapOverview> mapOverview( new QgsLayoutItemMapOverview( mapOverviewElem.attribute( QStringLiteral( "name" ) ), layoutItem ) );
940  mapOverview->readXml( mapOverviewElem, doc, context );
941  const QString frameMapId = mapOverviewElem.attribute( QStringLiteral( "frameMap" ), QStringLiteral( "-1" ) );
942  if ( frameMapId != QLatin1String( "-1" ) && mapId2Uuid.contains( frameMapId ) )
943  {
944  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ frameMapId ] ) );
945  if ( mapInstance )
946  {
947  mapOverview->setLinkedMap( mapInstance );
948  }
949  layoutItem->mOverviewStack->addOverview( mapOverview.release() );
950  }
951  }
952 
953  //grids
954  layoutItem->mGridStack->readXml( itemElem, doc, context );
955 
956  //load grid / grid annotation in old xml format
957  //only do this if the grid stack didn't load any grids, otherwise this will
958  //be the dummy element created by QGIS >= 2.5 (refs #10905)
959  const QDomNodeList gridNodeList = itemElem.elementsByTagName( QStringLiteral( "Grid" ) );
960  if ( layoutItem->mGridStack->size() == 0 && !gridNodeList.isEmpty() )
961  {
962  const QDomElement gridElem = gridNodeList.at( 0 ).toElement();
963  QgsLayoutItemMapGrid *mapGrid = new QgsLayoutItemMapGrid( QObject::tr( "Grid %1" ).arg( 1 ), layoutItem );
964  mapGrid->setEnabled( gridElem.attribute( QStringLiteral( "show" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
965  mapGrid->setStyle( QgsLayoutItemMapGrid::GridStyle( gridElem.attribute( QStringLiteral( "gridStyle" ), QStringLiteral( "0" ) ).toInt() ) );
966  mapGrid->setIntervalX( gridElem.attribute( QStringLiteral( "intervalX" ), QStringLiteral( "0" ) ).toDouble() );
967  mapGrid->setIntervalY( gridElem.attribute( QStringLiteral( "intervalY" ), QStringLiteral( "0" ) ).toDouble() );
968  mapGrid->setOffsetX( gridElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble() );
969  mapGrid->setOffsetY( gridElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble() );
970  mapGrid->setCrossLength( gridElem.attribute( QStringLiteral( "crossLength" ), QStringLiteral( "3" ) ).toDouble() );
971  mapGrid->setFrameStyle( static_cast< QgsLayoutItemMapGrid::FrameStyle >( gridElem.attribute( QStringLiteral( "gridFrameStyle" ), QStringLiteral( "0" ) ).toInt() ) );
972  mapGrid->setFrameWidth( gridElem.attribute( QStringLiteral( "gridFrameWidth" ), QStringLiteral( "2.0" ) ).toDouble() );
973  mapGrid->setFramePenSize( gridElem.attribute( QStringLiteral( "gridFramePenThickness" ), QStringLiteral( "0.5" ) ).toDouble() );
974  mapGrid->setFramePenColor( QgsSymbolLayerUtils::decodeColor( gridElem.attribute( QStringLiteral( "framePenColor" ), QStringLiteral( "0,0,0" ) ) ) );
975  mapGrid->setFrameFillColor1( QgsSymbolLayerUtils::decodeColor( gridElem.attribute( QStringLiteral( "frameFillColor1" ), QStringLiteral( "255,255,255,255" ) ) ) );
976  mapGrid->setFrameFillColor2( QgsSymbolLayerUtils::decodeColor( gridElem.attribute( QStringLiteral( "frameFillColor2" ), QStringLiteral( "0,0,0,255" ) ) ) );
977  mapGrid->setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "gridBlendMode" ), QStringLiteral( "0" ) ).toUInt() ) ) );
978  const QDomElement gridSymbolElem = gridElem.firstChildElement( QStringLiteral( "symbol" ) );
979  QgsLineSymbol *lineSymbol = nullptr;
980  if ( gridSymbolElem.isNull() )
981  {
982  //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
983  lineSymbol = QgsLineSymbol::createSimple( QVariantMap() );
984  lineSymbol->setWidth( gridElem.attribute( QStringLiteral( "penWidth" ), QStringLiteral( "0" ) ).toDouble() );
985  lineSymbol->setColor( QColor( gridElem.attribute( QStringLiteral( "penColorRed" ), QStringLiteral( "0" ) ).toInt(),
986  gridElem.attribute( QStringLiteral( "penColorGreen" ), QStringLiteral( "0" ) ).toInt(),
987  gridElem.attribute( QStringLiteral( "penColorBlue" ), QStringLiteral( "0" ) ).toInt() ) );
988  }
989  else
990  {
991  lineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( gridSymbolElem, context );
992  }
993  mapGrid->setLineSymbol( lineSymbol );
994 
995  //annotation
996  const QDomNodeList annotationNodeList = gridElem.elementsByTagName( QStringLiteral( "Annotation" ) );
997  if ( !annotationNodeList.isEmpty() )
998  {
999  const QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
1000  mapGrid->setAnnotationEnabled( annotationElem.attribute( QStringLiteral( "show" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
1001  mapGrid->setAnnotationFormat( QgsLayoutItemMapGrid::AnnotationFormat( annotationElem.attribute( QStringLiteral( "format" ), QStringLiteral( "0" ) ).toInt() ) );
1002  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "leftPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Left );
1003  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "rightPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Right );
1004  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "topPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Top );
1005  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "bottomPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Bottom );
1006  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "leftDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Left );
1007  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "rightDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Right );
1008  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "topDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Top );
1009  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "bottomDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Bottom );
1010  mapGrid->setAnnotationFrameDistance( annotationElem.attribute( QStringLiteral( "frameDistance" ), QStringLiteral( "0" ) ).toDouble() );
1011  QFont annotationFont;
1012  annotationFont.fromString( annotationElem.attribute( QStringLiteral( "font" ), QString() ) );
1013 
1014  QgsTextFormat annotationFormat = mapGrid->annotationTextFormat();
1015  annotationFormat.setFont( annotationFont );
1016  if ( annotationFont.pointSizeF() > 0 )
1017  {
1018  annotationFormat.setSize( annotationFont.pointSizeF() );
1019  annotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
1020  }
1021  else if ( annotationFont.pixelSize() > 0 )
1022  {
1023  annotationFormat.setSize( annotationFont.pixelSize() );
1024  annotationFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
1025  }
1026  annotationFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1027  mapGrid->setAnnotationTextFormat( annotationFormat );
1028 
1029  mapGrid->setAnnotationPrecision( annotationElem.attribute( QStringLiteral( "precision" ), QStringLiteral( "3" ) ).toInt() );
1030  }
1031  layoutItem->mGridStack->addGrid( mapGrid );
1032  }
1033 
1034  //atlas
1035  const QDomNodeList atlasNodeList = itemElem.elementsByTagName( QStringLiteral( "AtlasMap" ) );
1036  if ( !atlasNodeList.isEmpty() )
1037  {
1038  const QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
1039  layoutItem->mAtlasDriven = ( atlasElem.attribute( QStringLiteral( "atlasDriven" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
1040  if ( atlasElem.hasAttribute( QStringLiteral( "fixedScale" ) ) ) // deprecated XML
1041  {
1042  layoutItem->setAtlasScalingMode( atlasElem.attribute( QStringLiteral( "fixedScale" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) ? QgsLayoutItemMap::AtlasScalingMode::Fixed : QgsLayoutItemMap::AtlasScalingMode::Auto );
1043  }
1044  else if ( atlasElem.hasAttribute( QStringLiteral( "scalingMode" ) ) )
1045  {
1046  layoutItem->setAtlasScalingMode( static_cast<QgsLayoutItemMap::AtlasScalingMode>( atlasElem.attribute( QStringLiteral( "scalingMode" ) ).toInt() ) );
1047  }
1048  layoutItem->setAtlasMargin( atlasElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "0.1" ) ).toDouble() );
1049  }
1050 
1051  layoutItem->updateBoundingRect();
1052 
1053  return true;
1054 }
1055 
1056 bool QgsCompositionConverter::readScaleBarXml( QgsLayoutItemScaleBar *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QgsStringMap &mapId2Uuid )
1057 {
1058  Q_UNUSED( project )
1059  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1060 
1061  layoutItem->setHeight( itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble() );
1062  layoutItem->setHeight( itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble() );
1063  layoutItem->setLabelBarSpace( itemElem.attribute( QStringLiteral( "labelBarSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
1064  layoutItem->setBoxContentSpace( itemElem.attribute( QStringLiteral( "boxContentSpace" ), QStringLiteral( "1.0" ) ).toDouble() );
1065  layoutItem->setNumberOfSegments( itemElem.attribute( QStringLiteral( "numSegments" ), QStringLiteral( "2" ) ).toInt() );
1066  layoutItem->setNumberOfSegmentsLeft( itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt() );
1067  layoutItem->setUnitsPerSegment( itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble() );
1068  layoutItem->setSegmentSizeMode( static_cast<QgsScaleBarSettings::SegmentSizeMode>( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
1069  layoutItem->setMinimumBarWidth( itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toDouble() );
1070  layoutItem->setMaximumBarWidth( itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toDouble() );
1071  layoutItem->mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
1072  layoutItem->setMapUnitsPerScaleBarUnit( itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble() );
1073  layoutItem->setUnitLabel( itemElem.attribute( QStringLiteral( "unitLabel" ) ) );
1074 
1075  QFont f;
1076  if ( !QgsFontUtils::setFromXmlChildNode( f, itemElem, QStringLiteral( "scaleBarFont" ) ) )
1077  {
1078  f.fromString( itemElem.attribute( QStringLiteral( "font" ), QString() ) );
1079  }
1081  layoutItem->setFont( f );
1083 
1084  //colors
1085  //fill color
1086  const QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
1087  if ( !fillColorList.isEmpty() )
1088  {
1089  const QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
1090  bool redOk, greenOk, blueOk, alphaOk;
1091  int fillRed, fillGreen, fillBlue, fillAlpha;
1092 
1093  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1094  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1095  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1096  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1097 
1098  if ( redOk && greenOk && blueOk && alphaOk )
1099  {
1100  layoutItem->fillSymbol()->setColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
1101  }
1102  }
1103  else
1104  {
1105  layoutItem->fillSymbol()->setColor( QColor( itemElem.attribute( QStringLiteral( "brushColor" ), QStringLiteral( "#000000" ) ) ) );
1106  }
1107 
1108  //fill color 2
1109  const QDomNodeList fillColor2List = itemElem.elementsByTagName( QStringLiteral( "fillColor2" ) );
1110  if ( !fillColor2List.isEmpty() )
1111  {
1112  const QDomElement fillColor2Elem = fillColor2List.at( 0 ).toElement();
1113  bool redOk, greenOk, blueOk, alphaOk;
1114  int fillRed, fillGreen, fillBlue, fillAlpha;
1115 
1116  fillRed = fillColor2Elem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1117  fillGreen = fillColor2Elem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1118  fillBlue = fillColor2Elem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1119  fillAlpha = fillColor2Elem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1120 
1121  if ( redOk && greenOk && blueOk && alphaOk )
1122  {
1123  layoutItem->alternateFillSymbol()->setColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
1124  }
1125  }
1126  else
1127  {
1128  layoutItem->alternateFillSymbol()->setColor( QColor( itemElem.attribute( QStringLiteral( "brush2Color" ), QStringLiteral( "#ffffff" ) ) ) );
1129  }
1130 
1131  std::unique_ptr< QgsLineSymbol > lineSymbol = std::make_unique< QgsLineSymbol >();
1132  std::unique_ptr< QgsSimpleLineSymbolLayer > lineSymbolLayer = std::make_unique< QgsSimpleLineSymbolLayer >();
1133  lineSymbolLayer->setWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble() );
1134  lineSymbolLayer->setWidthUnit( QgsUnitTypes::RenderMillimeters );
1135  lineSymbolLayer->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "lineJoinStyle" ), QStringLiteral( "miter" ) ) ) );
1136  lineSymbolLayer->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( itemElem.attribute( QStringLiteral( "lineCapStyle" ), QStringLiteral( "square" ) ) ) );
1137  //stroke color
1138  const QDomNodeList strokeColorList = itemElem.elementsByTagName( QStringLiteral( "strokeColor" ) );
1139  if ( !strokeColorList.isEmpty() )
1140  {
1141  const QDomElement strokeColorElem = strokeColorList.at( 0 ).toElement();
1142  bool redOk, greenOk, blueOk, alphaOk;
1143  int strokeRed, strokeGreen, strokeBlue, strokeAlpha;
1144 
1145  strokeRed = strokeColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1146  strokeGreen = strokeColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1147  strokeBlue = strokeColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1148  strokeAlpha = strokeColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1149 
1150  if ( redOk && greenOk && blueOk && alphaOk )
1151  {
1152  lineSymbolLayer->setColor( QColor( strokeRed, strokeGreen, strokeBlue, strokeAlpha ) );
1153  }
1154  }
1155  else
1156  {
1157  lineSymbolLayer->setColor( QColor( itemElem.attribute( QStringLiteral( "penColor" ), QStringLiteral( "#000000" ) ) ) );
1158  }
1159  lineSymbol->changeSymbolLayer( 0, lineSymbolLayer.release() );
1160  layoutItem->setDivisionLineSymbol( lineSymbol->clone() );
1161  layoutItem->setSubdivisionLineSymbol( lineSymbol->clone() );
1162  layoutItem->setLineSymbol( lineSymbol.release() );
1163 
1164  //font color
1165  const QDomNodeList textColorList = itemElem.elementsByTagName( QStringLiteral( "textColor" ) );
1166  if ( !textColorList.isEmpty() )
1167  {
1168  const QDomElement textColorElem = textColorList.at( 0 ).toElement();
1169  bool redOk, greenOk, blueOk, alphaOk;
1170  int textRed, textGreen, textBlue, textAlpha;
1171 
1172  textRed = textColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1173  textGreen = textColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1174  textBlue = textColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1175  textAlpha = textColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1176 
1177  if ( redOk && greenOk && blueOk && alphaOk )
1178  {
1180  layoutItem->setFontColor( QColor( textRed, textGreen, textBlue, textAlpha ) );
1182  }
1183  }
1184  else
1185  {
1186  QColor c;
1187  c.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
1189  layoutItem->setFontColor( c );
1191  }
1192 
1193  //style
1194  const QString styleString = itemElem.attribute( QStringLiteral( "style" ), QString() );
1195  layoutItem->setStyle( QObject::tr( styleString.toLocal8Bit().data() ) );
1196 
1197  if ( itemElem.attribute( QStringLiteral( "unitType" ) ).isEmpty() )
1198  {
1200  switch ( itemElem.attribute( QStringLiteral( "units" ) ).toInt() )
1201  {
1202  case 0:
1204  break;
1205  case 1:
1207  break;
1208  case 2:
1210  break;
1211  case 3:
1213  break;
1214  }
1215  layoutItem->setUnits( u );
1216  }
1217  else
1218  {
1219  layoutItem->setUnits( QgsUnitTypes::decodeDistanceUnit( itemElem.attribute( QStringLiteral( "unitType" ) ) ) );
1220  }
1221  layoutItem->setAlignment( static_cast< QgsScaleBarSettings::Alignment >( itemElem.attribute( QStringLiteral( "alignment" ), QStringLiteral( "0" ) ).toInt() ) );
1222 
1223  //composer map: use uuid
1224  const QString mapId = itemElem.attribute( QStringLiteral( "mapId" ), QStringLiteral( "-1" ) );
1225  if ( mapId != QLatin1String( "-1" ) && mapId2Uuid.contains( mapId ) )
1226  {
1227  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ mapId ] ) );
1228  if ( mapInstance )
1229  {
1230  layoutItem->setLinkedMap( mapInstance );
1231  }
1232  }
1233 
1234  return true;
1235 }
1236 
1237 bool QgsCompositionConverter::readLegendXml( QgsLayoutItemLegend *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QgsStringMap &mapId2Uuid )
1238 {
1239  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1240 
1241  QgsPathResolver pathResolver;
1242  if ( project )
1243  pathResolver = project->pathResolver();
1244  QgsReadWriteContext context;
1245  context.setPathResolver( pathResolver );
1246  context.setProjectTranslator( const_cast<QgsProject *>( project ) );
1247 
1248  //composer map: use uuid
1249  const QString mapId = itemElem.attribute( QStringLiteral( "map" ), QStringLiteral( "-1" ) );
1250  if ( mapId != QLatin1String( "-1" ) && mapId2Uuid.contains( mapId ) )
1251  {
1252  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ mapId ] ) );
1253  if ( mapInstance )
1254  {
1255  layoutItem->setLinkedMap( mapInstance );
1256  }
1257  }
1258 
1259  //read general properties
1260  layoutItem->setTitle( itemElem.attribute( QStringLiteral( "title" ) ) );
1261  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
1262  {
1263  layoutItem->setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
1264  }
1265  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
1266  if ( colCount < 1 ) colCount = 1;
1267  layoutItem->setColumnCount( colCount );
1268  layoutItem->setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
1269  layoutItem->setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
1270 
1271  const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
1272  if ( !stylesNodeList.isEmpty() )
1273  {
1274  const QDomNode stylesNode = stylesNodeList.at( 0 );
1275  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
1276  {
1277  const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
1278  QgsLegendStyle style;
1279  style.readXml( styleElem, QDomDocument() );
1280  const QString name = styleElem.attribute( QStringLiteral( "name" ) );
1282  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
1283  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
1284  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
1285  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
1286  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
1287  else continue;
1288  layoutItem->setStyle( s, style );
1289  }
1290  }
1291 
1292  //font color
1293  QColor fontClr;
1294  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
1295  layoutItem->setFontColor( fontClr );
1296 
1297  //spaces
1298  layoutItem->setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
1299  layoutItem->setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
1300 
1301  layoutItem->setSymbolWidth( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble() );
1302  layoutItem->setSymbolHeight( itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() );
1303  layoutItem->setWmsLegendWidth( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble() );
1304  layoutItem->setWmsLegendHeight( itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() );
1305  layoutItem->setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble() );
1306 
1307  layoutItem->setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
1308  layoutItem->setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
1309  layoutItem->setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
1310 
1311  layoutItem->setWrapString( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
1312 
1313  layoutItem->mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
1314  layoutItem->mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
1315  layoutItem->mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
1316 
1317  // QGIS >= 2.6
1318  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
1319  if ( layerTreeElem.isNull() )
1320  layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
1321 
1322  if ( !layerTreeElem.isNull() )
1323  {
1324  QgsLayerTree *tree( QgsLayerTree::readXml( layerTreeElem, context ) );
1325  if ( project )
1326  tree->resolveReferences( project, true );
1327  layoutItem->setCustomLayerTree( tree );
1328  }
1329  else
1330  {
1331  layoutItem->setCustomLayerTree( nullptr );
1332  }
1333 
1334  return true;
1335 }
1336 
1337 bool QgsCompositionConverter::readAtlasXml( QgsLayoutAtlas *atlasItem, const QDomElement &itemElem, const QgsProject *project )
1338 {
1339  atlasItem->setEnabled( itemElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1340 
1341  // look for stored layer name
1342  const QString layerId = itemElem.attribute( QStringLiteral( "coverageLayer" ) );
1343  const QString layerName = itemElem.attribute( QStringLiteral( "coverageLayerName" ) );
1344  const QString layerSource = itemElem.attribute( QStringLiteral( "coverageLayerSource" ) );
1345  const QString layerProvider = itemElem.attribute( QStringLiteral( "coverageLayerProvider" ) );
1346 
1347  QgsVectorLayerRef layerRef( layerId, layerName, layerSource, layerProvider );
1348  atlasItem->setCoverageLayer( layerRef.resolveWeakly( project ) );
1349 
1350  atlasItem->setPageNameExpression( itemElem.attribute( QStringLiteral( "pageNameExpression" ), QString() ) );
1351  QString errorString;
1352  atlasItem->setFilenameExpression( itemElem.attribute( QStringLiteral( "filenamePattern" ), QString() ), errorString );
1353  // note: no error reporting for errorString
1354  atlasItem->setSortFeatures( itemElem.attribute( QStringLiteral( "sortFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1355  if ( atlasItem->sortFeatures() )
1356  {
1357  atlasItem->setSortExpression( itemElem.attribute( QStringLiteral( "sortKey" ), QString() ) );
1358  atlasItem->setSortAscending( itemElem.attribute( QStringLiteral( "sortAscending" ), QStringLiteral( "true" ) ) == QLatin1String( "true" ) );
1359  }
1360  atlasItem->setFilterFeatures( itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1361  if ( atlasItem->filterFeatures( ) )
1362  {
1363  QString expErrorString;
1364  atlasItem->setFilterExpression( itemElem.attribute( QStringLiteral( "featureFilter" ), QString() ), expErrorString );
1365  // note: no error reporting for errorString
1366  }
1367 
1368  atlasItem->setHideCoverage( itemElem.attribute( QStringLiteral( "hideCoverage" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1369 
1370  return true;
1371 
1372 }
1373 
1374 bool QgsCompositionConverter::readHtmlXml( QgsLayoutItemHtml *layoutItem, const QDomElement &itemElem, const QgsProject *project )
1375 {
1376  Q_UNUSED( project )
1377  readOldComposerObjectXml( layoutItem, itemElem );
1378 
1379  //first create the frames
1380  layoutItem->setResizeMode( static_cast< QgsLayoutMultiFrame::ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
1381  const QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
1382  for ( int i = 0; i < frameList.size(); ++i )
1383  {
1384  const QDomElement frameElem = frameList.at( i ).toElement();
1385  QgsLayoutFrame *newFrame = new QgsLayoutFrame( layoutItem->layout(), layoutItem );
1386  restoreGeneralComposeItemProperties( newFrame, frameElem );
1387  // Read frame XML
1388  const double x = itemElem.attribute( QStringLiteral( "sectionX" ) ).toDouble();
1389  const double y = itemElem.attribute( QStringLiteral( "sectionY" ) ).toDouble();
1390  const double width = itemElem.attribute( QStringLiteral( "sectionWidth" ) ).toDouble();
1391  const double height = itemElem.attribute( QStringLiteral( "sectionHeight" ) ).toDouble();
1392  newFrame->setContentSection( QRectF( x, y, width, height ) );
1393  newFrame->setHidePageIfEmpty( itemElem.attribute( QStringLiteral( "hidePageIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1394  newFrame->setHideBackgroundIfEmpty( itemElem.attribute( QStringLiteral( "hideBackgroundIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1395  layoutItem->addFrame( newFrame, false );
1396  }
1397 
1398  bool contentModeOK;
1399  layoutItem->setContentMode( static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( QStringLiteral( "contentMode" ) ).toInt( &contentModeOK ) ) );
1400  if ( !contentModeOK )
1401  {
1402  layoutItem->setContentMode( QgsLayoutItemHtml::ContentMode::Url );
1403  }
1404  layoutItem->setEvaluateExpressions( itemElem.attribute( QStringLiteral( "evaluateExpressions" ), QStringLiteral( "true" ) ) == QLatin1String( "true" ) );
1405  layoutItem->setUseSmartBreaks( itemElem.attribute( QStringLiteral( "useSmartBreaks" ), QStringLiteral( "true" ) ) == QLatin1String( "true" ) );
1406  layoutItem->setMaxBreakDistance( itemElem.attribute( QStringLiteral( "maxBreakDistance" ), QStringLiteral( "10" ) ).toDouble() );
1407  layoutItem->setHtml( itemElem.attribute( QStringLiteral( "html" ) ) );
1408  layoutItem->setUserStylesheet( itemElem.attribute( QStringLiteral( "stylesheet" ) ) );
1409  layoutItem->setUserStylesheetEnabled( itemElem.attribute( QStringLiteral( "stylesheetEnabled" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1410 
1411  //finally load the set url
1412  const QString urlString = itemElem.attribute( QStringLiteral( "url" ) );
1413  if ( !urlString.isEmpty() )
1414  {
1415  layoutItem->setUrl( urlString );
1416  }
1417  layoutItem->loadHtml( true );
1418 
1419  return true;
1420 }
1421 
1422 bool QgsCompositionConverter::readTableXml( QgsLayoutItemAttributeTable *layoutItem, const QDomElement &itemElem, const QgsProject *project )
1423 {
1424 
1425  Q_UNUSED( project )
1426  readOldComposerObjectXml( layoutItem, itemElem );
1427 
1428  //first create the frames
1429  layoutItem->setResizeMode( static_cast< QgsLayoutMultiFrame::ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
1430  const QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
1431  for ( int i = 0; i < frameList.size(); ++i )
1432  {
1433  const QDomElement frameElem = frameList.at( i ).toElement();
1434  QgsLayoutFrame *newFrame = new QgsLayoutFrame( layoutItem->layout(), layoutItem );
1435  restoreGeneralComposeItemProperties( newFrame, frameElem );
1436  // Read frame XML
1437  const double x = itemElem.attribute( QStringLiteral( "sectionX" ) ).toDouble();
1438  const double y = itemElem.attribute( QStringLiteral( "sectionY" ) ).toDouble();
1439  const double width = itemElem.attribute( QStringLiteral( "sectionWidth" ) ).toDouble();
1440  const double height = itemElem.attribute( QStringLiteral( "sectionHeight" ) ).toDouble();
1441  newFrame->setContentSection( QRectF( x, y, width, height ) );
1442  newFrame->setHidePageIfEmpty( itemElem.attribute( QStringLiteral( "hidePageIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1443  newFrame->setHideBackgroundIfEmpty( itemElem.attribute( QStringLiteral( "hideBackgroundIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1444  layoutItem->addFrame( newFrame, false );
1445  }
1446 
1447  layoutItem->setEmptyTableBehavior( static_cast<QgsLayoutTable::EmptyTableMode>( itemElem.attribute( QStringLiteral( "emptyTableMode" ), QStringLiteral( "0" ) ).toInt() ) );
1448  layoutItem->setEmptyTableMessage( itemElem.attribute( QStringLiteral( "emptyTableMessage" ), QObject::tr( "No matching records" ) ) );
1449  layoutItem->setShowEmptyRows( itemElem.attribute( QStringLiteral( "showEmptyRows" ), QStringLiteral( "0" ) ).toInt() );
1450  QFont headerFont;
1451  if ( !QgsFontUtils::setFromXmlChildNode( headerFont, itemElem, QStringLiteral( "headerFontProperties" ) ) )
1452  {
1453  headerFont.fromString( itemElem.attribute( QStringLiteral( "headerFont" ), QString() ) );
1454  }
1455  QgsTextFormat headerFormat = layoutItem->headerTextFormat();
1456  headerFormat.setFont( headerFont );
1457  if ( headerFont.pointSizeF() > 0 )
1458  {
1459  headerFormat.setSize( headerFont.pointSizeF() );
1460  headerFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
1461  }
1462  else if ( headerFont.pixelSize() > 0 )
1463  {
1464  headerFormat.setSize( headerFont.pixelSize() );
1465  headerFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
1466  }
1467  headerFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "headerFontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1468  layoutItem->setHeaderTextFormat( headerFormat );
1469  layoutItem->setHeaderHAlignment( static_cast<QgsLayoutTable::HeaderHAlignment>( itemElem.attribute( QStringLiteral( "headerHAlignment" ), QStringLiteral( "0" ) ).toInt() ) ) ;
1470  layoutItem->setHeaderMode( static_cast<QgsLayoutTable::HeaderMode>( itemElem.attribute( QStringLiteral( "headerMode" ), QStringLiteral( "0" ) ).toInt() ) );
1471 
1472  QFont contentFont;
1473  if ( !QgsFontUtils::setFromXmlChildNode( contentFont, itemElem, QStringLiteral( "contentFontProperties" ) ) )
1474  {
1475  contentFont.fromString( itemElem.attribute( QStringLiteral( "contentFont" ), QString() ) );
1476  }
1477  QgsTextFormat contentFormat = layoutItem->contentTextFormat();
1478  contentFormat.setFont( contentFont );
1479  if ( contentFont.pointSizeF() > 0 )
1480  {
1481  contentFormat.setSize( contentFont.pointSizeF() );
1482  contentFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
1483  }
1484  else if ( contentFont.pixelSize() > 0 )
1485  {
1486  contentFormat.setSize( contentFont.pixelSize() );
1487  contentFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
1488  }
1489  contentFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "contentFontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1490  layoutItem->setContentTextFormat( contentFormat );
1491 
1492  layoutItem->setCellMargin( itemElem.attribute( QStringLiteral( "cellMargin" ), QStringLiteral( "1.0" ) ).toDouble() );
1493  layoutItem->setGridStrokeWidth( itemElem.attribute( QStringLiteral( "gridStrokeWidth" ), QStringLiteral( "0.5" ) ).toDouble() );
1494  layoutItem->setHorizontalGrid( itemElem.attribute( QStringLiteral( "horizontalGrid" ), QStringLiteral( "1" ) ).toInt() );
1495  layoutItem->setVerticalGrid( itemElem.attribute( QStringLiteral( "verticalGrid" ), QStringLiteral( "1" ) ).toInt() );
1496  layoutItem->setShowGrid( itemElem.attribute( QStringLiteral( "showGrid" ), QStringLiteral( "1" ) ).toInt() );
1497  layoutItem->setGridColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "gridColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1498  layoutItem->setBackgroundColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "backgroundColor" ), QStringLiteral( "255,255,255,0" ) ) ) );
1499  layoutItem->setWrapBehavior( static_cast<QgsLayoutTable::WrapBehavior>( itemElem.attribute( QStringLiteral( "wrapBehavior" ), QStringLiteral( "0" ) ).toInt() ) );
1500 
1501  //restore column specifications
1502  layoutItem->mColumns.clear();
1503  layoutItem->mSortColumns.clear();
1504 
1505  const QDomNodeList columnsList = itemElem.elementsByTagName( QStringLiteral( "displayColumns" ) );
1506  if ( !columnsList.isEmpty() )
1507  {
1508  const QDomElement columnsElem = columnsList.at( 0 ).toElement();
1509  const QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
1510  for ( int i = 0; i < columnEntryList.size(); ++i )
1511  {
1512  const QDomElement columnElem = columnEntryList.at( i ).toElement();
1513  QgsLayoutTableColumn column;
1514  column.mHAlignment = static_cast< Qt::AlignmentFlag >( columnElem.attribute( QStringLiteral( "hAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() );
1515  column.mVAlignment = static_cast< Qt::AlignmentFlag >( columnElem.attribute( QStringLiteral( "vAlignment" ), QString::number( Qt::AlignVCenter ) ).toInt() );
1516  column.mHeading = columnElem.attribute( QStringLiteral( "heading" ), QString() );
1517  column.mAttribute = columnElem.attribute( QStringLiteral( "attribute" ), QString() );
1518  column.mSortByRank = columnElem.attribute( QStringLiteral( "sortByRank" ), QStringLiteral( "0" ) ).toInt();
1519  column.mSortOrder = static_cast< Qt::SortOrder >( columnElem.attribute( QStringLiteral( "sortOrder" ), QString::number( Qt::AscendingOrder ) ).toInt() );
1520  column.mWidth = columnElem.attribute( QStringLiteral( "width" ), QStringLiteral( "0.0" ) ).toDouble();
1521 
1522  const QDomNodeList bgColorList = columnElem.elementsByTagName( QStringLiteral( "backgroundColor" ) );
1523  if ( !bgColorList.isEmpty() )
1524  {
1525  const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
1526  bool redOk, greenOk, blueOk, alphaOk;
1527  int bgRed, bgGreen, bgBlue, bgAlpha;
1528  bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1529  bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1530  bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1531  bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1532  if ( redOk && greenOk && blueOk && alphaOk )
1533  {
1534  column.mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
1535  }
1536  }
1537  layoutItem->mColumns.append( column );
1538 
1539  // sorting columns are now (QGIS 3.14+) handled in a dedicated list
1540  // copy the display columns if sortByRank > 0 and then, sort them by rank
1542  std::copy_if( layoutItem->mColumns.begin(), layoutItem->mColumns.end(), std::back_inserter( layoutItem->mSortColumns ), []( const QgsLayoutTableColumn & col ) {return col.sortByRank() > 0;} );
1543  std::sort( layoutItem->mSortColumns.begin(), layoutItem->mSortColumns.end(), []( const QgsLayoutTableColumn & a, const QgsLayoutTableColumn & b ) {return a.sortByRank() < b.sortByRank();} );
1545  }
1546  }
1547 
1548  //restore cell styles
1549  const QDomNodeList stylesList = itemElem.elementsByTagName( QStringLiteral( "cellStyles" ) );
1550  if ( !stylesList.isEmpty() )
1551  {
1552  const QDomElement stylesElem = stylesList.at( 0 ).toElement();
1553 
1554  QMap< QgsLayoutTable::CellStyleGroup, QString >::const_iterator it = layoutItem->mCellStyleNames.constBegin();
1555  for ( ; it != layoutItem->mCellStyleNames.constEnd(); ++it )
1556  {
1557  const QString styleName = it.value();
1558  const QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
1559  if ( !styleList.isEmpty() )
1560  {
1561  const QDomElement styleElem = styleList.at( 0 ).toElement();
1562  QgsLayoutTableStyle *style = layoutItem->mCellStyles.value( it.key() );
1563  if ( style )
1564  style->readXml( styleElem );
1565  }
1566  }
1567  }
1568 
1569  // look for stored layer name
1570  const QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
1571  const QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
1572  const QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
1573  const QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
1574 
1575  QgsVectorLayerRef layerRef( layerId, layerName, layerSource, layerProvider );
1576  layoutItem->setVectorLayer( layerRef.resolveWeakly( project ) );
1577 
1578  return true;
1579 }
1580 
1581 bool QgsCompositionConverter::readGroupXml( QgsLayoutItemGroup *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QList< QgsLayoutObject * > &items )
1582 {
1583  Q_UNUSED( project )
1584 
1585  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1586 
1587  const QDomNodeList nodes = itemElem.elementsByTagName( "ComposerItemGroupElement" );
1588  for ( int i = 0, n = nodes.size(); i < n; ++i )
1589  {
1590  const QDomElement groupElement = nodes.at( i ).toElement();
1591  const QString elementUuid = groupElement.attribute( "uuid" );
1592 
1593  for ( QgsLayoutObject *item : items )
1594  {
1595  if ( dynamic_cast<QgsLayoutItem *>( item ) && static_cast<QgsLayoutItem *>( item )->uuid() == elementUuid )
1596  {
1597  layoutItem->addItem( static_cast<QgsLayoutItem *>( item ) );
1598  break;
1599  }
1600  }
1601  }
1602 
1603  return true;
1604 }
1605 
1606 template <class T, class T2>
1607 bool QgsCompositionConverter::readPolyXml( T *layoutItem, const QDomElement &itemElem, const QgsProject *project )
1608 {
1609  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1610  const QDomNodeList nodeList = itemElem.elementsByTagName( QStringLiteral( "node" ) );
1611  if ( !nodeList.isEmpty() )
1612  {
1613  QPolygonF polygon;
1614  for ( int i = 0; i < nodeList.length(); i++ )
1615  {
1616  const QDomElement node = nodeList.at( i ).toElement();
1617  polygon.append( QPointF( node.attribute( QStringLiteral( "x" ) ).toDouble( ), node.attribute( QStringLiteral( "y" ) ).toDouble() ) );
1618  }
1619  layoutItem->setNodes( polygon );
1620  }
1621  if ( itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).size() )
1622  {
1623  const QDomElement symbolElement = itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).at( 0 ).toElement();
1624  QgsReadWriteContext context;
1625  if ( project )
1626  context.setPathResolver( project->pathResolver( ) );
1627  T2 *styleSymbol = QgsSymbolLayerUtils::loadSymbol<T2>( symbolElement, context );
1628  if ( styleSymbol )
1629  layoutItem->setSymbol( styleSymbol );
1630  }
1631  // Disable frame for shapes
1632  layoutItem->setFrameEnabled( false );
1633  layoutItem->setBackgroundEnabled( false );
1634  return true;
1635 }
1636 
1637 
1638 bool QgsCompositionConverter::readXml( QgsLayoutItem *layoutItem, const QDomElement &itemElem )
1639 {
1640  if ( itemElem.isNull() )
1641  {
1642  return false;
1643  }
1644 
1645  readOldComposerObjectXml( layoutItem, itemElem );
1646 
1647  //uuid
1648  layoutItem->mUuid = itemElem.attribute( QStringLiteral( "uuid" ), QUuid::createUuid().toString() );
1649 
1650  // temporary for groups imported from templates
1651  layoutItem->mTemplateUuid = itemElem.attribute( QStringLiteral( "templateUuid" ) );
1652 
1653  //id
1654  const QString id = itemElem.attribute( QStringLiteral( "id" ), QString() );
1655  layoutItem->setId( id );
1656 
1657  //frame
1658  const QString frame = itemElem.attribute( QStringLiteral( "frame" ) );
1659  layoutItem->setFrameEnabled( frame.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 ) ;
1660 
1661  //frame
1662  const QString background = itemElem.attribute( QStringLiteral( "background" ) );
1663  layoutItem->setBackgroundEnabled( background.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 );
1664 
1665  //position lock for mouse moves/resizes
1666  const QString positionLock = itemElem.attribute( QStringLiteral( "positionLock" ) );
1667  layoutItem->setLocked( positionLock.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 );
1668 
1669  //visibility
1670  layoutItem->setVisibility( itemElem.attribute( QStringLiteral( "visibility" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
1671 
1672  layoutItem->mParentGroupUuid = itemElem.attribute( QStringLiteral( "groupUuid" ) );
1673  if ( !layoutItem->mParentGroupUuid.isEmpty() )
1674  {
1675  if ( QgsLayoutItemGroup *group = layoutItem->parentGroup() )
1676  {
1677  group->addItem( layoutItem );
1678  }
1679  }
1680  layoutItem->mTemplateUuid = itemElem.attribute( QStringLiteral( "templateUuid" ) );
1681 
1682 
1683  const QRectF position = itemPosition( layoutItem, itemElem );
1684 
1685  // TODO: missing?
1686  // mLastValidViewScaleFactor = itemElem.attribute( QStringLiteral( "lastValidViewScaleFactor" ), QStringLiteral( "-1" ) ).toDouble();
1687 
1688  layoutItem->setZValue( itemElem.attribute( QStringLiteral( "zValue" ) ).toDouble() );
1689 
1690  //pen
1691  const QDomNodeList frameColorList = itemElem.elementsByTagName( QStringLiteral( "FrameColor" ) );
1692  if ( !frameColorList.isEmpty() )
1693  {
1694  const QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
1695  bool redOk, greenOk, blueOk, alphaOk, widthOk;
1696  int penRed, penGreen, penBlue, penAlpha;
1697  double penWidth;
1698 
1699  penWidth = itemElem.attribute( QStringLiteral( "outlineWidth" ) ).toDouble( &widthOk );
1700  penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1701  penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1702  penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1703  penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1704  layoutItem->setFrameJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "frameJoinStyle" ), QStringLiteral( "miter" ) ) ) );
1705 
1706  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
1707  {
1708  layoutItem->setFrameStrokeColor( QColor( penRed, penGreen, penBlue, penAlpha ) );
1709  layoutItem->setFrameStrokeWidth( QgsLayoutMeasurement( penWidth ) );
1710  QPen framePen( layoutItem->frameStrokeColor() );
1711  framePen.setWidthF( layoutItem->frameStrokeWidth( ).length() );
1712  framePen.setJoinStyle( layoutItem->frameJoinStyle( ) );
1713  layoutItem->setPen( framePen );
1714  //apply any data defined settings
1715  layoutItem->refreshFrame( false );
1716  }
1717  }
1718 
1719  //brush
1720  const QDomNodeList bgColorList = itemElem.elementsByTagName( QStringLiteral( "BackgroundColor" ) );
1721  if ( !bgColorList.isEmpty() )
1722  {
1723  const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
1724  bool redOk, greenOk, blueOk, alphaOk;
1725  int bgRed, bgGreen, bgBlue, bgAlpha;
1726  bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1727  bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1728  bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1729  bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1730  if ( redOk && greenOk && blueOk && alphaOk )
1731  {
1732  layoutItem->mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
1733  layoutItem->setBrush( QBrush( layoutItem->mBackgroundColor, Qt::SolidPattern ) );
1734  }
1735  //apply any data defined settings
1736  layoutItem->refreshBackgroundColor( false );
1737  }
1738 
1739  //blend mode
1740  layoutItem->setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) ) );
1741 
1742  //opacity
1743  if ( itemElem.hasAttribute( QStringLiteral( "opacity" ) ) )
1744  {
1745  layoutItem->setItemOpacity( itemElem.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) ).toDouble() );
1746  }
1747  else
1748  {
1749  layoutItem->setItemOpacity( 1.0 - itemElem.attribute( QStringLiteral( "transparency" ), QStringLiteral( "0" ) ).toInt() / 100.0 );
1750  }
1751 
1752  layoutItem->mExcludeFromExports = itemElem.attribute( QStringLiteral( "excludeFromExports" ), QStringLiteral( "0" ) ).toInt();
1753  layoutItem->mEvaluatedExcludeFromExports = layoutItem->mExcludeFromExports;
1754 
1755  // positioning
1756  layoutItem->attemptSetSceneRect( position );
1757  //rotation
1758  layoutItem->setItemRotation( itemElem.attribute( QStringLiteral( "itemRotation" ), QStringLiteral( "0" ) ).toDouble(), false );
1759 
1760  layoutItem->mBlockUndoCommands = false;
1761 
1762  return true;
1763 }
1764 
1765 
1766 
1767 bool QgsCompositionConverter::readOldComposerObjectXml( QgsLayoutObject *layoutItem,
1768  const QDomElement &itemElem )
1769 {
1770  if ( itemElem.isNull() )
1771  {
1772  return false;
1773  }
1774 
1775  //old (pre 3.0) data defined properties
1776  QgsCompositionConverter::readOldDataDefinedPropertyMap( itemElem, layoutItem->mDataDefinedProperties );
1777 
1778  const QDomNode propsNode = itemElem.namedItem( QStringLiteral( "dataDefinedProperties" ) );
1779  if ( !propsNode.isNull() )
1780  {
1781  layoutItem->mDataDefinedProperties.readXml( propsNode.toElement(), sPropertyDefinitions );
1782  }
1784  {
1785  // upgrade transparency -> opacity
1787  exp = QStringLiteral( "100.0 - (%1)" ).arg( exp );
1790  }
1791 
1792  //custom properties
1793  layoutItem->mCustomProperties.readXml( itemElem );
1794 
1795  return true;
1796 }
1797 
1798 
1799 void QgsCompositionConverter::readOldDataDefinedPropertyMap( const QDomElement &itemElem, QgsPropertyCollection &dataDefinedProperties )
1800 {
1801  const QgsPropertiesDefinition defs = QgsCompositionConverter::propertyDefinitions();
1802  QgsPropertiesDefinition::const_iterator i = defs.constBegin();
1803  for ( ; i != defs.constEnd(); ++i )
1804  {
1805  const QString elemName = i.value().name();
1806  const QDomNodeList ddNodeList = itemElem.elementsByTagName( elemName );
1807  if ( !ddNodeList.isEmpty() )
1808  {
1809  const QDomElement ddElem = ddNodeList.at( 0 ).toElement();
1810  const QgsProperty prop = readOldDataDefinedProperty( static_cast< QgsCompositionConverter::DataDefinedProperty >( i.key() ), ddElem );
1811  if ( prop )
1812  dataDefinedProperties.setProperty( i.key(), prop );
1813  }
1814  }
1815 }
1816 
1817 QgsProperty QgsCompositionConverter::readOldDataDefinedProperty( const QgsCompositionConverter::DataDefinedProperty property, const QDomElement &ddElem )
1818 {
1820  {
1821  //invalid property
1822  return QgsProperty();
1823  }
1824 
1825  const QString active = ddElem.attribute( QStringLiteral( "active" ) );
1826  bool isActive = false;
1827  if ( active.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
1828  {
1829  isActive = true;
1830  }
1831  const QString field = ddElem.attribute( QStringLiteral( "field" ) );
1832  const QString expr = ddElem.attribute( QStringLiteral( "expr" ) );
1833 
1834  const QString useExpr = ddElem.attribute( QStringLiteral( "useExpr" ) );
1835  bool isExpression = false;
1836  if ( useExpr.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
1837  {
1838  isExpression = true;
1839  }
1840 
1841  if ( isExpression )
1842  return QgsProperty::fromExpression( expr, isActive );
1843  else
1844  return QgsProperty::fromField( field, isActive );
1845 }
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
DataDefinedProperty
Composition data defined properties for different item types.
@ PaperOrientation
Paper orientation.
@ MapYMax
Map extent y maximum.
@ MapStylePreset
Layer and style map theme.
@ MapAtlasMargin
Map atlas margin.
@ MapYMin
Map extent y minimum.
@ MapXMin
Map extent x minimum.
@ ScalebarFillColor2
Scalebar secondary fill color.
@ ExcludeFromExports
Exclude item from exports.
@ NumPages
Number of pages in composition.
@ PictureSvgBackgroundColor
SVG background color.
@ LegendColumnCount
Legend column count.
@ PictureSvgStrokeWidth
SVG stroke width.
@ ScalebarFillColor
Scalebar fill color.
@ PictureSvgStrokeColor
SVG stroke color.
@ PageNumber
Page number for item placement.
@ ScalebarLineColor
Scalebar line color.
@ Transparency
Item transparency (deprecated)
@ MapXMax
Map extent x maximum.
@ TestProperty
Dummy property with no effect on item.
@ AllProperties
All properties for item.
@ PresetPaperSize
Preset paper size for composition.
@ BackgroundColor
Item background color.
@ PositionY
Y position on page.
@ PositionX
X position on page.
@ ScalebarLineWidth
Scalebar line width.
@ PictureSource
Picture source url.
MarkerMode
The MarkerMode enum is the old QGIS 2.x arrow marker mode.
static bool isCompositionTemplate(const QDomDocument &document)
Check if the given document is a composition template.
static std::unique_ptr< QgsPrintLayout > createLayoutFromCompositionXml(const QDomElement &composerElement, QgsProject *project)
createLayoutFromCompositionXml is a factory that creates layout instances from a QGIS 2....
static QDomDocument convertCompositionTemplate(const QDomDocument &document, QgsProject *project)
Convert a composition template document to a layout template.
static QList< QgsLayoutObject * > addItemsFromCompositionXml(QgsPrintLayout *layout, const QDomElement &parentElement, QPointF *position=nullptr, bool pasteInPlace=false)
addItemsFromCompositionXml parse a QGIS 2.x composition XML in the parentElement, converts the 2....
This class represents a coordinate reference system (CRS).
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
static QgsLayerTree * readXml(QDomElement &element, const QgsReadWriteContext &context)
Load the layer tree from an XML element.
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
bool filterFeatures() const
Returns true if features should be filtered in the coverage layer.
void setCoverageLayer(QgsVectorLayer *layer)
Sets the coverage layer to use for the atlas features.
bool setFilterExpression(const QString &expression, QString &errorString)
Sets the expression used for filtering features in the coverage layer.
void setSortAscending(bool ascending)
Sets whether features should be sorted in an ascending order.
void setEnabled(bool enabled)
Sets whether the atlas is enabled.
void setPageNameExpression(const QString &expression)
Sets the expression (or field name) used for calculating the page name.
bool setFilenameExpression(const QString &expression, QString &errorString)
Sets the filename expression used for generating output filenames for each atlas page.
void setSortFeatures(bool enabled)
Sets whether features should be sorted in the atlas.
void setSortExpression(const QString &expression)
Sets the expression (or field name) to use for sorting features.
void setFilterFeatures(bool filtered)
Sets whether features should be filtered in the coverage layer.
bool sortFeatures() const
Returns true if features should be sorted in the atlas.
void setHideCoverage(bool hide)
Sets whether the coverage layer should be hidden in map items in the layouts.
Base class for frame items, which form a layout multiframe item.
void setContentSection(const QRectF &section)
Sets the visible part of the multiframe's content which is visible within this frame (relative to the...
void setHideBackgroundIfEmpty(bool hideBackgroundIfEmpty)
Sets whether the background and frame stroke should be hidden if this frame is empty.
void setHidePageIfEmpty(bool hidePageIfEmpty)
Sets whether the page should be hidden (ie, not included in layout exports) if this frame is empty.
A layout table subclass that displays attributes from a vector layer.
void setVectorLayer(QgsVectorLayer *layer)
Sets the vector layer from which to display feature attributes.
A container for grouping several QgsLayoutItems.
void addItem(QgsLayoutItem *item)
Adds an item to the group.
A layout multiframe subclass for HTML content.
void setUrl(const QUrl &url)
Sets the url for content to display in the item when the item is using the QgsLayoutItemHtml::Url mod...
ContentMode
Source modes for the HTML content to render in the item.
void setEvaluateExpressions(bool evaluateExpressions)
Sets whether the html item will evaluate QGIS expressions prior to rendering the HTML content.
void setMaxBreakDistance(double distance)
Sets the maximum distance allowed when calculating where to place page breaks in the html.
void setUserStylesheetEnabled(bool enabled)
Sets whether user stylesheets are enabled for the HTML content.
void setHtml(const QString &html)
Sets the html to display in the item when the item is using the QgsLayoutItemHtml::ManualHtml mode.
void setUseSmartBreaks(bool useSmartBreaks)
Sets whether the html item should use smart breaks.
void setUserStylesheet(const QString &stylesheet)
Sets the user stylesheet CSS rules to use while rendering the HTML content.
void setContentMode(ContentMode mode)
Sets the source mode for item's HTML content.
void loadHtml(bool useCache=false, const QgsExpressionContext *context=nullptr)
Reloads the html source from the url and redraws the item.
A layout item subclass for text labels.
void setHAlign(Qt::AlignmentFlag alignment)
Sets the horizontal alignment of the label.
void setMarginX(double margin)
Sets the horizontal margin between the edge of the frame and the label contents, in layout units.
void setText(const QString &text)
Sets the label's preset text.
void setMarginY(double margin)
Sets the vertical margin between the edge of the frame and the label contents, in layout units.
void setMode(Mode mode)
Sets the label's current mode, allowing the label to switch between font based and HTML based renderi...
void setTextFormat(const QgsTextFormat &format)
Sets the text format used for drawing text in the label.
void setVAlign(Qt::AlignmentFlag alignment)
Sets for the vertical alignment of the label.
A layout item subclass for map legends.
void setSplitLayer(bool enabled)
Sets whether the legend items from a single layer can be split over multiple columns.
void setColumnSpace(double spacing)
Sets the legend column spacing.
void setBoxSpace(double space)
Sets the legend box space.
void setEqualColumnWidth(bool equalize)
Sets whether column widths should be equalized.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
void setSymbolWidth(double width)
Sets the legend symbol width.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to associate with the legend.
void setWmsLegendWidth(double width)
Sets the WMS legend width.
void setTitle(const QString &title)
Sets the legend title.
void setFontColor(const QColor &color)
Sets the legend font color.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
void setStyle(QgsLegendStyle::Style component, const QgsLegendStyle &style)
Sets the style of component to style for the legend.
void setLineSpacing(double spacing)
Sets the spacing in-between multiple lines.
void setSymbolHeight(double height)
Sets the legend symbol height.
void setWmsLegendHeight(double height)
Sets the WMS legend height.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setWrapString(const QString &string)
Sets the legend text wrapping string.
void setColumnCount(int count)
Sets the legend column count.
An individual grid which is drawn above the map content in a QgsLayoutItemMap.
GridStyle
Grid drawing style.
void setFrameFillColor2(const QColor &color)
Sets the second fill color used for the grid frame.
void setAnnotationFormat(const AnnotationFormat format)
Sets the format for drawing grid annotations.
void setIntervalY(double interval)
Sets the interval between grid lines in the y-direction.
void setAnnotationEnabled(const bool enabled)
Sets whether annotations should be shown for the grid.
QgsTextFormat annotationTextFormat() const
Returns the text format used when rendering grid annotations.
void setFramePenColor(const QColor &color)
Sets the color of the stroke drawn in the grid frame.
void setFramePenSize(const double width)
Sets the width of the stroke drawn in the grid frame.
AnnotationPosition
Position for grid annotations.
void setAnnotationPosition(AnnotationPosition position, BorderSide side)
Sets the position for the grid annotations on a specified side of the map frame.
void setIntervalX(double interval)
Sets the interval between grid lines in the x-direction.
void setCrossLength(const double length)
Sets the length (in layout units) of the cross segments drawn for the grid.
void setEnabled(bool enabled) override
Controls whether the item will be drawn.
void setAnnotationFrameDistance(const double distance)
Sets the distance between the map frame and annotations.
void setAnnotationTextFormat(const QgsTextFormat &format)
Sets the text format to use when rendering grid annotations.
void setBlendMode(const QPainter::CompositionMode mode)
Sets the blending mode used for drawing the grid.
void setFrameStyle(const FrameStyle style)
Sets the grid frame style.
void setAnnotationPrecision(const int precision)
Sets the coordinate precision for grid annotations.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used for drawing grid lines.
AnnotationFormat
Format for displaying grid annotations.
AnnotationDirection
Direction of grid annotations.
void setOffsetY(double offset)
Sets the offset for grid lines in the y-direction.
FrameStyle
Style for grid frame.
void setFrameWidth(const double width)
Sets the grid frame width (in layout units).
void setFrameFillColor1(const QColor &color)
Sets the first fill color used for the grid frame.
void setOffsetX(double offset)
Sets the offset for grid lines in the x-direction.
void setAnnotationDirection(AnnotationDirection direction, BorderSide side)
Sets the direction for drawing frame annotations for the specified map side.
void setStyle(GridStyle style)
Sets the grid style, which controls how the grid is drawn over the map's contents.
An individual overview which is drawn above the map content in a QgsLayoutItemMap,...
Layout graphical items for displaying a map.
void setFollowVisibilityPreset(bool follow)
Sets whether the map should follow a map theme.
void setFollowVisibilityPresetName(const QString &name)
Sets preset name for map rendering.
void setKeepLayerSet(bool enabled)
Sets whether the stored layer set should be used or the current layer set of the associated project.
AtlasScalingMode
Scaling modes used for the serial rendering (atlas)
void updateBoundingRect()
Updates the bounding rect of this item. Call this function before doing any changes related to annota...
void setDrawAnnotations(bool draw)
Sets whether annotations are drawn within the map.
void setExtent(const QgsRectangle &extent)
Sets a new extent for the map.
void setAtlasScalingMode(AtlasScalingMode mode)
Sets the current atlas scaling mode.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the map's preset crs (coordinate reference system).
void setAtlasMargin(double margin)
Sets the margin size (percentage) used when the map is in atlas mode.
void setMapRotation(double rotation)
Sets the rotation for the map - this does not affect the layout item shape, only the way the map is d...
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Item representing the paper in a layout.
void setPageSize(const QgsLayoutSize &size)
Sets the size of the page.
static QgsLayoutItemPage * create(QgsLayout *layout)
Returns a new page item for the specified layout.
A layout item subclass that displays SVG files or raster format images (jpg, png, ....
void setPictureAnchor(QgsLayoutItem::ReferencePoint anchor)
Sets the picture's anchor point, which controls how it is placed within the picture item's frame.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map object for rotation.
void setPicturePath(const QString &path, Format format=FormatUnknown)
Sets the source path of the image (may be svg or a raster format).
ResizeMode
Controls how pictures are scaled within the item's frame.
Layout item for node based polygon shapes.
Layout item for node based polyline shapes.
void setArrowHeadWidth(double width)
Sets the width of line arrow heads in mm.
void setEndMarker(MarkerMode mode)
Sets the end marker mode, which controls what marker is drawn at the end of the line.
void setEndSvgMarkerPath(const QString &path)
Sets the path to a SVG marker to draw at the end of the line.
void setArrowHeadStrokeWidth(double width)
Sets the pen width in millimeters for the stroke of the arrow head.
void setArrowHeadFillColor(const QColor &color)
Sets the color used to fill the arrow head.
void setArrowHeadStrokeColor(const QColor &color)
Sets the color used to draw the stroke around the arrow head.
void setStartMarker(MarkerMode mode)
Sets the start marker mode, which controls what marker is drawn at the start of the line.
void setStartSvgMarkerPath(const QString &path)
Sets the path to a SVG marker to draw at the start of the line.
A layout item subclass for scale bars.
void setNumberOfSegments(int segments)
Sets the number of segments included in the scalebar.
QgsFillSymbol * alternateFillSymbol() const
Returns the secondary fill symbol used to render the scalebar (only used for some scalebar types).
void setMinimumBarWidth(double minWidth)
Sets the minimum width (in millimeters) for scale bar segments.
QgsFillSymbol * fillSymbol() const
Returns the primary fill symbol used to render the scalebar (only used for some scalebar types).
void setMaximumBarWidth(double maxWidth)
Sets the maximum width (in millimeters) for scale bar segments.
void setSegmentSizeMode(QgsScaleBarSettings::SegmentSizeMode mode)
Sets the size mode for scale bar segments.
void setDivisionLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the scalebar divisions (only used for some scalebar types).
void setLabelBarSpace(double space)
Sets the spacing (in millimeters) between labels and the scalebar.
void setUnits(QgsUnitTypes::DistanceUnit units)
Sets the distance units used by the scalebar.
void setAlignment(QgsScaleBarSettings::Alignment alignment)
Sets the scalebar alignment.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the scalebar (only used for some scalebar types).
void setStyle(const QString &name)
Sets the scale bar style by name.
void setMapUnitsPerScaleBarUnit(double units)
Sets the number of map units per scale bar unit used by the scalebar.
void setHeight(double height)
Sets the scalebar height (in millimeters).
void setNumberOfSegmentsLeft(int segments)
Sets the number of segments included in the left part of the scalebar.
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map item linked to the scalebar.
Q_DECL_DEPRECATED void setFontColor(const QColor &color)
Sets the color used for drawing text in the scalebar.
void setBoxContentSpace(double space)
Sets the space (margin) between the scalebar box and content in millimeters.
void setUnitLabel(const QString &label)
Sets the label for units.
void setUnitsPerSegment(double units)
Sets the number of scalebar units per segment.
Q_DECL_DEPRECATED void setFont(const QFont &font)
Sets the font used for drawing text in the scalebar.
void setSubdivisionLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the scalebar subdivisions (only used for some scalebar types).
Layout item for basic filled shapes (e.g.
void setShapeType(QgsLayoutItemShape::Shape type)
Sets the type of shape (e.g.
void setSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to draw the shape.
void setCornerRadius(QgsLayoutMeasurement radius)
Sets the corner radius for rounded rectangle corners.
Base class for graphical items within a QgsLayout.
virtual void setFrameStrokeWidth(QgsLayoutMeasurement width)
Sets the frame stroke width.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutItemGroup * parentGroup() const
Returns the item's parent group, if the item is part of a QgsLayoutItemGroup group.
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item's rotation, in degrees clockwise.
QgsLayoutMeasurement frameStrokeWidth() const
Returns the frame's stroke width.
void setItemOpacity(double opacity)
Sets the item's opacity.
virtual void setVisibility(bool visible)
Sets whether the item is visible.
ReferencePoint
Fixed position reference point.
virtual void setId(const QString &id)
Set the item's id name.
void setFrameStrokeColor(const QColor &color)
Sets the frame stroke color.
void setFrameJoinStyle(Qt::PenJoinStyle style)
Sets the join style used when drawing the item's frame.
void refreshBackgroundColor(bool updateItem=true)
Refresh item's background color, considering data defined colors.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
void setLocked(bool locked)
Sets whether the item is locked, preventing mouse interactions with the item.
void refreshFrame(bool updateItem=true)
Refresh item's frame, considering data defined colors and frame size.
virtual QString uuid() const
Returns the item identification string.
virtual void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1)
Attempts to move the item to a specified point.
void setBlendMode(QPainter::CompositionMode mode)
Sets the item's composition blending mode.
bool frameEnabled() const
Returns true if the item includes a frame.
void attemptMoveBy(double deltaX, double deltaY)
Attempts to shift the item's position by a specified deltaX and deltaY, in layout units.
void setReferencePoint(ReferencePoint point)
Sets the reference point for positioning of the layout item.
bool hasBackground() const
Returns true if the item has a background.
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates.
QColor frameStrokeColor() const
Returns the frame's stroke color.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
Qt::PenJoinStyle frameJoinStyle() const
Returns the join style used for drawing the item's frame.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
double length() const
Returns the length of the measurement.
void setResizeMode(ResizeMode mode)
Sets the resize mode for the multiframe, and recalculates frame sizes to match.
virtual void addFrame(QgsLayoutFrame *frame, bool recalcFrameSizes=true)
Adds a frame to the multiframe.
ResizeMode
Specifies the behavior for creating new frames to fit the multiframe's content.
QList< QgsLayoutFrame * > frames() const
Returns a list of all child frames for this multiframe.
void setNodes(const QPolygonF &nodes)
Sets the nodes the shape consists of.
void setNorthOffset(double offset)
Sets the offset added to the arrows's rotation from a map's North.
NorthMode
Method for syncing rotation to a map's North direction.
void setNorthMode(NorthMode mode)
Sets the mode used to calculate the arrow rotation.
A base class for objects which belong to a layout.
QgsObjectCustomProperties mCustomProperties
Custom properties for object.
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
double spaceBetweenPages() const
Returns the space between pages, in layout units.
QList< QgsLayoutItemPage * > pages()
Returns a list of pages in the collection.
int pageCount() const
Returns the number of pages in the collection.
This class provides a method of storing points, consisting of an x and y coordinate,...
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:41
double height() const
Returns the height of the size.
Definition: qgslayoutsize.h:90
Stores properties of a column for a QgsLayoutTable.
Styling option for a layout table cell.
bool readXml(const QDomElement &styleElem)
Reads the style's properties from XML.
void setHeaderHAlignment(HeaderHAlignment alignment)
Sets the horizontal alignment for table headers.
void setShowEmptyRows(bool showEmpty)
Sets whether empty rows should be drawn.
void setVerticalGrid(bool verticalGrid)
Sets whether the grid's vertical lines should be drawn in the table.
void setGridColor(const QColor &color)
Sets the color used for grid lines in the table.
void setCellMargin(double margin)
Sets the margin distance in mm between cell borders and their contents.
void setBackgroundColor(const QColor &color)
Sets the color used for background of table.
void setContentTextFormat(const QgsTextFormat &format)
Sets the format used to draw content text in the table.
QgsTextFormat contentTextFormat() const
Returns the format used to draw content text in the table.
void setWrapBehavior(WrapBehavior behavior)
Sets the wrap behavior for the table, which controls how text within cells is automatically wrapped.
void setEmptyTableBehavior(EmptyTableMode mode)
Sets the behavior mode for empty tables with no content rows.
void setHeaderMode(HeaderMode mode)
Sets the display mode for headers in the table.
void setHorizontalGrid(bool horizontalGrid)
Sets whether the grid's horizontal lines should be drawn in the table.
HeaderMode
Controls where headers are shown in the table.
QgsLayoutTableColumns mColumns
Columns to show in table.
void setShowGrid(bool showGrid)
Sets whether grid lines should be drawn in the table.
QgsTextFormat headerTextFormat() const
Returns the format used to draw header text in the table.
void setEmptyTableMessage(const QString &message)
Sets the message for empty tables with no content rows.
void setHeaderTextFormat(const QgsTextFormat &format)
Sets the format used to draw header text in the table.
QgsLayoutTableSortColumns mSortColumns
Columns to sort the table.
void setGridStrokeWidth(double width)
Sets the width in mm for grid lines in the table.
WrapBehavior
Controls how long strings in the table are handled.
HeaderHAlignment
Controls how headers are horizontally aligned in a table.
EmptyTableMode
Controls how empty tables are displayed.
QMap< CellStyleGroup, QgsLayoutTableStyle * > mCellStyles
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
Definition: qgslayout.cpp:459
QgsLayoutItem * itemByUuid(const QString &uuid, bool includeTemplateUuids=false) const
Returns the layout item with matching uuid unique identifier, or nullptr if a matching item could not...
Definition: qgslayout.cpp:238
void addLayoutItem(QgsLayoutItem *item)
Adds an item to the layout.
Definition: qgslayout.cpp:540
QgsProject * project() const
The project associated with the layout.
Definition: qgslayout.cpp:132
Contains detailed styling information relating to how a layout legend should be rendered.
void readXml(const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context=QgsReadWriteContext())
Reads the component's style definition from an XML element.
Style
Component of legends which can be styled.
@ Group
Legend group title.
@ Symbol
Symbol icon (excluding label)
@ Subgroup
Legend subgroup title.
@ Title
Legend title.
@ SymbolLabel
Symbol label (excluding icon)
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
static QgsLineSymbol * createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
void setWidth(double width)
Sets the width for the whole line symbol.
Stores style information (renderer, opacity, labeling, diagrams etc.) applicable to a map layer.
void readXml(const QDomElement &styleElement)
Read style configuration (for project file reading)
QString xmlData() const
Returns XML content of the style.
void readXml(const QDomNode &parentNode, const QString &keyStartsWith=QString())
Read store contents from an XML node.
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
Resolves relative paths into absolute paths and vice versa.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Print layout, a QgsLayout subclass for static or atlas-based layouts.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:101
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Definition for a property.
Definition: qgsproperty.h:47
@ Double
Double value (including negative values)
Definition: qgsproperty.h:57
@ StrokeWidth
Line stroke width.
Definition: qgsproperty.h:72
@ String
Any string value.
Definition: qgsproperty.h:61
@ BlendMode
Blend mode.
Definition: qgsproperty.h:67
@ Boolean
Boolean value.
Definition: qgsproperty.h:53
@ IntegerPositiveGreaterZero
Non-zero positive integer values.
Definition: qgsproperty.h:56
@ IntegerPositive
Positive integer values (including 0)
Definition: qgsproperty.h:55
@ Opacity
Opacity (0-100)
Definition: qgsproperty.h:62
@ Rotation
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:60
@ ColorWithAlpha
Color with alpha channel.
Definition: qgsproperty.h:64
@ DoublePositive
Positive double value (including 0)
Definition: qgsproperty.h:58
@ DataTypeString
Property requires a string value.
Definition: qgsproperty.h:92
A store for object properties.
Definition: qgsproperty.h:231
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
The class is used as a container of context for various read/write operations on other objects.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Alignment
Scalebar alignment.
SegmentSizeMode
Modes for setting size for scale bar segments.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QColor decodeColor(const QString &str)
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:541
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
@ DistanceUnknownUnit
Unknown distance unit.
Definition: qgsunittypes.h:78
@ DistanceFeet
Imperial feet.
Definition: qgsunittypes.h:71
@ DistanceNauticalMiles
Nautical miles.
Definition: qgsunittypes.h:72
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:171
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2065
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2064
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2026
const QgsField & field
Definition: qgsfield.h:463
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.