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;
50 LayoutContextPreviewSettingRestorer(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
51 LayoutContextPreviewSettingRestorer &operator=(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
55 bool mPreviousSetting =
false;
58 class LayoutGuideHider
65 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
68 mPrevVisibility.insert( guide, guide->item()->isVisible() );
69 guide->item()->setVisible(
false );
75 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
77 it.key()->item()->setVisible( it.value() );
81 LayoutGuideHider(
const LayoutGuideHider &other ) =
delete;
82 LayoutGuideHider &operator=(
const LayoutGuideHider &other ) =
delete;
86 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
92 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
94 for ( QGraphicsItem *item : items )
96 mPrevVisibility[item] = item->isVisible();
103 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
111 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
113 it.key()->setVisible( it.value() );
117 LayoutItemHider(
const LayoutItemHider &other ) =
delete;
118 LayoutItemHider &operator=(
const LayoutItemHider &other ) =
delete;
122 QHash<QGraphicsItem *, bool> mPrevVisibility;
143 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
154 LayoutContextPreviewSettingRestorer restorer( mLayout );
157 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
166 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
177 LayoutContextPreviewSettingRestorer restorer( mLayout );
180 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
182 if ( imageSize.isValid() && ( !
qgsDoubleNear( static_cast< double >( imageSize.width() ) / imageSize.height(),
183 paperRect.width() / paperRect.height(), 0.008 ) ) )
195 class LayoutItemCacheSettingRestorer
199 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
202 const QList< QGraphicsItem * > items = mLayout->items();
203 for ( QGraphicsItem *item : items )
205 mPrevCacheMode.insert( item, item->cacheMode() );
206 item->setCacheMode( QGraphicsItem::NoCache );
210 ~LayoutItemCacheSettingRestorer()
212 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
214 it.key()->setCacheMode( it.value() );
218 LayoutItemCacheSettingRestorer(
const LayoutItemCacheSettingRestorer &other ) =
delete;
219 LayoutItemCacheSettingRestorer &operator=(
const LayoutItemCacheSettingRestorer &other ) =
delete;
223 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
230 QPaintDevice *paintDevice = painter->device();
231 if ( !paintDevice || !mLayout )
236 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
237 ( void )cacheRestorer;
238 LayoutContextPreviewSettingRestorer restorer( mLayout );
240 LayoutGuideHider guideHider( mLayout );
245 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
253 LayoutContextPreviewSettingRestorer restorer( mLayout );
256 double resolution = mLayout->renderContext().dpi();
258 if ( imageSize.isValid() )
262 resolution = ( imageSize.width() / region.width()
263 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
271 int width = imageSize.isValid() ? imageSize.width()
272 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
273 int height = imageSize.isValid() ? imageSize.height()
274 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
276 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
277 if ( !image.isNull() )
279 image.setDotsPerMeterX( static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
280 image.setDotsPerMeterY( static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
281 image.fill( Qt::transparent );
282 QPainter imagePainter( &image );
284 if ( !imagePainter.isActive() )
292 class LayoutContextSettingsRestorer
296 LayoutContextSettingsRestorer(
QgsLayout *layout )
305 ~LayoutContextSettingsRestorer()
307 mLayout->renderContext().setDpi( mPreviousDpi );
308 mLayout->renderContext().setFlags( mPreviousFlags );
309 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
310 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
313 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
314 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
318 double mPreviousDpi = 0;
319 QgsLayoutRenderContext::Flags mPreviousFlags =
nullptr;
321 int mPreviousExportLayer = 0;
331 if ( settings.
dpi <= 0 )
332 settings.
dpi = mLayout->renderContext().dpi();
334 mErrorFileName.clear();
336 int worldFilePageNo = -1;
339 worldFilePageNo = referenceMap->page();
342 QFileInfo fi( filePath );
346 pageDetails.
baseName = fi.completeBaseName();
349 LayoutContextPreviewSettingRestorer restorer( mLayout );
351 LayoutContextSettingsRestorer dpiRestorer( mLayout );
353 mLayout->renderContext().setDpi( settings.
dpi );
354 mLayout->renderContext().setFlags( settings.
flags );
357 if ( settings.
pages.empty() )
359 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
364 for (
int page : qgis::as_const( settings.
pages ) )
366 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
371 for (
int page : qgis::as_const( pages ) )
373 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
380 QImage image = createImage( settings, page, bounds, skip );
385 pageDetails.
page = page;
388 if ( image.isNull() )
390 mErrorFileName = outputFilePath;
394 if ( !saveImage( image, outputFilePath, pageDetails.
extension, settings.
exportMetadata ? mLayout->project() : nullptr ) )
396 mErrorFileName = outputFilePath;
400 const bool shouldGeoreference = ( page == worldFilePageNo );
401 if ( shouldGeoreference )
403 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
408 double a, b,
c, d, e, f;
409 if ( bounds.isValid() )
414 QFileInfo fi( outputFilePath );
416 QString outputSuffix = fi.suffix();
417 QString worldFileName = fi.absolutePath() +
'/' + fi.baseName() +
'.' 418 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
420 writeWorldFile( worldFileName, a, b, c, d, e, f );
435 int total = iterator->
count();
436 double step = total > 0 ? 100.0 / total : 100.0;
438 while ( iterator->
next() )
443 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
445 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
455 QString filePath = iterator->
filePath( baseFilePath, extension );
456 ExportResult result = exporter.exportToImage( filePath, settings );
460 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 ) );
478 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
482 if ( settings.
dpi <= 0 )
483 settings.
dpi = mLayout->renderContext().dpi();
485 mErrorFileName.clear();
487 LayoutContextPreviewSettingRestorer restorer( mLayout );
489 LayoutContextSettingsRestorer contextRestorer( mLayout );
490 ( void )contextRestorer;
491 mLayout->renderContext().setDpi( settings.
dpi );
493 mLayout->renderContext().setFlags( settings.
flags );
505 preparePrintAsPdf( mLayout, printer, filePath );
506 preparePrint( mLayout, printer,
false );
508 if ( !p.begin( &printer ) )
517 const bool shouldGeoreference = mLayout->pageCollection()->pageCount() == 1;
520 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldGeoreference, settings.
exportMetadata );
537 int total = iterator->
count();
538 double step = total > 0 ? 100.0 / total : 100.0;
541 while ( iterator->
next() )
546 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
548 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
560 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
562 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
563 ( void )contextRestorer;
579 preparePrintAsPdf( iterator->
layout(), printer, fileName );
580 preparePrint( iterator->
layout(), printer, false );
582 if ( !p.begin( &printer ) )
595 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 ) );
619 int total = iterator->
count();
620 double step = total > 0 ? 100.0 / total : 100.0;
622 while ( iterator->
next() )
627 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
629 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
638 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
645 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 ) );
667 if ( settings.
dpi <= 0 )
668 settings.
dpi = mLayout->renderContext().dpi();
670 mErrorFileName.clear();
672 LayoutContextPreviewSettingRestorer restorer( mLayout );
674 LayoutContextSettingsRestorer contextRestorer( mLayout );
675 ( void )contextRestorer;
676 mLayout->renderContext().setDpi( settings.
dpi );
678 mLayout->renderContext().setFlags( settings.
flags );
684 preparePrint( mLayout, printer,
true );
686 if ( !p.begin( &printer ) )
709 int total = iterator->
count();
710 double step = total > 0 ? 100.0 / total : 100.0;
713 while ( iterator->
next() )
718 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
720 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ).arg( total ) );
732 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
734 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
735 ( void )contextRestorer;
747 preparePrint( iterator->
layout(), printer, true );
749 if ( !p.begin( &printer ) )
783 if ( settings.
dpi <= 0 )
784 settings.
dpi = mLayout->renderContext().dpi();
786 mErrorFileName.clear();
788 LayoutContextPreviewSettingRestorer restorer( mLayout );
790 LayoutContextSettingsRestorer contextRestorer( mLayout );
791 ( void )contextRestorer;
792 mLayout->renderContext().setDpi( settings.
dpi );
794 mLayout->renderContext().setFlags( settings.
flags );
798 QFileInfo fi( filePath );
801 pageDetails.
baseName = fi.baseName();
802 pageDetails.
extension = fi.completeSuffix();
806 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
808 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
813 pageDetails.
page = i;
820 if ( mLayout->pageCollection()->pageCount() == 1 )
823 bounds = mLayout->layoutBounds(
true );
828 bounds = mLayout->pageItemBounds( i,
true );
837 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
841 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
843 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
844 if ( width == 0 || height == 0 )
852 const QRectF paperRect = QRectF( pageItem->pos().x(),
854 pageItem->rect().width(),
855 pageItem->rect().height() );
858 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
859 Qt::IntersectsItemBoundingRect,
860 Qt::AscendingOrder );
862 LayoutItemHider itemHider( items );
865 int layoutItemLayerIdx = 0;
866 auto it = items.constBegin();
867 for (
unsigned svgLayerId = 1; it != items.constEnd(); ++svgLayerId )
871 QString layerName = QObject::tr(
"Layer %1" ).arg( svgLayerId );
875 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
876 ++layoutItemLayerIdx;
881 for ( ; it != items.constEnd(); ++it )
895 ExportResult result = renderToLayeredSvg( settings, width, height, i, bounds, fileName, svgLayerId, layerName, svg, svgDocRoot, settings.
exportMetadata );
901 mLayout->renderContext().setCurrentExportLayer( -1 );
902 layoutItemLayerIdx = 0;
908 appendMetadataToSvg( svg );
910 QFile out( fileName );
911 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
914 mErrorFileName = fileName;
918 out.write( svg.toByteArray() );
924 QSvgGenerator generator;
927 generator.setTitle( mLayout->project()->metadata().title() );
928 generator.setDescription( mLayout->project()->metadata().abstract() );
930 generator.setOutputDevice( &svgBuffer );
931 generator.setSize( QSize( width, height ) );
932 generator.setViewBox( QRect( 0, 0, width, height ) );
933 generator.setResolution( static_cast< int >( std::round( settings.
dpi ) ) );
936 bool createOk = p.begin( &generator );
939 mErrorFileName = fileName;
952 svgBuffer.open( QIODevice::ReadOnly );
956 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
958 mErrorFileName = fileName;
963 appendMetadataToSvg( svg );
965 QFile out( fileName );
966 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
969 mErrorFileName = fileName;
973 out.write( svg.toByteArray() );
988 int total = iterator->
count();
989 double step = total > 0 ? 100.0 / total : 100.0;
991 while ( iterator->
next() )
996 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
998 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
1008 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
1015 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 ) );
1032 void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPrinter &printer,
const QString &filePath )
1034 printer.setOutputFileName( filePath );
1035 printer.setOutputFormat( QPrinter::PdfFormat );
1037 updatePrinterPageSize( layout, printer, firstPageToBeExported( layout ) );
1046 void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPrinter &printer,
bool setFirstPageSize )
1048 printer.setFullPage(
true );
1049 printer.setColorMode( QPrinter::Color );
1052 printer.setResolution( static_cast< int>( std::round( layout->
renderContext().
dpi() ) ) );
1054 if ( setFirstPageSize )
1056 updatePrinterPageSize( layout, printer, firstPageToBeExported( layout ) );
1062 if ( mLayout->pageCollection()->pageCount() == 0 )
1065 preparePrint( mLayout, printer,
true );
1067 if ( !p.begin( &printer ) )
1073 printPrivate( printer, p );
1078 QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPrinter &printer, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1081 int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
1082 int toPage = ( printer.toPage() < 1 ) ? mLayout->pageCollection()->pageCount() - 1 : printer.toPage() - 1;
1084 bool pageExported =
false;
1087 for (
int i = fromPage; i <= toPage; ++i )
1089 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1094 updatePrinterPageSize( mLayout, printer, i );
1095 if ( ( pageExported && i > fromPage ) || startNewPage )
1101 if ( !image.isNull() )
1103 QRectF targetArea( 0, 0, image.width(), image.height() );
1104 painter.drawImage( targetArea, image, targetArea );
1110 pageExported =
true;
1115 for (
int i = fromPage; i <= toPage; ++i )
1117 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1122 updatePrinterPageSize( mLayout, printer, i );
1124 if ( ( pageExported && i > fromPage ) || startNewPage )
1129 pageExported =
true;
1135 void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPrinter &printer,
int page )
1140 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1141 QPageLayout::Portrait,
1142 QMarginsF( 0, 0, 0, 0 ) );
1143 pageLayout.setMode( QPageLayout::FullPageMode );
1144 printer.setPageLayout( pageLayout );
1145 printer.setFullPage(
true );
1146 printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1149 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 1153 QSvgGenerator generator;
1154 if ( includeMetadata )
1157 generator.setTitle( l->name() );
1158 else if ( mLayout->project() )
1159 generator.setTitle( mLayout->project()->title() );
1162 generator.setOutputDevice( &svgBuffer );
1163 generator.setSize( QSize( static_cast< int >( std::round( width ) ),
1164 static_cast< int >( std::round( height ) ) ) );
1165 generator.setViewBox( QRect( 0, 0,
1166 static_cast< int >( std::round( width ) ),
1167 static_cast< int >( std::round( height ) ) ) );
1168 generator.setResolution( static_cast< int >( std::round( settings.
dpi ) ) );
1170 QPainter svgPainter( &generator );
1182 svgBuffer.open( QIODevice::ReadOnly );
1186 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1188 mErrorFileName = filename;
1191 if ( 1 == svgLayerId )
1193 svg = QDomDocument( doc.doctype() );
1194 svg.appendChild( svg.importNode( doc.firstChild(), false ) );
1195 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ), false );
1196 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1197 svg.appendChild( svgDocRoot );
1199 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ), true );
1200 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1201 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1202 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1203 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ), true );
1204 svgDocRoot.appendChild( defs );
1205 svgDocRoot.appendChild( mainGroup );
1210 void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const 1213 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1214 metadataElement.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"qgismetadata" ) );
1215 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1216 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1218 auto addTextNode = [&workElement, &svg](
const QString & tag,
const QString & value )
1220 QDomElement element = svg.createElement( tag );
1221 QDomText t = svg.createTextNode( value );
1222 element.appendChild( t );
1223 workElement.appendChild( element );
1226 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1227 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1228 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1229 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1230 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1232 auto addAgentNode = [&workElement, &svg](
const QString & tag,
const QString & value )
1234 QDomElement element = svg.createElement( tag );
1235 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1236 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1237 QDomText t = svg.createTextNode( value );
1238 titleElement.appendChild( t );
1239 agentElement.appendChild( titleElement );
1240 element.appendChild( agentElement );
1241 workElement.appendChild( element );
1244 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1245 addAgentNode( QStringLiteral(
"dc:publisher" ), QStringLiteral(
"QGIS %1" ).arg(
Qgis::QGIS_VERSION ) );
1249 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1250 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1252 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1254 const QStringList words = it.value();
1255 for (
const QString &keyword : words )
1257 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1258 QDomText t = svg.createTextNode( keyword );
1259 liElement.appendChild( t );
1260 bagElement.appendChild( liElement );
1263 element.appendChild( bagElement );
1264 workElement.appendChild( element );
1267 rdfElement.appendChild( workElement );
1268 metadataElement.appendChild( rdfElement );
1269 svg.documentElement().appendChild( metadataElement );
1272 std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const 1275 map = mLayout->referenceMap();
1281 dpi = mLayout->renderContext().dpi();
1284 QRectF exportRegion = region;
1285 if ( !exportRegion.isValid() )
1287 int pageNumber = map->
page();
1290 double pageY = page->pos().y();
1291 QSizeF pageSize = page->rect().size();
1292 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1296 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1299 double outputHeightMM = exportRegion.height();
1300 double outputWidthMM = exportRegion.width();
1304 double mapXCenter = mapExtent.
center().
x();
1305 double mapYCenter = mapExtent.
center().
y();
1307 double sinAlpha = std::sin( alpha );
1308 double cosAlpha = std::cos( alpha );
1311 QPointF mapItemPos = map->pos();
1313 mapItemPos.rx() -= exportRegion.left();
1314 mapItemPos.ry() -= exportRegion.top();
1317 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1318 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1319 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1320 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1321 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1324 double X0 = paperExtent.
xMinimum();
1325 double Y0 = paperExtent.
yMaximum();
1330 double X1 = X0 - mapXCenter;
1331 double Y1 = Y0 - mapYCenter;
1332 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1333 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1334 X0 = X2 + mapXCenter;
1335 Y0 = Y2 + mapYCenter;
1339 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1340 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1341 double pixelWidthScale = paperExtent.
width() / pageWidthPixels;
1342 double pixelHeightScale = paperExtent.
height() / pageHeightPixels;
1345 std::unique_ptr<double[]> t(
new double[6] );
1347 t[1] = cosAlpha * pixelWidthScale;
1348 t[2] = -sinAlpha * pixelWidthScale;
1350 t[4] = -sinAlpha * pixelHeightScale;
1351 t[5] = -cosAlpha * pixelHeightScale;
1356 void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const 1358 QFile worldFile( worldFileName );
1359 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
1363 QTextStream fout( &worldFile );
1367 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1368 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1369 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1370 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1371 fout << QString::number( c,
'f', 12 ) <<
"\r\n";
1372 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1377 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1380 bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const 1385 if ( !map && includeGeoreference )
1386 map = mLayout->referenceMap();
1388 std::unique_ptr<double[]> t;
1390 if ( map && includeGeoreference )
1393 dpi = mLayout->renderContext().dpi();
1395 t = computeGeoTransform( map, exportRegion, dpi );
1400 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toLocal8Bit().constData() );
1405 GDALSetGeoTransform( outputDS.get(), t.get() );
1407 if ( includeMetadata )
1409 QString creationDateString;
1410 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1411 if ( creationDateTime.isValid() )
1413 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1414 if ( creationDateTime.timeZone().isValid() )
1416 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1417 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1418 offsetFromUtc = std::abs( offsetFromUtc );
1419 int offsetHours = offsetFromUtc / 3600;
1420 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1421 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1424 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toLocal8Bit().constData(), nullptr );
1426 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toLocal8Bit().constData(), nullptr );
1428 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toLocal8Bit().constData(), nullptr );
1429 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toLocal8Bit().constData(), nullptr );
1430 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toLocal8Bit().constData(), nullptr );
1431 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toLocal8Bit().constData(), nullptr );
1434 QStringList allKeywords;
1435 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1437 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1439 const QString keywordString = allKeywords.join(
';' );
1440 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toLocal8Bit().constData(), nullptr );
1444 GDALSetProjection( outputDS.get(), map->
crs().
toWkt().toLocal8Bit().constData() );
1446 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1462 int pageNumber = map->
page();
1464 double pageY = page->pos().y();
1465 QSizeF pageSize = page->rect().size();
1466 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
1482 double destinationHeight = exportRegion.height();
1483 double destinationWidth = exportRegion.width();
1485 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1490 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1491 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1493 double xCenter = mapExtent.
center().
x();
1494 double yCenter = mapExtent.
center().
y();
1497 QPointF mapItemPos = map->pos();
1499 mapItemPos.rx() -= exportRegion.left();
1500 mapItemPos.ry() -= exportRegion.top();
1502 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1503 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1504 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
1506 double X0 = paperExtent.
xMinimum();
1507 double Y0 = paperExtent.
yMinimum();
1510 dpi = mLayout->renderContext().dpi();
1512 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
1513 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
1515 double Ww = paperExtent.
width() / widthPx;
1516 double Hh = paperExtent.
height() / heightPx;
1525 s[5] = Y0 + paperExtent.
height();
1529 r[0] = std::cos( alpha );
1530 r[1] = -std::sin( alpha );
1531 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
1532 r[3] = std::sin( alpha );
1533 r[4] = std::cos( alpha );
1534 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
1537 a = r[0] * s[0] + r[1] * s[3];
1538 b = r[0] * s[1] + r[1] * s[4];
1539 c = r[0] * s[2] + r[1] * s[5] + r[2];
1540 d = r[3] * s[0] + r[4] * s[3];
1541 e = r[3] * s[1] + r[4] * s[4];
1542 f = r[3] * s[2] + r[4] * s[5] + r[5];
1552 if ( mLayout->pageCollection()->pageCount() == 1 )
1555 bounds = mLayout->layoutBounds(
true );
1560 bounds = mLayout->pageItemBounds( page,
true );
1562 if ( bounds.width() <= 0 || bounds.height() <= 0 )
1570 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
1582 int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
1585 for (
int i = 0; i < pageCount; ++i )
1599 if ( details.
page == 0 )
1609 bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata )
1611 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
1612 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
1614 w.setCompression( 1 );
1616 if ( projectForMetadata )
1618 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
1620 w.setText( QStringLiteral(
"Creator" ), creator );
1621 w.setText( QStringLiteral(
"Producer" ), creator );
1622 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
1623 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
1624 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
1627 QStringList allKeywords;
1628 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1630 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1632 const QString keywordString = allKeywords.join(
';' );
1633 w.setText( QStringLiteral(
"Keywords" ), keywordString );
1635 return w.write( image );
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported...
int pageCount() const
Returns the number of pages in the collection.
void setDpi(double dpi)
Sets the dpi for outputting the layout.
bool isCanceled() const
Tells whether the operation has been canceled already.
A rectangle specified with double values.
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.
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.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
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)
virtual int numberExportLayers() const
Returns the number of layers that this item requires for exporting during layered exports (e...
void setProgress(double progress)
Sets the current progress for the feedback object.
void renderRegion(QPainter *painter, const QRectF ®ion) const
Renders a region from the layout to a painter.
QString toWkt() const
Returns a WKT representation of this CRS.
QgsRenderContext::TextRenderFormat textRenderFormat() const
Returns the text render format, which dictates how text is rendered (e.g.
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.
double left() const
Returns the left margin.
QImage renderRegionToImage(const QRectF ®ion, QSize imageSize=QSize(), double dpi=-1) const
Renders a region of the layout to an image.
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
QString directory
Target folder.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
QgsLayoutExporter(QgsLayout *layout)
Constructor for QgsLayoutExporter, for the specified layout.
QgsLayoutRenderContext::Flags flags() const
Returns the current combination of flags used for rendering the 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 renderPageToImage(int page, QSize imageSize=QSize(), double dpi=-1) const
Renders a full page to an image.
Contains details of a page being exported by the class.
QSize imageSize
Manual size in pixels for output image.
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, QgsUnitTypes::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
Layout graphical items for displaying a map.
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
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
Always render text using path objects (AKA outlines/curves).
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
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...
QgsPointXY center() const
Returns the center point of the rectangle.
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.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Reads and writes project states.
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.
QString baseName
Base part of filename (i.e. file name without extension or '.')
Could not create layered SVG file.
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.
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
Use antialiasing when drawing items.
Handles rendering and exports of layouts to various formats.
bool shouldExportPage(int page) const
Returns whether the specified page number should be included in exports of the layouts.
Contains settings relating to exporting layouts to PDF.
bool generateWorldFile
Set to true to generate an external world file alongside exported images.
int page() const
Returns the page the item is currently on, with the first page returning 0.
QgsRectangle extent() const
Returns the current map extent.
double right() const
Returns the right margin.
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.
double bottom() const
Returns the bottom margin.
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.
double top() const
Returns the top margin.
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)
int page
Page number, where 0 = first page.
bool exportMetadata
Indicates whether PDF export should include metadata generated from the layout's project's metadata...
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
int currentExportLayer() const
Returns the current item layer to draw while exporting.
void renderPage(QPainter *painter, int page) const
Renders a full page to a destination painter.
bool exportMetadata
Indicates whether image export should include metadata generated from the layout's project's metadata...
Could not start printing to destination device.
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.
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f, double dpi=-1) const
Compute world file parameters.
double width() const
Returns the width of the rectangle.
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double dpi() const
Returns the dpi for outputting the layout.
virtual QString generateFileName(const PageExportDetails &details) const
Generates the file name for a page during export.
virtual bool beginRender()=0
Called when rendering begins, before iteration commences.
double height() const
Returns the height of the rectangle.
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...
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.
QgsLayout * layout() const
Returns the layout linked to this exporter.
Error iterating over layout.