[layouts] Improve logic of splitting layouts into separate layers when

exporting to a multi-layer format

Now, items are either
- placed onto the same layer as other items (simple items like labels, lines, pictures)
- placed onto the same layer as only other items of equal types (semi-complex items like scalebars or pages)
- placed onto their own unique layers (complex items like legends, maps)

Plus lots of tests covering this, where previously there was few
This commit is contained in:
Nyall Dawson 2019-08-10 06:43:59 +10:00
parent b6ab6df69f
commit 208beb7f8c
25 changed files with 440 additions and 35 deletions

View File

@ -115,6 +115,8 @@ Returns whether the frame is empty.
virtual QgsExpressionContext createExpressionContext() const;
virtual ExportLayerBehavior exportLayerBehavior() const;
protected:

View File

@ -384,6 +384,25 @@ Sets the item's parent ``group``.
.. seealso:: :py:func:`isGroupMember`
.. seealso:: :py:func:`parentGroup`
%End
enum ExportLayerBehavior
{
CanGroupWithAnyOtherItem,
CanGroupWithItemsOfSameType,
MustPlaceInOwnLayer,
ItemContainsSubLayers,
};
virtual ExportLayerBehavior exportLayerBehavior() const;
%Docstring
Returns the behavior of this item during exporting to layered exports (e.g. SVG).
.. seealso:: :py:func:`numberExportLayers`
.. seealso:: :py:func:`exportLayerDetails`
.. versionadded:: 3.10
%End
virtual int numberExportLayers() const;
@ -394,6 +413,10 @@ Returns 0 if this item is to be placed on the same layer as the previous item,
Items which require multiply layers should check QgsLayoutContext.currentExportLayer() during
their rendering to determine which layer should be drawn.
.. seealso:: :py:func:`exportLayerBehavior`
.. seealso:: :py:func:`exportLayerDetails`
%End
struct ExportLayerDetail

View File

@ -71,6 +71,7 @@ Returns a list of items contained by the group.
virtual void finalizeRestoreFromXml();
virtual ExportLayerBehavior exportLayerBehavior() const;
protected:
virtual void draw( QgsLayoutItemRenderContext &context );

View File

@ -527,6 +527,8 @@ Returns the legend's renderer settings object.
virtual QgsExpressionContext createExpressionContext() const;
virtual ExportLayerBehavior exportLayerBehavior() const;
public slots:

View File

@ -89,6 +89,8 @@ The caller takes responsibility for deleting the returned object.
virtual int numberExportLayers() const;
virtual ExportLayerBehavior exportLayerBehavior() const;
virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails( int layer ) const;
virtual void setFrameStrokeWidth( QgsLayoutMeasurement width );

View File

@ -97,6 +97,8 @@ page orientation.
virtual QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = 0 ) /Factory/;
virtual ExportLayerBehavior exportLayerBehavior() const;
public slots:

View File

@ -547,6 +547,8 @@ Adjusts the scale bar box size and updates the item.
virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const;
virtual ExportLayerBehavior exportLayerBehavior() const;
protected:

View File

