[FEATURE][composer] Option to restrict image/SVG outputs to content

If selected, then the images output by composer will include only
the area of the composition with content. There's also an option
for margins to add around the item bounds if required.

If the composition includes a single page, then the output will
be sized to include EVERYTHING on the composition. If it's a
multi-page composition, then each page will be cropped to only
include the area of that page with items.

A new image export options dialog has been added to facilitate
this, which also includes handy shortcuts for overriding the
print resolution or exported image dimensions.

Sponsored by NIWA
This commit is contained in:
Nyall Dawson 2015-09-10 21:26:16 +10:00
parent 2f343008c1
commit dd759370bd
11 changed files with 1252 additions and 106 deletions

View File

@ -601,18 +601,59 @@ class QgsComposition : QGraphicsScene
*/
bool exportAsPDF( const QString& file );
//! print composer page to image
//! If the image does not fit into memory, a null image is returned
/** Renders a composer page to an image.
* @param page page number, 0 based such that the first page is page 0
* @returns rendered image, or null image if image does not fit into available memory
* @see renderRectAsRaster()
* @see renderPage()
*/
QImage printPageAsRaster( int page );
/** Render a page to a paint device
/** Renders a portion of the composition to an image. This method can be used to render
* sections of pages rather than full pages.
* @param rect region of composition to render
* @returns rendered image, or null image if image does not fit into available memory
* @note added in QGIS 2.12
* @see printPageAsRaster()
* @see renderRect()
*/
QImage renderRectAsRaster( const QRectF& rect );
/** Renders a full page to a paint device.
* @param p destination painter
* @param page page number, 0 based such that the first page is page 0 */
* @param page page number, 0 based such that the first page is page 0
* @see renderRect()
* @see printPageAsRaster()
*/
void renderPage( QPainter* p, int page );
/** Compute world file parameters */
/** Renders a portion of the composition to a paint device. This method can be used
* to render sections of pages rather than full pages.
* @param p destination painter
* @param rect region of composition to render
* @note added in QGIS 2.12
* @see renderPage()
* @see renderRectAsRaster()
*/
void renderRect( QPainter* p, const QRectF& rect );
/** Compute world file parameters. Assumes the whole page containing the associated map item
* will be exported.
*/
void computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const;
/** Computes the world file parameters for a specified region of the composition.
* @param exportRegion region of the composition which will be associated with world file
* @param a
* @param b
* @param c
* @param d
* @param e
* @param f
* @note added in QGIS 2.12
*/
void computeWorldFileParameters( const QRectF& exportRegion, double& a, double& b, double& c, double& d, double& e, double& f ) const;
QgsAtlasComposition& atlasComposition();
/** Resizes a QRectF relative to the change from boundsBefore to boundsAfter
@ -703,6 +744,20 @@ class QgsComposition : QGraphicsScene
*/
QStringList customProperties() const;
/** Returns the bounding box of the items contained on a specified page.
* @param pageNumber page number, where 0 is the first page
* @param visibleOnly set to true to only include visible items
* @note added in QGIS 2.12
*/
QRectF pageItemBounds( int pageNumber, bool visibleOnly = false ) const;
/** Calculates the bounds of all non-gui items in the composition. Ignores snap lines and mouse handles.
* @param ignorePages set to true to ignore page items
* @param margin optional marginal (in percent, eg 0.05 = 5% ) to add around items
*/
QRectF compositionBounds( bool ignorePages = false, double margin = 0.0 ) const;
public slots:
/** Casts object to the proper subclass type and calls corresponding itemAdded signal*/
void sendItemAddedSignal( QgsComposerItem* item );

View File

