QGIS API Documentation  3.21.0-Master (5b68dc587e)
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  layoutItem->setFont( font );
542 
543  //font color
544  const QDomNodeList fontColorList = itemElem.elementsByTagName( QStringLiteral( "FontColor" ) );
545  if ( !fontColorList.isEmpty() )
546  {
547  const QDomElement fontColorElem = fontColorList.at( 0 ).toElement();
548  const int red = fontColorElem.attribute( QStringLiteral( "red" ), QStringLiteral( "0" ) ).toInt();
549  const int green = fontColorElem.attribute( QStringLiteral( "green" ), QStringLiteral( "0" ) ).toInt();
550  const int blue = fontColorElem.attribute( QStringLiteral( "blue" ), QStringLiteral( "0" ) ).toInt();
551  layoutItem->setFontColor( QColor( red, green, blue ) );
552  }
553  else
554  {
555  layoutItem->setFontColor( QColor( 0, 0, 0 ) );
556  }
557 
558  return true;
559 }
560 
561 bool QgsCompositionConverter::readShapeXml( QgsLayoutItemShape *layoutItem, const QDomElement &itemElem, const QgsProject *project )
562 {
563  Q_UNUSED( project )
564  layoutItem->setShapeType( static_cast<QgsLayoutItemShape::Shape>( itemElem.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() ) );
565  layoutItem->setCornerRadius( QgsLayoutMeasurement( itemElem.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() ) );
566 
567  restoreGeneralComposeItemProperties( layoutItem, itemElem );
568 
569  QgsReadWriteContext context;
570  if ( project )
571  context.setPathResolver( project->pathResolver() );
572 
573  if ( itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).size() )
574  {
575  const QDomElement symbolElement = itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).at( 0 ).toElement();
576  const std::unique_ptr< QgsFillSymbol > shapeStyleSymbol( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElement, context ) );
577  if ( shapeStyleSymbol )
578  layoutItem->setSymbol( shapeStyleSymbol.get() );
579  }
580  else
581  {
582  //upgrade project file from 2.0 to use symbol styling
583  QVariantMap properties;
584  properties.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( layoutItem->brush().color() ) );
585  if ( layoutItem->hasBackground() )
586  {
587  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
588  }
589  else
590  {
591  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
592  }
593  if ( layoutItem->frameEnabled() )
594  {
595  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
596  }
597  else
598  {
599  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
600  }
601  properties.insert( QStringLiteral( "color_border" ), QgsSymbolLayerUtils::encodeColor( layoutItem->pen().color() ) );
602  properties.insert( QStringLiteral( "width_border" ), QString::number( layoutItem->pen().widthF() ) );
603 
604  //for pre 2.0 projects, shape color and outline were specified in a different element...
605  const QDomNodeList outlineColorList = itemElem.elementsByTagName( QStringLiteral( "OutlineColor" ) );
606  if ( !outlineColorList.isEmpty() )
607  {
608  const QDomElement frameColorElem = outlineColorList.at( 0 ).toElement();
609  bool redOk, greenOk, blueOk, alphaOk, widthOk;
610  int penRed, penGreen, penBlue, penAlpha;
611  double penWidth;
612 
613  penWidth = itemElem.attribute( QStringLiteral( "outlineWidth" ) ).toDouble( &widthOk );
614  penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toInt( &redOk );
615  penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toInt( &greenOk );
616  penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toInt( &blueOk );
617  penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toInt( &alphaOk );
618 
619  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
620  {
621  properties.insert( QStringLiteral( "color_border" ), QgsSymbolLayerUtils::encodeColor( QColor( penRed, penGreen, penBlue, penAlpha ) ) );
622  properties.insert( QStringLiteral( "width_border" ), QString::number( penWidth ) );
623  }
624  }
625  const QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "FillColor" ) );
626  if ( !fillColorList.isEmpty() )
627  {
628  const QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
629  bool redOk, greenOk, blueOk, alphaOk;
630  int fillRed, fillGreen, fillBlue, fillAlpha;
631 
632  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toInt( &redOk );
633  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toInt( &greenOk );
634  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toInt( &blueOk );
635  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toInt( &alphaOk );
636 
637  if ( redOk && greenOk && blueOk && alphaOk )
638  {
639  properties.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) ) );
640  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
641  }
642  }
643  if ( itemElem.hasAttribute( QStringLiteral( "transparentFill" ) ) )
644  {
645  //old style (pre 2.0) of specifying that shapes had no fill
646  const bool hasOldTransparentFill = itemElem.attribute( QStringLiteral( "transparentFill" ), QStringLiteral( "0" ) ).toInt();
647  if ( hasOldTransparentFill )
648  {
649  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
650  }
651  }
652 
653  const std::unique_ptr< QgsFillSymbol > shapeStyleSymbol( QgsFillSymbol::createSimple( properties ) );
654  layoutItem->setSymbol( shapeStyleSymbol.get() );
655  }
656  // Disable frame for shapes
657  layoutItem->setFrameEnabled( false );
658  layoutItem->setBackgroundEnabled( false );
659 
660  return true;
661 }
662 
663 bool QgsCompositionConverter::readPictureXml( QgsLayoutItemPicture *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QgsStringMap &mapId2Uuid )
664 {
665  restoreGeneralComposeItemProperties( layoutItem, itemElem );
666 
667  layoutItem->mResizeMode = QgsLayoutItemPicture::ResizeMode( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() );
668  //when loading from xml, default to anchor point of middle to match pre 2.4 behavior
669  bool positionModeOk = false;
670  layoutItem->mReferencePoint = static_cast< QgsLayoutItem::ReferencePoint >( itemElem.attribute( QStringLiteral( "positionMode" ) ).toInt( &positionModeOk ) );
671  if ( !positionModeOk )
672  {
673  layoutItem->mReferencePoint = QgsLayoutItem::ReferencePoint::UpperLeft;
674  }
675  bool anchorPointOk = false;
676 
677  layoutItem->setPictureAnchor( static_cast< QgsLayoutItem::ReferencePoint >( itemElem.attribute( QStringLiteral( "anchorPoint" ), QString::number( QgsLayoutItem::ReferencePoint::Middle ) ).toInt( &anchorPointOk ) ) );
678  if ( !anchorPointOk )
679  {
680  layoutItem->mPictureAnchor = QgsLayoutItem::ReferencePoint::UpperLeft;
681  }
682  layoutItem->mSvgFillColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "svgFillColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 255, 255, 255 ) ) ) );
683  layoutItem->mSvgStrokeColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "svgBorderColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 0, 0, 0 ) ) ) );
684  layoutItem->mSvgStrokeWidth = itemElem.attribute( QStringLiteral( "svgBorderWidth" ), QStringLiteral( "0.2" ) ).toDouble();
685 
686  QString imagePath = itemElem.attribute( QStringLiteral( "file" ) );
687  if ( project )
688  {
689  // convert from relative path to absolute. For SVG we also need to consider system SVG paths
690  const QgsPathResolver pathResolver = project->pathResolver();
691  if ( imagePath.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
692  imagePath = QgsSymbolLayerUtils::svgSymbolNameToPath( imagePath, pathResolver );
693  else
694  imagePath = pathResolver.readPath( imagePath );
695  }
696  layoutItem->setPicturePath( imagePath );
697  layoutItem->mPictureHeight = itemElem.attribute( QStringLiteral( "pictureHeight" ), QStringLiteral( "10" ) ).toDouble();
698  layoutItem->mPictureWidth = itemElem.attribute( QStringLiteral( "pictureWidth" ), QStringLiteral( "10" ) ).toDouble();
699 
700  //picture rotation
701  if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "pictureRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
702  {
703  layoutItem->mPictureRotation = itemElem.attribute( QStringLiteral( "pictureRotation" ), QStringLiteral( "0" ) ).toDouble();
704  }
705 
706  //rotation map
707  layoutItem->mNorthArrowHandler->setNorthMode( static_cast< QgsLayoutNorthArrowHandler::NorthMode >( itemElem.attribute( QStringLiteral( "northMode" ), QStringLiteral( "0" ) ).toInt() ) );
708  layoutItem->mNorthArrowHandler->setNorthOffset( itemElem.attribute( QStringLiteral( "northOffset" ), QStringLiteral( "0" ) ).toDouble() );
709 
710  const QString rotationMapId = itemElem.attribute( QStringLiteral( "mapId" ), QStringLiteral( "-1" ) );
711  if ( rotationMapId != QLatin1String( "-1" ) )
712  {
713  // Find uuid for map with given id
714  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ rotationMapId ] ) );
715  if ( mapInstance )
716  {
717  layoutItem->setLinkedMap( mapInstance );
718  }
719  }
720  return true;
721 }
722 
723 bool QgsCompositionConverter::readArrowXml( QgsLayoutItemPolyline *layoutItem, const QDomElement &itemElem, const QgsProject *project )
724 {
725  readPolyXml<QgsLayoutItemPolyline, QgsLineSymbol>( layoutItem, itemElem, project );
726  QPolygonF polygon;
727  const QDomNodeList startPointList = itemElem.elementsByTagName( QStringLiteral( "StartPoint" ) );
728  if ( ! startPointList.isEmpty() )
729  {
730  const QDomElement node = startPointList.at( 0 ).toElement();
731  polygon.append( QPointF( node.attribute( QStringLiteral( "x" ) ).toDouble( ), node.attribute( QStringLiteral( "y" ) ).toDouble() ) );
732  }
733  const QDomNodeList stopPointList = itemElem.elementsByTagName( QStringLiteral( "StopPoint" ) );
734  if ( ! stopPointList.isEmpty() )
735  {
736  const QDomElement node = stopPointList.at( 0 ).toElement();
737  polygon.append( QPointF( node.attribute( QStringLiteral( "x" ) ).toDouble( ), node.attribute( QStringLiteral( "y" ) ).toDouble() ) );
738  }
739 
740  const QgsCompositionConverter::MarkerMode markerMode = static_cast< QgsCompositionConverter::MarkerMode>( itemElem.attribute( QStringLiteral( "markerMode" ), QStringLiteral( "0" ) ).toInt( ) );
741 
742  if ( markerMode == QgsCompositionConverter::MarkerMode::DefaultMarker )
743  {
744  layoutItem->setEndMarker( QgsLayoutItemPolyline::MarkerMode::ArrowHead );
745  layoutItem->setStartMarker( QgsLayoutItemPolyline::MarkerMode::NoMarker );
746  layoutItem->setArrowHeadFillColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "arrowHeadFillColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 255, 255, 255 ) ) ) ) );
747  layoutItem->setArrowHeadStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "arrowHeadOutlineColor" ), QgsSymbolLayerUtils::encodeColor( QColor( 255, 255, 255 ) ) ) ) );
748  layoutItem->setArrowHeadStrokeWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "1.0" ) ).toDouble( ) );
749  layoutItem->setArrowHeadWidth( itemElem.attribute( QStringLiteral( "arrowHeadWidth" ), QStringLiteral( "1.0" ) ).toDouble( ) );
750  }
751  else if ( markerMode == QgsCompositionConverter::MarkerMode::SVGMarker )
752  {
753  QString endMarkerFile = itemElem.attribute( QStringLiteral( "endMarkerFile" ) );
754  QString startMarkerFile = itemElem.attribute( QStringLiteral( "endMarkerFile" ) );
755 
756  // Fix the paths
757  if ( project )
758  {
759  // convert from relative path to absolute. For SVG we also need to consider system SVG paths
760  const QgsPathResolver pathResolver = project->pathResolver();
761  if ( !endMarkerFile.isEmpty() )
762  {
763  if ( endMarkerFile.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
764  endMarkerFile = QgsSymbolLayerUtils::svgSymbolNameToPath( endMarkerFile, pathResolver );
765  else
766  endMarkerFile = pathResolver.readPath( endMarkerFile );
767  }
768  if ( !startMarkerFile.isEmpty() )
769  {
770  if ( startMarkerFile.endsWith( QLatin1String( ".svg" ), Qt::CaseInsensitive ) )
771  startMarkerFile = QgsSymbolLayerUtils::svgSymbolNameToPath( startMarkerFile, pathResolver );
772  else
773  startMarkerFile = pathResolver.readPath( startMarkerFile );
774  }
775  }
776  if ( !endMarkerFile.isEmpty() )
777  {
778  layoutItem->setEndMarker( QgsLayoutItemPolyline::MarkerMode::SvgMarker );
779  layoutItem->setEndSvgMarkerPath( endMarkerFile );
780  }
781  if ( !startMarkerFile.isEmpty() )
782  {
783  layoutItem->setStartMarker( QgsLayoutItemPolyline::MarkerMode::SvgMarker );
784  layoutItem->setStartSvgMarkerPath( startMarkerFile );
785  }
786  }
787  else // NoMarker
788  {
789  layoutItem->setEndMarker( QgsLayoutItemPolyline::MarkerMode::NoMarker );
790  layoutItem->setStartMarker( QgsLayoutItemPolyline::MarkerMode::NoMarker );
791  }
792  // Calculate the margin
793  const double margin = polygon.boundingRect().left() - layoutItem->pos().x();
794  polygon.translate( - polygon.boundingRect().left() + margin, - polygon.boundingRect().top() + margin );
795  layoutItem->setNodes( polygon );
796 
797  return true;
798 }
799 
800 bool QgsCompositionConverter::readMapXml( QgsLayoutItemMap *layoutItem, const QDomElement &itemElem, const QgsProject *project, QgsStringMap &mapId2Uuid )
801 {
802  restoreGeneralComposeItemProperties( layoutItem, itemElem );
803 
804  mapId2Uuid[ itemElem.attribute( QStringLiteral( "id" ) ) ] = layoutItem->uuid();
805 
806  // TODO: Unused but all the layouts readXML require it (I'd suggest to remove it from the API)
807  const QDomDocument doc;
808 
809  QgsReadWriteContext context;
810 
811  if ( project )
812  context.setPathResolver( project->pathResolver() );
813 
814  //extent
815  const QDomNodeList extentNodeList = itemElem.elementsByTagName( QStringLiteral( "Extent" ) );
816  if ( !extentNodeList.isEmpty() )
817  {
818  const QDomElement extentElem = extentNodeList.at( 0 ).toElement();
819  double xmin, xmax, ymin, ymax;
820  xmin = extentElem.attribute( QStringLiteral( "xmin" ) ).toDouble();
821  xmax = extentElem.attribute( QStringLiteral( "xmax" ) ).toDouble();
822  ymin = extentElem.attribute( QStringLiteral( "ymin" ) ).toDouble();
823  ymax = extentElem.attribute( QStringLiteral( "ymax" ) ).toDouble();
824  layoutItem->setExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
825  }
826 
827  const QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
828  if ( !crsNodeList.isEmpty() )
829  {
830  const QDomElement crsElem = crsNodeList.at( 0 ).toElement();
831  layoutItem->crs().readXml( crsElem );
832  }
833  else
834  {
835  layoutItem->setCrs( QgsCoordinateReferenceSystem() );
836  }
837 
838  //map rotation
839  if ( !qgsDoubleNear( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble(), 0.0 ) )
840  {
841  layoutItem->setMapRotation( itemElem.attribute( QStringLiteral( "mapRotation" ), QStringLiteral( "0" ) ).toDouble() );
842  }
843 
844  // follow map theme
845  layoutItem->setFollowVisibilityPreset( itemElem.attribute( QStringLiteral( "followPreset" ) ).compare( QLatin1String( "true" ) ) == 0 );
846  layoutItem->setFollowVisibilityPresetName( itemElem.attribute( QStringLiteral( "followPresetName" ) ) );
847 
848  //mKeepLayerSet flag
849  const QString keepLayerSetFlag = itemElem.attribute( QStringLiteral( "keepLayerSet" ) );
850  if ( keepLayerSetFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
851  {
852  layoutItem->setKeepLayerSet( true );
853  }
854  else
855  {
856  layoutItem->setKeepLayerSet( false );
857  }
858 
859  const QString drawCanvasItemsFlag = itemElem.attribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
860  if ( drawCanvasItemsFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
861  {
862  layoutItem->setDrawAnnotations( true );
863  }
864  else
865  {
866  layoutItem->setDrawAnnotations( false );
867  }
868 
869  layoutItem->mLayerStyleOverrides.clear();
870 
871  //mLayers
872  layoutItem->mLayers.clear();
873 
874  const QDomNodeList layerSetNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerSet" ) );
875  if ( !layerSetNodeList.isEmpty() )
876  {
877  const QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
878  const QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( QStringLiteral( "Layer" ) );
879  layoutItem->mLayers.reserve( layerIdNodeList.size() );
880  for ( int i = 0; i < layerIdNodeList.size(); ++i )
881  {
882  const QDomElement layerElem = layerIdNodeList.at( i ).toElement();
883  const QString layerId = layerElem.text();
884  const QString layerName = layerElem.attribute( QStringLiteral( "name" ) );
885  const QString layerSource = layerElem.attribute( QStringLiteral( "source" ) );
886  const QString layerProvider = layerElem.attribute( QStringLiteral( "provider" ) );
887 
888  QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
889  ref.resolveWeakly( project );
890  layoutItem->mLayers << ref;
891  }
892  }
893 
894  // override styles
895  const QDomNodeList layerStylesNodeList = itemElem.elementsByTagName( QStringLiteral( "LayerStyles" ) );
896  layoutItem->mKeepLayerStyles = !layerStylesNodeList.isEmpty();
897  if ( layoutItem->mKeepLayerStyles )
898  {
899  const QDomElement layerStylesElem = layerStylesNodeList.at( 0 ).toElement();
900  const QDomNodeList layerStyleNodeList = layerStylesElem.elementsByTagName( QStringLiteral( "LayerStyle" ) );
901  for ( int i = 0; i < layerStyleNodeList.size(); ++i )
902  {
903  const QDomElement &layerStyleElement = layerStyleNodeList.at( i ).toElement();
904  const QString layerId = layerStyleElement.attribute( QStringLiteral( "layerid" ) );
905  const QString layerName = layerStyleElement.attribute( QStringLiteral( "name" ) );
906  const QString layerSource = layerStyleElement.attribute( QStringLiteral( "source" ) );
907  const QString layerProvider = layerStyleElement.attribute( QStringLiteral( "provider" ) );
908  QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
909  ref.resolveWeakly( project );
910 
911  QgsMapLayerStyle style;
912  style.readXml( layerStyleElement );
913  layoutItem->mLayerStyleOverrides.insert( ref.layerId, style.xmlData() );
914  }
915  }
916 
917  layoutItem->mDrawing = false;
918  layoutItem->mNumCachedLayers = 0;
919  layoutItem->mCacheInvalidated = true;
920 
921  //overviews
922  //read overview stack
923  const QDomNodeList mapOverviewNodeList = itemElem.elementsByTagName( QStringLiteral( "ComposerMapOverview" ) );
924  for ( int i = 0; i < mapOverviewNodeList.size(); ++i )
925  {
926  const QDomElement mapOverviewElem = mapOverviewNodeList.at( i ).toElement();
927  std::unique_ptr<QgsLayoutItemMapOverview> mapOverview( new QgsLayoutItemMapOverview( mapOverviewElem.attribute( QStringLiteral( "name" ) ), layoutItem ) );
928  mapOverview->readXml( mapOverviewElem, doc, context );
929  const QString frameMapId = mapOverviewElem.attribute( QStringLiteral( "frameMap" ), QStringLiteral( "-1" ) );
930  if ( frameMapId != QLatin1String( "-1" ) && mapId2Uuid.contains( frameMapId ) )
931  {
932  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ frameMapId ] ) );
933  if ( mapInstance )
934  {
935  mapOverview->setLinkedMap( mapInstance );
936  }
937  layoutItem->mOverviewStack->addOverview( mapOverview.release() );
938  }
939  }
940 
941  //grids
942  layoutItem->mGridStack->readXml( itemElem, doc, context );
943 
944  //load grid / grid annotation in old xml format
945  //only do this if the grid stack didn't load any grids, otherwise this will
946  //be the dummy element created by QGIS >= 2.5 (refs #10905)
947  const QDomNodeList gridNodeList = itemElem.elementsByTagName( QStringLiteral( "Grid" ) );
948  if ( layoutItem->mGridStack->size() == 0 && !gridNodeList.isEmpty() )
949  {
950  const QDomElement gridElem = gridNodeList.at( 0 ).toElement();
951  QgsLayoutItemMapGrid *mapGrid = new QgsLayoutItemMapGrid( QObject::tr( "Grid %1" ).arg( 1 ), layoutItem );
952  mapGrid->setEnabled( gridElem.attribute( QStringLiteral( "show" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
953  mapGrid->setStyle( QgsLayoutItemMapGrid::GridStyle( gridElem.attribute( QStringLiteral( "gridStyle" ), QStringLiteral( "0" ) ).toInt() ) );
954  mapGrid->setIntervalX( gridElem.attribute( QStringLiteral( "intervalX" ), QStringLiteral( "0" ) ).toDouble() );
955  mapGrid->setIntervalY( gridElem.attribute( QStringLiteral( "intervalY" ), QStringLiteral( "0" ) ).toDouble() );
956  mapGrid->setOffsetX( gridElem.attribute( QStringLiteral( "offsetX" ), QStringLiteral( "0" ) ).toDouble() );
957  mapGrid->setOffsetY( gridElem.attribute( QStringLiteral( "offsetY" ), QStringLiteral( "0" ) ).toDouble() );
958  mapGrid->setCrossLength( gridElem.attribute( QStringLiteral( "crossLength" ), QStringLiteral( "3" ) ).toDouble() );
959  mapGrid->setFrameStyle( static_cast< QgsLayoutItemMapGrid::FrameStyle >( gridElem.attribute( QStringLiteral( "gridFrameStyle" ), QStringLiteral( "0" ) ).toInt() ) );
960  mapGrid->setFrameWidth( gridElem.attribute( QStringLiteral( "gridFrameWidth" ), QStringLiteral( "2.0" ) ).toDouble() );
961  mapGrid->setFramePenSize( gridElem.attribute( QStringLiteral( "gridFramePenThickness" ), QStringLiteral( "0.5" ) ).toDouble() );
962  mapGrid->setFramePenColor( QgsSymbolLayerUtils::decodeColor( gridElem.attribute( QStringLiteral( "framePenColor" ), QStringLiteral( "0,0,0" ) ) ) );
963  mapGrid->setFrameFillColor1( QgsSymbolLayerUtils::decodeColor( gridElem.attribute( QStringLiteral( "frameFillColor1" ), QStringLiteral( "255,255,255,255" ) ) ) );
964  mapGrid->setFrameFillColor2( QgsSymbolLayerUtils::decodeColor( gridElem.attribute( QStringLiteral( "frameFillColor2" ), QStringLiteral( "0,0,0,255" ) ) ) );
965  mapGrid->setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "gridBlendMode" ), QStringLiteral( "0" ) ).toUInt() ) ) );
966  const QDomElement gridSymbolElem = gridElem.firstChildElement( QStringLiteral( "symbol" ) );
967  QgsLineSymbol *lineSymbol = nullptr;
968  if ( gridSymbolElem.isNull() )
969  {
970  //old project file, read penWidth /penColorRed, penColorGreen, penColorBlue
971  lineSymbol = QgsLineSymbol::createSimple( QVariantMap() );
972  lineSymbol->setWidth( gridElem.attribute( QStringLiteral( "penWidth" ), QStringLiteral( "0" ) ).toDouble() );
973  lineSymbol->setColor( QColor( gridElem.attribute( QStringLiteral( "penColorRed" ), QStringLiteral( "0" ) ).toInt(),
974  gridElem.attribute( QStringLiteral( "penColorGreen" ), QStringLiteral( "0" ) ).toInt(),
975  gridElem.attribute( QStringLiteral( "penColorBlue" ), QStringLiteral( "0" ) ).toInt() ) );
976  }
977  else
978  {
979  lineSymbol = QgsSymbolLayerUtils::loadSymbol<QgsLineSymbol>( gridSymbolElem, context );
980  }
981  mapGrid->setLineSymbol( lineSymbol );
982 
983  //annotation
984  const QDomNodeList annotationNodeList = gridElem.elementsByTagName( QStringLiteral( "Annotation" ) );
985  if ( !annotationNodeList.isEmpty() )
986  {
987  const QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
988  mapGrid->setAnnotationEnabled( annotationElem.attribute( QStringLiteral( "show" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
989  mapGrid->setAnnotationFormat( QgsLayoutItemMapGrid::AnnotationFormat( annotationElem.attribute( QStringLiteral( "format" ), QStringLiteral( "0" ) ).toInt() ) );
990  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "leftPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Left );
991  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "rightPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Right );
992  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "topPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Top );
993  mapGrid->setAnnotationPosition( QgsLayoutItemMapGrid::AnnotationPosition( annotationElem.attribute( QStringLiteral( "bottomPosition" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Bottom );
994  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "leftDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Left );
995  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "rightDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Right );
996  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "topDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Top );
997  mapGrid->setAnnotationDirection( QgsLayoutItemMapGrid::AnnotationDirection( annotationElem.attribute( QStringLiteral( "bottomDirection" ), QStringLiteral( "0" ) ).toInt() ), QgsLayoutItemMapGrid::Bottom );
998  mapGrid->setAnnotationFrameDistance( annotationElem.attribute( QStringLiteral( "frameDistance" ), QStringLiteral( "0" ) ).toDouble() );
999  QFont annotationFont;
1000  annotationFont.fromString( annotationElem.attribute( QStringLiteral( "font" ), QString() ) );
1001 
1002  QgsTextFormat annotationFormat = mapGrid->annotationTextFormat();
1003  annotationFormat.setFont( annotationFont );
1004  if ( annotationFont.pointSizeF() > 0 )
1005  {
1006  annotationFormat.setSize( annotationFont.pointSizeF() );
1007  annotationFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
1008  }
1009  else if ( annotationFont.pixelSize() > 0 )
1010  {
1011  annotationFormat.setSize( annotationFont.pixelSize() );
1012  annotationFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
1013  }
1014  annotationFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1015  mapGrid->setAnnotationTextFormat( annotationFormat );
1016 
1017  mapGrid->setAnnotationPrecision( annotationElem.attribute( QStringLiteral( "precision" ), QStringLiteral( "3" ) ).toInt() );
1018  }
1019  layoutItem->mGridStack->addGrid( mapGrid );
1020  }
1021 
1022  //atlas
1023  const QDomNodeList atlasNodeList = itemElem.elementsByTagName( QStringLiteral( "AtlasMap" ) );
1024  if ( !atlasNodeList.isEmpty() )
1025  {
1026  const QDomElement atlasElem = atlasNodeList.at( 0 ).toElement();
1027  layoutItem->mAtlasDriven = ( atlasElem.attribute( QStringLiteral( "atlasDriven" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
1028  if ( atlasElem.hasAttribute( QStringLiteral( "fixedScale" ) ) ) // deprecated XML
1029  {
1030  layoutItem->setAtlasScalingMode( atlasElem.attribute( QStringLiteral( "fixedScale" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) ? QgsLayoutItemMap::AtlasScalingMode::Fixed : QgsLayoutItemMap::AtlasScalingMode::Auto );
1031  }
1032  else if ( atlasElem.hasAttribute( QStringLiteral( "scalingMode" ) ) )
1033  {
1034  layoutItem->setAtlasScalingMode( static_cast<QgsLayoutItemMap::AtlasScalingMode>( atlasElem.attribute( QStringLiteral( "scalingMode" ) ).toInt() ) );
1035  }
1036  layoutItem->setAtlasMargin( atlasElem.attribute( QStringLiteral( "margin" ), QStringLiteral( "0.1" ) ).toDouble() );
1037  }
1038 
1039  layoutItem->updateBoundingRect();
1040 
1041  return true;
1042 }
1043 
1044 bool QgsCompositionConverter::readScaleBarXml( QgsLayoutItemScaleBar *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QgsStringMap &mapId2Uuid )
1045 {
1046  Q_UNUSED( project )
1047  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1048 
1049  layoutItem->setHeight( itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble() );
1050  layoutItem->setHeight( itemElem.attribute( QStringLiteral( "height" ), QStringLiteral( "5.0" ) ).toDouble() );
1051  layoutItem->setLabelBarSpace( itemElem.attribute( QStringLiteral( "labelBarSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
1052  layoutItem->setBoxContentSpace( itemElem.attribute( QStringLiteral( "boxContentSpace" ), QStringLiteral( "1.0" ) ).toDouble() );
1053  layoutItem->setNumberOfSegments( itemElem.attribute( QStringLiteral( "numSegments" ), QStringLiteral( "2" ) ).toInt() );
1054  layoutItem->setNumberOfSegmentsLeft( itemElem.attribute( QStringLiteral( "numSegmentsLeft" ), QStringLiteral( "0" ) ).toInt() );
1055  layoutItem->setUnitsPerSegment( itemElem.attribute( QStringLiteral( "numUnitsPerSegment" ), QStringLiteral( "1.0" ) ).toDouble() );
1056  layoutItem->setSegmentSizeMode( static_cast<QgsScaleBarSettings::SegmentSizeMode>( itemElem.attribute( QStringLiteral( "segmentSizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
1057  layoutItem->setMinimumBarWidth( itemElem.attribute( QStringLiteral( "minBarWidth" ), QStringLiteral( "50" ) ).toDouble() );
1058  layoutItem->setMaximumBarWidth( itemElem.attribute( QStringLiteral( "maxBarWidth" ), QStringLiteral( "150" ) ).toDouble() );
1059  layoutItem->mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
1060  layoutItem->setMapUnitsPerScaleBarUnit( itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble() );
1061  layoutItem->setUnitLabel( itemElem.attribute( QStringLiteral( "unitLabel" ) ) );
1062 
1063  QFont f;
1064  if ( !QgsFontUtils::setFromXmlChildNode( f, itemElem, QStringLiteral( "scaleBarFont" ) ) )
1065  {
1066  f.fromString( itemElem.attribute( QStringLiteral( "font" ), QString() ) );
1067  }
1069  layoutItem->setFont( f );
1071 
1072  //colors
1073  //fill color
1074  const QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
1075  if ( !fillColorList.isEmpty() )
1076  {
1077  const QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
1078  bool redOk, greenOk, blueOk, alphaOk;
1079  int fillRed, fillGreen, fillBlue, fillAlpha;
1080 
1081  fillRed = fillColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1082  fillGreen = fillColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1083  fillBlue = fillColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1084  fillAlpha = fillColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1085 
1086  if ( redOk && greenOk && blueOk && alphaOk )
1087  {
1088  layoutItem->fillSymbol()->setColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
1089  }
1090  }
1091  else
1092  {
1093  layoutItem->fillSymbol()->setColor( QColor( itemElem.attribute( QStringLiteral( "brushColor" ), QStringLiteral( "#000000" ) ) ) );
1094  }
1095 
1096  //fill color 2
1097  const QDomNodeList fillColor2List = itemElem.elementsByTagName( QStringLiteral( "fillColor2" ) );
1098  if ( !fillColor2List.isEmpty() )
1099  {
1100  const QDomElement fillColor2Elem = fillColor2List.at( 0 ).toElement();
1101  bool redOk, greenOk, blueOk, alphaOk;
1102  int fillRed, fillGreen, fillBlue, fillAlpha;
1103 
1104  fillRed = fillColor2Elem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1105  fillGreen = fillColor2Elem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1106  fillBlue = fillColor2Elem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1107  fillAlpha = fillColor2Elem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1108 
1109  if ( redOk && greenOk && blueOk && alphaOk )
1110  {
1111  layoutItem->alternateFillSymbol()->setColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) );
1112  }
1113  }
1114  else
1115  {
1116  layoutItem->alternateFillSymbol()->setColor( QColor( itemElem.attribute( QStringLiteral( "brush2Color" ), QStringLiteral( "#ffffff" ) ) ) );
1117  }
1118 
1119  std::unique_ptr< QgsLineSymbol > lineSymbol = std::make_unique< QgsLineSymbol >();
1120  std::unique_ptr< QgsSimpleLineSymbolLayer > lineSymbolLayer = std::make_unique< QgsSimpleLineSymbolLayer >();
1121  lineSymbolLayer->setWidth( itemElem.attribute( QStringLiteral( "outlineWidth" ), QStringLiteral( "0.3" ) ).toDouble() );
1122  lineSymbolLayer->setWidthUnit( QgsUnitTypes::RenderMillimeters );
1123  lineSymbolLayer->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "lineJoinStyle" ), QStringLiteral( "miter" ) ) ) );
1124  lineSymbolLayer->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( itemElem.attribute( QStringLiteral( "lineCapStyle" ), QStringLiteral( "square" ) ) ) );
1125  //stroke color
1126  const QDomNodeList strokeColorList = itemElem.elementsByTagName( QStringLiteral( "strokeColor" ) );
1127  if ( !strokeColorList.isEmpty() )
1128  {
1129  const QDomElement strokeColorElem = strokeColorList.at( 0 ).toElement();
1130  bool redOk, greenOk, blueOk, alphaOk;
1131  int strokeRed, strokeGreen, strokeBlue, strokeAlpha;
1132 
1133  strokeRed = strokeColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1134  strokeGreen = strokeColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1135  strokeBlue = strokeColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1136  strokeAlpha = strokeColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1137 
1138  if ( redOk && greenOk && blueOk && alphaOk )
1139  {
1140  lineSymbolLayer->setColor( QColor( strokeRed, strokeGreen, strokeBlue, strokeAlpha ) );
1141  }
1142  }
1143  else
1144  {
1145  lineSymbolLayer->setColor( QColor( itemElem.attribute( QStringLiteral( "penColor" ), QStringLiteral( "#000000" ) ) ) );
1146  }
1147  lineSymbol->changeSymbolLayer( 0, lineSymbolLayer.release() );
1148  layoutItem->setDivisionLineSymbol( lineSymbol->clone() );
1149  layoutItem->setSubdivisionLineSymbol( lineSymbol->clone() );
1150  layoutItem->setLineSymbol( lineSymbol.release() );
1151 
1152  //font color
1153  const QDomNodeList textColorList = itemElem.elementsByTagName( QStringLiteral( "textColor" ) );
1154  if ( !textColorList.isEmpty() )
1155  {
1156  const QDomElement textColorElem = textColorList.at( 0 ).toElement();
1157  bool redOk, greenOk, blueOk, alphaOk;
1158  int textRed, textGreen, textBlue, textAlpha;
1159 
1160  textRed = textColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1161  textGreen = textColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1162  textBlue = textColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1163  textAlpha = textColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1164 
1165  if ( redOk && greenOk && blueOk && alphaOk )
1166  {
1168  layoutItem->setFontColor( QColor( textRed, textGreen, textBlue, textAlpha ) );
1170  }
1171  }
1172  else
1173  {
1174  QColor c;
1175  c.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
1177  layoutItem->setFontColor( c );
1179  }
1180 
1181  //style
1182  const QString styleString = itemElem.attribute( QStringLiteral( "style" ), QString() );
1183  layoutItem->setStyle( QObject::tr( styleString.toLocal8Bit().data() ) );
1184 
1185  if ( itemElem.attribute( QStringLiteral( "unitType" ) ).isEmpty() )
1186  {
1188  switch ( itemElem.attribute( QStringLiteral( "units" ) ).toInt() )
1189  {
1190  case 0:
1192  break;
1193  case 1:
1195  break;
1196  case 2:
1198  break;
1199  case 3:
1201  break;
1202  }
1203  layoutItem->setUnits( u );
1204  }
1205  else
1206  {
1207  layoutItem->setUnits( QgsUnitTypes::decodeDistanceUnit( itemElem.attribute( QStringLiteral( "unitType" ) ) ) );
1208  }
1209  layoutItem->setAlignment( static_cast< QgsScaleBarSettings::Alignment >( itemElem.attribute( QStringLiteral( "alignment" ), QStringLiteral( "0" ) ).toInt() ) );
1210 
1211  //composer map: use uuid
1212  const QString mapId = itemElem.attribute( QStringLiteral( "mapId" ), QStringLiteral( "-1" ) );
1213  if ( mapId != QLatin1String( "-1" ) && mapId2Uuid.contains( mapId ) )
1214  {
1215  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ mapId ] ) );
1216  if ( mapInstance )
1217  {
1218  layoutItem->setLinkedMap( mapInstance );
1219  }
1220  }
1221 
1222  return true;
1223 }
1224 
1225 bool QgsCompositionConverter::readLegendXml( QgsLayoutItemLegend *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QgsStringMap &mapId2Uuid )
1226 {
1227  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1228 
1229  QgsPathResolver pathResolver;
1230  if ( project )
1231  pathResolver = project->pathResolver();
1232  QgsReadWriteContext context;
1233  context.setPathResolver( pathResolver );
1234  context.setProjectTranslator( const_cast<QgsProject *>( project ) );
1235 
1236  //composer map: use uuid
1237  const QString mapId = itemElem.attribute( QStringLiteral( "map" ), QStringLiteral( "-1" ) );
1238  if ( mapId != QLatin1String( "-1" ) && mapId2Uuid.contains( mapId ) )
1239  {
1240  QgsLayoutItemMap *mapInstance = qobject_cast<QgsLayoutItemMap *>( layoutItem->layout()->itemByUuid( mapId2Uuid[ mapId ] ) );
1241  if ( mapInstance )
1242  {
1243  layoutItem->setLinkedMap( mapInstance );
1244  }
1245  }
1246 
1247  //read general properties
1248  layoutItem->setTitle( itemElem.attribute( QStringLiteral( "title" ) ) );
1249  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
1250  {
1251  layoutItem->setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
1252  }
1253  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
1254  if ( colCount < 1 ) colCount = 1;
1255  layoutItem->setColumnCount( colCount );
1256  layoutItem->setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
1257  layoutItem->setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
1258 
1259  const QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
1260  if ( !stylesNodeList.isEmpty() )
1261  {
1262  const QDomNode stylesNode = stylesNodeList.at( 0 );
1263  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
1264  {
1265  const QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
1266  QgsLegendStyle style;
1267  style.readXml( styleElem, QDomDocument() );
1268  const QString name = styleElem.attribute( QStringLiteral( "name" ) );
1270  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
1271  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
1272  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
1273  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
1274  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
1275  else continue;
1276  layoutItem->setStyle( s, style );
1277  }
1278  }
1279 
1280  //font color
1281  QColor fontClr;
1282  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
1283  layoutItem->setFontColor( fontClr );
1284 
1285  //spaces
1286  layoutItem->setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
1287  layoutItem->setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
1288 
1289  layoutItem->setSymbolWidth( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble() );
1290  layoutItem->setSymbolHeight( itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() );
1291  layoutItem->setWmsLegendWidth( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble() );
1292  layoutItem->setWmsLegendHeight( itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() );
1293  layoutItem->setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble() );
1294 
1295  layoutItem->setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
1296  layoutItem->setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
1297  layoutItem->setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
1298 
1299  layoutItem->setWrapString( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
1300 
1301  layoutItem->mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
1302  layoutItem->mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
1303  layoutItem->mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
1304 
1305  // QGIS >= 2.6
1306  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
1307  if ( layerTreeElem.isNull() )
1308  layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
1309 
1310  if ( !layerTreeElem.isNull() )
1311  {
1312  QgsLayerTree *tree( QgsLayerTree::readXml( layerTreeElem, context ) );
1313  if ( project )
1314  tree->resolveReferences( project, true );
1315  layoutItem->setCustomLayerTree( tree );
1316  }
1317  else
1318  {
1319  layoutItem->setCustomLayerTree( nullptr );
1320  }
1321 
1322  return true;
1323 }
1324 
1325 bool QgsCompositionConverter::readAtlasXml( QgsLayoutAtlas *atlasItem, const QDomElement &itemElem, const QgsProject *project )
1326 {
1327  atlasItem->setEnabled( itemElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1328 
1329  // look for stored layer name
1330  const QString layerId = itemElem.attribute( QStringLiteral( "coverageLayer" ) );
1331  const QString layerName = itemElem.attribute( QStringLiteral( "coverageLayerName" ) );
1332  const QString layerSource = itemElem.attribute( QStringLiteral( "coverageLayerSource" ) );
1333  const QString layerProvider = itemElem.attribute( QStringLiteral( "coverageLayerProvider" ) );
1334 
1335  QgsVectorLayerRef layerRef( layerId, layerName, layerSource, layerProvider );
1336  atlasItem->setCoverageLayer( layerRef.resolveWeakly( project ) );
1337 
1338  atlasItem->setPageNameExpression( itemElem.attribute( QStringLiteral( "pageNameExpression" ), QString() ) );
1339  QString errorString;
1340  atlasItem->setFilenameExpression( itemElem.attribute( QStringLiteral( "filenamePattern" ), QString() ), errorString );
1341  // note: no error reporting for errorString
1342  atlasItem->setSortFeatures( itemElem.attribute( QStringLiteral( "sortFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1343  if ( atlasItem->sortFeatures() )
1344  {
1345  atlasItem->setSortExpression( itemElem.attribute( QStringLiteral( "sortKey" ), QString() ) );
1346  atlasItem->setSortAscending( itemElem.attribute( QStringLiteral( "sortAscending" ), QStringLiteral( "true" ) ) == QLatin1String( "true" ) );
1347  }
1348  atlasItem->setFilterFeatures( itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1349  if ( atlasItem->filterFeatures( ) )
1350  {
1351  QString expErrorString;
1352  atlasItem->setFilterExpression( itemElem.attribute( QStringLiteral( "featureFilter" ), QString() ), expErrorString );
1353  // note: no error reporting for errorString
1354  }
1355 
1356  atlasItem->setHideCoverage( itemElem.attribute( QStringLiteral( "hideCoverage" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1357 
1358  return true;
1359 
1360 }
1361 
1362 bool QgsCompositionConverter::readHtmlXml( QgsLayoutItemHtml *layoutItem, const QDomElement &itemElem, const QgsProject *project )
1363 {
1364  Q_UNUSED( project )
1365  readOldComposerObjectXml( layoutItem, itemElem );
1366 
1367  //first create the frames
1368  layoutItem->setResizeMode( static_cast< QgsLayoutMultiFrame::ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
1369  const QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
1370  for ( int i = 0; i < frameList.size(); ++i )
1371  {
1372  const QDomElement frameElem = frameList.at( i ).toElement();
1373  QgsLayoutFrame *newFrame = new QgsLayoutFrame( layoutItem->layout(), layoutItem );
1374  restoreGeneralComposeItemProperties( newFrame, frameElem );
1375  // Read frame XML
1376  const double x = itemElem.attribute( QStringLiteral( "sectionX" ) ).toDouble();
1377  const double y = itemElem.attribute( QStringLiteral( "sectionY" ) ).toDouble();
1378  const double width = itemElem.attribute( QStringLiteral( "sectionWidth" ) ).toDouble();
1379  const double height = itemElem.attribute( QStringLiteral( "sectionHeight" ) ).toDouble();
1380  newFrame->setContentSection( QRectF( x, y, width, height ) );
1381  newFrame->setHidePageIfEmpty( itemElem.attribute( QStringLiteral( "hidePageIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1382  newFrame->setHideBackgroundIfEmpty( itemElem.attribute( QStringLiteral( "hideBackgroundIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1383  layoutItem->addFrame( newFrame, false );
1384  }
1385 
1386  bool contentModeOK;
1387  layoutItem->setContentMode( static_cast< QgsLayoutItemHtml::ContentMode >( itemElem.attribute( QStringLiteral( "contentMode" ) ).toInt( &contentModeOK ) ) );
1388  if ( !contentModeOK )
1389  {
1390  layoutItem->setContentMode( QgsLayoutItemHtml::ContentMode::Url );
1391  }
1392  layoutItem->setEvaluateExpressions( itemElem.attribute( QStringLiteral( "evaluateExpressions" ), QStringLiteral( "true" ) ) == QLatin1String( "true" ) );
1393  layoutItem->setUseSmartBreaks( itemElem.attribute( QStringLiteral( "useSmartBreaks" ), QStringLiteral( "true" ) ) == QLatin1String( "true" ) );
1394  layoutItem->setMaxBreakDistance( itemElem.attribute( QStringLiteral( "maxBreakDistance" ), QStringLiteral( "10" ) ).toDouble() );
1395  layoutItem->setHtml( itemElem.attribute( QStringLiteral( "html" ) ) );
1396  layoutItem->setUserStylesheet( itemElem.attribute( QStringLiteral( "stylesheet" ) ) );
1397  layoutItem->setUserStylesheetEnabled( itemElem.attribute( QStringLiteral( "stylesheetEnabled" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) );
1398 
1399  //finally load the set url
1400  const QString urlString = itemElem.attribute( QStringLiteral( "url" ) );
1401  if ( !urlString.isEmpty() )
1402  {
1403  layoutItem->setUrl( urlString );
1404  }
1405  layoutItem->loadHtml( true );
1406 
1407  return true;
1408 }
1409 
1410 bool QgsCompositionConverter::readTableXml( QgsLayoutItemAttributeTable *layoutItem, const QDomElement &itemElem, const QgsProject *project )
1411 {
1412 
1413  Q_UNUSED( project )
1414  readOldComposerObjectXml( layoutItem, itemElem );
1415 
1416  //first create the frames
1417  layoutItem->setResizeMode( static_cast< QgsLayoutMultiFrame::ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() ) );
1418  const QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
1419  for ( int i = 0; i < frameList.size(); ++i )
1420  {
1421  const QDomElement frameElem = frameList.at( i ).toElement();
1422  QgsLayoutFrame *newFrame = new QgsLayoutFrame( layoutItem->layout(), layoutItem );
1423  restoreGeneralComposeItemProperties( newFrame, frameElem );
1424  // Read frame XML
1425  const double x = itemElem.attribute( QStringLiteral( "sectionX" ) ).toDouble();
1426  const double y = itemElem.attribute( QStringLiteral( "sectionY" ) ).toDouble();
1427  const double width = itemElem.attribute( QStringLiteral( "sectionWidth" ) ).toDouble();
1428  const double height = itemElem.attribute( QStringLiteral( "sectionHeight" ) ).toDouble();
1429  newFrame->setContentSection( QRectF( x, y, width, height ) );
1430  newFrame->setHidePageIfEmpty( itemElem.attribute( QStringLiteral( "hidePageIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1431  newFrame->setHideBackgroundIfEmpty( itemElem.attribute( QStringLiteral( "hideBackgroundIfEmpty" ), QStringLiteral( "0" ) ).toInt() );
1432  layoutItem->addFrame( newFrame, false );
1433  }
1434 
1435  layoutItem->setEmptyTableBehavior( static_cast<QgsLayoutTable::EmptyTableMode>( itemElem.attribute( QStringLiteral( "emptyTableMode" ), QStringLiteral( "0" ) ).toInt() ) );
1436  layoutItem->setEmptyTableMessage( itemElem.attribute( QStringLiteral( "emptyTableMessage" ), QObject::tr( "No matching records" ) ) );
1437  layoutItem->setShowEmptyRows( itemElem.attribute( QStringLiteral( "showEmptyRows" ), QStringLiteral( "0" ) ).toInt() );
1438  QFont headerFont;
1439  if ( !QgsFontUtils::setFromXmlChildNode( headerFont, itemElem, QStringLiteral( "headerFontProperties" ) ) )
1440  {
1441  headerFont.fromString( itemElem.attribute( QStringLiteral( "headerFont" ), QString() ) );
1442  }
1443  QgsTextFormat headerFormat = layoutItem->headerTextFormat();
1444  headerFormat.setFont( headerFont );
1445  if ( headerFont.pointSizeF() > 0 )
1446  {
1447  headerFormat.setSize( headerFont.pointSizeF() );
1448  headerFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
1449  }
1450  else if ( headerFont.pixelSize() > 0 )
1451  {
1452  headerFormat.setSize( headerFont.pixelSize() );
1453  headerFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
1454  }
1455  headerFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "headerFontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1456  layoutItem->setHeaderTextFormat( headerFormat );
1457  layoutItem->setHeaderHAlignment( static_cast<QgsLayoutTable::HeaderHAlignment>( itemElem.attribute( QStringLiteral( "headerHAlignment" ), QStringLiteral( "0" ) ).toInt() ) ) ;
1458  layoutItem->setHeaderMode( static_cast<QgsLayoutTable::HeaderMode>( itemElem.attribute( QStringLiteral( "headerMode" ), QStringLiteral( "0" ) ).toInt() ) );
1459 
1460  QFont contentFont;
1461  if ( !QgsFontUtils::setFromXmlChildNode( contentFont, itemElem, QStringLiteral( "contentFontProperties" ) ) )
1462  {
1463  contentFont.fromString( itemElem.attribute( QStringLiteral( "contentFont" ), QString() ) );
1464  }
1465  QgsTextFormat contentFormat = layoutItem->contentTextFormat();
1466  contentFormat.setFont( contentFont );
1467  if ( contentFont.pointSizeF() > 0 )
1468  {
1469  contentFormat.setSize( contentFont.pointSizeF() );
1470  contentFormat.setSizeUnit( QgsUnitTypes::RenderPoints );
1471  }
1472  else if ( contentFont.pixelSize() > 0 )
1473  {
1474  contentFormat.setSize( contentFont.pixelSize() );
1475  contentFormat.setSizeUnit( QgsUnitTypes::RenderPixels );
1476  }
1477  contentFormat.setColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "contentFontColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1478  layoutItem->setContentTextFormat( contentFormat );
1479 
1480  layoutItem->setCellMargin( itemElem.attribute( QStringLiteral( "cellMargin" ), QStringLiteral( "1.0" ) ).toDouble() );
1481  layoutItem->setGridStrokeWidth( itemElem.attribute( QStringLiteral( "gridStrokeWidth" ), QStringLiteral( "0.5" ) ).toDouble() );
1482  layoutItem->setHorizontalGrid( itemElem.attribute( QStringLiteral( "horizontalGrid" ), QStringLiteral( "1" ) ).toInt() );
1483  layoutItem->setVerticalGrid( itemElem.attribute( QStringLiteral( "verticalGrid" ), QStringLiteral( "1" ) ).toInt() );
1484  layoutItem->setShowGrid( itemElem.attribute( QStringLiteral( "showGrid" ), QStringLiteral( "1" ) ).toInt() );
1485  layoutItem->setGridColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "gridColor" ), QStringLiteral( "0,0,0,255" ) ) ) );
1486  layoutItem->setBackgroundColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "backgroundColor" ), QStringLiteral( "255,255,255,0" ) ) ) );
1487  layoutItem->setWrapBehavior( static_cast<QgsLayoutTable::WrapBehavior>( itemElem.attribute( QStringLiteral( "wrapBehavior" ), QStringLiteral( "0" ) ).toInt() ) );
1488 
1489  //restore column specifications
1490  layoutItem->mColumns.clear();
1491  layoutItem->mSortColumns.clear();
1492 
1493  const QDomNodeList columnsList = itemElem.elementsByTagName( QStringLiteral( "displayColumns" ) );
1494  if ( !columnsList.isEmpty() )
1495  {
1496  const QDomElement columnsElem = columnsList.at( 0 ).toElement();
1497  const QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
1498  for ( int i = 0; i < columnEntryList.size(); ++i )
1499  {
1500  const QDomElement columnElem = columnEntryList.at( i ).toElement();
1501  QgsLayoutTableColumn column;
1502  column.mHAlignment = static_cast< Qt::AlignmentFlag >( columnElem.attribute( QStringLiteral( "hAlignment" ), QString::number( Qt::AlignLeft ) ).toInt() );
1503  column.mVAlignment = static_cast< Qt::AlignmentFlag >( columnElem.attribute( QStringLiteral( "vAlignment" ), QString::number( Qt::AlignVCenter ) ).toInt() );
1504  column.mHeading = columnElem.attribute( QStringLiteral( "heading" ), QString() );
1505  column.mAttribute = columnElem.attribute( QStringLiteral( "attribute" ), QString() );
1506  column.mSortByRank = columnElem.attribute( QStringLiteral( "sortByRank" ), QStringLiteral( "0" ) ).toInt();
1507  column.mSortOrder = static_cast< Qt::SortOrder >( columnElem.attribute( QStringLiteral( "sortOrder" ), QString::number( Qt::AscendingOrder ) ).toInt() );
1508  column.mWidth = columnElem.attribute( QStringLiteral( "width" ), QStringLiteral( "0.0" ) ).toDouble();
1509 
1510  const QDomNodeList bgColorList = columnElem.elementsByTagName( QStringLiteral( "backgroundColor" ) );
1511  if ( !bgColorList.isEmpty() )
1512  {
1513  const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
1514  bool redOk, greenOk, blueOk, alphaOk;
1515  int bgRed, bgGreen, bgBlue, bgAlpha;
1516  bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1517  bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1518  bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1519  bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1520  if ( redOk && greenOk && blueOk && alphaOk )
1521  {
1522  column.mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
1523  }
1524  }
1525  layoutItem->mColumns.append( column );
1526 
1527  // sorting columns are now (QGIS 3.14+) handled in a dedicated list
1528  // copy the display columns if sortByRank > 0 and then, sort them by rank
1530  std::copy_if( layoutItem->mColumns.begin(), layoutItem->mColumns.end(), std::back_inserter( layoutItem->mSortColumns ), []( const QgsLayoutTableColumn & col ) {return col.sortByRank() > 0;} );
1531  std::sort( layoutItem->mSortColumns.begin(), layoutItem->mSortColumns.end(), []( const QgsLayoutTableColumn & a, const QgsLayoutTableColumn & b ) {return a.sortByRank() < b.sortByRank();} );
1533  }
1534  }
1535 
1536  //restore cell styles
1537  const QDomNodeList stylesList = itemElem.elementsByTagName( QStringLiteral( "cellStyles" ) );
1538  if ( !stylesList.isEmpty() )
1539  {
1540  const QDomElement stylesElem = stylesList.at( 0 ).toElement();
1541 
1542  QMap< QgsLayoutTable::CellStyleGroup, QString >::const_iterator it = layoutItem->mCellStyleNames.constBegin();
1543  for ( ; it != layoutItem->mCellStyleNames.constEnd(); ++it )
1544  {
1545  const QString styleName = it.value();
1546  const QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
1547  if ( !styleList.isEmpty() )
1548  {
1549  const QDomElement styleElem = styleList.at( 0 ).toElement();
1550  QgsLayoutTableStyle *style = layoutItem->mCellStyles.value( it.key() );
1551  if ( style )
1552  style->readXml( styleElem );
1553  }
1554  }
1555  }
1556 
1557  // look for stored layer name
1558  const QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
1559  const QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
1560  const QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
1561  const QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
1562 
1563  QgsVectorLayerRef layerRef( layerId, layerName, layerSource, layerProvider );
1564  layoutItem->setVectorLayer( layerRef.resolveWeakly( project ) );
1565 
1566  return true;
1567 }
1568 
1569 bool QgsCompositionConverter::readGroupXml( QgsLayoutItemGroup *layoutItem, const QDomElement &itemElem, const QgsProject *project, const QList< QgsLayoutObject * > &items )
1570 {
1571  Q_UNUSED( project )
1572 
1573  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1574 
1575  const QDomNodeList nodes = itemElem.elementsByTagName( "ComposerItemGroupElement" );
1576  for ( int i = 0, n = nodes.size(); i < n; ++i )
1577  {
1578  const QDomElement groupElement = nodes.at( i ).toElement();
1579  const QString elementUuid = groupElement.attribute( "uuid" );
1580 
1581  for ( QgsLayoutObject *item : items )
1582  {
1583  if ( dynamic_cast<QgsLayoutItem *>( item ) && static_cast<QgsLayoutItem *>( item )->uuid() == elementUuid )
1584  {
1585  layoutItem->addItem( static_cast<QgsLayoutItem *>( item ) );
1586  break;
1587  }
1588  }
1589  }
1590 
1591  return true;
1592 }
1593 
1594 template <class T, class T2>
1595 bool QgsCompositionConverter::readPolyXml( T *layoutItem, const QDomElement &itemElem, const QgsProject *project )
1596 {
1597  restoreGeneralComposeItemProperties( layoutItem, itemElem );
1598  const QDomNodeList nodeList = itemElem.elementsByTagName( QStringLiteral( "node" ) );
1599  if ( !nodeList.isEmpty() )
1600  {
1601  QPolygonF polygon;
1602  for ( int i = 0; i < nodeList.length(); i++ )
1603  {
1604  const QDomElement node = nodeList.at( i ).toElement();
1605  polygon.append( QPointF( node.attribute( QStringLiteral( "x" ) ).toDouble( ), node.attribute( QStringLiteral( "y" ) ).toDouble() ) );
1606  }
1607  layoutItem->setNodes( polygon );
1608  }
1609  if ( itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).size() )
1610  {
1611  const QDomElement symbolElement = itemElem.elementsByTagName( QStringLiteral( "symbol" ) ).at( 0 ).toElement();
1612  QgsReadWriteContext context;
1613  if ( project )
1614  context.setPathResolver( project->pathResolver( ) );
1615  T2 *styleSymbol = QgsSymbolLayerUtils::loadSymbol<T2>( symbolElement, context );
1616  if ( styleSymbol )
1617  layoutItem->setSymbol( styleSymbol );
1618  }
1619  // Disable frame for shapes
1620  layoutItem->setFrameEnabled( false );
1621  layoutItem->setBackgroundEnabled( false );
1622  return true;
1623 }
1624 
1625 
1626 bool QgsCompositionConverter::readXml( QgsLayoutItem *layoutItem, const QDomElement &itemElem )
1627 {
1628  if ( itemElem.isNull() )
1629  {
1630  return false;
1631  }
1632 
1633  readOldComposerObjectXml( layoutItem, itemElem );
1634 
1635  //uuid
1636  layoutItem->mUuid = itemElem.attribute( QStringLiteral( "uuid" ), QUuid::createUuid().toString() );
1637 
1638  // temporary for groups imported from templates
1639  layoutItem->mTemplateUuid = itemElem.attribute( QStringLiteral( "templateUuid" ) );
1640 
1641  //id
1642  const QString id = itemElem.attribute( QStringLiteral( "id" ), QString() );
1643  layoutItem->setId( id );
1644 
1645  //frame
1646  const QString frame = itemElem.attribute( QStringLiteral( "frame" ) );
1647  layoutItem->setFrameEnabled( frame.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 ) ;
1648 
1649  //frame
1650  const QString background = itemElem.attribute( QStringLiteral( "background" ) );
1651  layoutItem->setBackgroundEnabled( background.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 );
1652 
1653  //position lock for mouse moves/resizes
1654  const QString positionLock = itemElem.attribute( QStringLiteral( "positionLock" ) );
1655  layoutItem->setLocked( positionLock.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 );
1656 
1657  //visibility
1658  layoutItem->setVisibility( itemElem.attribute( QStringLiteral( "visibility" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
1659 
1660  layoutItem->mParentGroupUuid = itemElem.attribute( QStringLiteral( "groupUuid" ) );
1661  if ( !layoutItem->mParentGroupUuid.isEmpty() )
1662  {
1663  if ( QgsLayoutItemGroup *group = layoutItem->parentGroup() )
1664  {
1665  group->addItem( layoutItem );
1666  }
1667  }
1668  layoutItem->mTemplateUuid = itemElem.attribute( QStringLiteral( "templateUuid" ) );
1669 
1670 
1671  const QRectF position = itemPosition( layoutItem, itemElem );
1672 
1673  // TODO: missing?
1674  // mLastValidViewScaleFactor = itemElem.attribute( QStringLiteral( "lastValidViewScaleFactor" ), QStringLiteral( "-1" ) ).toDouble();
1675 
1676  layoutItem->setZValue( itemElem.attribute( QStringLiteral( "zValue" ) ).toDouble() );
1677 
1678  //pen
1679  const QDomNodeList frameColorList = itemElem.elementsByTagName( QStringLiteral( "FrameColor" ) );
1680  if ( !frameColorList.isEmpty() )
1681  {
1682  const QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
1683  bool redOk, greenOk, blueOk, alphaOk, widthOk;
1684  int penRed, penGreen, penBlue, penAlpha;
1685  double penWidth;
1686 
1687  penWidth = itemElem.attribute( QStringLiteral( "outlineWidth" ) ).toDouble( &widthOk );
1688  penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1689  penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1690  penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1691  penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1692  layoutItem->setFrameJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( itemElem.attribute( QStringLiteral( "frameJoinStyle" ), QStringLiteral( "miter" ) ) ) );
1693 
1694  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
1695  {
1696  layoutItem->setFrameStrokeColor( QColor( penRed, penGreen, penBlue, penAlpha ) );
1697  layoutItem->setFrameStrokeWidth( QgsLayoutMeasurement( penWidth ) );
1698  QPen framePen( layoutItem->frameStrokeColor() );
1699  framePen.setWidthF( layoutItem->frameStrokeWidth( ).length() );
1700  framePen.setJoinStyle( layoutItem->frameJoinStyle( ) );
1701  layoutItem->setPen( framePen );
1702  //apply any data defined settings
1703  layoutItem->refreshFrame( false );
1704  }
1705  }
1706 
1707  //brush
1708  const QDomNodeList bgColorList = itemElem.elementsByTagName( QStringLiteral( "BackgroundColor" ) );
1709  if ( !bgColorList.isEmpty() )
1710  {
1711  const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
1712  bool redOk, greenOk, blueOk, alphaOk;
1713  int bgRed, bgGreen, bgBlue, bgAlpha;
1714  bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
1715  bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
1716  bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
1717  bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
1718  if ( redOk && greenOk && blueOk && alphaOk )
1719  {
1720  layoutItem->mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
1721  layoutItem->setBrush( QBrush( layoutItem->mBackgroundColor, Qt::SolidPattern ) );
1722  }
1723  //apply any data defined settings
1724  layoutItem->refreshBackgroundColor( false );
1725  }
1726 
1727  //blend mode
1728  layoutItem->setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) ) );
1729 
1730  //opacity
1731  if ( itemElem.hasAttribute( QStringLiteral( "opacity" ) ) )
1732  {
1733  layoutItem->setItemOpacity( itemElem.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) ).toDouble() );
1734  }
1735  else
1736  {
1737  layoutItem->setItemOpacity( 1.0 - itemElem.attribute( QStringLiteral( "transparency" ), QStringLiteral( "0" ) ).toInt() / 100.0 );
1738  }
1739 
1740  layoutItem->mExcludeFromExports = itemElem.attribute( QStringLiteral( "excludeFromExports" ), QStringLiteral( "0" ) ).toInt();
1741  layoutItem->mEvaluatedExcludeFromExports = layoutItem->mExcludeFromExports;
1742 
1743  // positioning
1744  layoutItem->attemptSetSceneRect( position );
1745  //rotation
1746  layoutItem->setItemRotation( itemElem.attribute( QStringLiteral( "itemRotation" ), QStringLiteral( "0" ) ).toDouble(), false );
1747 
1748  layoutItem->mBlockUndoCommands = false;
1749 
1750  return true;
1751 }
1752 
1753 
1754 
1755 bool QgsCompositionConverter::readOldComposerObjectXml( QgsLayoutObject *layoutItem,
1756  const QDomElement &itemElem )
1757 {
1758  if ( itemElem.isNull() )
1759  {
1760  return false;
1761  }
1762 
1763  //old (pre 3.0) data defined properties
1764  QgsCompositionConverter::readOldDataDefinedPropertyMap( itemElem, layoutItem->mDataDefinedProperties );
1765 
1766  const QDomNode propsNode = itemElem.namedItem( QStringLiteral( "dataDefinedProperties" ) );
1767  if ( !propsNode.isNull() )
1768  {
1769  layoutItem->mDataDefinedProperties.readXml( propsNode.toElement(), sPropertyDefinitions );
1770  }
1772  {
1773  // upgrade transparency -> opacity
1775  exp = QStringLiteral( "100.0 - (%1)" ).arg( exp );
1778  }
1779 
1780  //custom properties
1781  layoutItem->mCustomProperties.readXml( itemElem );
1782 
1783  return true;
1784 }
1785 
1786 
1787 void QgsCompositionConverter::readOldDataDefinedPropertyMap( const QDomElement &itemElem, QgsPropertyCollection &dataDefinedProperties )
1788 {
1789  const QgsPropertiesDefinition defs = QgsCompositionConverter::propertyDefinitions();
1790  QgsPropertiesDefinition::const_iterator i = defs.constBegin();
1791  for ( ; i != defs.constEnd(); ++i )
1792  {
1793  const QString elemName = i.value().name();
1794  const QDomNodeList ddNodeList = itemElem.elementsByTagName( elemName );
1795  if ( !ddNodeList.isEmpty() )
1796  {
1797  const QDomElement ddElem = ddNodeList.at( 0 ).toElement();
1798  const QgsProperty prop = readOldDataDefinedProperty( static_cast< QgsCompositionConverter::DataDefinedProperty >( i.key() ), ddElem );
1799  if ( prop )
1800  dataDefinedProperties.setProperty( i.key(), prop );
1801  }
1802  }
1803 }
1804 
1805 QgsProperty QgsCompositionConverter::readOldDataDefinedProperty( const QgsCompositionConverter::DataDefinedProperty property, const QDomElement &ddElem )
1806 {
1808  {
1809  //invalid property
1810  return QgsProperty();
1811  }
1812 
1813  const QString active = ddElem.attribute( QStringLiteral( "active" ) );
1814  bool isActive = false;
1815  if ( active.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
1816  {
1817  isActive = true;
1818  }
1819  const QString field = ddElem.attribute( QStringLiteral( "field" ) );
1820  const QString expr = ddElem.attribute( QStringLiteral( "expr" ) );
1821 
1822  const QString useExpr = ddElem.attribute( QStringLiteral( "useExpr" ) );
1823  bool isExpression = false;
1824  if ( useExpr.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
1825  {
1826  isExpression = true;
1827  }
1828 
1829  if ( isExpression )
1830  return QgsProperty::fromExpression( expr, isActive );
1831  else
1832  return QgsProperty::fromField( field, isActive );
1833 }
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 setFont(const QFont &font)
Sets the label's current font.
void setMode(Mode mode)
Sets the label's current mode, allowing the label to switch between font based and HTML based renderi...
void setVAlign(Qt::AlignmentFlag alignment)
Sets for the vertical alignment of the label.
void setFontColor(const QColor &color)
Sets the label font color.
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:48
@ Double
Double value (including negative values)
Definition: qgsproperty.h:58
@ StrokeWidth
Line stroke width.
Definition: qgsproperty.h:73
@ String
Any string value.
Definition: qgsproperty.h:62
@ BlendMode
Blend mode.
Definition: qgsproperty.h:68
@ Boolean
Boolean value.
Definition: qgsproperty.h:54
@ IntegerPositiveGreaterZero
Non-zero positive integer values.
Definition: qgsproperty.h:57
@ IntegerPositive
Positive integer values (including 0)
Definition: qgsproperty.h:56
@ Opacity
Opacity (0-100)
Definition: qgsproperty.h:63
@ Rotation
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:61
@ ColorWithAlpha
Color with alpha channel.
Definition: qgsproperty.h:65
@ DoublePositive
Positive double value (including 0)
Definition: qgsproperty.h:59
@ DataTypeString
Property requires a string value.
Definition: qgsproperty.h:93
A store for object properties.
Definition: qgsproperty.h:232
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:1730
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1729
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
QMap< QString, QString > QgsStringMap
Definition: qgis.h:1691
const QgsField & field
Definition: qgsfield.h:463
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.