@ -880,9 +880,9 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &f
Qt::IntersectsItemBoundingRect,
Qt::AscendingOrder );
auto exportFunc = [this, &settings, width, height, i, bounds, fileName, &svg, &svgDocRoot]( unsigned int layerId, const QString & layerName )->QgsLayoutExporter::ExportResult
auto exportFunc = [this, &settings, width, height, i, bounds, fileName, &svg, &svgDocRoot]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
{
return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerName, svg, svgDocRoot, settings.exportMetadata );
return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.exportMetadata );
};
ExportResult res = handleLayeredExport( items, exportFunc );
if ( res != Success )
@ -1127,7 +1127,7 @@ void QgsLayoutExporter::updatePrinterPageSize( QgsLayout *layout, QPrinter &prin
printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
}
QgsLayoutExporter::ExportResult QgsLayoutExporter::renderToLayeredSvg( const SvgExportSettings &settings, double width, double height, int page, const QRectF &bounds, const QString &filename, int svgLayerId, const QString &layerName, QDomDocument &svg, QDomNode &svgDocRoot, bool includeMetadata ) const
QgsLayoutExporter::ExportResult QgsLayoutExporter::renderToLayeredSvg( const SvgExportSettings &settings, double width, double height, int page, const QRectF &bounds, const QString &filename, unsigned int svgLayerId, const QString &layerName, QDomDocument &svg, QDomNode &svgDocRoot, bool includeMetadata ) const
{
QBuffer svgBuffer;
{
@ -1430,52 +1430,123 @@ bool QgsLayoutExporter::georeferenceOutputPrivate( const QString &file, QgsLayou
}
QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QList<QGraphicsItem *> &items,
const std::function<QgsLayoutExporter::ExportResult( unsigned int, const QString & )> &exportFunc )
const std::function<QgsLayoutExporter::ExportResult( unsigned int, const QgsLayoutItem::ExportLayerDetail & )> &exportFunc )
{
LayoutItemHider itemHider( items );
( void )itemHider;
int layoutItemLayerIdx = 0;
auto it = items.constBegin();
for ( unsigned int layerId = 1; it != items.constEnd(); ++layerId )
int prevType = -1;
QgsLayoutItem::ExportLayerBehavior prevItemBehavior = QgsLayoutItem::CanGroupWithAnyOtherItem;
unsigned int layerId = 1;
QgsLayoutItem::ExportLayerDetail layerDetails;
layerDetails.name = QObject::tr( "Layer %1" ).arg( layerId );
itemHider.hideAll();
bool haveUnexportedItems = false;
for ( auto it = items.constBegin(); it != items.constEnd(); ++it )
{
itemHider.hideAll();
QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( *it );
const QString layerName = QObject::tr( "Layer %1" ).arg( layerId );
if ( layoutItem && layoutItem->numberExportLayers() > 0 )
bool canPlaceInExistingLayer = false;
if ( layoutItem )
{
layoutItem->show();
mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
++layoutItemLayerIdx;
switch ( layoutItem->exportLayerBehavior() )
{
case QgsLayoutItem::CanGroupWithAnyOtherItem:
{
switch ( prevItemBehavior )
{
case QgsLayoutItem::CanGroupWithAnyOtherItem:
canPlaceInExistingLayer = true;
break;
case QgsLayoutItem::CanGroupWithItemsOfSameType:
canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->type();
break;
case QgsLayoutItem::MustPlaceInOwnLayer:
case QgsLayoutItem::ItemContainsSubLayers:
canPlaceInExistingLayer = false;
break;
}
break;
}
case QgsLayoutItem::CanGroupWithItemsOfSameType:
{
switch ( prevItemBehavior )
{
case QgsLayoutItem::CanGroupWithAnyOtherItem:
case QgsLayoutItem::CanGroupWithItemsOfSameType:
canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->type();
break;
case QgsLayoutItem::MustPlaceInOwnLayer:
case QgsLayoutItem::ItemContainsSubLayers:
canPlaceInExistingLayer = false;
break;
}
break;
}
case QgsLayoutItem::MustPlaceInOwnLayer:
case QgsLayoutItem::ItemContainsSubLayers:
canPlaceInExistingLayer = false;
break;
}
prevItemBehavior = layoutItem->exportLayerBehavior();
prevType = layoutItem->type();
}
else
{
// show all items until the next item that renders on a separate layer
for ( ; it != items.constEnd(); ++it )
prevItemBehavior = QgsLayoutItem::MustPlaceInOwnLayer;
}
if ( canPlaceInExistingLayer )
{
( *it )->show();
haveUnexportedItems = true;
}
else
{
if ( haveUnexportedItems )
{
layoutItem = dynamic_cast<QgsLayoutItem *>( *it );
if ( layoutItem && layoutItem->numberExportLayers() > 0 )
ExportResult result = exportFunc( layerId, layerDetails );
if ( result != Success )
return result;
layerId++;
layerDetails.name = QObject::tr( "Layer %1" ).arg( layerId );
haveUnexportedItems = false;
}
itemHider.hideAll();
( *it )->show();
if ( layoutItem && layoutItem->exportLayerBehavior() == QgsLayoutItem::ItemContainsSubLayers )
{
for ( int layoutItemLayerIdx = 0; layoutItemLayerIdx < layoutItem->numberExportLayers(); layoutItemLayerIdx++ )
{
break;
}
else
{
( *it )->show();
mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
layerDetails = layoutItem->exportLayerDetails( layoutItemLayerIdx );
ExportResult result = exportFunc( layerId, layerDetails );
if ( result != Success )
return result;
layerId++;
layerDetails.name = QObject::tr( "Layer %1" ).arg( layerId );
}
mLayout->renderContext().setCurrentExportLayer( -1 );
haveUnexportedItems = false;
}
else
{
haveUnexportedItems = true;
}
}
ExportResult result = exportFunc( layerId, layerName );
}
if ( haveUnexportedItems )
{
ExportResult result = exportFunc( layerId, layerDetails );
if ( result != Success )
return result;
if ( layoutItem && layoutItem->numberExportLayers() > 0 && layoutItem->numberExportLayers() == layoutItemLayerIdx ) // restore and pass to next item
{
mLayout->renderContext().setCurrentExportLayer( -1 );
layoutItemLayerIdx = 0;
++it;
}
}
return Success;
}