@ -124,6 +124,7 @@ SET(QGIS_APP_SRCS
composer/qgscomposerarrowwidget.cpp
composer/qgscomposerattributetablewidget.cpp
composer/qgscomposerhtmlwidget.cpp
composer/qgscomposerimageexportoptionsdialog.cpp
composer/qgscomposeritemwidget.cpp
composer/qgscomposerlabelwidget.cpp
composer/qgscomposerpicturewidget.cpp
@ -278,6 +279,7 @@ SET (QGIS_APP_MOC_HDRS
composer/qgscomposerarrowwidget.h
composer/qgscomposerattributetablewidget.h
composer/qgscomposerhtmlwidget.h
composer/qgscomposerimageexportoptionsdialog.h
composer/qgscomposeritemwidget.h
composer/qgscomposerlabelwidget.h
composer/qgscomposerlegendwidget.h

View File

@ -61,6 +61,7 @@
#include "qgspaperitem.h"
#include "qgsmaplayerregistry.h"
#include "qgsprevieweffect.h"
#include "qgscomposerimageexportoptionsdialog.h"
#include "ui_qgssvgexportoptions.h"
#include <QCloseEvent>
@ -1965,6 +1966,19 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
return;
}
//get some defaults from the composition
bool cropToContents = mComposition->customProperty( "imageCropToContents", false ).toBool();
int marginTop = mComposition->customProperty( "imageCropMarginTop", 0 ).toInt();
int marginRight = mComposition->customProperty( "imageCropMarginRight", 0 ).toInt();
int marginBottom = mComposition->customProperty( "imageCropMarginBottom", 0 ).toInt();
int marginLeft = mComposition->customProperty( "imageCropMarginLeft", 0 ).toInt();
QgsComposerImageExportOptionsDialog imageDlg( this );
imageDlg.setImageSize( QSizeF( mComposition->paperWidth(), mComposition->paperHeight() ) );
imageDlg.setResolution( mComposition->printResolution() );
imageDlg.setCropToContents( cropToContents );
imageDlg.setCropMargins( marginTop, marginRight, marginBottom, marginLeft );
QgsAtlasComposition* atlasMap = &mComposition->atlasComposition();
if ( mode == QgsComposer::Single )
{
@ -1983,6 +1997,17 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
return;
}
if ( !imageDlg.exec() )
return;
cropToContents = imageDlg.cropToContents();
imageDlg.getCropMargins( marginTop, marginRight, marginBottom, marginLeft );
mComposition->setCustomProperty( "imageCropToContents", cropToContents );
mComposition->setCustomProperty( "imageCropMarginTop", marginTop );
mComposition->setCustomProperty( "imageCropMarginRight", marginRight );
mComposition->setCustomProperty( "imageCropMarginBottom", marginBottom );
mComposition->setCustomProperty( "imageCropMarginLeft", marginLeft );
mView->setPaintingEnabled( false );
int worldFilePageNo = -1;
@ -1997,7 +2022,38 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
{
continue;
}
QImage image = mComposition->printPageAsRaster( i );
QImage image;
QRectF bounds;
if ( cropToContents )
{
if ( mComposition->numPages() == 1 )
{
// single page, so include everything
bounds = mComposition->compositionBounds( true );
}
else
{
// multi page, so just clip to items on current page
bounds = mComposition->pageItemBounds( i, true );
}
if ( bounds.width() <= 0 || bounds.height() <= 0 )
{
//invalid size, skip page
continue;
}
double pixelToMm = 25.4 / mComposition->printResolution();
bounds = bounds.adjusted( -marginLeft * pixelToMm,
-marginTop * pixelToMm,
marginRight * pixelToMm,
marginBottom * pixelToMm );
image = mComposition->renderRectAsRaster( bounds );
}
else
{
image = mComposition->printPageAsRaster( i );
}
if ( image.isNull() )
{
QMessageBox::warning( 0, tr( "Memory Allocation Error" ),
@ -2037,7 +2093,10 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
{
// should generate world file for this page
double a, b, c, d, e, f;
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
if ( bounds.isValid() )
mComposition->computeWorldFileParameters( bounds, a, b, c, d, e, f );
else
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
QFileInfo fi( outputFilePath );
// build the world file name
@ -2127,6 +2186,17 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
return;
}
if ( !imageDlg.exec() )
return;
cropToContents = imageDlg.cropToContents();
imageDlg.getCropMargins( marginTop, marginRight, marginBottom, marginLeft );
mComposition->setCustomProperty( "imageCropToContents", cropToContents );
mComposition->setCustomProperty( "imageCropMarginTop", marginTop );
mComposition->setCustomProperty( "imageCropMarginRight", marginRight );
mComposition->setCustomProperty( "imageCropMarginBottom", marginBottom );
mComposition->setCustomProperty( "imageCropMarginLeft", marginLeft );
myQSettings.setValue( "/UI/lastSaveAtlasAsImagesDir", dir );
// So, now we can render the atlas
@ -2184,7 +2254,38 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
{
continue;
}
QImage image = mComposition->printPageAsRaster( i );
QImage image;
QRectF bounds;
if ( cropToContents )
{
if ( mComposition->numPages() == 1 )
{
// single page, so include everything
bounds = mComposition->compositionBounds( true );
}
else
{
// multi page, so just clip to items on current page
bounds = mComposition->pageItemBounds( i, true );
}
if ( bounds.width() <= 0 || bounds.height() <= 0 )
{
//invalid size, skip page
continue;
}
double pixelToMm = 25.4 / mComposition->printResolution();
bounds = bounds.adjusted( -marginLeft * pixelToMm,
-marginTop * pixelToMm,
marginRight * pixelToMm,
marginBottom * pixelToMm );
image = mComposition->renderRectAsRaster( bounds );
}
else
{
image = mComposition->printPageAsRaster( i );
}
QString imageFilename = filename;
if ( i != 0 )
@ -2210,7 +2311,10 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )
{
// should generate world file for this page
double a, b, c, d, e, f;
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
if ( bounds.isValid() )
mComposition->computeWorldFileParameters( bounds, a, b, c, d, e, f );
else
mComposition->computeWorldFileParameters( a, b, c, d, e, f );
QFileInfo fi( imageFilename );
// build the world file name
@ -2317,6 +2421,11 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
QString outputDir;
bool groupLayers = false;
bool prevSettingLabelsAsOutlines = QgsProject::instance()->readBoolEntry( "PAL", "/DrawOutlineLabels", true );
bool clipToContent = false;
double marginTop = 0.0;
double marginRight = 0.0;
double marginBottom = 0.0;
double marginLeft = 0.0;
if ( mode == QgsComposer::Single )
{
@ -2335,26 +2444,13 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
// open file dialog
outputFileName = QFileDialog::getSaveFileName(
this,
tr( "Choose a file name to save the map as" ),
tr( "Choose a file name to save the composition as" ),
outputFileName,
tr( "SVG Format" ) + " (*.svg *.SVG)" );
if ( outputFileName.isEmpty() )
return;
// open otions dialog
{
QDialog dialog;
Ui::QgsSvgExportOptionsDialog options;
options.setupUi( &dialog );
options.chkTextAsOutline->setChecked( prevSettingLabelsAsOutlines );
dialog.exec();
groupLayers = options.chkMapLayersAsGroup->isChecked();
//temporarily override label draw outlines setting
QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", options.chkTextAsOutline->isChecked() );
}
if ( !outputFileName.endsWith( ".svg", Qt::CaseInsensitive ) )
{
outputFileName += ".svg";
@ -2400,24 +2496,42 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
QMessageBox::Ok );
return;
}
// open otions dialog
{
QDialog dialog;
Ui::QgsSvgExportOptionsDialog options;
options.setupUi( &dialog );
options.chkTextAsOutline->setChecked( prevSettingLabelsAsOutlines );
dialog.exec();
groupLayers = options.chkMapLayersAsGroup->isChecked();
//temporarily override label draw outlines setting
QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", options.chkTextAsOutline->isChecked() );
}
myQSettings.setValue( "/UI/lastSaveAtlasAsSvgDir", outputDir );
}
// open options dialog
QDialog dialog;
Ui::QgsSvgExportOptionsDialog options;
options.setupUi( &dialog );
options.chkTextAsOutline->setChecked( prevSettingLabelsAsOutlines );
options.chkMapLayersAsGroup->setChecked( mComposition->customProperty( "svgGroupLayers", false ).toBool() );
options.mClipToContentGroupBox->setChecked( mComposition->customProperty( "svgCropToContents", false ).toBool() );
options.mTopMarginSpinBox->setValue( mComposition->customProperty( "svgCropMarginTop", 0 ).toInt() );
options.mRightMarginSpinBox->setValue( mComposition->customProperty( "svgCropMarginRight", 0 ).toInt() );
options.mBottomMarginSpinBox->setValue( mComposition->customProperty( "svgCropMarginBottom", 0 ).toInt() );
options.mLeftMarginSpinBox->setValue( mComposition->customProperty( "svgCropMarginLeft", 0 ).toInt() );
if ( dialog.exec() != QDialog::Accepted )
return;
groupLayers = options.chkMapLayersAsGroup->isChecked();
clipToContent = options.mClipToContentGroupBox->isChecked();
marginTop = options.mTopMarginSpinBox->value();
marginRight = options.mRightMarginSpinBox->value();
marginBottom = options.mBottomMarginSpinBox->value();
marginLeft = options.mLeftMarginSpinBox->value();
//save dialog settings
mComposition->setCustomProperty( "svgGroupLayers", groupLayers );
mComposition->setCustomProperty( "svgCropToContents", clipToContent );
mComposition->setCustomProperty( "svgCropMarginTop", marginTop );
mComposition->setCustomProperty( "svgCropMarginRight", marginRight );
mComposition->setCustomProperty( "svgCropMarginBottom", marginBottom );
mComposition->setCustomProperty( "svgCropMarginLeft", marginLeft );
//temporarily override label draw outlines setting
QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", options.chkTextAsOutline->isChecked() );
mView->setPaintingEnabled( false );
int featureI = 0;
@ -2488,10 +2602,33 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
generator.setFileName( currentFileName );
}
QRectF bounds;
if ( clipToContent )
{
if ( mComposition->numPages() == 1 )
{
// single page, so include everything
bounds = mComposition->compositionBounds( true );
}
else
{
// multi page, so just clip to items on current page
bounds = mComposition->pageItemBounds( i, true );
}
bounds = bounds.adjusted( -marginLeft, -marginTop, marginRight, marginBottom );
}
else
bounds = QRectF( 0, 0, mComposition->paperWidth(), mComposition->paperHeight() );
//width in pixel
int width = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
int width = ( int )( bounds.width() * mComposition->printResolution() / 25.4 );
//height in pixel
int height = ( int )( mComposition->paperHeight() * mComposition->printResolution() / 25.4 );
int height = ( int )( bounds.height() * mComposition->printResolution() / 25.4 );
if ( width == 0 || height == 0 )
{
//invalid size, skip this page
continue;
}
generator.setSize( QSize( width, height ) );
generator.setViewBox( QRect( 0, 0, width, height ) );
generator.setResolution( mComposition->printResolution() ); //because the rendering is done in mm, convert the dpi
@ -2509,15 +2646,18 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
return;
}
mComposition->renderPage( &p, i );
if ( clipToContent )
mComposition->renderRect( &p, bounds );
else
mComposition->renderPage( &p, i );
p.end();
}
}
else
{
//width and height in pixel
const int width = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
const int height = ( int )( mComposition->paperHeight() * mComposition->printResolution() / 25.4 );
const int pageWidth = ( int )( mComposition->paperWidth() * mComposition->printResolution() / 25.4 );
const int pageHeight = ( int )( mComposition->paperHeight() * mComposition->printResolution() / 25.4 );
QList< QgsPaperItem* > paperItems( mComposition->pages() );
for ( int i = 0; i < mComposition->numPages(); ++i )
@ -2526,6 +2666,34 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
{
continue;
}
int width = pageWidth;
int height = pageHeight;
QRectF bounds;
if ( clipToContent )
{
if ( mComposition->numPages() == 1 )
{
// single page, so include everything
bounds = mComposition->compositionBounds( true );
}
else
{
// multi page, so just clip to items on current page
bounds = mComposition->pageItemBounds( i, true );
}
bounds = bounds.adjusted( -marginLeft, -marginTop, marginRight, marginBottom );
width = bounds.width() * mComposition->printResolution() / 25.4;
height = bounds.height() * mComposition->printResolution() / 25.4;
}
if ( width == 0 || height == 0 )
{
//invalid size, skip this page
continue;
}
QDomDocument svg;
QDomNode svgDocRoot;
QgsPaperItem * paperItem = paperItems[i];
@ -2581,7 +2749,10 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
generator.setResolution( mComposition->printResolution() ); //because the rendering is done in mm, convert the dpi
QPainter p( &generator );
mComposition->renderPage( &p, i );
if ( clipToContent )
mComposition->renderRect( &p, bounds );
else
mComposition->renderPage( &p, i );
}
// post-process svg output to create groups in a single svg file
// we create inkscape layers since it's nice and clean and free

