28 #include <QImageWriter> 30 #include <QSvgGenerator> 36 class LayoutContextPreviewSettingRestorer
40 LayoutContextPreviewSettingRestorer(
QgsLayout *layout )
42 , mPreviousSetting( layout->renderContext().mIsPreviewRender )
44 mLayout->renderContext().mIsPreviewRender =
false;
47 ~LayoutContextPreviewSettingRestorer()
49 mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
52 LayoutContextPreviewSettingRestorer(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
53 LayoutContextPreviewSettingRestorer &operator=(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
57 bool mPreviousSetting =
false;
60 class LayoutGuideHider
67 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
70 mPrevVisibility.insert( guide, guide->item()->isVisible() );
71 guide->item()->setVisible(
false );
77 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
79 it.key()->item()->setVisible( it.value() );
83 LayoutGuideHider(
const LayoutGuideHider &other ) =
delete;
84 LayoutGuideHider &operator=(
const LayoutGuideHider &other ) =
delete;
88 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
94 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
96 for ( QGraphicsItem *item : items )
98 mPrevVisibility[item] = item->isVisible();
105 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
113 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
115 it.key()->setVisible( it.value() );
119 LayoutItemHider(
const LayoutItemHider &other ) =
delete;
120 LayoutItemHider &operator=(
const LayoutItemHider &other ) =
delete;
124 QHash<QGraphicsItem *, bool> mPrevVisibility;
145 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
156 LayoutContextPreviewSettingRestorer restorer( mLayout );
159 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
168 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
179 LayoutContextPreviewSettingRestorer restorer( mLayout );
182 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
184 if ( imageSize.isValid() && ( !
qgsDoubleNear( static_cast< double >( imageSize.width() ) / imageSize.height(),
185 paperRect.width() / paperRect.height(), 0.008 ) ) )
197 class LayoutItemCacheSettingRestorer
201 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
204 const QList< QGraphicsItem * > items = mLayout->items();
205 for ( QGraphicsItem *item : items )
207 mPrevCacheMode.insert( item, item->cacheMode() );
208 item->setCacheMode( QGraphicsItem::NoCache );
212 ~LayoutItemCacheSettingRestorer()
214 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
216 it.key()->setCacheMode( it.value() );
220 LayoutItemCacheSettingRestorer(
const LayoutItemCacheSettingRestorer &other ) =
delete;
221 LayoutItemCacheSettingRestorer &operator=(
const LayoutItemCacheSettingRestorer &other ) =
delete;
225 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
232 QPaintDevice *paintDevice = painter->device();
233 if ( !paintDevice || !mLayout )
238 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
239 ( void )cacheRestorer;
240 LayoutContextPreviewSettingRestorer restorer( mLayout );
242 LayoutGuideHider guideHider( mLayout );
247 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
255 LayoutContextPreviewSettingRestorer restorer( mLayout );
258 double resolution = mLayout->renderContext().dpi();
260 if ( imageSize.isValid() )
264 resolution = ( imageSize.width() / region.width()
265 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
273 int width = imageSize.isValid() ? imageSize.width()
274 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
275 int height = imageSize.isValid() ? imageSize.height()
276 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
278 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
279 if ( !image.isNull() )
281 image.setDotsPerMeterX( static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
282 image.setDotsPerMeterY( static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
283 image.fill( Qt::transparent );
284 QPainter imagePainter( &image );
286 if ( !imagePainter.isActive() )
294 class LayoutContextSettingsRestorer
298 LayoutContextSettingsRestorer(
QgsLayout *layout )
307 ~LayoutContextSettingsRestorer()
309 mLayout->renderContext().setDpi( mPreviousDpi );
310 mLayout->renderContext().setFlags( mPreviousFlags );
311 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
312 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
315 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
316 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
320 double mPreviousDpi = 0;
321 QgsLayoutRenderContext::Flags mPreviousFlags =
nullptr;
323 int mPreviousExportLayer = 0;
333 if ( settings.
dpi <= 0 )
334 settings.
dpi = mLayout->renderContext().dpi();
336 mErrorFileName.clear();
338 int worldFilePageNo = -1;
341 worldFilePageNo = referenceMap->page();
344 QFileInfo fi( filePath );
348 pageDetails.
baseName = fi.completeBaseName();
351 LayoutContextPreviewSettingRestorer restorer( mLayout );
353 LayoutContextSettingsRestorer dpiRestorer( mLayout );
355 mLayout->renderContext().setDpi( settings.
dpi );
356 mLayout->renderContext().setFlags( settings.
flags );
359 if ( settings.
pages.empty() )
361 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
366 for (
int page : qgis::as_const( settings.
pages ) )
368 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
373 for (
int page : qgis::as_const( pages ) )
375 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
382 QImage image = createImage( settings, page, bounds, skip );
387 pageDetails.
page = page;
390 if ( image.isNull() )
392 mErrorFileName = outputFilePath;
396 if ( !saveImage( image, outputFilePath, pageDetails.
extension, settings.
exportMetadata ? mLayout->project() : nullptr ) )
398 mErrorFileName = outputFilePath;
402 const bool shouldGeoreference = ( page == worldFilePageNo );
403 if ( shouldGeoreference )
405 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
410 double a, b,
c, d, e, f;
411 if ( bounds.isValid() )
416 QFileInfo fi( outputFilePath );
418 QString outputSuffix = fi.suffix();
419 QString worldFileName = fi.absolutePath() +
'/' + fi.baseName() +
'.' 420 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
422 writeWorldFile( worldFileName, a, b, c, d, e, f );
437 int total = iterator->
count();
438 double step = total > 0 ? 100.0 / total : 100.0;
440 while ( iterator->
next() )
445 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
447 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
457 QString filePath = iterator->
filePath( baseFilePath, extension );
458 ExportResult result = exporter.exportToImage( filePath, settings );
462 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 ) );
484 if ( settings.
dpi <= 0 )
485 settings.
dpi = mLayout->renderContext().dpi();
487 mErrorFileName.clear();
489 LayoutContextPreviewSettingRestorer restorer( mLayout );
491 LayoutContextSettingsRestorer contextRestorer( mLayout );
492 ( void )contextRestorer;
493 mLayout->renderContext().setDpi( settings.
dpi );
495 mLayout->renderContext().setFlags( settings.
flags );
507 preparePrintAsPdf( mLayout, printer, filePath );
508 preparePrint( mLayout, printer,
false );
510 if ( !p.begin( &printer ) )
519 const bool shouldGeoreference = mLayout->pageCollection()->pageCount() == 1;
522 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldGeoreference, settings.
exportMetadata );
539 int total = iterator->
count();
540 double step = total > 0 ? 100.0 / total : 100.0;
543 while ( iterator->
next() )
548 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
550 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
562 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
564 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
565 ( void )contextRestorer;
581 preparePrintAsPdf( iterator->
layout(), printer, fileName );
582 preparePrint( iterator->
layout(), printer, false );
584 if ( !p.begin( &printer ) )
597 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 ) );
621 int total = iterator->
count();
622 double step = total > 0 ? 100.0 / total : 100.0;
624 while ( iterator->
next() )
629 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
631 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
640 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
647 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 ) );
669 if ( settings.
dpi <= 0 )
670 settings.
dpi = mLayout->renderContext().dpi();
672 mErrorFileName.clear();
674 LayoutContextPreviewSettingRestorer restorer( mLayout );
676 LayoutContextSettingsRestorer contextRestorer( mLayout );
677 ( void )contextRestorer;
678 mLayout->renderContext().setDpi( settings.
dpi );
680 mLayout->renderContext().setFlags( settings.
flags );
686 preparePrint( mLayout, printer,
true );
688 if ( !p.begin( &printer ) )
711 int total = iterator->
count();
712 double step = total > 0 ? 100.0 / total : 100.0;
715 while ( iterator->
next() )
720 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
722 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ).arg( total ) );
734 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
736 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
737 ( void )contextRestorer;
749 preparePrint( iterator->
layout(), printer, true );
751 if ( !p.begin( &printer ) )
785 if ( settings.
dpi <= 0 )
786 settings.
dpi = mLayout->renderContext().dpi();
788 mErrorFileName.clear();
790 LayoutContextPreviewSettingRestorer restorer( mLayout );
792 LayoutContextSettingsRestorer contextRestorer( mLayout );
793 ( void )contextRestorer;
794 mLayout->renderContext().setDpi( settings.
dpi );
796 mLayout->renderContext().setFlags( settings.
flags );
800 QFileInfo fi( filePath );
803 pageDetails.
baseName = fi.baseName();
804 pageDetails.
extension = fi.completeSuffix();
808 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
810 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
815 pageDetails.
page = i;
822 if ( mLayout->pageCollection()->pageCount() == 1 )
825 bounds = mLayout->layoutBounds(
true );
830 bounds = mLayout->pageItemBounds( i,
true );
839 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
843 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
845 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
846 if ( width == 0 || height == 0 )
854 const QRectF paperRect = QRectF( pageItem->pos().x(),
856 pageItem->rect().width(),
857 pageItem->rect().height() );
860 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
861 Qt::IntersectsItemBoundingRect,
862 Qt::AscendingOrder );
864 LayoutItemHider itemHider( items );
867 int layoutItemLayerIdx = 0;
868 auto it = items.constBegin();
869 for (
unsigned svgLayerId = 1; it != items.constEnd(); ++svgLayerId )
873 QString layerName = QObject::tr(
"Layer %1" ).arg( svgLayerId );
877 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
878 ++layoutItemLayerIdx;
883 for ( ; it != items.constEnd(); ++it )
897 ExportResult result = renderToLayeredSvg( settings, width, height, i, bounds, fileName, svgLayerId, layerName, svg, svgDocRoot, settings.
exportMetadata );
903 mLayout->renderContext().setCurrentExportLayer( -1 );
904 layoutItemLayerIdx = 0;
910 appendMetadataToSvg( svg );
912 QFile out( fileName );
913 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
916 mErrorFileName = fileName;
920 out.write( svg.toByteArray() );
926 QSvgGenerator generator;
929 generator.setTitle( mLayout->project()->metadata().title() );
930 generator.setDescription( mLayout->project()->metadata().abstract() );
932 generator.setOutputDevice( &svgBuffer );
933 generator.setSize( QSize( width, height ) );
934 generator.setViewBox( QRect( 0, 0, width, height ) );
935 generator.setResolution( static_cast< int >( std::round( settings.
dpi ) ) );
938 bool createOk = p.begin( &generator );
941 mErrorFileName = fileName;
954 svgBuffer.open( QIODevice::ReadOnly );
958 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
960 mErrorFileName = fileName;
965 appendMetadataToSvg( svg );
967 QFile out( fileName );
968 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
971 mErrorFileName = fileName;
975 out.write( svg.toByteArray() );
990 int total = iterator->
count();
991 double step = total > 0 ? 100.0 / total : 100.0;
993 while ( iterator->
next() )
998 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1000 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
1010 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
1017 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 ) );
1034 void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPrinter &printer,
const QString &filePath )
1036 printer.setOutputFileName( filePath );
1037 printer.setOutputFormat( QPrinter::PdfFormat );
1039 updatePrinterPageSize( layout, printer, firstPageToBeExported( layout ) );
1048 void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPrinter &printer,
bool setFirstPageSize )
1050 printer.setFullPage(
true );
1051 printer.setColorMode( QPrinter::Color );
1054 printer.setResolution( static_cast< int>( std::round( layout->
renderContext().
dpi() ) ) );
1056 if ( setFirstPageSize )
1058 updatePrinterPageSize( layout, printer, firstPageToBeExported( layout ) );
1064 preparePrint( mLayout, printer,
true );
1066 if ( !p.begin( &printer ) )
1072 printPrivate( printer, p );
1077 QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPrinter &printer, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1080 int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
1081 int toPage = ( printer.toPage() < 1 ) ? mLayout->pageCollection()->pageCount() - 1 : printer.toPage() - 1;
1083 bool pageExported =
false;
1086 for (
int i = fromPage; i <= toPage; ++i )
1088 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1093 updatePrinterPageSize( mLayout, printer, i );
1094 if ( ( pageExported && i > fromPage ) || startNewPage )
1100 if ( !image.isNull() )
1102 QRectF targetArea( 0, 0, image.width(), image.height() );
1103 painter.drawImage( targetArea, image, targetArea );
1109 pageExported =
true;
1114 for (
int i = fromPage; i <= toPage; ++i )
1116 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1121 updatePrinterPageSize( mLayout, printer, i );
1123 if ( ( pageExported && i > fromPage ) || startNewPage )
1128 pageExported =
true;
1134 void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPrinter &printer,
int page )
1139 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1140 QPageLayout::Portrait,
1141 QMarginsF( 0, 0, 0, 0 ) );
1142 pageLayout.setMode( QPageLayout::FullPageMode );
1143 printer.setPageLayout( pageLayout );
1144 printer.setFullPage(
true );
1145 printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1148 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 1152 QSvgGenerator generator;
1153 if ( includeMetadata )
1156 generator.setTitle( l->name() );
1157 else if ( mLayout->project() )
1158 generator.setTitle( mLayout->project()->title() );
1161 generator.setOutputDevice( &svgBuffer );
1162 generator.setSize( QSize( static_cast< int >( std::round( width ) ),
1163 static_cast< int >( std::round( height ) ) ) );
1164 generator.setViewBox( QRect( 0, 0,
1165 static_cast< int >( std::round( width ) ),
1166 static_cast< int >( std::round( height ) ) ) );
1167 generator.setResolution( static_cast< int >( std::round( settings.
dpi ) ) );
1169 QPainter svgPainter( &generator );
1181 svgBuffer.open( QIODevice::ReadOnly );
1185 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1187 mErrorFileName = filename;
1190 if ( 1 == svgLayerId )
1192 svg = QDomDocument( doc.doctype() );
1193 svg.appendChild( svg.importNode( doc.firstChild(), false ) );
1194 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ), false );
1195 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1196 svg.appendChild( svgDocRoot );
1198 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ), true );
1199 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1200 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1201 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1202 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ), true );
1203 svgDocRoot.appendChild( defs );
1204 svgDocRoot.appendChild( mainGroup );
1209 void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const 1212 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1213 metadataElement.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"qgismetadata" ) );
1214 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1215 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1217 auto addTextNode = [&workElement, &svg](
const QString & tag,
const QString & value )
1219 QDomElement element = svg.createElement( tag );
1220 QDomText t = svg.createTextNode( value );
1221 element.appendChild( t );
1222 workElement.appendChild( element );
1225 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1226 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1227 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1228 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1229 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1231 auto addAgentNode = [&workElement, &svg](
const QString & tag,
const QString & value )
1233 QDomElement element = svg.createElement( tag );
1234 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1235 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1236 QDomText t = svg.createTextNode( value );
1237 titleElement.appendChild( t );
1238 agentElement.appendChild( titleElement );
1239 element.appendChild( agentElement );
1240 workElement.appendChild( element );
1243 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1244 addAgentNode( QStringLiteral(
"dc:publisher" ), QStringLiteral(
"QGIS %1" ).arg(
Qgis::QGIS_VERSION ) );
1248 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1249 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1251 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1253 const QStringList words = it.value();
1254 for (
const QString &keyword : words )
1256 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1257 QDomText t = svg.createTextNode( keyword );
1258 liElement.appendChild( t );
1259 bagElement.appendChild( liElement );
1262 element.appendChild( bagElement );
1263 workElement.appendChild( element );
1266 rdfElement.appendChild( workElement );
1267 metadataElement.appendChild( rdfElement );
1268 svg.documentElement().appendChild( metadataElement );
1271 std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const 1274 map = mLayout->referenceMap();
1280 dpi = mLayout->renderContext().dpi();
1283 QRectF exportRegion = region;
1284 if ( !exportRegion.isValid() )
1286 int pageNumber = map->
page();
1289 double pageY = page->pos().y();
1290 QSizeF pageSize = page->rect().size();
1291 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1295 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1298 double outputHeightMM = exportRegion.height();
1299 double outputWidthMM = exportRegion.width();
1303 double mapXCenter = mapExtent.
center().
x();
1304 double mapYCenter = mapExtent.
center().
y();
1306 double sinAlpha = std::sin( alpha );
1307 double cosAlpha = std::cos( alpha );
1310 QPointF mapItemPos = map->pos();
1312 mapItemPos.rx() -= exportRegion.left();
1313 mapItemPos.ry() -= exportRegion.top();
1316 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1317 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1318 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1319 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1320 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1323 double X0 = paperExtent.
xMinimum();
1324 double Y0 = paperExtent.
yMaximum();
1329 double X1 = X0 - mapXCenter;
1330 double Y1 = Y0 - mapYCenter;
1331 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1332 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1333 X0 = X2 + mapXCenter;
1334 Y0 = Y2 + mapYCenter;
1338 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1339 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1340 double pixelWidthScale = paperExtent.
width() / pageWidthPixels;
1341 double pixelHeightScale = paperExtent.
height() / pageHeightPixels;
1344 std::unique_ptr<double[]> t(
new double[6] );
1346 t[1] = cosAlpha * pixelWidthScale;
1347 t[2] = -sinAlpha * pixelWidthScale;
1349 t[4] = -sinAlpha * pixelHeightScale;
1350 t[5] = -cosAlpha * pixelHeightScale;
1355 void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const 1357 QFile worldFile( worldFileName );
1358 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1362 QTextStream fout( &worldFile );
1366 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1367 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1368 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1369 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1370 fout << QString::number( c,
'f', 12 ) <<
"\r\n";
1371 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1376 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1379 bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const 1384 if ( !map && includeGeoreference )
1385 map = mLayout->referenceMap();
1387 std::unique_ptr<double[]> t;
1389 if ( map && includeGeoreference )
1392 dpi = mLayout->renderContext().dpi();
1394 t = computeGeoTransform( map, exportRegion, dpi );
1399 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toLocal8Bit().constData() );
1404 GDALSetGeoTransform( outputDS.get(), t.get() );
1406 if ( includeMetadata )
1408 QString creationDateString;
1409 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1410 if ( creationDateTime.isValid() )
1412 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1413 if ( creationDateTime.timeZone().isValid() )
1415 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1416 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1417 offsetFromUtc = std::abs( offsetFromUtc );
1418 int offsetHours = offsetFromUtc / 3600;
1419 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1420 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1423 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toLocal8Bit().constData(), nullptr );
1425 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toLocal8Bit().constData(), nullptr );
1427 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toLocal8Bit().constData(), nullptr );
1428 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toLocal8Bit().constData(), nullptr );
1429 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toLocal8Bit().constData(), nullptr );
1430 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toLocal8Bit().constData(), nullptr );
1433 QStringList allKeywords;
1434 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1436 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1438 const QString keywordString = allKeywords.join(
';' );
1439 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toLocal8Bit().constData(), nullptr );
1443 GDALSetProjection( outputDS.get(), map->
crs().
toWkt().toLocal8Bit().constData() );
1445 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1461 int pageNumber = map->
page();
1463 double pageY = page->pos().y();
1464 QSizeF pageSize = page->rect().size();
1465 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
1481 double destinationHeight = exportRegion.height();
1482 double destinationWidth = exportRegion.width();
1484 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1489 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1490 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1492 double xCenter = mapExtent.
center().
x();
1493 double yCenter = mapExtent.
center().
y();
1496 QPointF mapItemPos = map->pos();
1498 mapItemPos.rx() -= exportRegion.left();
1499 mapItemPos.ry() -= exportRegion.top();
1501 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1502 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1503 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
1505 double X0 = paperExtent.
xMinimum();
1506 double Y0 = paperExtent.
yMinimum();
1509 dpi = mLayout->renderContext().dpi();
1511 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
1512 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
1514 double Ww = paperExtent.
width() / widthPx;
1515 double Hh = paperExtent.
height() / heightPx;
1524 s[5] = Y0 + paperExtent.
height();
1528 r[0] = std::cos( alpha );
1529 r[1] = -std::sin( alpha );
1530 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
1531 r[3] = std::sin( alpha );
1532 r[4] = std::cos( alpha );
1533 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
1536 a = r[0] * s[0] + r[1] * s[3];
1537 b = r[0] * s[1] + r[1] * s[4];
1538 c = r[0] * s[2] + r[1] * s[5] + r[2];
1539 d = r[3] * s[0] + r[4] * s[3];
1540 e = r[3] * s[1] + r[4] * s[4];
1541 f = r[3] * s[2] + r[4] * s[5] + r[5];
1551 if ( mLayout->pageCollection()->pageCount() == 1 )
1554 bounds = mLayout->layoutBounds(
true );
1559 bounds = mLayout->pageItemBounds( page,
true );
1561 if ( bounds.width() <= 0 || bounds.height() <= 0 )
1569 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
1581 int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
1584 for (
int i = 0; i < pageCount; ++i )
1598 if ( details.
page == 0 )
1608 bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata )
1610 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
1611 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
1613 w.setCompression( 1 );
1615 if ( projectForMetadata )
1617 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
1619 w.setText( QStringLiteral(
"Creator" ), creator );
1620 w.setText( QStringLiteral(
"Producer" ), creator );
1621 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
1622 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
1623 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
1626 QStringList allKeywords;
1627 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1629 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1631 const QString keywordString = allKeywords.join(
';' );
1632 w.setText( QStringLiteral(
"Keywords" ), keywordString );
1634 return w.write( image );
1637 #endif // ! QT_NO_PRINTER 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.
bool shouldExportPage(int page) const
Returns whether the specified page number should be included in exports of the layouts.
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.
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, QgsUnitTypes::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
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.
QgsRenderContext::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e.g.
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 cancellation 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
void setTextRenderFormat(QgsRenderContext::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
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.
Always render text using path objects (AKA outlines/curves).
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.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
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.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
TextRenderFormat
Options for rendering text.
Use antialiasing when drawing items.
QgsRenderContext::TextRenderFormat textRenderFormat() const
Returns the text render format, which dictates how text is rendered (e.g.
int pageCount() const
Returns the number of pages in the collection.
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...
QgsRenderContext::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e.g.
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
Contains settings relating to exporting layouts to raster images.
void setFlags(QgsLayoutRenderContext::Flags flags)
Sets the combination of flags that will be used for rendering the layout.
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...
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
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.