26 #include <QImageWriter> 28 #include <QSvgGenerator> 34 class LayoutContextPreviewSettingRestorer
38 LayoutContextPreviewSettingRestorer(
QgsLayout *layout )
40 , mPreviousSetting( layout->renderContext().mIsPreviewRender )
42 mLayout->renderContext().mIsPreviewRender =
false;
45 ~LayoutContextPreviewSettingRestorer()
47 mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
52 bool mPreviousSetting =
false;
55 class LayoutGuideHider
62 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
65 mPrevVisibility.insert( guide, guide->item()->isVisible() );
66 guide->item()->setVisible(
false );
72 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
74 it.key()->item()->setVisible( it.value() );
80 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
86 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
88 for ( QGraphicsItem *item : items )
90 mPrevVisibility[item] = item->isVisible();
97 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
105 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
107 it.key()->setVisible( it.value() );
113 QHash<QGraphicsItem *, bool> mPrevVisibility;
134 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
145 LayoutContextPreviewSettingRestorer restorer( mLayout );
148 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
157 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
168 LayoutContextPreviewSettingRestorer restorer( mLayout );
171 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
173 if ( imageSize.isValid() && ( !
qgsDoubleNear( static_cast< double >( imageSize.width() ) / imageSize.height(),
174 paperRect.width() / paperRect.height(), 0.008 ) ) )
186 class LayoutItemCacheSettingRestorer
190 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
193 const QList< QGraphicsItem * > items = mLayout->items();
194 for ( QGraphicsItem *item : items )
196 mPrevCacheMode.insert( item, item->cacheMode() );
197 item->setCacheMode( QGraphicsItem::NoCache );
201 ~LayoutItemCacheSettingRestorer()
203 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
205 it.key()->setCacheMode( it.value() );
211 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
218 QPaintDevice *paintDevice = painter->device();
219 if ( !paintDevice || !mLayout )
224 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
225 ( void )cacheRestorer;
226 LayoutContextPreviewSettingRestorer restorer( mLayout );
228 LayoutGuideHider guideHider( mLayout );
233 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
241 LayoutContextPreviewSettingRestorer restorer( mLayout );
244 double resolution = mLayout->renderContext().dpi();
246 if ( imageSize.isValid() )
250 resolution = ( imageSize.width() / region.width()
251 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
259 int width = imageSize.isValid() ? imageSize.width()
260 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
261 int height = imageSize.isValid() ? imageSize.height()
262 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
264 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
265 if ( !image.isNull() )
267 image.setDotsPerMeterX( resolution / 25.4 * 1000 );
268 image.setDotsPerMeterY( resolution / 25.4 * 1000 );
269 image.fill( Qt::transparent );
270 QPainter imagePainter( &image );
272 if ( !imagePainter.isActive() )
280 class LayoutContextSettingsRestorer
284 LayoutContextSettingsRestorer(
QgsLayout *layout )
292 ~LayoutContextSettingsRestorer()
294 mLayout->renderContext().setDpi( mPreviousDpi );
295 mLayout->renderContext().setFlags( mPreviousFlags );
296 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
301 double mPreviousDpi = 0;
302 QgsLayoutRenderContext::Flags mPreviousFlags =
nullptr;
303 int mPreviousExportLayer = 0;
313 if ( settings.
dpi <= 0 )
314 settings.
dpi = mLayout->renderContext().dpi();
316 mErrorFileName.clear();
318 int worldFilePageNo = -1;
321 worldFilePageNo = referenceMap->page();
324 QFileInfo fi( filePath );
328 pageDetails.
baseName = fi.baseName();
329 pageDetails.
extension = fi.completeSuffix();
331 LayoutContextPreviewSettingRestorer restorer( mLayout );
333 LayoutContextSettingsRestorer dpiRestorer( mLayout );
335 mLayout->renderContext().setDpi( settings.
dpi );
336 mLayout->renderContext().setFlags( settings.
flags );
339 if ( settings.
pages.empty() )
341 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
346 for (
int page : qgis::as_const( settings.
pages ) )
348 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
353 for (
int page : qgis::as_const( pages ) )
355 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
362 QImage image = createImage( settings, page, bounds, skip );
367 pageDetails.
page = page;
370 if ( image.isNull() )
372 mErrorFileName = outputFilePath;
376 if ( !saveImage( image, outputFilePath, pageDetails.
extension, settings.
exportMetadata ? mLayout->project() : nullptr ) )
378 mErrorFileName = outputFilePath;
382 const bool shouldGeoreference = ( page == worldFilePageNo );
383 if ( shouldGeoreference )
385 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
390 double a, b,
c, d, e, f;
391 if ( bounds.isValid() )
396 QFileInfo fi( outputFilePath );
398 QString outputSuffix = fi.suffix();
399 QString worldFileName = fi.absolutePath() +
'/' + fi.baseName() +
'.' 400 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
402 writeWorldFile( worldFileName, a, b, c, d, e, f );
417 int total = iterator->
count();
418 double step = total > 0 ? 100.0 / total : 100.0;
420 while ( iterator->
next() )
425 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
427 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
437 QString filePath = iterator->
filePath( baseFilePath, extension );
438 ExportResult result = exporter.exportToImage( filePath, settings );
442 error = QObject::tr(
"Cannot write to %1. This file may be open in another application." ).arg( filePath );
464 if ( settings.
dpi <= 0 )
465 settings.
dpi = mLayout->renderContext().dpi();
467 mErrorFileName.clear();
469 LayoutContextPreviewSettingRestorer restorer( mLayout );
471 LayoutContextSettingsRestorer contextRestorer( mLayout );
472 ( void )contextRestorer;
473 mLayout->renderContext().setDpi( settings.
dpi );
483 preparePrintAsPdf( mLayout, printer, filePath );
484 preparePrint( mLayout, printer,
false );
486 if ( !p.begin( &printer ) )
495 const bool shouldGeoreference = mLayout->pageCollection()->pageCount() == 1;
498 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldGeoreference, settings.
exportMetadata );
515 int total = iterator->
count();
516 double step = total > 0 ? 100.0 / total : 100.0;
519 while ( iterator->
next() )
524 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
526 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
538 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
540 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
541 ( void )contextRestorer;
553 preparePrintAsPdf( iterator->
layout(), printer, fileName );
554 preparePrint( iterator->
layout(), printer, false );
556 if ( !p.begin( &printer ) )
569 error = QObject::tr(
"Cannot write to %1. This file may be open in another application." ).arg( fileName );
593 int total = iterator->
count();
594 double step = total > 0 ? 100.0 / total : 100.0;
596 while ( iterator->
next() )
601 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
603 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
612 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
619 error = QObject::tr(
"Cannot write to %1. This file may be open in another application." ).arg( filePath );
641 if ( settings.
dpi <= 0 )
642 settings.
dpi = mLayout->renderContext().dpi();
644 mErrorFileName.clear();
646 LayoutContextPreviewSettingRestorer restorer( mLayout );
648 LayoutContextSettingsRestorer contextRestorer( mLayout );
649 ( void )contextRestorer;
650 mLayout->renderContext().setDpi( settings.
dpi );
657 preparePrint( mLayout, printer,
true );
659 if ( !p.begin( &printer ) )
682 int total = iterator->
count();
683 double step = total > 0 ? 100.0 / total : 100.0;
686 while ( iterator->
next() )
691 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
693 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ).arg( total ) );
705 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
707 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
708 ( void )contextRestorer;
718 preparePrint( iterator->
layout(), printer, true );
720 if ( !p.begin( &printer ) )
754 if ( settings.
dpi <= 0 )
755 settings.
dpi = mLayout->renderContext().dpi();
757 mErrorFileName.clear();
759 LayoutContextPreviewSettingRestorer restorer( mLayout );
761 LayoutContextSettingsRestorer contextRestorer( mLayout );
762 ( void )contextRestorer;
763 mLayout->renderContext().setDpi( settings.
dpi );
767 QFileInfo fi( filePath );
770 pageDetails.
baseName = fi.baseName();
771 pageDetails.
extension = fi.completeSuffix();
775 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
777 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
782 pageDetails.
page = i;
789 if ( mLayout->pageCollection()->pageCount() == 1 )
792 bounds = mLayout->layoutBounds(
true );
797 bounds = mLayout->pageItemBounds( i,
true );
806 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
810 int width = ( int )( bounds.width() * settings.
dpi / inchesToLayoutUnits );
812 int height = ( int )( bounds.height() * settings.
dpi / inchesToLayoutUnits );
813 if ( width == 0 || height == 0 )
821 const QRectF paperRect = QRectF( pageItem->pos().x(),
823 pageItem->rect().width(),
824 pageItem->rect().height() );
827 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
828 Qt::IntersectsItemBoundingRect,
829 Qt::AscendingOrder );
831 LayoutItemHider itemHider( items );
834 int layoutItemLayerIdx = 0;
835 auto it = items.constBegin();
836 for (
unsigned svgLayerId = 1; it != items.constEnd(); ++svgLayerId )
840 QString layerName = QObject::tr(
"Layer %1" ).arg( svgLayerId );
844 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
845 ++layoutItemLayerIdx;
850 for ( ; it != items.constEnd(); ++it )
864 ExportResult result = renderToLayeredSvg( settings, width, height, i, bounds, fileName, svgLayerId, layerName, svg, svgDocRoot, settings.
exportMetadata );
870 mLayout->renderContext().setCurrentExportLayer( -1 );
871 layoutItemLayerIdx = 0;
877 appendMetadataToSvg( svg );
879 QFile out( fileName );
880 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
883 mErrorFileName = fileName;
887 out.write( svg.toByteArray() );
893 QSvgGenerator generator;
896 generator.setTitle( mLayout->project()->metadata().title() );
897 generator.setDescription( mLayout->project()->metadata().abstract() );
899 generator.setOutputDevice( &svgBuffer );
900 generator.setSize( QSize( width, height ) );
901 generator.setViewBox( QRect( 0, 0, width, height ) );
902 generator.setResolution( settings.
dpi );
905 bool createOk = p.begin( &generator );
908 mErrorFileName = fileName;
921 svgBuffer.open( QIODevice::ReadOnly );
925 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
927 mErrorFileName = fileName;
932 appendMetadataToSvg( svg );
934 QFile out( fileName );
935 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
938 mErrorFileName = fileName;
942 out.write( svg.toByteArray() );
957 int total = iterator->
count();
958 double step = total > 0 ? 100.0 / total : 100.0;
960 while ( iterator->
next() )
965 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
967 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
977 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
984 error = QObject::tr(
"Cannot write to %1. This file may be open in another application." ).arg( filePath );
1001 void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPrinter &printer,
const QString &filePath )
1003 printer.setOutputFileName( filePath );
1010 printer.setOutputFormat( QPrinter::PdfFormat );
1012 updatePrinterPageSize( layout, printer, 0 );
1021 void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPrinter &printer,
bool setFirstPageSize )
1023 printer.setFullPage(
true );
1024 printer.setColorMode( QPrinter::Color );
1029 if ( setFirstPageSize )
1031 updatePrinterPageSize( layout, printer, 0 );
1037 preparePrint( mLayout, printer,
true );
1039 if ( !p.begin( &printer ) )
1045 printPrivate( printer, p );
1050 QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPrinter &printer, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1053 int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
1054 int toPage = ( printer.toPage() < 1 ) ? mLayout->pageCollection()->pageCount() - 1 : printer.toPage() - 1;
1056 bool pageExported =
false;
1059 for (
int i = fromPage; i <= toPage; ++i )
1061 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1066 updatePrinterPageSize( mLayout, printer, i );
1067 if ( ( pageExported && i > fromPage ) || startNewPage )
1073 if ( !image.isNull() )
1075 QRectF targetArea( 0, 0, image.width(), image.height() );
1076 painter.drawImage( targetArea, image, targetArea );
1082 pageExported =
true;
1087 for (
int i = fromPage; i <= toPage; ++i )
1089 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1094 updatePrinterPageSize( mLayout, printer, i );
1096 if ( ( pageExported && i > fromPage ) || startNewPage )
1101 pageExported =
true;
1107 void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPrinter &printer,
int page )
1111 printer.setOrientation( QPrinter::Portrait );
1114 printer.setPaperSize( pageSizeMM.
toQSizeF(), QPrinter::Millimeter );
1117 QgsLayoutExporter::ExportResult QgsLayoutExporter::renderToLayeredSvg(
const SvgExportSettings &settings,
double width,
double height,
int page, QRectF bounds,
const QString &filename,
int svgLayerId,
const QString &layerName, QDomDocument &svg, QDomNode &svgDocRoot,
bool includeMetadata )
const 1121 QSvgGenerator generator;
1122 if ( includeMetadata )
1125 generator.setTitle( l->name() );
1126 else if ( mLayout->project() )
1127 generator.setTitle( mLayout->project()->title() );
1130 generator.setOutputDevice( &svgBuffer );
1131 generator.setSize( QSize( width, height ) );
1132 generator.setViewBox( QRect( 0, 0, width, height ) );
1133 generator.setResolution( settings.
dpi );
1135 QPainter svgPainter( &generator );
1147 svgBuffer.open( QIODevice::ReadOnly );
1151 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1153 mErrorFileName = filename;
1156 if ( 1 == svgLayerId )
1158 svg = QDomDocument( doc.doctype() );
1159 svg.appendChild( svg.importNode( doc.firstChild(), false ) );
1160 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ), false );
1161 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1162 svg.appendChild( svgDocRoot );
1164 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ), true );
1165 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1166 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1167 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1168 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ), true );
1169 svgDocRoot.appendChild( defs );
1170 svgDocRoot.appendChild( mainGroup );
1175 void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const 1178 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1179 metadataElement.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"qgismetadata" ) );
1180 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1181 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1183 auto addTextNode = [&workElement, &svg](
const QString & tag,
const QString & value )
1185 QDomElement element = svg.createElement( tag );
1186 QDomText t = svg.createTextNode( value );
1187 element.appendChild( t );
1188 workElement.appendChild( element );
1191 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1192 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1193 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1194 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1195 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1197 auto addAgentNode = [&workElement, &svg](
const QString & tag,
const QString & value )
1199 QDomElement element = svg.createElement( tag );
1200 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1201 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1202 QDomText t = svg.createTextNode( value );
1203 titleElement.appendChild( t );
1204 agentElement.appendChild( titleElement );
1205 element.appendChild( agentElement );
1206 workElement.appendChild( element );
1209 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1210 addAgentNode( QStringLiteral(
"dc:publisher" ), QStringLiteral(
"QGIS %1" ).arg(
Qgis::QGIS_VERSION ) );
1214 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1215 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1217 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1219 const QStringList words = it.value();
1220 for (
const QString &keyword : words )
1222 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1223 QDomText t = svg.createTextNode( keyword );
1224 liElement.appendChild( t );
1225 bagElement.appendChild( liElement );
1228 element.appendChild( bagElement );
1229 workElement.appendChild( element );
1232 rdfElement.appendChild( workElement );
1233 metadataElement.appendChild( rdfElement );
1234 svg.documentElement().appendChild( metadataElement );
1237 std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const 1240 map = mLayout->referenceMap();
1246 dpi = mLayout->renderContext().dpi();
1249 QRectF exportRegion = region;
1250 if ( !exportRegion.isValid() )
1252 int pageNumber = map->
page();
1255 double pageY = page->pos().y();
1256 QSizeF pageSize = page->rect().size();
1257 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1261 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1264 double outputHeightMM = exportRegion.height();
1265 double outputWidthMM = exportRegion.width();
1269 double mapXCenter = mapExtent.
center().
x();
1270 double mapYCenter = mapExtent.
center().
y();
1272 double sinAlpha = std::sin( alpha );
1273 double cosAlpha = std::cos( alpha );
1276 QPointF mapItemPos = map->pos();
1278 mapItemPos.rx() -= exportRegion.left();
1279 mapItemPos.ry() -= exportRegion.top();
1282 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1283 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1284 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1285 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1286 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1289 double X0 = paperExtent.
xMinimum();
1290 double Y0 = paperExtent.
yMaximum();
1295 double X1 = X0 - mapXCenter;
1296 double Y1 = Y0 - mapYCenter;
1297 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1298 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1299 X0 = X2 + mapXCenter;
1300 Y0 = Y2 + mapYCenter;
1304 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1305 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1306 double pixelWidthScale = paperExtent.
width() / pageWidthPixels;
1307 double pixelHeightScale = paperExtent.
height() / pageHeightPixels;
1310 std::unique_ptr<double[]> t(
new double[6] );
1312 t[1] = cosAlpha * pixelWidthScale;
1313 t[2] = -sinAlpha * pixelWidthScale;
1315 t[4] = -sinAlpha * pixelHeightScale;
1316 t[5] = -cosAlpha * pixelHeightScale;
1321 void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const 1323 QFile worldFile( worldFileName );
1324 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
1328 QTextStream fout( &worldFile );
1332 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1333 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1334 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1335 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1336 fout << QString::number( c,
'f', 12 ) <<
"\r\n";
1337 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1342 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1345 bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const 1350 if ( !map && includeGeoreference )
1351 map = mLayout->referenceMap();
1353 std::unique_ptr<double[]> t;
1355 if ( map && includeGeoreference )
1358 dpi = mLayout->renderContext().dpi();
1360 t = computeGeoTransform( map, exportRegion, dpi );
1365 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toLocal8Bit().constData() );
1370 GDALSetGeoTransform( outputDS.get(), t.get() );
1372 if ( includeMetadata )
1374 QString creationDateString;
1375 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1376 if ( creationDateTime.isValid() )
1378 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1379 if ( creationDateTime.timeZone().isValid() )
1381 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1382 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1383 offsetFromUtc = std::abs( offsetFromUtc );
1384 int offsetHours = offsetFromUtc / 3600;
1385 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1386 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1389 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toLocal8Bit().constData(), nullptr );
1391 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toLocal8Bit().constData(), nullptr );
1393 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toLocal8Bit().constData(), nullptr );
1394 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toLocal8Bit().constData(), nullptr );
1395 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toLocal8Bit().constData(), nullptr );
1396 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toLocal8Bit().constData(), nullptr );
1399 QStringList allKeywords;
1400 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1402 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1404 const QString keywordString = allKeywords.join(
';' );
1405 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toLocal8Bit().constData(), nullptr );
1409 GDALSetProjection( outputDS.get(), map->
crs().
toWkt().toLocal8Bit().constData() );
1411 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1427 int pageNumber = map->
page();
1429 double pageY = page->pos().y();
1430 QSizeF pageSize = page->rect().size();
1431 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
1447 double destinationHeight = exportRegion.height();
1448 double destinationWidth = exportRegion.width();
1450 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1455 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1456 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1458 double xCenter = mapExtent.
center().
x();
1459 double yCenter = mapExtent.
center().
y();
1462 QPointF mapItemPos = map->pos();
1464 mapItemPos.rx() -= exportRegion.left();
1465 mapItemPos.ry() -= exportRegion.top();
1467 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1468 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1469 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
1471 double X0 = paperExtent.
xMinimum();
1472 double Y0 = paperExtent.
yMinimum();
1475 dpi = mLayout->renderContext().dpi();
1477 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
1478 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
1480 double Ww = paperExtent.
width() / widthPx;
1481 double Hh = paperExtent.
height() / heightPx;
1490 s[5] = Y0 + paperExtent.
height();
1494 r[0] = std::cos( alpha );
1495 r[1] = -std::sin( alpha );
1496 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
1497 r[3] = std::sin( alpha );
1498 r[4] = std::cos( alpha );
1499 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
1502 a = r[0] * s[0] + r[1] * s[3];
1503 b = r[0] * s[1] + r[1] * s[4];
1504 c = r[0] * s[2] + r[1] * s[5] + r[2];
1505 d = r[3] * s[0] + r[4] * s[3];
1506 e = r[3] * s[1] + r[4] * s[4];
1507 f = r[3] * s[2] + r[4] * s[5] + r[5];
1517 if ( mLayout->pageCollection()->pageCount() == 1 )
1520 bounds = mLayout->layoutBounds(
true );
1525 bounds = mLayout->pageItemBounds( page,
true );
1527 if ( bounds.width() <= 0 || bounds.height() <= 0 )
1535 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
1549 if ( details.
page == 0 )
1559 bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata )
1561 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
1562 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
1564 w.setCompression( 1 );
1566 if ( projectForMetadata )
1570 w.setText(
"Creator", creator );
1571 w.setText(
"Producer", creator );
1574 w.setText(
"Title", projectForMetadata->
metadata().
title() );
1577 QStringList allKeywords;
1578 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1580 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1582 const QString keywordString = allKeywords.join(
';' );
1583 w.setText(
"Keywords", keywordString );
1585 return w.write( image );
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported...
double right() const
Returns the right margin.
void setDpi(double dpi)
Sets the dpi for outputting the layout.
A rectangle specified with double values.
void renderRegion(QPainter *painter, const QRectF ®ion) const
Renders a region from the layout to a painter.
static const QString QGIS_VERSION
Version string.
Contains settings relating to printing layouts.
Base class for graphical items within a QgsLayout.
QgsMargins cropMargins
Crop to content margins, in layout units.
Unable to allocate memory required to export.
virtual bool endRender()=0
Ends the render, performing any required cleanup tasks.
void setFlag(QgsLayoutRenderContext::Flag flag, bool on=true)
Enables or disables a particular rendering flag for the layout.
QgsLayoutMeasurement convert(const QgsLayoutMeasurement &measurement, QgsUnitTypes::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
Contains the configuration for a single snap guide used by a layout.
Could not write to destination file, likely due to a lock held by another application.
ExportResult exportToImage(const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings)
Exports the layout to the filePath, using the specified export settings.
bool exportMetadata
Indicates whether SVG export should include RDF metadata generated from the layout's project's metada...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
void setProgress(double progress)
Sets the current progress for the feedback object.
virtual bool next()=0
Iterates to next feature, returning false if no more features exist to iterate over.
QgsMargins cropMargins
Crop to content margins, in pixels.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
ExportResult print(QPrinter &printer, const QgsLayoutExporter::PrintExportSettings &settings)
Prints the layout to a printer, using the specified export settings.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
int currentExportLayer() const
Returns the current item layer to draw while exporting.
bool georeferenceOutput(const QString &file, QgsLayoutItemMap *referenceMap=nullptr, const QRectF &exportRegion=QRectF(), double dpi=-1) const
Georeferences a file (image of PDF) exported from the layout.
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
QString directory
Target folder.
QgsLayoutExporter(QgsLayout *layout)
Constructor for QgsLayoutExporter, for the specified layout.
Base class for feedback objects to be used for cancelation of something running in a worker thread...
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
QImage renderRegionToImage(const QRectF ®ion, QSize imageSize=QSize(), double dpi=-1) const
Renders a region of the layout to an image.
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
Contains details of a page being exported by the class.
QSize imageSize
Manual size in pixels for output image.
QgsRectangle extent() const
Returns the current map extent.
Layout graphical items for displaying a map.
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
virtual QString generateFileName(const PageExportDetails &details) const
Generates the file name for a page during export.
bool exportAsLayers
Set to true to export as a layered SVG file.
An abstract base class for QgsLayout based classes which can be exported by QgsLayoutExporter.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout...
QgsProjectMetadata metadata
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f, double dpi=-1) const
Compute world file parameters.
double bottom() const
Returns the bottom margin.
QgsLayoutRenderContext::Flags flags() const
Returns the current combination of flags used for rendering the layout.
double dpi() const
Returns the dpi for outputting the layout.
double width() const
Returns the width of the rectangle.
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
virtual int numberExportLayers() const
Returns the number of layers that this item requires for exporting during layered exports (e...
virtual int count()=0
Returns the number of features to iterate over.
QString extension
File suffix/extension (without the leading '.')
virtual QgsLayout * layout()=0
Returns the layout associated with the iterator.
double top() const
Returns the top margin.
Reads and writes project states.
QString baseName
Base part of filename (i.e. file name without extension or '.')
Could not create layered SVG file.
int page() const
Returns the page the item is currently on, with the first page returning 0.
ExportResult exportToSvg(const QString &filePath, const QgsLayoutExporter::SvgExportSettings &settings)
Exports the layout as an SVG to the filePath, using the specified export settings.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Use antialiasing when drawing items.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Handles rendering and exports of layouts to various formats.
Contains settings relating to exporting layouts to PDF.
bool generateWorldFile
Set to true to generate an external world file alongside exported images.
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
Force output in vector format where possible, even if items require rasterization to keep their corre...
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
Contains settings relating to exporting layouts to raster images.
static ExportResult exportToPdfs(QgsAbstractLayoutIterator *iterator, const QString &baseFilePath, const QgsLayoutExporter::PdfExportSettings &settings, QString &error, QgsFeedback *feedback=nullptr)
Exports a layout iterator to multiple PDF files, with the specified export settings.
bool isCanceled() const
Tells whether the operation has been canceled already.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Enable advanced effects such as blend modes.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
ExportResult exportToPdf(const QString &filePath, const QgsLayoutExporter::PdfExportSettings &settings)
Exports the layout as a PDF to the filePath, using the specified export settings. ...
static void fixEngineFlags(QPaintEngine *engine)
QString toWkt() const
Returns a WKT representation of this CRS.
int page
Page number, where 0 = first page.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
bool exportMetadata
Indicates whether PDF export should include metadata generated from the layout's project's metadata...
double yMaximum() const
Returns the y maximum value (top side of rectangle).
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
bool exportMetadata
Indicates whether image export should include metadata generated from the layout's project's metadata...
Could not start printing to destination device.
QImage renderPageToImage(int page, QSize imageSize=QSize(), double dpi=-1) const
Renders a full page to an image.
QgsPointXY center() const
Returns the center point of the rectangle.
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported...
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Interface for master layout type objects, such as print layouts and reports.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
Contains settings relating to exporting layouts to SVG.
virtual bool beginRender()=0
Called when rendering begins, before iteration commences.
double left() const
Returns the left margin.
QgsLayout * layout() const
Returns the layout linked to this exporter.
virtual QString filePath(const QString &baseFilePath, const QString &extension)=0
Returns the file path for the current feature, based on a specified base file path and extension...
double height() const
Returns the height of the rectangle.
Item representing the paper in a layout.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
ExportResult
Result codes for exporting layouts.
void renderPage(QPainter *painter, int page) const
Renders a full page to a destination painter.
Error iterating over layout.