View File

@ -0,0 +1,160 @@
/***************************************************************************
qgscomposerimageexportoptionsdialog.cpp
---------------------------------------
begin : September 2015
copyright : (C) 2015 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscomposerimageexportoptionsdialog.h"
#include <QSettings>
#include <QCheckBox>
#include <QPushButton>
QgsComposerImageExportOptionsDialog::QgsComposerImageExportOptionsDialog( QWidget* parent, Qt::WindowFlags flags )
: QDialog( parent, flags )
{
setupUi( this );
connect( mClipToContentGroupBox, SIGNAL(toggled(bool)), this, SLOT(clipToContentsToggled(bool)));
QSettings settings;
restoreGeometry( settings.value( "/Windows/ComposerImageExportOptionsDialog/geometry" ).toByteArray() );
}
QgsComposerImageExportOptionsDialog::~QgsComposerImageExportOptionsDialog()
{
QSettings settings;
settings.setValue( "/Windows/ComposerImageExportOptionsDialog/geometry", saveGeometry() );
}
void QgsComposerImageExportOptionsDialog::setResolution(int resolution)
{
mResolutionSpinBox->setValue( resolution );
if ( mImageSize.isValid() )
{
mWidthSpinBox->blockSignals( true );
mHeightSpinBox->blockSignals( true );
mWidthSpinBox->setValue( mImageSize.width() * resolution / 25.4 );
mHeightSpinBox->setValue( mImageSize.height() * resolution / 25.4 );
mWidthSpinBox->blockSignals( false );
mHeightSpinBox->blockSignals( false );
}
}
int QgsComposerImageExportOptionsDialog::resolution() const
{
return mResolutionSpinBox->value();
}
void QgsComposerImageExportOptionsDialog::setImageSize( const QSizeF& size )
{
mImageSize = size;
mWidthSpinBox->blockSignals( true );
mHeightSpinBox->blockSignals( true );
mWidthSpinBox->setValue( size.width() * mResolutionSpinBox->value() / 25.4 );
mHeightSpinBox->setValue( size.height() * mResolutionSpinBox->value() / 25.4 );
mWidthSpinBox->blockSignals( false );
mHeightSpinBox->blockSignals( false );
}
int QgsComposerImageExportOptionsDialog::width() const
{
return mWidthSpinBox->value();
}
int QgsComposerImageExportOptionsDialog::height() const
{
return mHeightSpinBox->value();
}
void QgsComposerImageExportOptionsDialog::setCropToContents(bool crop)
{
mClipToContentGroupBox->setChecked( crop );
}
bool QgsComposerImageExportOptionsDialog::cropToContents() const
{
return mClipToContentGroupBox->isChecked();
}
void QgsComposerImageExportOptionsDialog::getCropMargins(int& topMargin, int& rightMargin, int& bottomMargin, int& leftMargin) const
{
topMargin = mTopMarginSpinBox->value();
rightMargin = mRightMarginSpinBox->value();
bottomMargin = mBottomMarginSpinBox->value();
leftMargin = mLeftMarginSpinBox->value();
}
void QgsComposerImageExportOptionsDialog::setCropMargins(int topMargin, int rightMargin, int bottomMargin, int leftMargin)
{
mTopMarginSpinBox->setValue( topMargin );
mRightMarginSpinBox->setValue( rightMargin );
mBottomMarginSpinBox->setValue( bottomMargin );
mLeftMarginSpinBox->setValue( leftMargin );
}
void QgsComposerImageExportOptionsDialog::on_mWidthSpinBox_valueChanged(int value)
{
mHeightSpinBox->blockSignals( true );
mResolutionSpinBox->blockSignals( true );
mHeightSpinBox->setValue( mImageSize.height() * value / mImageSize.width() );
mResolutionSpinBox->setValue( value * 25.4 / mImageSize.width() );
mHeightSpinBox->blockSignals( false );
mResolutionSpinBox->blockSignals( false);
}
void QgsComposerImageExportOptionsDialog::on_mHeightSpinBox_valueChanged(int value)
{
mWidthSpinBox->blockSignals( true );
mResolutionSpinBox->blockSignals( true );
mWidthSpinBox->setValue( mImageSize.width() * value / mImageSize.height() );
mResolutionSpinBox->setValue( value * 25.4 / mImageSize.height() );
mWidthSpinBox->blockSignals( false );
mResolutionSpinBox->blockSignals( false);
}
void QgsComposerImageExportOptionsDialog::on_mResolutionSpinBox_valueChanged(int value)
{
mWidthSpinBox->blockSignals( true );
mHeightSpinBox->blockSignals( true );
mWidthSpinBox->setValue( mImageSize.width() * value / 25.4 );
mHeightSpinBox->setValue( mImageSize.height() * value / 25.4 );
mWidthSpinBox->blockSignals( false );
mHeightSpinBox->blockSignals( false);
}
void QgsComposerImageExportOptionsDialog::clipToContentsToggled(bool state)
{
mWidthSpinBox->setEnabled( !state );
mHeightSpinBox->setEnabled( !state );
if ( state )
{
mWidthSpinBox->blockSignals( true );
mWidthSpinBox->setValue( 0 );
mWidthSpinBox->blockSignals( false );
mHeightSpinBox->blockSignals( true );
mHeightSpinBox->setValue( 0 );
mHeightSpinBox->blockSignals( false );
}
else
{
mWidthSpinBox->blockSignals( true );
mWidthSpinBox->setValue( mImageSize.width() * mResolutionSpinBox->value() / 25.4 );
mWidthSpinBox->blockSignals( false );
mHeightSpinBox->blockSignals( true );
mHeightSpinBox->setValue( mImageSize.height() * mResolutionSpinBox->value() / 25.4 );
mHeightSpinBox->blockSignals( false );
}
}

View File

@ -0,0 +1,112 @@
/***************************************************************************
qgscomposerimageexportoptionsdialog.h
-------------------------------------
begin : September 2015
copyright : (C) 2015 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSCOMPOSERIMAGEEXPORTOPTIONSDIALOG_H
#define QGSCOMPOSERIMAGEEXPORTOPTIONSDIALOG_H
#include <QDialog>
#include "ui_qgscomposerimageexportoptions.h"
#include "qgscomposertablev2.h"
/** A dialog for customising the properties of an exported image file.
* /note added in QGIS 2.12
*/
class QgsComposerImageExportOptionsDialog: public QDialog, private Ui::QgsComposerImageExportOptionsDialog
{
Q_OBJECT
public:
/** Constructor for QgsComposerImageExportOptionsDialog
* @param parent parent widget
* @param flags window flags
*/
QgsComposerImageExportOptionsDialog( QWidget* parent = 0, Qt::WindowFlags flags = 0 );
~QgsComposerImageExportOptionsDialog();
/** Sets the initial resolution displayed in the dialog.
* @param resolution default resolution in DPI
* @see resolution()
*/
void setResolution( int resolution );
/** Returns the selected resolution from the dialog.
* @returns image resolution in DPI
* @see setResolution()
*/
int resolution() const;
/** Sets the target image size. This is used to calculate the default size in pixels
* and also for determining the image's width to height ratio.
* @param size image size
*/
void setImageSize( const QSizeF& size );
/** Returns the user-set image width in pixels.
* @see height
*/
int width() const;
/** Returns the user-set image height in pixels.
* @see width
*/
int height() const;
/** Sets whether the crop to contents option should be checked in the dialog
* @param crop set to true to check crop to contents
* @see cropToContents()
*/
void setCropToContents( bool crop );
/** Returns whether the crop to contents option is checked in the dialog.
* @see setCropToContents()
*/
bool cropToContents() const;
/** Fetches the current crop to contents margin values, in pixels.
* @param topMargin destination for top margin
* @param rightMargin destination for right margin
* @param bottomMargin destination for bottom margin
* @param leftMargin destination for left margin
*/
void getCropMargins( int& topMargin, int& rightMargin, int& bottomMargin, int& leftMargin ) const;
/** Sets the current crop to contents margin values, in pixels.
* @param topMargin top margin
* @param rightMargin right margin
* @param bottomMargin bottom margin
* @param leftMargin left margin
*/
void setCropMargins( int topMargin, int rightMargin, int bottomMargin, int leftMargin );
private slots:
void on_mWidthSpinBox_valueChanged( int value );
void on_mHeightSpinBox_valueChanged( int value );
void on_mResolutionSpinBox_valueChanged( int value );
void clipToContentsToggled( bool state );
private:
QSizeF mImageSize;
};
#endif // QGSCOMPOSERIMAGEEXPORTOPTIONSDIALOG_H

