30 #include <QImageWriter>
32 #include <QSvgGenerator>
38 class LayoutContextPreviewSettingRestorer
42 LayoutContextPreviewSettingRestorer(
QgsLayout *layout )
44 , mPreviousSetting( layout->renderContext().mIsPreviewRender )
46 mLayout->renderContext().mIsPreviewRender =
false;
49 ~LayoutContextPreviewSettingRestorer()
51 mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
54 LayoutContextPreviewSettingRestorer(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
55 LayoutContextPreviewSettingRestorer &operator=(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
59 bool mPreviousSetting =
false;
62 class LayoutGuideHider
69 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
72 mPrevVisibility.insert( guide, guide->item()->isVisible() );
73 guide->item()->setVisible(
false );
79 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
81 it.key()->item()->setVisible( it.value() );
85 LayoutGuideHider(
const LayoutGuideHider &other ) =
delete;
86 LayoutGuideHider &operator=(
const LayoutGuideHider &other ) =
delete;
90 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
96 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
98 mItemsToIterate.reserve( items.count() );
99 for ( QGraphicsItem *item : items )
101 const bool isVisible = item->isVisible();
102 mPrevVisibility[item] = isVisible;
104 mItemsToIterate.append( item );
106 layoutItem->setProperty(
"wasVisible", isVisible );
114 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
122 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
124 it.key()->setVisible( it.value() );
126 layoutItem->setProperty(
"wasVisible", QVariant() );
130 QList< QGraphicsItem * > itemsToIterate()
const {
return mItemsToIterate; }
132 LayoutItemHider(
const LayoutItemHider &other ) =
delete;
133 LayoutItemHider &operator=(
const LayoutItemHider &other ) =
delete;
137 QList<QGraphicsItem * > mItemsToIterate;
138 QHash<QGraphicsItem *, bool> mPrevVisibility;
159 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
170 LayoutContextPreviewSettingRestorer restorer( mLayout );
173 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
182 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
193 LayoutContextPreviewSettingRestorer restorer( mLayout );
196 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
198 if ( imageSize.isValid() && ( !
qgsDoubleNear(
static_cast< double >( imageSize.width() ) / imageSize.height(),
199 paperRect.width() / paperRect.height(), 0.008 ) ) )
211 class LayoutItemCacheSettingRestorer
215 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
218 const QList< QGraphicsItem * > items = mLayout->items();
219 for ( QGraphicsItem *item : items )
221 mPrevCacheMode.insert( item, item->cacheMode() );
222 item->setCacheMode( QGraphicsItem::NoCache );
226 ~LayoutItemCacheSettingRestorer()
228 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
230 it.key()->setCacheMode( it.value() );
234 LayoutItemCacheSettingRestorer(
const LayoutItemCacheSettingRestorer &other ) =
delete;
235 LayoutItemCacheSettingRestorer &operator=(
const LayoutItemCacheSettingRestorer &other ) =
delete;
239 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
246 QPaintDevice *paintDevice = painter->device();
247 if ( !paintDevice || !mLayout )
252 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
253 ( void )cacheRestorer;
254 LayoutContextPreviewSettingRestorer restorer( mLayout );
256 LayoutGuideHider guideHider( mLayout );
261 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
269 LayoutContextPreviewSettingRestorer restorer( mLayout );
272 double resolution = mLayout->renderContext().dpi();
274 if ( imageSize.isValid() )
278 resolution = ( imageSize.width() / region.width()
279 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
287 int width = imageSize.isValid() ? imageSize.width()
288 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
289 int height = imageSize.isValid() ? imageSize.height()
290 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
292 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
293 if ( !image.isNull() )
295 image.setDotsPerMeterX(
static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
296 image.setDotsPerMeterY(
static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
297 image.fill( Qt::transparent );
298 QPainter imagePainter( &image );
300 if ( !imagePainter.isActive() )
308 class LayoutContextSettingsRestorer
313 LayoutContextSettingsRestorer(
QgsLayout *layout )
315 , mPreviousDpi( layout->renderContext().dpi() )
316 , mPreviousFlags( layout->renderContext().flags() )
317 , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
318 , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
319 , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
320 , mExportThemes( layout->renderContext().exportThemes() )
321 , mPredefinedScales( layout->renderContext().predefinedScales() )
326 ~LayoutContextSettingsRestorer()
328 mLayout->renderContext().setDpi( mPreviousDpi );
329 mLayout->renderContext().setFlags( mPreviousFlags );
330 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
332 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
334 mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
335 mLayout->renderContext().setExportThemes( mExportThemes );
336 mLayout->renderContext().setPredefinedScales( mPredefinedScales );
339 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
340 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
344 double mPreviousDpi = 0;
345 QgsLayoutRenderContext::Flags mPreviousFlags =
nullptr;
347 int mPreviousExportLayer = 0;
349 QStringList mExportThemes;
350 QVector< double > mPredefinedScales;
361 if ( settings.
dpi <= 0 )
362 settings.
dpi = mLayout->renderContext().dpi();
364 mErrorFileName.clear();
366 int worldFilePageNo = -1;
369 worldFilePageNo = referenceMap->page();
372 QFileInfo fi( filePath );
376 pageDetails.
baseName = fi.completeBaseName();
379 LayoutContextPreviewSettingRestorer restorer( mLayout );
381 LayoutContextSettingsRestorer dpiRestorer( mLayout );
383 mLayout->renderContext().setDpi( settings.
dpi );
384 mLayout->renderContext().setFlags( settings.
flags );
388 if ( settings.
pages.empty() )
390 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
395 for (
int page : qgis::as_const( settings.
pages ) )
397 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
402 for (
int page : qgis::as_const( pages ) )
404 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
411 QImage image = createImage( settings, page, bounds, skip );
416 pageDetails.
page = page;
419 if ( image.isNull() )
421 mErrorFileName = outputFilePath;
425 if ( !saveImage( image, outputFilePath, pageDetails.
extension, settings.
exportMetadata ? mLayout->project() :
nullptr ) )
427 mErrorFileName = outputFilePath;
431 const bool shouldGeoreference = ( page == worldFilePageNo );
432 if ( shouldGeoreference )
434 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
439 double a, b,
c, d, e, f;
440 if ( bounds.isValid() )
445 QFileInfo fi( outputFilePath );
447 QString outputSuffix = fi.suffix();
448 QString worldFileName = fi.absolutePath() +
'/' + fi.completeBaseName() +
'.'
449 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
451 writeWorldFile( worldFileName, a, b,
c, d, e, f );
466 int total = iterator->
count();
467 double step = total > 0 ? 100.0 / total : 100.0;
469 while ( iterator->
next() )
474 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
476 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
486 QString filePath = iterator->
filePath( baseFilePath, extension );
491 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( filePath ) );
509 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
513 if ( settings.
dpi <= 0 )
514 settings.
dpi = mLayout->renderContext().dpi();
516 mErrorFileName.clear();
518 LayoutContextPreviewSettingRestorer restorer( mLayout );
520 LayoutContextSettingsRestorer contextRestorer( mLayout );
521 ( void )contextRestorer;
522 mLayout->renderContext().setDpi( settings.
dpi );
527 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
530 std::unique_ptr< QgsLayoutGeoPdfExporter > geoPdfExporter;
532 geoPdfExporter = qgis::make_unique< QgsLayoutGeoPdfExporter >( mLayout );
534 mLayout->renderContext().setFlags( settings.
flags );
542 mLayout->renderContext().setExportThemes( settings.
exportThemes );
554 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
556 QList< QgsLayoutGeoPdfExporter::ComponentLayerDetail > pdfComponents;
566 component.
name = layerDetail.name;
567 component.
mapLayerId = layerDetail.mapLayerId;
568 component.
opacity = layerDetail.opacity;
570 component.
group = layerDetail.mapTheme;
571 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' ) ) );
572 pdfComponents << component;
573 preparePrintAsPdf( mLayout, printer, component.sourcePdfPath );
574 preparePrint( mLayout, printer,
false );
576 if ( !p.begin( &printer ) )
584 return layerExportResult;
586 result = handleLayeredExport( items, exportFunc );
590 if ( settings.writeGeoPdf )
593 details.
dpi = settings.dpi;
595 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
599 if ( settings.exportMetadata )
602 details.
author = mLayout->project()->metadata().author();
605 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
606 details.
subject = mLayout->project()->metadata().abstract();
607 details.
title = mLayout->project()->metadata().title();
608 details.
keywords = mLayout->project()->metadata().keywords();
611 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
617 if ( settings.appendGeoreference )
620 QList< QgsLayoutItemMap * > maps;
621 mLayout->layoutItems( maps );
625 georef.
crs = map->crs();
627 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
628 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
629 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
630 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
643 const QTransform t = map->layoutToMapCoordsTransform();
644 const QgsPointXY topLeftMap = t.map( topLeft );
645 const QgsPointXY topRightMap = t.map( topRight );
646 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
647 const QgsPointXY bottomRightMap = t.map( bottomRight );
659 details.
layerOrder = geoPdfExporter->layerOrder();
664 if ( !geoPdfExporter->finalize( pdfComponents, filePath, details ) )
675 preparePrintAsPdf( mLayout, printer, filePath );
676 preparePrint( mLayout, printer,
false );
678 if ( !p.begin( &printer ) )
687 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
690 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
708 int total = iterator->
count();
709 double step = total > 0 ? 100.0 / total : 100.0;
712 while ( iterator->
next() )
717 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
719 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
731 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
733 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
734 ( void )contextRestorer;
756 preparePrintAsPdf( iterator->
layout(), printer, fileName );
757 preparePrint( iterator->
layout(), printer,
false );
759 if ( !p.begin( &printer ) )
772 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( fileName ) );
796 int total = iterator->
count();
797 double step = total > 0 ? 100.0 / total : 100.0;
799 while ( iterator->
next() )
804 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
806 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
815 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
822 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( filePath ) );
844 if ( settings.
dpi <= 0 )
845 settings.
dpi = mLayout->renderContext().dpi();
847 mErrorFileName.clear();
849 LayoutContextPreviewSettingRestorer restorer( mLayout );
851 LayoutContextSettingsRestorer contextRestorer( mLayout );
852 ( void )contextRestorer;
853 mLayout->renderContext().setDpi( settings.
dpi );
855 mLayout->renderContext().setFlags( settings.
flags );
862 preparePrint( mLayout, printer,
true );
864 if ( !p.begin( &printer ) )
887 int total = iterator->
count();
888 double step = total > 0 ? 100.0 / total : 100.0;
891 while ( iterator->
next() )
896 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
898 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ).arg( total ) );
910 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
912 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
913 ( void )contextRestorer;
926 preparePrint( iterator->
layout(), printer,
true );
928 if ( !p.begin( &printer ) )
962 if ( settings.
dpi <= 0 )
963 settings.
dpi = mLayout->renderContext().dpi();
965 mErrorFileName.clear();
967 LayoutContextPreviewSettingRestorer restorer( mLayout );
969 LayoutContextSettingsRestorer contextRestorer( mLayout );
970 ( void )contextRestorer;
971 mLayout->renderContext().setDpi( settings.
dpi );
973 mLayout->renderContext().setFlags( settings.
flags );
980 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
983 QFileInfo fi( filePath );
986 pageDetails.
baseName = fi.baseName();
987 pageDetails.
extension = fi.completeSuffix();
991 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
993 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
998 pageDetails.
page = i;
1005 if ( mLayout->pageCollection()->pageCount() == 1 )
1008 bounds = mLayout->layoutBounds(
true );
1013 bounds = mLayout->pageItemBounds( i,
true );
1022 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1026 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1028 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1029 if ( width == 0 || height == 0 )
1038 const QRectF paperRect = QRectF( pageItem->pos().x(),
1039 pageItem->pos().y(),
1040 pageItem->rect().width(),
1041 pageItem->rect().height() );
1043 QDomNode svgDocRoot;
1044 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
1045 Qt::IntersectsItemBoundingRect,
1046 Qt::AscendingOrder );
1050 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1052 ExportResult res = handleLayeredExport( items, exportFunc );
1057 appendMetadataToSvg( svg );
1059 QFile out( fileName );
1060 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1063 mErrorFileName = fileName;
1067 out.write( svg.toByteArray() );
1073 QSvgGenerator generator;
1076 generator.setTitle( mLayout->project()->metadata().title() );
1077 generator.setDescription( mLayout->project()->metadata().abstract() );
1079 generator.setOutputDevice( &svgBuffer );
1080 generator.setSize( QSize( width, height ) );
1081 generator.setViewBox( QRect( 0, 0, width, height ) );
1082 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1085 bool createOk = p.begin( &generator );
1088 mErrorFileName = fileName;
1101 svgBuffer.open( QIODevice::ReadOnly );
1105 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1107 mErrorFileName = fileName;
1112 appendMetadataToSvg( svg );
1114 QFile out( fileName );
1115 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1118 mErrorFileName = fileName;
1122 out.write( svg.toByteArray() );
1137 int total = iterator->
count();
1138 double step = total > 0 ? 100.0 / total : 100.0;
1140 while ( iterator->
next() )
1145 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1147 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
1157 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
1164 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( filePath ) );
1181 void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPrinter &printer,
const QString &filePath )
1183 printer.setOutputFileName( filePath );
1184 printer.setOutputFormat( QPrinter::PdfFormat );
1186 updatePrinterPageSize(
layout, printer, firstPageToBeExported(
layout ) );
1195 void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPrinter &printer,
bool setFirstPageSize )
1197 printer.setFullPage(
true );
1198 printer.setColorMode( QPrinter::Color );
1203 if ( setFirstPageSize )
1205 updatePrinterPageSize(
layout, printer, firstPageToBeExported(
layout ) );
1211 if ( mLayout->pageCollection()->pageCount() == 0 )
1214 preparePrint( mLayout, printer,
true );
1216 if ( !p.begin( &printer ) )
1222 printPrivate( printer, p );
1227 QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPrinter &printer, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1230 int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
1231 int toPage = ( printer.toPage() < 1 ) ? mLayout->pageCollection()->pageCount() - 1 : printer.toPage() - 1;
1233 bool pageExported =
false;
1236 for (
int i = fromPage; i <= toPage; ++i )
1238 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1243 updatePrinterPageSize( mLayout, printer, i );
1244 if ( ( pageExported && i > fromPage ) || startNewPage )
1250 if ( !image.isNull() )
1252 QRectF targetArea( 0, 0, image.width(), image.height() );
1253 painter.drawImage( targetArea, image, targetArea );
1259 pageExported =
true;
1264 for (
int i = fromPage; i <= toPage; ++i )
1266 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1271 updatePrinterPageSize( mLayout, printer, i );
1273 if ( ( pageExported && i > fromPage ) || startNewPage )
1278 pageExported =
true;
1284 void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPrinter &printer,
int page )
1289 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1290 QPageLayout::Portrait,
1291 QMarginsF( 0, 0, 0, 0 ) );
1292 pageLayout.setMode( QPageLayout::FullPageMode );
1293 printer.setPageLayout( pageLayout );
1294 printer.setFullPage(
true );
1295 printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1298 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
1302 QSvgGenerator generator;
1303 if ( includeMetadata )
1306 generator.setTitle( l->name() );
1307 else if ( mLayout->project() )
1308 generator.setTitle( mLayout->project()->title() );
1311 generator.setOutputDevice( &svgBuffer );
1312 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
1313 static_cast< int >( std::round( height ) ) ) );
1314 generator.setViewBox( QRect( 0, 0,
1315 static_cast< int >( std::round( width ) ),
1316 static_cast< int >( std::round( height ) ) ) );
1317 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1319 QPainter svgPainter( &generator );
1320 if ( settings.cropToContents )
1331 svgBuffer.open( QIODevice::ReadOnly );
1335 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1337 mErrorFileName = filename;
1340 if ( 1 == svgLayerId )
1342 svg = QDomDocument( doc.doctype() );
1343 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1344 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ),
false );
1345 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1346 svg.appendChild( svgDocRoot );
1348 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ),
true );
1349 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1350 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1351 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1352 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ),
true );
1353 svgDocRoot.appendChild( defs );
1354 svgDocRoot.appendChild( mainGroup );
1359 void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1362 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1363 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1364 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdf" ), QStringLiteral(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
1365 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdfs" ), QStringLiteral(
"http://www.w3.org/2000/01/rdf-schema#" ) );
1366 rdfElement.setAttribute( QStringLiteral(
"xmlns:dc" ), QStringLiteral(
"http://purl.org/dc/elements/1.1/" ) );
1367 QDomElement descriptionElement = svg.createElement( QStringLiteral(
"rdf:Description" ) );
1368 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1369 workElement.setAttribute( QStringLiteral(
"rdf:about" ), QString() );
1371 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1374 QDomElement element = svg.createElement( tag );
1375 QDomText t = svg.createTextNode( value );
1376 element.appendChild( t );
1377 workElement.appendChild( element );
1380 descriptionElement.setAttribute( tag, value );
1383 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1384 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1385 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1386 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1387 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1389 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1392 QDomElement inkscapeElement = svg.createElement( tag );
1393 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1394 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1395 QDomText t = svg.createTextNode( value );
1396 titleElement.appendChild( t );
1397 agentElement.appendChild( titleElement );
1398 inkscapeElement.appendChild( agentElement );
1399 workElement.appendChild( inkscapeElement );
1402 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1403 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1404 t = svg.createTextNode( value );
1405 liElement.appendChild( t );
1406 bagElement.appendChild( liElement );
1408 QDomElement element = svg.createElement( tag );
1409 element.appendChild( bagElement );
1410 descriptionElement.appendChild( element );
1413 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1414 addAgentNode( QStringLiteral(
"dc:publisher" ), QStringLiteral(
"QGIS %1" ).arg(
Qgis::version() ) );
1418 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1419 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1421 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1423 const QStringList words = it.value();
1424 for (
const QString &keyword : words )
1426 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1427 QDomText t = svg.createTextNode( keyword );
1428 liElement.appendChild( t );
1429 bagElement.appendChild( liElement );
1432 element.appendChild( bagElement );
1433 workElement.appendChild( element );
1434 descriptionElement.appendChild( element );
1437 rdfElement.appendChild( descriptionElement );
1438 rdfElement.appendChild( workElement );
1439 metadataElement.appendChild( rdfElement );
1440 svg.documentElement().appendChild( metadataElement );
1441 svg.documentElement().setAttribute( QStringLiteral(
"xmlns:cc" ), QStringLiteral(
"http://creativecommons.org/ns#" ) );
1444 std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1447 map = mLayout->referenceMap();
1453 dpi = mLayout->renderContext().dpi();
1456 QRectF exportRegion = region;
1457 if ( !exportRegion.isValid() )
1459 int pageNumber = map->
page();
1462 double pageY = page->pos().y();
1463 QSizeF pageSize = page->rect().size();
1464 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1468 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1471 double outputHeightMM = exportRegion.height();
1472 double outputWidthMM = exportRegion.width();
1476 double mapXCenter = mapExtent.
center().
x();
1477 double mapYCenter = mapExtent.
center().
y();
1479 double sinAlpha = std::sin( alpha );
1480 double cosAlpha = std::cos( alpha );
1483 QPointF mapItemPos = map->pos();
1485 mapItemPos.rx() -= exportRegion.left();
1486 mapItemPos.ry() -= exportRegion.top();
1489 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1490 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1491 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1492 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1493 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1496 double X0 = paperExtent.xMinimum();
1497 double Y0 = paperExtent.yMaximum();
1502 double X1 = X0 - mapXCenter;
1503 double Y1 = Y0 - mapYCenter;
1504 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1505 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1506 X0 = X2 + mapXCenter;
1507 Y0 = Y2 + mapYCenter;
1511 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1512 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1513 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1514 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1517 std::unique_ptr<double[]> t(
new double[6] );
1519 t[1] = cosAlpha * pixelWidthScale;
1520 t[2] = -sinAlpha * pixelWidthScale;
1522 t[4] = -sinAlpha * pixelHeightScale;
1523 t[5] = -cosAlpha * pixelHeightScale;
1528 void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1530 QFile worldFile( worldFileName );
1531 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1535 QTextStream fout( &worldFile );
1539 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1540 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1541 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1542 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1543 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1544 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1549 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1552 bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1557 if ( !map && includeGeoreference )
1558 map = mLayout->referenceMap();
1560 std::unique_ptr<double[]> t;
1562 if ( map && includeGeoreference )
1565 dpi = mLayout->renderContext().dpi();
1567 t = computeGeoTransform( map, exportRegion, dpi );
1572 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toLocal8Bit().constData() );
1577 GDALSetGeoTransform( outputDS.get(), t.get() );
1579 if ( includeMetadata )
1581 QString creationDateString;
1582 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1583 if ( creationDateTime.isValid() )
1585 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1586 if ( creationDateTime.timeZone().isValid() )
1588 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1589 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1590 offsetFromUtc = std::abs( offsetFromUtc );
1591 int offsetHours = offsetFromUtc / 3600;
1592 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1593 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1596 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toLocal8Bit().constData(),
nullptr );
1598 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toLocal8Bit().constData(),
nullptr );
1599 const QString creator = QStringLiteral(
"QGIS %1" ).arg(
Qgis::version() );
1600 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toLocal8Bit().constData(),
nullptr );
1601 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toLocal8Bit().constData(),
nullptr );
1602 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toLocal8Bit().constData(),
nullptr );
1603 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toLocal8Bit().constData(),
nullptr );
1606 QStringList allKeywords;
1607 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1609 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1611 const QString keywordString = allKeywords.join(
';' );
1612 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toLocal8Bit().constData(),
nullptr );
1618 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1625 if ( items.count() == 1 )
1629 QString name = layoutItem->displayName();
1631 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1632 name = name.mid( 1, name.length() - 2 );
1636 else if ( items.count() > 1 )
1638 QStringList currentLayerItemTypes;
1639 for ( QGraphicsItem *item : items )
1645 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1646 currentLayerItemTypes << itemType;
1647 else if ( currentLayerItemTypes.contains( itemType ) )
1649 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1654 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1655 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1658 return currentLayerItemTypes.join( QStringLiteral(
", " ) );
1660 return QObject::tr(
"Layer %1" ).arg( layerId );
1666 LayoutItemHider itemHider( items );
1671 unsigned int layerId = 1;
1673 itemHider.hideAll();
1674 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1675 QList< QGraphicsItem * > currentLayerItems;
1676 for ( QGraphicsItem *item : itemsToIterate )
1680 bool canPlaceInExistingLayer =
false;
1687 switch ( prevItemBehavior )
1690 canPlaceInExistingLayer =
true;
1694 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1699 canPlaceInExistingLayer =
false;
1707 switch ( prevItemBehavior )
1711 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1716 canPlaceInExistingLayer =
false;
1724 canPlaceInExistingLayer =
false;
1729 canPlaceInExistingLayer =
false;
1733 prevType = layoutItem->
type();
1740 if ( canPlaceInExistingLayer )
1742 currentLayerItems << item;
1747 if ( !currentLayerItems.isEmpty() )
1751 ExportResult result = exportFunc( layerId, layerDetails );
1755 currentLayerItems.clear();
1758 itemHider.hideAll();
1763 int layoutItemLayerIdx = 0;
1765 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1771 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1775 ExportResult result = exportFunc( layerId, layerDetails );
1780 layoutItemLayerIdx++;
1782 layerDetails.mapLayerId.clear();
1784 mLayout->renderContext().setCurrentExportLayer( -1 );
1787 currentLayerItems.clear();
1791 currentLayerItems << item;
1795 if ( !currentLayerItems.isEmpty() )
1798 ExportResult result = exportFunc( layerId, layerDetails );
1813 return simplifyMethod;
1827 int pageNumber = map->
page();
1829 double pageY = page->pos().y();
1830 QSizeF pageSize = page->rect().size();
1831 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
1847 double destinationHeight = exportRegion.height();
1848 double destinationWidth = exportRegion.width();
1850 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1855 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1856 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1858 double xCenter = mapExtent.
center().
x();
1859 double yCenter = mapExtent.
center().
y();
1862 QPointF mapItemPos = map->pos();
1864 mapItemPos.rx() -= exportRegion.left();
1865 mapItemPos.ry() -= exportRegion.top();
1867 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1868 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1869 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
1871 double X0 = paperExtent.
xMinimum();
1872 double Y0 = paperExtent.
yMinimum();
1875 dpi = mLayout->renderContext().dpi();
1877 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
1878 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
1880 double Ww = paperExtent.
width() / widthPx;
1881 double Hh = paperExtent.
height() / heightPx;
1890 s[5] = Y0 + paperExtent.
height();
1894 r[0] = std::cos( alpha );
1895 r[1] = -std::sin( alpha );
1896 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
1897 r[3] = std::sin( alpha );
1898 r[4] = std::cos( alpha );
1899 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
1902 a = r[0] * s[0] + r[1] * s[3];
1903 b = r[0] * s[1] + r[1] * s[4];
1904 c = r[0] * s[2] + r[1] * s[5] + r[2];
1905 d = r[3] * s[0] + r[4] * s[3];
1906 e = r[3] * s[1] + r[4] * s[4];
1907 f = r[3] * s[2] + r[4] * s[5] + r[5];
1917 if ( mLayout->pageCollection()->pageCount() == 1 )
1920 bounds = mLayout->layoutBounds(
true );
1925 bounds = mLayout->pageItemBounds( page,
true );
1927 if ( bounds.width() <= 0 || bounds.height() <= 0 )
1935 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
1947 int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
1950 for (
int i = 0; i < pageCount; ++i )
1964 if ( details.
page == 0 )
1974 bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata )
1976 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
1977 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
1979 w.setCompression( 1 );
1981 if ( projectForMetadata )
1983 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
1984 const QString creator = QStringLiteral(
"QGIS %1" ).arg(
Qgis::version() );
1985 w.setText( QStringLiteral(
"Creator" ), creator );
1986 w.setText( QStringLiteral(
"Producer" ), creator );
1987 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
1988 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
1989 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
1992 QStringList allKeywords;
1993 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1995 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1997 const QString keywordString = allKeywords.join(
';' );
1998 w.setText( QStringLiteral(
"Keywords" ), keywordString );
2000 return w.write( image );
2003 #endif // ! QT_NO_PRINTER