Skip to content

Commit

Permalink
Work on PDF export
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 17, 2017
1 parent a59dce5 commit 91179f1
Show file tree
Hide file tree
Showing 9 changed files with 491 additions and 35 deletions.
40 changes: 37 additions & 3 deletions python/core/layout/qgslayoutexporter.sip
Expand Up @@ -122,6 +122,7 @@ to render sections of pages rather than full pages.
Success,
MemoryError,
FileError,
PrintError,
};

struct ImageExportSettings
Expand All @@ -133,7 +134,7 @@ Constructor for ImageExportSettings

double dpi;
%Docstring
Resolution to export layout at
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
%End

QSize imageSize;
Expand Down Expand Up @@ -173,7 +174,7 @@ Resolution to export layout at

bool generateWorldFile;
%Docstring
Set to true to generate an external world file alonside
Set to true to generate an external world file alongside
exported images.
%End

Expand All @@ -184,7 +185,7 @@ Resolution to export layout at

};

ExportResult exportToImage( const QString &filePath, const ImageExportSettings &settings );
ExportResult exportToImage( const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings );
%Docstring
Exports the layout to the a ``filePath``, using the specified export ``settings``.

Expand All @@ -197,6 +198,39 @@ Resolution to export layout at
:rtype: ExportResult
%End

struct PdfExportSettings
{
PdfExportSettings();
%Docstring
Constructor for PdfExportSettings
%End

double dpi;
%Docstring
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
%End

bool rasterizeWholeImage;
%Docstring
Set to true to force whole layout to be rasterized while exporting
%End

QgsLayoutContext::Flags flags;
%Docstring
Layout context flags, which control how the export will be created.
%End

};

ExportResult exportToPdf( const QString &filePath, const QgsLayoutExporter::PdfExportSettings &settings );
%Docstring
Exports the layout as a PDF to the a ``filePath``, using the specified export ``settings``.

Returns a result code indicating whether the export was successful or an
error was encountered.
:rtype: ExportResult
%End

QString errorFile() const;
%Docstring
Returns the file name corresponding to the last error encountered during
Expand Down
140 changes: 136 additions & 4 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Expand Up @@ -172,6 +172,7 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionRemoveLayout, &QAction::triggered, this, &QgsLayoutDesignerDialog::deleteLayout );

connect( mActionExportAsImage, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportToRaster );
connect( mActionExportAsPDF, &QAction::triggered, this, &QgsLayoutDesignerDialog::exportToPdf );

connect( mActionShowGrid, &QAction::triggered, this, &QgsLayoutDesignerDialog::showGrid );
connect( mActionSnapGrid, &QAction::triggered, this, &QgsLayoutDesignerDialog::snapToGrid );
Expand Down Expand Up @@ -1499,6 +1500,7 @@ void QgsLayoutDesignerDialog::exportToRaster()
mLayout->setCustomProperty( QStringLiteral( "imageAntialias" ), imageDlg.antialiasing() );

mView->setPaintingEnabled( false );
QApplication::setOverrideCursor( Qt::BusyCursor );

QgsLayoutExporter exporter( mLayout );

Expand All @@ -1518,11 +1520,12 @@ void QgsLayoutDesignerDialog::exportToRaster()
switch ( exporter.exportToImage( fileNExt.first, settings ) )
{
case QgsLayoutExporter::Success:
case QgsLayoutExporter::PrintError:
break;

case QgsLayoutExporter::FileError:
QMessageBox::warning( this, tr( "Image Export Error" ),
QString( tr( "Cannot write to %1.\n\nThis file may be open in another application." ) ).arg( exporter.errorFile() ),
tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( exporter.errorFile() ),
QMessageBox::Ok,
QMessageBox::Ok );
break;
Expand All @@ -1536,10 +1539,105 @@ void QgsLayoutDesignerDialog::exportToRaster()
QMessageBox::Ok, QMessageBox::Ok );
break;


}
QApplication::restoreOverrideCursor();
mView->setPaintingEnabled( true );
}