View File

@ -325,6 +325,34 @@ QRectF QgsComposition::compositionBounds( bool ignorePages, double margin ) cons
return bounds;
}
QRectF QgsComposition::pageItemBounds( int pageNumber, bool visibleOnly ) const
{
//start with an empty rectangle
QRectF bounds;
//add all QgsComposerItems on page
QList<QGraphicsItem *> itemList = items();
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
for ( ; itemIt != itemList.end(); ++itemIt )
{
const QgsComposerItem* composerItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
const QgsPaperItem* paperItem = dynamic_cast<const QgsPaperItem*>( *itemIt );
if ( composerItem && !paperItem && itemPageNumber( composerItem ) == pageNumber )
{
if ( visibleOnly && !composerItem->isVisible() )
continue;
//expand bounds with current item's bounds
if ( bounds.isValid() )
bounds = bounds.united(( *itemIt )->sceneBoundingRect() );
else
bounds = ( *itemIt )->sceneBoundingRect();
}
}
return bounds;
}
void QgsComposition::setPaperSize( const double width, const double height, bool keepRelativeItemPosition )
{
if ( width == mPageWidth && height == mPageHeight )
@ -2877,6 +2905,23 @@ QImage QgsComposition::printPageAsRaster( int page )
return image;
}
QImage QgsComposition::renderRectAsRaster( const QRectF& rect )
{
int width = ( int )( printResolution() * rect.width() / 25.4 );
int height = ( int )( printResolution() * rect.height() / 25.4 );
QImage image( QSize( width, height ), QImage::Format_ARGB32 );
if ( !image.isNull() )
{
image.setDotsPerMeterX( printResolution() / 25.4 * 1000 );
image.setDotsPerMeterY( printResolution() / 25.4 * 1000 );
image.fill( Qt::transparent );
QPainter imagePainter( &image );
renderRect( &imagePainter, rect );
if ( !imagePainter.isActive() ) return QImage();
}
return image;
}
void QgsComposition::renderPage( QPainter* p, int page )
{
if ( mPages.size() <= page )
@ -2890,21 +2935,25 @@ void QgsComposition::renderPage( QPainter* p, int page )
return;
}
QRectF paperRect = QRectF( paperItem->pos().x(), paperItem->pos().y(), paperItem->rect().width(), paperItem->rect().height() );
renderRect( p, paperRect );
}
void QgsComposition::renderRect( QPainter* p, const QRectF& rect )
{
QPaintDevice* paintDevice = p->device();
if ( !paintDevice )
{
return;
}
QRectF paperRect = QRectF( paperItem->pos().x(), paperItem->pos().y(), paperItem->rect().width(), paperItem->rect().height() );
QgsComposition::PlotStyle savedPlotStyle = mPlotStyle;
mPlotStyle = QgsComposition::Print;
setSnapLinesVisible( false );
//hide background before rendering
setBackgroundBrush( Qt::NoBrush );
render( p, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), paperRect );
render( p, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), rect );
//show background after rendering
setBackgroundBrush( QColor( 215, 215, 215 ) );
setSnapLinesVisible( true );
@ -2937,6 +2986,19 @@ QGraphicsView *QgsComposition::graphicsView() const
}
void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const
{
if ( !mWorldFileMap )
{
return;
}
int pageNumber = mWorldFileMap->page() - 1;
double pageY = pageNumber * ( mPageHeight + mSpaceBetweenPages );
QRectF pageRect( 0, pageY, mPageWidth, mPageHeight );
computeWorldFileParameters( pageRect, a, b, c, d, e, f );
}
void QgsComposition::computeWorldFileParameters( const QRectF& exportRegion, double& a, double& b, double& c, double& d, double& e, double& f ) const
{
// World file parameters : affine transformation parameters from pixel coordinates to map coordinates
@ -2945,8 +3007,8 @@ void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c
return;
}
double destinationHeight = paperHeight();
double destinationWidth = paperWidth();
double destinationHeight = exportRegion.height();
double destinationWidth = exportRegion.width();
QRectF mapItemSceneRect = mWorldFileMap->mapRectToScene( mWorldFileMap->rect() );
QgsRectangle mapExtent = *mWorldFileMap->currentMapExtent();
@ -2959,10 +3021,14 @@ void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c
double xCenter = mapExtent.center().x();
double yCenter = mapExtent.center().y();
// get the extent (in map units) for the page
QPointF mapItemPosOnPage = mWorldFileMap->pagePos();
double xmin = mapExtent.xMinimum() - mapItemPosOnPage.x() * xRatio;
double ymax = mapExtent.yMaximum() + mapItemPosOnPage.y() * yRatio;
// get the extent (in map units) for the region
QPointF mapItemPos = mWorldFileMap->pos();
//adjust item position so it is relative to export region
mapItemPos.rx() -= exportRegion.left();
mapItemPos.ry() -= exportRegion.top();
double xmin = mapExtent.xMinimum() - mapItemPos.x() * xRatio;
double ymax = mapExtent.yMaximum() + mapItemPos.y() * yRatio;
QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
double X0 = paperExtent.xMinimum();

