Skip to content

Commit 6e71130

Browse files
committedMay 18, 2020
[layouts][api] Expose to public API the option to create separate
files for each logical layer in a layout The backend for this was previously used only when creating a GeoPDF export, where we create an individual PDF per layout logical layer which are then composited by GDAL. By publicly exposing this functionality via the QgsLayoutExporter API we allow for plugins which can export layouts to seperate logical PDF files, allowing for other (non-GDAL) tools to be used to later composite the layers (e.g. Adobe Illustrator (which stupidly cannot handle multi-layer single PDF files!)) Sponsored by SMEC/SJ
1 parent 0d54cac commit 6e71130

File tree

3 files changed

+92
-64
lines changed

3 files changed

+92
-64
lines changed
 

‎python/core/auto_generated/layout/qgslayoutexporter.sip.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ Constructor for PdfExportSettings
198198

199199
bool writeGeoPdf;
200200

201+
bool exportLayersAsSeperateFiles;
202+
201203
bool useIso32000ExtensionFormatGeoreferencing;
202204

203205
bool useOgcBestPracticeFormatGeoreferencing;

‎src/core/layout/qgslayoutexporter.cpp

Lines changed: 75 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f
528528
}
529529

530530
std::unique_ptr< QgsLayoutGeoPdfExporter > geoPdfExporter;
531-
if ( settings.writeGeoPdf )
531+
if ( settings.writeGeoPdf || settings.exportLayersAsSeperateFiles )
532532
geoPdfExporter = qgis::make_unique< QgsLayoutGeoPdfExporter >( mLayout );
533533

534534
mLayout->renderContext().setFlags( settings.flags );
@@ -542,27 +542,31 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f
542542
mLayout->renderContext().setExportThemes( settings.exportThemes );
543543

544544
ExportResult result = Success;
545-
if ( settings.writeGeoPdf )
545+
if ( settings.writeGeoPdf || settings.exportLayersAsSeperateFiles )
546546
{
547547
mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagRenderLabelsByMapLayer, true );
548548

549549
// here we need to export layers to individual PDFs
550550
PdfExportSettings subSettings = settings;
551551
subSettings.writeGeoPdf = false;
552+
subSettings.exportLayersAsSeperateFiles = false;
552553

553554
const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
554555

555556
QList< QgsLayoutGeoPdfExporter::ComponentLayerDetail > pdfComponents;
556557