View File

@ -585,7 +585,7 @@ class CORE_EXPORT QgsLayoutExporter
static void updatePrinterPageSize( QgsLayout *layout, QPrinter &printer, int page );
ExportResult renderToLayeredSvg( const SvgExportSettings &settings, double width, double height, int page, const QRectF &bounds,
const QString &filename, int svgLayerId, const QString &layerName,
const QString &filename, unsigned int svgLayerId, const QString &layerName,
QDomDocument &svg, QDomNode &svgDocRoot, bool includeMetadata ) const;
void appendMetadataToSvg( QDomDocument &svg ) const;
@ -593,10 +593,11 @@ class CORE_EXPORT QgsLayoutExporter
bool georeferenceOutputPrivate( const QString &file, QgsLayoutItemMap *referenceMap = nullptr,
const QRectF &exportRegion = QRectF(), double dpi = -1, bool includeGeoreference = true, bool includeMetadata = false ) const;
ExportResult handleLayeredExport( const QList<QGraphicsItem *> &items, const std::function<QgsLayoutExporter::ExportResult( unsigned int layerId, const QString &layerName )> &exportFunc );
ExportResult handleLayeredExport( const QList<QGraphicsItem *> &items, const std::function<QgsLayoutExporter::ExportResult( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail &layerDetails )> &exportFunc );
static QgsVectorSimplifyMethod createExportSimplifyMethod();
friend class TestQgsLayout;
friend class TestQgsLayoutExporter;
};

View File