View File

@ -664,18 +664,59 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
*/
bool exportAsPDF( const QString& file );
//! print composer page to image
//! If the image does not fit into memory, a null image is returned
/** Renders a composer page to an image.
* @param page page number, 0 based such that the first page is page 0
* @returns rendered image, or null image if image does not fit into available memory
* @see renderRectAsRaster()
* @see renderPage()
*/
QImage printPageAsRaster( int page );
/** Render a page to a paint device
/** Renders a portion of the composition to an image. This method can be used to render
* sections of pages rather than full pages.
* @param rect region of composition to render
* @returns rendered image, or null image if image does not fit into available memory
* @note added in QGIS 2.12
* @see printPageAsRaster()
* @see renderRect()
*/
QImage renderRectAsRaster( const QRectF& rect );
/** Renders a full page to a paint device.
* @param p destination painter
* @param page page number, 0 based such that the first page is page 0 */
* @param page page number, 0 based such that the first page is page 0
* @see renderRect()
* @see printPageAsRaster()
*/
void renderPage( QPainter* p, int page );
/** Compute world file parameters */
/** Renders a portion of the composition to a paint device. This method can be used
* to render sections of pages rather than full pages.
* @param p destination painter
* @param rect region of composition to render
* @note added in QGIS 2.12
* @see renderPage()
* @see renderRectAsRaster()
*/
void renderRect( QPainter* p, const QRectF& rect );
/** Compute world file parameters. Assumes the whole page containing the associated map item
* will be exported.
*/
void computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const;
/** Computes the world file parameters for a specified region of the composition.
* @param exportRegion region of the composition which will be associated with world file
* @param a
* @param b
* @param c
* @param d
* @param e
* @param f
* @note added in QGIS 2.12
*/
void computeWorldFileParameters( const QRectF& exportRegion, double& a, double& b, double& c, double& d, double& e, double& f ) const;
QgsAtlasComposition& atlasComposition() { return mAtlasComposition; }
/** Resizes a QRectF relative to the change from boundsBefore to boundsAfter
@ -766,6 +807,19 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
*/
QStringList customProperties() const;
/** Returns the bounding box of the items contained on a specified page.
* @param pageNumber page number, where 0 is the first page
* @param visibleOnly set to true to only include visible items
* @note added in QGIS 2.12
*/
QRectF pageItemBounds( int pageNumber, bool visibleOnly = false ) const;
/** Calculates the bounds of all non-gui items in the composition. Ignores snap lines and mouse handles.
* @param ignorePages set to true to ignore page items
* @param margin optional marginal (in percent, eg 0.05 = 5% ) to add around items
*/
QRectF compositionBounds( bool ignorePages = false, double margin = 0.0 ) const;
public slots:
/** Casts object to the proper subclass type and calls corresponding itemAdded signal*/
void sendItemAddedSignal( QgsComposerItem* item );
@ -894,12 +948,6 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
QgsComposition(); //default constructor is forbidden
/** Calculates the bounds of all non-gui items in the composition. Ignores snap lines and mouse handles.
* @param ignorePages set to true to ignore page items
* @param margin optional marginal (in percent, eg 0.05 = 5% ) to add around items
*/
QRectF compositionBounds( bool ignorePages = false, double margin = 0.0 ) const;
/** Reset z-values of items based on position in z list*/
void updateZValues( const bool addUndoCommands = true );

