@@ -78,6 +78,39 @@ class LayoutGuideHider
78
78
QHash< QgsLayoutGuide *, bool > mPrevVisibility ;
79
79
};
80
80
81
+ class LayoutItemHider
82
+ {
83
+ public:
84
+ explicit LayoutItemHider ( const QList<QGraphicsItem *> &items )
85
+ {
86
+ for ( QGraphicsItem *item : items )
87
+ {
88
+ mPrevVisibility [item] = item->isVisible ();
89
+ item->hide ();
90
+ }
91
+ }
92
+
93
+ void hideAll ()
94
+ {
95
+ for ( auto it = mPrevVisibility .constBegin (); it != mPrevVisibility .constEnd (); ++it )
96
+ {
97
+ it.key ()->hide ();
98
+ }
99
+ }
100
+
101
+ ~LayoutItemHider ()
102
+ {
103
+ for ( auto it = mPrevVisibility .constBegin (); it != mPrevVisibility .constEnd (); ++it )
104
+ {
105
+ it.key ()->setVisible ( it.value () );
106
+ }
107
+ }
108
+
109
+ private:
110
+
111
+ QHash<QGraphicsItem *, bool > mPrevVisibility ;
112
+ };
113
+
81
114
// /@endcond PRIVATE
82
115
83
116
QgsLayoutExporter::QgsLayoutExporter ( QgsLayout *layout )
@@ -240,19 +273,22 @@ class LayoutContextSettingsRestorer
240
273
: mLayout ( layout )
241
274
, mPreviousDpi ( layout->context ().dpi() )
242
275
, mPreviousFlags( layout->context ().flags() )
276
+ , mPreviousExportLayer( layout->context ().currentExportLayer() )
243
277
{
244
278
}
245
279
246
280
~LayoutContextSettingsRestorer ()
247
281
{
248
282
mLayout ->context ().setDpi ( mPreviousDpi );
249
283
mLayout ->context ().setFlags ( mPreviousFlags );
284
+ mLayout ->context ().setCurrentExportLayer ( mPreviousExportLayer );
250
285
}
251
286
252
287
private:
253
288
QgsLayout *mLayout = nullptr ;
254
289
double mPreviousDpi = 0 ;
255
290
QgsLayoutContext::Flags mPreviousFlags = 0 ;
291
+ int mPreviousExportLayer = 0 ;
256
292
};
257
293
// /@endcond PRIVATE
258
294
@@ -439,10 +475,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &f
439
475
pageDetails.page = i;
440
476
QString fileName = generateFileName ( pageDetails );
441
477
442
- QSvgGenerator generator;
443
- generator.setTitle ( mLayout ->project ()->title () );
444
- generator.setFileName ( fileName );
445
-
478
+ QgsLayoutItemPage *pageItem = mLayout ->pageCollection ()->page ( i );
446
479
QRectF bounds;
447
480
if ( settings.cropToContents )
448
481
{
@@ -463,7 +496,6 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &f
463
496
}
464
497
else
465
498
{
466
- QgsLayoutItemPage *pageItem = mLayout ->pageCollection ()->page ( i );
467
499
bounds = QRectF ( pageItem->pos ().x (), pageItem->pos ().y (), pageItem->rect ().width (), pageItem->rect ().height () );
468
500
}
469
501
@@ -476,24 +508,98 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &f
476
508
// invalid size, skip this page
477
509
continue ;
478
510
}
479
- generator.setSize ( QSize ( width, height ) );
480
- generator.setViewBox ( QRect ( 0 , 0 , width, height ) );
481
- generator.setResolution ( settings.dpi );
482
511
483
- QPainter p;
484
- bool createOk = p.begin ( &generator );
485
- if ( !createOk )
512
+ if ( settings.exportAsLayers )
486
513
{
487
- mErrorFileName = fileName;
488
- return FileError;
489
- }
514
+ const QRectF paperRect = QRectF ( pageItem->pos ().x (),
515
+ pageItem->pos ().y (),
516
+ pageItem->rect ().width (),
517
+ pageItem->rect ().height () );
518
+ QDomDocument svg;
519
+ QDomNode svgDocRoot;
520
+ const QList<QGraphicsItem *> items = mLayout ->items ( paperRect,
521
+ Qt::IntersectsItemBoundingRect,
522
+ Qt::AscendingOrder );
523
+
524
+ LayoutItemHider itemHider ( items );
525
+ ( void )itemHider;
526
+
527
+ int layoutItemLayerIdx = 0 ;
528
+ auto it = items.constBegin ();
529
+ for ( unsigned svgLayerId = 1 ; it != items.constEnd (); ++svgLayerId )
530
+ {
531
+ itemHider.hideAll ();
532
+ QgsLayoutItem *layoutItem = dynamic_cast <QgsLayoutItem *>( *it );
533
+ QString layerName = QObject::tr ( " Layer %1" ).arg ( svgLayerId );
534
+ if ( layoutItem && layoutItem->numberExportLayers () > 0 )
535
+ {
536
+ layoutItem->show ();
537
+ mLayout ->context ().setCurrentExportLayer ( layoutItemLayerIdx );
538
+ ++layoutItemLayerIdx;
539
+ }
540
+ else
541
+ {
542
+ // show all items until the next item that renders on a separate layer
543
+ for ( ; it != items.constEnd (); ++it )
544
+ {
545
+ layoutItem = dynamic_cast <QgsLayoutItem *>( *it );
546
+ if ( layoutItem && layoutItem->numberExportLayers () > 0 )
547
+ {
548
+ break ;
549
+ }
550
+ else
551
+ {
552
+ ( *it )->show ();
553
+ }
554
+ }
555
+ }
556
+
557
+ ExportResult result = renderToLayeredSvg ( settings, width, height, i, bounds, fileName, svgLayerId, layerName, svg, svgDocRoot );
558
+ if ( result != Success )
559
+ return result;
560
+
561
+ if ( layoutItem && layoutItem->numberExportLayers () > 0 && layoutItem->numberExportLayers () == layoutItemLayerIdx ) // restore and pass to next item
562
+ {
563
+ mLayout ->context ().setCurrentExportLayer ( -1 );
564
+ layoutItemLayerIdx = 0 ;
565
+ ++it;
566
+ }
567
+ }
490
568
491
- if ( settings.cropToContents )
492
- renderRegion ( &p, bounds );
569
+ QFile out ( fileName );
570
+ bool openOk = out.open ( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
571
+ if ( !openOk )
572
+ {
573
+ mErrorFileName = fileName;
574
+ return FileError;
575
+ }
576
+
577
+ out.write ( svg.toByteArray () );
578
+ }
493
579
else
494
- renderPage ( &p, i );
580
+ {
581
+ QSvgGenerator generator;
582
+ generator.setTitle ( mLayout ->project ()->title () );
583
+ generator.setFileName ( fileName );
584
+ generator.setSize ( QSize ( width, height ) );
585
+ generator.setViewBox ( QRect ( 0 , 0 , width, height ) );
586
+ generator.setResolution ( settings.dpi );
587
+
588
+ QPainter p;
589
+ bool createOk = p.begin ( &generator );
590
+ if ( !createOk )
591
+ {
592
+ mErrorFileName = fileName;
593
+ return FileError;
594
+ }
595
+
596
+ if ( settings.cropToContents )
597
+ renderRegion ( &p, bounds );
598
+ else
599
+ renderPage ( &p, i );
495
600
496
- p.end ();
601
+ p.end ();
602
+ }
497
603
}
498
604
499
605
return Success;
@@ -622,6 +728,57 @@ void QgsLayoutExporter::updatePrinterPageSize( QPrinter &printer, int page )
622
728
printer.setPaperSize ( pageSizeMM.toQSizeF (), QPrinter::Millimeter );
623
729
}
624
730
731
+ 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 ) const
732
+ {
733
+ QBuffer svgBuffer;
734
+ {
735
+ QSvgGenerator generator;
736
+ generator.setTitle ( mLayout ->name () );
737
+ generator.setOutputDevice ( &svgBuffer );
738
+ generator.setSize ( QSize ( width, height ) );
739
+ generator.setViewBox ( QRect ( 0 , 0 , width, height ) );
740
+ generator.setResolution ( settings.dpi ); // because the rendering is done in mm, convert the dpi
741
+
742
+ QPainter svgPainter ( &generator );
743
+ if ( settings.cropToContents )
744
+ renderRegion ( &svgPainter, bounds );
745
+ else
746
+ renderPage ( &svgPainter, page );
747
+ }
748
+
749
+ // post-process svg output to create groups in a single svg file
750
+ // we create inkscape layers since it's nice and clean and free
751
+ // and fully svg compatible
752
+ {
753
+ svgBuffer.close ();
754
+ svgBuffer.open ( QIODevice::ReadOnly );
755
+ QDomDocument doc;
756
+ QString errorMsg;
757
+ int errorLine;
758
+ if ( ! doc.setContent ( &svgBuffer, false , &errorMsg, &errorLine ) )
759
+ {
760
+ mErrorFileName = filename;
761
+ return SvgLayerError;
762
+ }
763
+ if ( 1 == svgLayerId )
764
+ {
765
+ svg = QDomDocument ( doc.doctype () );
766
+ svg.appendChild ( svg.importNode ( doc.firstChild (), false ) );
767
+ svgDocRoot = svg.importNode ( doc.elementsByTagName ( QStringLiteral ( " svg" ) ).at ( 0 ), false );
768
+ svgDocRoot.toElement ().setAttribute ( QStringLiteral ( " xmlns:inkscape" ), QStringLiteral ( " http://www.inkscape.org/namespaces/inkscape" ) );
769
+ svg.appendChild ( svgDocRoot );
770
+ }
771
+ QDomNode mainGroup = svg.importNode ( doc.elementsByTagName ( QStringLiteral ( " g" ) ).at ( 0 ), true );
772
+ mainGroup.toElement ().setAttribute ( QStringLiteral ( " id" ), layerName );
773
+ mainGroup.toElement ().setAttribute ( QStringLiteral ( " inkscape:label" ), layerName );
774
+ mainGroup.toElement ().setAttribute ( QStringLiteral ( " inkscape:groupmode" ), QStringLiteral ( " layer" ) );
775
+ QDomNode defs = svg.importNode ( doc.elementsByTagName ( QStringLiteral ( " defs" ) ).at ( 0 ), true );
776
+ svgDocRoot.appendChild ( defs );
777
+ svgDocRoot.appendChild ( mainGroup );
778
+ }
779
+ return Success;
780
+ }
781
+
625
782
std::unique_ptr<double []> QgsLayoutExporter::computeGeoTransform ( const QgsLayoutItemMap *map, const QRectF ®ion, double dpi ) const
626
783
{
627
784
if ( !map )
0 commit comments