@ -134,6 +134,10 @@ QgsExpressionContext QgsLayoutFrame::createExpressionContext() const
return context;
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutFrame::exportLayerBehavior() const
{
return CanGroupWithItemsOfSameType;
}
QString QgsLayoutFrame::displayName() const
{

View File

@ -110,6 +110,7 @@ class CORE_EXPORT QgsLayoutFrame: public QgsLayoutItem
bool isEmpty() const;
QgsExpressionContext createExpressionContext() const override;
ExportLayerBehavior exportLayerBehavior() const override;
protected:

View File

@ -244,6 +244,11 @@ void QgsLayoutItem::setParentGroup( QgsLayoutItemGroup *group )
setFlag( QGraphicsItem::ItemIsSelectable, !static_cast< bool>( group ) ); //item in groups cannot be selected
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutItem::exportLayerBehavior() const
{
return CanGroupWithAnyOtherItem;
}
QgsLayoutItem::ExportLayerDetail QgsLayoutItem::exportLayerDetails( int ) const
{
return QgsLayoutItem::ExportLayerDetail();

View File

@ -414,6 +414,26 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
void setParentGroup( QgsLayoutItemGroup *group );
/**
* Behavior of item when exporting to layered outputs.
* \since QGIS 3.10
*/
enum ExportLayerBehavior
{
CanGroupWithAnyOtherItem, //!< Item can be placed on a layer with any other item (default behavior)
CanGroupWithItemsOfSameType, //!< Item can only be placed on layers with other items of the same type, but multiple items of this type can be grouped together
MustPlaceInOwnLayer, //!< Item must be placed in its own individual layer
ItemContainsSubLayers, //!< Item contains multiple sublayers which must be individually exported
};
/**
* Returns the behavior of this item during exporting to layered exports (e.g. SVG).
* \see numberExportLayers()
* \see exportLayerDetails()
* \since QGIS 3.10
*/
virtual ExportLayerBehavior exportLayerBehavior() const;
/**
* Returns the number of layers that this item requires for exporting during layered exports (e.g. SVG).
* Returns 0 if this item is to be placed on the same layer as the previous item,
@ -421,6 +441,9 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*
* Items which require multiply layers should check QgsLayoutContext::currentExportLayer() during
* their rendering to determine which layer should be drawn.
*
* \see exportLayerBehavior()
* \see exportLayerDetails()
*/
virtual int numberExportLayers() const { return 0; }

View File

@ -272,6 +272,11 @@ void QgsLayoutItemGroup::finalizeRestoreFromXml()
resetBoundingRect();
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemGroup::exportLayerBehavior() const
{
return MustPlaceInOwnLayer;
}
void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
{
}

View File

@ -75,7 +75,7 @@ class CORE_EXPORT QgsLayoutItemGroup: public QgsLayoutItem
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
void finalizeRestoreFromXml() override;
ExportLayerBehavior exportLayerBehavior() const override;
protected:
void draw( QgsLayoutItemRenderContext &context ) override;
bool writePropertiesToElement( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const override;

View File

@ -865,6 +865,11 @@ QgsExpressionContext QgsLayoutItemLegend::createExpressionContext() const
return context;
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemLegend::exportLayerBehavior() const
{
return MustPlaceInOwnLayer;
}
// -------------------------------------------------------------------------
#include "qgslayertreemodellegendnode.h"

View File

@ -496,6 +496,7 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem
void finalizeRestoreFromXml() override;
QgsExpressionContext createExpressionContext() const override;
ExportLayerBehavior exportLayerBehavior() const override;
public slots:

View File

@ -993,6 +993,11 @@ int QgsLayoutItemMap::numberExportLayers() const
+ ( frameEnabled() ? 1 : 0 );
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemMap::exportLayerBehavior() const
{
return ItemContainsSubLayers;
}
QgsLayoutItem::ExportLayerDetail QgsLayoutItemMap::exportLayerDetails( int layer ) const
{
ExportLayerDetail detail;

View File

@ -117,6 +117,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
// for now, map items behave a bit differently and don't implement draw. TODO - see if we can avoid this
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
int numberExportLayers() const override;
ExportLayerBehavior exportLayerBehavior() const override;
QgsLayoutItem::ExportLayerDetail exportLayerDetails( int layer ) const override;
void setFrameStrokeWidth( QgsLayoutMeasurement width ) override;

View File

@ -175,6 +175,11 @@ QgsAbstractLayoutUndoCommand *QgsLayoutItemPage::createCommand( const QString &t
return new QgsLayoutItemPageUndoCommand( this, text, id, parent );
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemPage::exportLayerBehavior() const
{
return CanGroupWithItemsOfSameType;
}
void QgsLayoutItemPage::redraw()
{
QgsLayoutItem::redraw();

View File

@ -124,6 +124,7 @@ class CORE_EXPORT QgsLayoutItemPage : public QgsLayoutItem
QRectF boundingRect() const override;
void attemptResize( const QgsLayoutSize &size, bool includesFrame = false ) override;
QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = nullptr ) override SIP_FACTORY;
ExportLayerBehavior exportLayerBehavior() const override;
public slots:

View File

@ -884,3 +884,8 @@ bool QgsLayoutItemScaleBar::accept( QgsStyleEntityVisitorInterface *visitor ) co
return true;
}
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemScaleBar::exportLayerBehavior() const
{
return CanGroupWithItemsOfSameType;
}

View File

@ -467,6 +467,7 @@ class CORE_EXPORT QgsLayoutItemScaleBar: public QgsLayoutItem
void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
void finalizeRestoreFromXml() override;
bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
ExportLayerBehavior exportLayerBehavior() const override;
protected:

View File

@ -136,6 +136,7 @@ SET(TESTS
testqgslayout.cpp
testqgslayoutatlas.cpp
testqgslayoutcontext.cpp
testqgslayoutexporter.cpp
testqgslayouthtml.cpp
testqgslayoutitem.cpp
testqgslayoutitemgroup.cpp

View File

@ -0,0 +1,236 @@
/***************************************************************************
testqgslayoutexporter.cpp
-----------------
begin : August 2019
copyright : (C) 2019 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgslayout.h"
#include "qgstest.h"
#include "qgsproject.h"
#include "qgslayoutexporter.h"
#include "qgslayoutitemlabel.h"
#include "qgslayoutitemshape.h"
#include "qgslayoutitemscalebar.h"
#include "qgslayoutitemmap.h"
#include "qgsvectorlayer.h"
#include "qgslayoutitemlegend.h"
class TestQgsLayoutExporter: public QObject
{
Q_OBJECT
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void testHandleLayeredExport();
};
void TestQgsLayoutExporter::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();
}
void TestQgsLayoutExporter::cleanupTestCase()
{
}
void TestQgsLayoutExporter::init()
{
}
void TestQgsLayoutExporter::cleanup()
{
}
void TestQgsLayoutExporter::testHandleLayeredExport()
{
QgsProject p;
QgsLayout l( &p );
QgsLayoutExporter exporter( &l );
QList< unsigned int > layerIds;
QStringList layerNames;
QStringList mapLayerIds;
auto exportFunc = [&layerIds, &layerNames, &mapLayerIds]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
{
layerIds << layerId;
layerNames << layerDetail.name;
mapLayerIds << layerDetail.mapLayerId;
return QgsLayoutExporter::Success;
};
QList< QGraphicsItem * > items;
QgsLayoutExporter::ExportResult res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QVERIFY( layerIds.isEmpty() );
QVERIFY( layerNames.isEmpty() );
QVERIFY( mapLayerIds.isEmpty() );
// add two pages to a layout
QgsLayoutItemPage *page1 = new QgsLayoutItemPage( &l );
items << page1;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l );
items << page2;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
items << label;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
items << shape;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
QgsLayoutItemLabel *label2 = new QgsLayoutItemLabel( &l );
items << label2;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
// add an item which can only be used with other similar items, should break the next label into a different layer
QgsLayoutItemScaleBar *scaleBar = new QgsLayoutItemScaleBar( &l );
items << scaleBar;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
QgsLayoutItemLabel *label3 = new QgsLayoutItemLabel( &l );
items << label3;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
// multiple scalebars can be placed in the same layer
QgsLayoutItemScaleBar *scaleBar2 = new QgsLayoutItemScaleBar( &l );
items << scaleBar2;
QgsLayoutItemScaleBar *scaleBar3 = new QgsLayoutItemScaleBar( &l );
items << scaleBar3;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
// with an item which has sublayers
QgsVectorLayer *linesLayer = new QgsVectorLayer( TEST_DATA_DIR + QStringLiteral( "/lines.shp" ),
QStringLiteral( "lines" ), QStringLiteral( "ogr" ) );
QVERIFY( linesLayer->isValid() );
QgsVectorLayer *pointsLayer = new QgsVectorLayer( TEST_DATA_DIR + QStringLiteral( "/points.shp" ),
QStringLiteral( "points" ), QStringLiteral( "ogr" ) );
QVERIFY( pointsLayer->isValid() );
p.addMapLayer( linesLayer );
p.addMapLayer( pointsLayer );
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
map->setFrameEnabled( false );
map->setBackgroundEnabled( false );
map->setCrs( linesLayer->crs() );
map->zoomToExtent( linesLayer->extent() );
map->setLayers( QList<QgsMapLayer *>() << linesLayer );
items << map;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 << 6 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) << QStringLiteral( "lines" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() << linesLayer->id() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
map->setFrameEnabled( true );
map->setBackgroundEnabled( true );
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) << QStringLiteral( "Map Background" ) << QStringLiteral( "lines" ) << QStringLiteral( "Map Frame" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() << QString() << linesLayer->id() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
// add two legends -- legends are complex and must be placed in an isolated layer
QgsLayoutItemLegend *legend = new QgsLayoutItemLegend( &l );
QgsLayoutItemLegend *legend2 = new QgsLayoutItemLegend( &l );
items << legend << legend2;
res = exporter.handleLayeredExport( items, exportFunc );
QCOMPARE( res, QgsLayoutExporter::Success );
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 );
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) << QStringLiteral( "Map Background" ) << QStringLiteral( "lines" ) << QStringLiteral( "Map Frame" ) << QStringLiteral( "Layer 9" ) << QStringLiteral( "Layer 10" ) );
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() << QString() << linesLayer->id() << QString() << QString() << QString() );
layerIds.clear();
layerNames.clear();
mapLayerIds.clear();
qDeleteAll( items );
}
QGSTEST_MAIN( TestQgsLayoutExporter )
#include "testqgslayoutexporter.moc"