557-
auto exportFunc = [this, &subSettings, &pdfComponents, &geoPdfExporter]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
558+
const QDir baseDir = settings.exportLayersAsSeperateFiles ? QFileInfo( filePath ).dir() : QDir();
559+
const QString baseFileName = settings.exportLayersAsSeperateFiles ? QFileInfo( filePath ).completeBaseName() : QString();
560+
561+
auto exportFunc = [this, &subSettings, &pdfComponents, &geoPdfExporter, &settings, &baseDir, &baseFileName]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
558562
{
559563
ExportResult layerExportResult = Success;
560564
QPrinter printer;
561565
QgsLayoutGeoPdfExporter::ComponentLayerDetail component;
562566
component.name = layerDetail.name;
563567
component.mapLayerId = layerDetail.mapLayerId;
564568
component.group = layerDetail.mapTheme;
565-
component.sourcePdfPath = geoPdfExporter->generateTemporaryFilepath( QStringLiteral( "layer_%1.pdf" ).arg( layerId ) );
569+
component.sourcePdfPath = settings.writeGeoPdf ? geoPdfExporter->generateTemporaryFilepath( QStringLiteral( "layer_%1.pdf" ).arg( layerId ) ) : baseDir.filePath( QStringLiteral( "%1_%2.pdf" ).arg( baseFileName ).arg( layerId, 4, 10, QChar( '0' ) ) );
566570
pdfComponents << component;
567571
preparePrintAsPdf( mLayout, printer, component.sourcePdfPath );
568572
preparePrint( mLayout, printer, false );
@@ -581,72 +585,79 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f
581585
if ( result != Success )
582586
return result;
583587

584-
QgsAbstractGeoPdfExporter::ExportDetails details;
585-
details.dpi = settings.dpi;
586-
// TODO - multipages
587-
QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
588-
QgsLayoutSize pageSizeMM = mLayout->renderContext().measurementConverter().convert( pageSize, QgsUnitTypes::LayoutMillimeters );
589-
details.pageSizeMm = pageSizeMM.toQSizeF();
590-
591-
if ( settings.exportMetadata )
588+
if ( settings.writeGeoPdf )
592589
{
593-
// copy layout metadata to GeoPDF export settings
594-
details.author = mLayout->project()->metadata().author();
595-
details.producer = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
596-
details.creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
597-
details.creationDateTime = mLayout->project()->metadata().creationDateTime();
598-
details.subject = mLayout->project()->metadata().abstract();
599-
details.title = mLayout->project()->metadata().title();
600-
details.keywords = mLayout->project()->metadata().keywords();
601-
}
590+
QgsAbstractGeoPdfExporter::ExportDetails details;
591+
details.dpi = settings.dpi;
592+
// TODO - multipages
593+
QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
594+
QgsLayoutSize pageSizeMM = mLayout->renderContext().measurementConverter().convert( pageSize, QgsUnitTypes::LayoutMillimeters );
595+
details.pageSizeMm = pageSizeMM.toQSizeF();
602596

603-
if ( settings.appendGeoreference )
604-
{
605-
// setup georeferencing
606-
QList< QgsLayoutItemMap * > maps;
607-
mLayout->layoutItems( maps );
608-
for ( QgsLayoutItemMap *map : qgis::as_const( maps ) )
597+
if ( settings.exportMetadata )
609598
{
610-
QgsAbstractGeoPdfExporter::GeoReferencedSection georef;
611-
georef.crs = map->crs();
612-
613-
const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
614-
const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
615-
const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
616-
const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
617-
const QgsLayoutPoint topLeftMm = mLayout->convertFromLayoutUnits( topLeft, QgsUnitTypes::LayoutMillimeters );
618-
const QgsLayoutPoint topRightMm = mLayout->convertFromLayoutUnits( topRight, QgsUnitTypes::LayoutMillimeters );
619-
const QgsLayoutPoint bottomLeftMm = mLayout->convertFromLayoutUnits( bottomLeft, QgsUnitTypes::LayoutMillimeters );
620-
const QgsLayoutPoint bottomRightMm = mLayout->convertFromLayoutUnits( bottomRight, QgsUnitTypes::LayoutMillimeters );
621-
622-
georef.pageBoundsPolygon.setExteriorRing( new QgsLineString( QVector< QgsPointXY >() << QgsPointXY( topLeftMm.x(), topLeftMm.y() )
623-
<< QgsPointXY( topRightMm.x(), topRightMm.y() )
624-
<< QgsPointXY( bottomRightMm.x(), bottomRightMm.y() )
625-
<< QgsPointXY( bottomLeftMm.x(), bottomLeftMm.y() )
626-
<< QgsPointXY( topLeftMm.x(), topLeftMm.y() ) ) );
627-
628-
georef.controlPoints.reserve( 4 );
629-
const QTransform t = map->layoutToMapCoordsTransform();
630-
const QgsPointXY topLeftMap = t.map( topLeft );
631-
const QgsPointXY topRightMap = t.map( topRight );
632-
const QgsPointXY bottomLeftMap = t.map( bottomLeft );
633-
const QgsPointXY bottomRightMap = t.map( bottomRight );
634-
635-
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( topLeftMm.x(), topLeftMm.y() ), topLeftMap );
636-
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( topRightMm.x(), topRightMm.y() ), topRightMap );
637-
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( bottomLeftMm.x(), bottomLeftMm.y() ), bottomLeftMap );
638-
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( bottomRightMm.x(), bottomRightMm.y() ), bottomRightMap );
639-
details.georeferencedSections << georef;
599+
// copy layout metadata to GeoPDF export settings
600+
details.author = mLayout->project()->metadata().author();
601+
details.producer = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
602+
details.creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
603+
details.creationDateTime = mLayout->project()->metadata().creationDateTime();
604+
details.subject = mLayout->project()->metadata().abstract();
605+
details.title = mLayout->project()->metadata().title();
606+
details.keywords = mLayout->project()->metadata().keywords();
640607
}
641-
}
642608