View File

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsComposerImageExportOptionsDialog</class>
<widget class="QDialog" name="QgsComposerImageExportOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>489</width>
<height>325</height>
</rect>
</property>
<property name="windowTitle">
<string>Image export options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Export options</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Export resolution</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Page height</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="2">
<widget class="QgsSpinBox" name="mResolutionSpinBox">
<property name="suffix">
<string> dpi</string>
</property>
<property name="prefix">
<string/>
</property>
<property name="maximum">
<number>3000</number>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QgsSpinBox" name="mWidthSpinBox">
<property name="specialValueText">
<string>Auto</string>
</property>
<property name="suffix">
<string> px</string>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>99999999</number>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Page width</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<widget class="QgsSpinBox" name="mHeightSpinBox">
<property name="specialValueText">
<string>Auto</string>
</property>
<property name="suffix">
<string> px</string>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>99999999</number>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="mClipToContentGroupBox">
<property name="title">
<string>Crop to content</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item>
<widget class="QgsSpinBox" name="mLeftMarginSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
<item>
<widget class="QgsSpinBox" name="mRightMarginSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Bottom</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Top margin</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QgsSpinBox" name="mTopMarginSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QgsSpinBox" name="mBottomMarginSpinBox">
<property name="suffix">
<string> px</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsSpinBox</class>
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
<customwidget>
<class>QgsCollapsibleGroupBoxBasic</class>
<extends>QGroupBox</extends>
<header location="global">qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mClipToContentGroupBox</tabstop>
<tabstop>mTopMarginSpinBox</tabstop>
<tabstop>mLeftMarginSpinBox</tabstop>
<tabstop>mRightMarginSpinBox</tabstop>
<tabstop>mBottomMarginSpinBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsComposerImageExportOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsComposerImageExportOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -6,56 +6,201 @@
<rect>
<x>0</x>
<y>0</y>
<width>471</width>
<height>103</height>
<width>489</width>
<height>282</height>
</rect>
</property>
<property name="windowTitle">
<string>SVG export options</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="chkMapLayersAsGroup">
<property name="text">
<string>Export map layers as svg groups (may affect label placement)</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkTextAsOutline">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Uncheck to render map labels as text objects. This will degrade the quality of the map labels but allow editing in vector illustration software.</string>
</property>
<property name="text">
<string>Render map labels as outlines</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="groupBox">
<property name="title">
<string>SVG options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="chkMapLayersAsGroup">
<property name="text">
<string>Export map layers as svg groups (may affect label placement)</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkTextAsOutline">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Uncheck to render map labels as text objects. This will degrade the quality of the map labels but allow editing in vector illustration software.</string>
</property>
<property name="text">
<string>Render map labels as outlines</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBoxBasic" name="mClipToContentGroupBox">
<property name="title">
<string>Crop to content</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="2">
<widget class="QgsDoubleSpinBox" name="mTopMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item>
<widget class="QgsDoubleSpinBox" name="mLeftMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
<item>
<widget class="QgsDoubleSpinBox" name="mRightMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Bottom</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QgsDoubleSpinBox" name="mBottomMarginSpinBox">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Top margin (mm)</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsCollapsibleGroupBoxBasic</class>
<extends>QGroupBox</extends>
<header location="global">qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>chkMapLayersAsGroup</tabstop>
<tabstop>chkTextAsOutline</tabstop>
<tabstop>mClipToContentGroupBox</tabstop>
<tabstop>mTopMarginSpinBox</tabstop>
<tabstop>mLeftMarginSpinBox</tabstop>
<tabstop>mRightMarginSpinBox</tabstop>
<tabstop>mBottomMarginSpinBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -201,6 +201,17 @@ void TestQgsComposerMap::worldFileGeneration()
QVERIFY( qgsDoubleNear( e, -4.17997, 0.001 ) );
QVERIFY( qgsDoubleNear( f, 3.34241e+06, 1e+03 ) );
//test computing parameters for specific region
mComposerMap->setItemPosition( 20, 20, QgsComposerItem::UpperLeft, 2 );
mComposition->computeWorldFileParameters( QRectF( 10, 5, 260, 200 ), a, b, c, d, e, f );
QVERIFY( qgsDoubleNear( a, 4.18061, 0.001 ) );
QVERIFY( qgsDoubleNear( b, 2.41321, 0.001 ) );
QVERIFY( qgsDoubleNear( c, 773810, 1 ) );
QVERIFY( qgsDoubleNear( d, 2.4137, 0.001 ) );
QVERIFY( qgsDoubleNear( e, -4.1798, 0.001 ) );
QVERIFY( qgsDoubleNear( f, 3.35331e+06, 1e+03 ) );
mComposition->setGenerateWorldFile( false );
mComposerMap->setMapRotation( 0.0 );