void QgsLayoutDesignerDialog::exportToPdf()
{
if ( containsWmsLayers() )
{
showWmsPrintingWarning();
}

if ( containsAdvancedEffects() )
{
showAdvancedEffectsWarning();
}

QgsSettings settings;
QString lastUsedFile = settings.value( QStringLiteral( "UI/lastSaveAsPdfFile" ), QStringLiteral( "qgis.pdf" ) ).toString();
QFileInfo file( lastUsedFile );
QString outputFileName;

#if 0// TODO
if ( hasAnAtlas && !atlasOnASingleFile &&
( mode == QgsComposer::Atlas || mComposition->atlasMode() == QgsComposition::PreviewAtlas ) )
{
outputFileName = QDir( file.path() ).filePath( atlasMap->currentFilename() ) + ".pdf";
}
else
{
#endif
outputFileName = file.path();
#if 0 //TODO
}
#endif

#ifdef Q_OS_MAC
mQgis->activateWindow();
this->raise();
#endif
outputFileName = QFileDialog::getSaveFileName(
this,
tr( "Export to PDF" ),
outputFileName,
tr( "PDF Format" ) + " (*.pdf *.PDF)" );
this->activateWindow();
if ( outputFileName.isEmpty() )
{
return;
}

if ( !outputFileName.endsWith( QLatin1String( ".pdf" ), Qt::CaseInsensitive ) )
{
outputFileName += QLatin1String( ".pdf" );
}

settings.setValue( QStringLiteral( "UI/lastSaveAsPdfFile" ), outputFileName );

mView->setPaintingEnabled( false );
QApplication::setOverrideCursor( Qt::BusyCursor );

QgsLayoutExporter::PdfExportSettings pdfSettings;
pdfSettings.rasteriseWholeImage = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool();

QgsLayoutExporter exporter( mLayout );
switch ( exporter.exportToPdf( outputFileName, pdfSettings ) )
{
case QgsLayoutExporter::Success:
break;

case QgsLayoutExporter::FileError:
QMessageBox::warning( this, tr( "Export to PDF" ),
tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( exporter.errorFile() ),
QMessageBox::Ok,
QMessageBox::Ok );
break;

case QgsLayoutExporter::PrintError:
QMessageBox::warning( this, tr( "Export to PDF" ),
tr( "Could not create print device." ),
QMessageBox::Ok,
QMessageBox::Ok );
break;


case QgsLayoutExporter::MemoryError:
QMessageBox::warning( nullptr, tr( "Memory Allocation Error" ),
tr( "Exporting the PDF "
"resulted in a memory overflow.\n\n"
"Please try a lower resolution or a smaller paper size." ),
QMessageBox::Ok, QMessageBox::Ok );
break;
}

mView->setPaintingEnabled( true );
QApplication::restoreOverrideCursor();
}

void QgsLayoutDesignerDialog::paste()
{
QPointF pt = mView->mapFromGlobal( QCursor::pos() );
Expand Down Expand Up @@ -1634,9 +1732,9 @@ void QgsLayoutDesignerDialog::createLayoutPropertiesWidget()
QgsLayoutGuideWidget *oldGuideWidget = qobject_cast<QgsLayoutGuideWidget *>( mGuideStack->takeMainPanel() );
delete oldGuideWidget;

QgsLayoutPropertiesWidget *widget = new QgsLayoutPropertiesWidget( mGeneralDock, mLayout );
widget->setDockMode( true );
mGeneralPropertiesStack->setMainPanel( widget );
mLayoutPropertiesWidget = new QgsLayoutPropertiesWidget( mGeneralDock, mLayout );
mLayoutPropertiesWidget->setDockMode( true );
mGeneralPropertiesStack->setMainPanel( mLayoutPropertiesWidget );

QgsLayoutGuideWidget *guideWidget = new QgsLayoutGuideWidget( mGuideDock, mLayout, mView );
guideWidget->setDockMode( true );
Expand Down Expand Up @@ -1685,6 +1783,40 @@ void QgsLayoutDesignerDialog::showWmsPrintingWarning()
}
}

bool QgsLayoutDesignerDialog::containsAdvancedEffects() const
{
QList< QgsLayoutItem *> items;
mLayout->layoutItems( items );

for ( QgsLayoutItem *currentItem : qgis::as_const( items ) )
{
if ( currentItem->containsAdvancedEffects() )
return true;
}
return false;
}

void QgsLayoutDesignerDialog::showAdvancedEffectsWarning()
{
bool rasterize = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool();
if ( rasterise )
return;

QgsMessageViewer *m = new QgsMessageViewer( this, QgsGuiUtils::ModalDialogFlags, false );
m->setWindowTitle( tr( "Composition Effects" ) );
m->setMessage( tr( "Advanced composition effects such as blend modes or vector layer transparency are enabled in this layout, which cannot be printed as vectors. Printing as a raster is recommended." ), QgsMessageOutput::MessageText );
m->setCheckBoxText( tr( "Print as raster" ) );
m->setCheckBoxState( Qt::Checked );
m->setCheckBoxVisible( true );
m->showMessage( true );

mLayout->setCustomProperty( QStringLiteral( "rasterise" ), m->checkBoxState() == Qt::Checked );
//make sure print as raster checkbox is updated
mLayoutPropertiesWidget->updateGui();

delete m;
}