643-
details.customLayerTreeGroups = geoPdfExporter->customLayerTreeGroups();
644-
details.includeFeatures = settings.includeGeoPdfFeatures;
645-
details.useOgcBestPracticeFormatGeoreferencing = settings.useOgcBestPracticeFormatGeoreferencing;
646-
details.useIso32000ExtensionFormatGeoreferencing = settings.useIso32000ExtensionFormatGeoreferencing;
609+
if ( settings.appendGeoreference )
610+
{
611+
// setup georeferencing
612+
QList< QgsLayoutItemMap * > maps;
613+
mLayout->layoutItems( maps );
614+
for ( QgsLayoutItemMap *map : qgis::as_const( maps ) )
615+
{
616+
QgsAbstractGeoPdfExporter::GeoReferencedSection georef;
617+
georef.crs = map->crs();
618+
619+
const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
620+
const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
621+
const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
622+
const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
623+
const QgsLayoutPoint topLeftMm = mLayout->convertFromLayoutUnits( topLeft, QgsUnitTypes::LayoutMillimeters );
624+
const QgsLayoutPoint topRightMm = mLayout->convertFromLayoutUnits( topRight, QgsUnitTypes::LayoutMillimeters );
625+
const QgsLayoutPoint bottomLeftMm = mLayout->convertFromLayoutUnits( bottomLeft, QgsUnitTypes::LayoutMillimeters );
626+
const QgsLayoutPoint bottomRightMm = mLayout->convertFromLayoutUnits( bottomRight, QgsUnitTypes::LayoutMillimeters );
627+
628+
georef.pageBoundsPolygon.setExteriorRing( new QgsLineString( QVector< QgsPointXY >() << QgsPointXY( topLeftMm.x(), topLeftMm.y() )
629+
<< QgsPointXY( topRightMm.x(), topRightMm.y() )
630+
<< QgsPointXY( bottomRightMm.x(), bottomRightMm.y() )
631+
<< QgsPointXY( bottomLeftMm.x(), bottomLeftMm.y() )
632+
<< QgsPointXY( topLeftMm.x(), topLeftMm.y() ) ) );
633+
634+
georef.controlPoints.reserve( 4 );
635+
const QTransform t = map->layoutToMapCoordsTransform();
636+
const QgsPointXY topLeftMap = t.map( topLeft );
637+
const QgsPointXY topRightMap = t.map( topRight );
638+
const QgsPointXY bottomLeftMap = t.map( bottomLeft );
639+
const QgsPointXY bottomRightMap = t.map( bottomRight );
640+
641+
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( topLeftMm.x(), topLeftMm.y() ), topLeftMap );
642+
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( topRightMm.x(), topRightMm.y() ), topRightMap );
643+
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( bottomLeftMm.x(), bottomLeftMm.y() ), bottomLeftMap );
644+
georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( bottomRightMm.x(), bottomRightMm.y() ), bottomRightMap );
645+
details.georeferencedSections << georef;
646+
}
647+
}
647648

648-
if ( !geoPdfExporter->finalize( pdfComponents, filePath, details ) )
649-
result = PrintError;
649+
details.customLayerTreeGroups = geoPdfExporter->customLayerTreeGroups();
650+
details.includeFeatures = settings.includeGeoPdfFeatures;
651+
details.useOgcBestPracticeFormatGeoreferencing = settings.useOgcBestPracticeFormatGeoreferencing;
652+
details.useIso32000ExtensionFormatGeoreferencing = settings.useIso32000ExtensionFormatGeoreferencing;
653+
654+
if ( !geoPdfExporter->finalize( pdfComponents, filePath, details ) )
655+
result = PrintError;
656+
}
657+
else
658+
{
659+
result = Success;
660+
}
650661
}
651662
else
652663
{

‎src/core/layout/qgslayoutexporter.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,21 @@ class CORE_EXPORT QgsLayoutExporter
323323
*/
324324
bool writeGeoPdf = false;
325325

326+
/**
327+
* TRUE if individual layers from the layout should be rendered to separate PDF files.
328+
*
329+
* This option allows for separation of logic layout layers to individual PDF files. For instance,
330+
* if this option is TRUE, then a separate PDF file will be created per layer per map item in the
331+
* layout. Additionally, separate PDF files may be created for other complex layout items, resulting
332+
* in a set of PDF files which contain logical atomic components of the layout.
333+
*
334+
* This option is designed to allow the PDF files to be composited back together in an external
335+
* application (e.g. Adobe Illustrator) as a non-QGIS, post-production step.
336+
*
337+
* \since QGIS 3.14
338+
*/
339+
bool exportLayersAsSeperateFiles = false;
340+
326341
/**
327342
* TRUE if ISO3200 extension format georeferencing should be used.
328343
*

0 commit comments

Comments
 (0)
Please sign in to comment.