View File

@ -47,6 +47,7 @@ class TestQgsComposition : public QObject
void pageIsEmpty(); //test the pageIsEmpty method
void customProperties();
void writeRetrieveCustomProperties();
void bounds();
void resizeToContents();
void resizeToContentsMargin();
void resizeToContentsMultiPage();
@ -326,6 +327,64 @@ void TestQgsComposition::writeRetrieveCustomProperties()
delete readComposition;
}
void TestQgsComposition::bounds()
{
//add some items to a composition
QgsComposition* composition = new QgsComposition( *mMapSettings );
QgsComposerShape* shape1 = new QgsComposerShape( composition );
shape1->setShapeType( QgsComposerShape::Rectangle );
composition->addComposerShape( shape1 );
shape1->setItemPosition( 90, 50, 90, 50, QgsComposerItem::UpperLeft, false, 1 );
shape1->setItemRotation( 45 );
QgsComposerShape* shape2 = new QgsComposerShape( composition );
shape2->setShapeType( QgsComposerShape::Rectangle );
composition->addComposerShape( shape2 );
shape2->setItemPosition( 100, 150, 110, 50, QgsComposerItem::UpperLeft, false, 1 );
QgsComposerShape* shape3 = new QgsComposerShape( composition );
shape3->setShapeType( QgsComposerShape::Rectangle );
composition->addComposerShape( shape3 );
shape3->setItemPosition( 210, 30, 50, 100, QgsComposerItem::UpperLeft, false, 2 );
QgsComposerShape* shape4 = new QgsComposerShape( composition );
shape4->setShapeType( QgsComposerShape::Rectangle );
composition->addComposerShape( shape4 );
shape4->setItemPosition( 10, 120, 50, 30, QgsComposerItem::UpperLeft, false, 2 );
shape4->setVisibility( false );
//check bounds
QRectF compositionBounds = composition->compositionBounds( false );
QVERIFY( qgsDoubleNear( compositionBounds.height(), 372.15, 0.01 ) );
QVERIFY( qgsDoubleNear( compositionBounds.width(), 301.00, 0.01 ) );
QVERIFY( qgsDoubleNear( compositionBounds.left(), -2, 0.01 ) );
QVERIFY( qgsDoubleNear( compositionBounds.top(), -2, 0.01 ) );
QRectF compositionBoundsNoPage = composition->compositionBounds( true );
QVERIFY( qgsDoubleNear( compositionBoundsNoPage.height(), 320.36, 0.01 ) );
QVERIFY( qgsDoubleNear( compositionBoundsNoPage.width(), 250.30, 0.01 ) );
QVERIFY( qgsDoubleNear( compositionBoundsNoPage.left(), 9.85, 0.01 ) );
QVERIFY( qgsDoubleNear( compositionBoundsNoPage.top(), 49.79, 0.01 ) );
QRectF page1Bounds = composition->pageItemBounds( 0, true );
QVERIFY( qgsDoubleNear( page1Bounds.height(), 150.36, 0.01 ) );
QVERIFY( qgsDoubleNear( page1Bounds.width(), 155.72, 0.01 ) );
QVERIFY( qgsDoubleNear( page1Bounds.left(), 54.43, 0.01 ) );
QVERIFY( qgsDoubleNear( page1Bounds.top(), 49.79, 0.01 ) );
QRectF page2Bounds = composition->pageItemBounds( 1, true );
QVERIFY( qgsDoubleNear( page2Bounds.height(), 100.30, 0.01 ) );
QVERIFY( qgsDoubleNear( page2Bounds.width(), 50.30, 0.01 ) );
QVERIFY( qgsDoubleNear( page2Bounds.left(), 209.85, 0.01 ) );
QVERIFY( qgsDoubleNear( page2Bounds.top(), 249.85, 0.01 ) );
QRectF page2BoundsWithHidden = composition->pageItemBounds( 1, false );
QVERIFY( qgsDoubleNear( page2BoundsWithHidden.height(), 120.30, 0.01 ) );
QVERIFY( qgsDoubleNear( page2BoundsWithHidden.width(), 250.30, 0.01 ) );
QVERIFY( qgsDoubleNear( page2BoundsWithHidden.left(), 9.85, 0.01 ) );
QVERIFY( qgsDoubleNear( page2BoundsWithHidden.top(), 249.85, 0.01 ) );
delete composition;
}
void TestQgsComposition::resizeToContents()
{
//add some items to a composition