void QgsLayoutDesignerDialog::selectItems( const QList<QgsLayoutItem *> items )
{
for ( QGraphicsItem *item : items )
Expand Down
10 changes: 10 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.h
Expand Up @@ -41,6 +41,7 @@ class QgsDockWidget;
class QUndoView;
class QTreeView;
class QgsLayoutItemsListView;
class QgsLayoutPropertiesWidget;

class QgsAppLayoutDesignerInterface : public QgsLayoutDesignerInterface
{
Expand Down Expand Up @@ -275,6 +276,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
void renameLayout();
void deleteLayout();
void exportToRaster();
void exportToPdf();

private:

Expand Down Expand Up @@ -322,6 +324,8 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
QgsDockWidget *mGuideDock = nullptr;
QgsPanelWidgetStack *mGuideStack = nullptr;

QgsLayoutPropertiesWidget *mLayoutPropertiesWidget = nullptr;

QUndoView *mUndoView = nullptr;
QgsDockWidget *mUndoDock = nullptr;

Expand Down Expand Up @@ -365,6 +369,12 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner

//! Displays a warning because of possible min/max size in WMS
void showWmsPrintingWarning();

//! True if the layout contains advanced effects, such as blend modes
bool containsAdvancedEffects() const;

//! Displays a warning because of incompatibility between blend modes and QPrinter
void showAdvancedEffectsWarning();
};

#endif // QGSLAYOUTDESIGNERDIALOG_H
Expand Down
10 changes: 10 additions & 0 deletions src/app/layout/qgslayoutpropertieswidget.cpp
Expand Up @@ -58,6 +58,8 @@ QgsLayoutPropertiesWidget::QgsLayoutPropertiesWidget( QWidget *parent, QgsLayout
mGenerateWorldFileCheckBox->setChecked( exportWorldFile );
connect( mGenerateWorldFileCheckBox, &QCheckBox::toggled, this, &QgsLayoutPropertiesWidget::worldFileToggled );

connect( mRasterizeCheckBox, &QCheckBox::toggled, this, &QgsLayoutPropertiesWidget::rasteriseToggled );

mTopMarginSpinBox->setValue( topMargin );
mMarginUnitsComboBox->linkToWidget( mTopMarginSpinBox );
mRightMarginSpinBox->setValue( rightMargin );
Expand Down Expand Up @@ -88,6 +90,9 @@ void QgsLayoutPropertiesWidget::updateGui()
{
whileBlocking( mReferenceMapComboBox )->setItem( mLayout->referenceMap() );
whileBlocking( mResolutionSpinBox )->setValue( mLayout->context().dpi() );

bool rasterise = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool();
whileBlocking( mRasterizeCheckBox )->setChecked( rasterise );
}

void QgsLayoutPropertiesWidget::updateSnappingElements()
Expand Down Expand Up @@ -189,6 +194,11 @@ void QgsLayoutPropertiesWidget::worldFileToggled()
mLayout->setCustomProperty( QStringLiteral( "exportWorldFile" ), mGenerateWorldFileCheckBox->isChecked() );
}

void QgsLayoutPropertiesWidget::rasteriseToggled()
{
mLayout->setCustomProperty( QStringLiteral( "rasterise" ), mRasterizeCheckBox->isChecked() );
}

void QgsLayoutPropertiesWidget::blockSignals( bool block )
{
mGridResolutionSpinBox->blockSignals( block );
Expand Down
6 changes: 5 additions & 1 deletion src/app/layout/qgslayoutpropertieswidget.h
Expand Up @@ -28,10 +28,13 @@ class QgsLayoutPropertiesWidget: public QgsPanelWidget, private Ui::QgsLayoutWid
public:
QgsLayoutPropertiesWidget( QWidget *parent, QgsLayout *layout );

private slots:
public slots:

//! Refreshes the gui to reflect the current layout settings
void updateGui();

private slots:

void gridResolutionChanged( double d );
void gridResolutionUnitsChanged( QgsUnitTypes::LayoutUnit unit );
void gridOffsetXChanged( double d );
Expand All @@ -43,6 +46,7 @@ class QgsLayoutPropertiesWidget: public QgsPanelWidget, private Ui::QgsLayoutWid
void referenceMapChanged( QgsLayoutItem *item );
void dpiChanged( int value );
void worldFileToggled();
void rasteriseToggled();

private:

Expand Down
1 change: 1 addition & 0 deletions src/core/layout/qgslayoutcontext.h
Expand Up @@ -235,6 +235,7 @@ class CORE_EXPORT QgsLayoutContext : public QObject
bool mPagesVisible = true;

friend class QgsLayoutExporter;
friend class LayoutItemCacheSettingRestorer;


};
Expand Down

0 comments on commit 91179f1

Please sign in to comment.