Skip to content
Permalink
Browse files

[FEATURE][composer] Add fit page to contents option

New option in the composition panel, with optional extra margins
to add

Sponsored by NIWA
  • Loading branch information
nyalldawson committed Sep 25, 2015
1 parent 2215d48 commit 2f343008c1716aaddbe6a72784b5a4ac6fcf5949
@@ -54,14 +54,16 @@ class QgsComposition : QGraphicsScene

~QgsComposition();

/** Changes size of paper item. Also moves all items so that they retain
* their same relative position to the top left corner of their current page.
* @param width page width in mm
* @param height page height in mm
* @see paperHeight
* @see paperWidth
*/
void setPaperSize( const double width, const double height );
/** Changes size of paper item.
* @param width page width in mm
* @param height page height in mm
* @param keepRelativeItemPosition if true, all items and guides will be moved so that they retain
* their same relative position to the top left corner of their current page.
* @see paperHeight
* @see paperWidth
*/
void setPaperSize( double width, double height,
bool keepRelativeItemPosition = true );

/** Height of paper item
* @returns height in mm
@@ -77,6 +79,48 @@ class QgsComposition : QGraphicsScene
*/
double paperWidth() const;

/** Resizes the composition page to fit the current contents of the composition.
* Calling this method resets the number of pages to 1, with the size set to the
* minimum size required to fit all existing composer items. Items will also be
* repositioned so that the new top-left bounds of the composition is at the point
* (marginLeft, marginTop). An optional margin can be specified.
* @param marginTop top margin (millimeters)
* @param marginRight right margin (millimeters)
* @param marginBottom bottom margin (millimeters)
* @param marginLeft left margin (millimeters)
* @note added in QGIS 2.12
* @see setResizeToContentsMargins()
* @see resizeToContentsMargins()
*/
void resizePageToContents( double marginTop = 0.0, double marginRight = 0.0,
double marginBottom = 0.0, double marginLeft = 0.0 );

/** Sets the resize to contents margins. These margins are saved in the composition
* so that they can be restored with the composer.
* @param marginTop top margin (millimeters)
* @param marginRight right margin (millimeters)
* @param marginBottom bottom margin (millimeters)
* @param marginLeft left margin (millimeters)
* @note added in QGIS 2.12
* @see resizePageToContents()
* @see resizeToContentsMargins()
*/
void setResizeToContentsMargins( double marginTop, double marginRight,
double marginBottom, double marginLeft );

/** Returns the resize to contents margins. These margins are saved in the composition
* so that they can be restored with the composer.
* @param marginTop reference for top margin (millimeters)
* @param marginRight reference for right margin (millimeters)
* @param marginBottom reference for bottom margin (millimeters)
* @param marginLeft reference for left margin (millimeters)
* @note added in QGIS 2.12
* @see resizePageToContents()
* @see setResizeToContentsMargins()
*/
void resizeToContentsMargins( double& marginTop /Out/, double& marginRight /Out/,
double& marginBottom /Out/, double& marginLeft /Out/ ) const;

/** Returns the vertical space between pages in a composer view
* @returns space between pages in mm
*/
@@ -63,6 +63,16 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c )
//read printout resolution from composition
mResolutionSpinBox->setValue( mComposition->printResolution() );

double topMargin = 0;
double rightMargin = 0;
double bottomMargin = 0;
double leftMargin = 0;
mComposition->resizeToContentsMargins( topMargin, rightMargin, bottomMargin, leftMargin );
mTopMarginSpinBox->setValue( topMargin );
mRightMarginSpinBox->setValue( rightMargin );
mBottomMarginSpinBox->setValue( bottomMargin );
mLeftMarginSpinBox->setValue( leftMargin );

//print as raster
mPrintAsRasterCheckBox->setChecked( mComposition->printAsRaster() );

@@ -105,6 +115,11 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c )
}
}

connect( mTopMarginSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( resizeMarginsChanged() ) );
connect( mRightMarginSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( resizeMarginsChanged() ) );
connect( mBottomMarginSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( resizeMarginsChanged() ) );
connect( mLeftMarginSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( resizeMarginsChanged() ) );

connect( mPaperSizeDDBtn, SIGNAL( dataDefinedChanged( const QString& ) ), this, SLOT( updateDataDefinedProperty() ) );
connect( mPaperSizeDDBtn, SIGNAL( dataDefinedActivated( bool ) ), this, SLOT( updateDataDefinedProperty() ) );
connect( mPaperSizeDDBtn, SIGNAL( dataDefinedActivated( bool ) ), mPaperSizeComboBox, SLOT( setDisabled( bool ) ) );
@@ -195,6 +210,17 @@ void QgsCompositionWidget::variablesChanged()
QgsExpressionContextUtils::setCompositionVariables( mComposition, mVariableEditor->variablesInActiveScope() );
}

void QgsCompositionWidget::resizeMarginsChanged()
{
if ( !mComposition )
return;

mComposition->setResizeToContentsMargins( mTopMarginSpinBox->value(),
mRightMarginSpinBox->value(),
mBottomMarginSpinBox->value(),
mLeftMarginSpinBox->value() );
}

void QgsCompositionWidget::setDataDefinedProperty( const QgsDataDefinedButton* ddBtn, QgsComposerObject::DataDefinedProperty property )
{
if ( !mComposition )
@@ -567,6 +593,19 @@ void QgsCompositionWidget::on_mPageStyleButton_clicked()
delete newSymbol;
}

void QgsCompositionWidget::on_mResizePageButton_clicked()
{
if ( !mComposition )
{
return;
}

mComposition->resizePageToContents( mTopMarginSpinBox->value(),
mRightMarginSpinBox->value(),
mBottomMarginSpinBox->value(),
mLeftMarginSpinBox->value() );
}

void QgsCompositionWidget::updatePageStyle()
{
if ( mComposition )
@@ -50,6 +50,7 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase
void on_mPaperHeightDoubleSpinBox_editingFinished();
void on_mNumPagesSpinBox_valueChanged( int value );
void on_mPageStyleButton_clicked();
void on_mResizePageButton_clicked();
void on_mResolutionSpinBox_valueChanged( const int value );
void on_mPrintAsRasterCheckBox_toggled( bool state );
void on_mGenerateWorldFileCheckBox_toggled( bool state );
@@ -85,6 +86,8 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase

void variablesChanged();

void resizeMarginsChanged();

private:
QgsComposition* mComposition;
QMap<QString, QgsCompositionPaper> mPaperMap;
@@ -105,6 +105,11 @@ void QgsComposition::init()
mItemsModel = 0;
mUndoStack = new QUndoStack();

mResizeToContentsMarginTop = 0;
mResizeToContentsMarginRight = 0;
mResizeToContentsMarginBottom = 0;
mResizeToContentsMarginLeft = 0;

//data defined strings
mDataDefinedNames.insert( QgsComposerObject::PresetPaperSize, QString( "dataDefinedPaperSize" ) );
mDataDefinedNames.insert( QgsComposerObject::PaperWidth, QString( "dataDefinedPaperWidth" ) );
@@ -220,7 +225,7 @@ void QgsComposition::loadDefaults()

void QgsComposition::updateBounds()
{
setSceneRect( compositionBounds() );
setSceneRect( compositionBounds( false, 0.05 ) );
}

void QgsComposition::refreshItems()
@@ -289,10 +294,10 @@ void QgsComposition::refreshDataDefinedProperty( const QgsComposerObject::DataDe
}
}

QRectF QgsComposition::compositionBounds() const
QRectF QgsComposition::compositionBounds( bool ignorePages, double margin ) const
{
//start with an empty rectangle
QRectF bounds = QRectF( 0, 0, 0, 0 );
QRectF bounds;

//add all QgsComposerItems and QgsPaperItems which are in the composition
QList<QGraphicsItem *> itemList = items();
@@ -301,37 +306,47 @@ QRectF QgsComposition::compositionBounds() const
{
const QgsComposerItem* composerItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
const QgsPaperItem* paperItem = dynamic_cast<const QgsPaperItem*>( *itemIt );
if (( composerItem || paperItem ) )
if (( composerItem && ( !paperItem || !ignorePages ) ) )
{
//expand bounds with current item's bounds
bounds = bounds.united(( *itemIt )->sceneBoundingRect() );
if ( bounds.isValid() )
bounds = bounds.united(( *itemIt )->sceneBoundingRect() );
else
bounds = ( *itemIt )->sceneBoundingRect();
}
}

//finally, expand bounds out by 5% page size to give a bit of a margin
bounds.adjust( -mPageWidth * 0.05, -mPageWidth * 0.05, mPageWidth * 0.05, mPageWidth * 0.05 );
if ( bounds.isValid() && margin > 0.0 )
{
//finally, expand bounds out by specified margin of page size
bounds.adjust( -mPageWidth * margin, -mPageWidth * margin, mPageWidth * margin, mPageWidth * margin );
}

return bounds;
}

void QgsComposition::setPaperSize( const double width, const double height )
void QgsComposition::setPaperSize( const double width, const double height, bool keepRelativeItemPosition )
{
if ( width == mPageWidth && height == mPageHeight )
{
return;
}

//update item positions
QList<QGraphicsItem *> itemList = items();
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
for ( ; itemIt != itemList.end(); ++itemIt )
if ( keepRelativeItemPosition )
{
QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIt );
if ( composerItem )
//update item positions
QList<QGraphicsItem *> itemList = items();
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
for ( ; itemIt != itemList.end(); ++itemIt )
{
composerItem->updatePagePos( width, height );
QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( *itemIt );
if ( composerItem )
{
composerItem->updatePagePos( width, height );
}
}
}

//update guide positions and size
QList< QGraphicsLineItem* >* guides = snapLines();
QList< QGraphicsLineItem* >::iterator guideIt = guides->begin();
@@ -347,11 +362,19 @@ void QgsComposition::setPaperSize( const double width, const double height )
else
{
//horizontal line
//move to new vertical position and change width of line
QPointF curPagePos = positionOnPage( line.p1() );
int curPage = pageNumberForPoint( line.p1() ) - 1;
double newY = curPage * ( height + spaceBetweenPages() ) + curPagePos.y();
( *guideIt )->setLine( 0, newY, width, newY );
if ( keepRelativeItemPosition )
{
//move to new vertical position and change width of line
QPointF curPagePos = positionOnPage( line.p1() );
int curPage = pageNumberForPoint( line.p1() ) - 1;
double newY = curPage * ( height + spaceBetweenPages() ) + curPagePos.y();
( *guideIt )->setLine( 0, newY, width, newY );
}
else
{
//just resize guide to new page size
( *guideIt )->setLine( 0, line.y1(), width, line.y1() );
}
}
}

@@ -378,6 +401,66 @@ double QgsComposition::paperWidth() const
return mPageWidth;
}

void QgsComposition::resizePageToContents( double marginTop, double marginRight, double marginBottom, double marginLeft )
{
//calculate current bounds
QRectF bounds = compositionBounds( true, 0.0 );

setNumPages( 1 );
double newWidth = bounds.width() + marginLeft + marginRight;
double newHeight = bounds.height() + marginTop + marginBottom;
setPaperSize( newWidth, newHeight, false );

//also move all items so that top-left of bounds is at marginLeft, marginTop
double diffX = marginLeft - bounds.left();
double diffY = marginTop - bounds.top();

QList<QGraphicsItem *> itemList = items();
Q_FOREACH ( QGraphicsItem* item, itemList )
{
QgsComposerItem* composerItem = dynamic_cast<QgsComposerItem *>( item );
if ( composerItem )
{
const QgsPaperItem* paperItem = dynamic_cast<const QgsPaperItem*>( item );

if ( !paperItem )
composerItem->move( diffX, diffY );
}
}

//also move guides
Q_FOREACH ( QGraphicsLineItem* guide, mSnapLines )
{
QLineF line = guide->line();
if ( line.dx() == 0 )
{
//vertical line
guide->setLine( line.x1() + diffX, 0, line.x1() + diffX, newHeight );
}
else
{
//horizontal line
guide->setLine( 0, line.y1() + diffY, newWidth, line.y1() + diffY );
}
}
}

void QgsComposition::setResizeToContentsMargins( double marginTop, double marginRight, double marginBottom, double marginLeft )
{
mResizeToContentsMarginTop = marginTop;
mResizeToContentsMarginRight = marginRight;
mResizeToContentsMarginBottom = marginBottom;
mResizeToContentsMarginLeft = marginLeft;
}

void QgsComposition::resizeToContentsMargins( double& marginTop, double& marginRight, double& marginBottom, double& marginLeft ) const
{
marginTop = mResizeToContentsMarginTop;
marginRight = mResizeToContentsMarginRight;
marginBottom = mResizeToContentsMarginBottom;
marginLeft = mResizeToContentsMarginLeft;
}

void QgsComposition::setNumPages( const int pages )
{
int currentPages = numPages();
@@ -830,6 +913,11 @@ bool QgsComposition::writeXML( QDomElement& composerElem, QDomDocument& doc )
compositionElem.setAttribute( "smartGuides", mSmartGuides ? 1 : 0 );
compositionElem.setAttribute( "snapTolerancePixels", mSnapTolerance );

compositionElem.setAttribute( "resizeToContentsMarginTop", mResizeToContentsMarginTop );
compositionElem.setAttribute( "resizeToContentsMarginRight", mResizeToContentsMarginRight );
compositionElem.setAttribute( "resizeToContentsMarginBottom", mResizeToContentsMarginBottom );
compositionElem.setAttribute( "resizeToContentsMarginLeft", mResizeToContentsMarginLeft );

//save items except paper items and frame items (they are saved with the corresponding multiframe)
QList<QGraphicsItem*> itemList = items();
QList<QGraphicsItem*>::const_iterator itemIt = itemList.constBegin();
@@ -906,6 +994,11 @@ bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocu
mSmartGuides = compositionElem.attribute( "smartGuides", "1" ).toInt() == 0 ? false : true;
mSnapTolerance = compositionElem.attribute( "snapTolerancePixels", "10" ).toInt();

mResizeToContentsMarginTop = compositionElem.attribute( "resizeToContentsMarginTop", "0" ).toDouble();
mResizeToContentsMarginRight = compositionElem.attribute( "resizeToContentsMarginRight", "0" ).toDouble();
mResizeToContentsMarginBottom = compositionElem.attribute( "resizeToContentsMarginBottom", "0" ).toDouble();
mResizeToContentsMarginLeft = compositionElem.attribute( "resizeToContentsMarginLeft", "0" ).toDouble();

//custom snap lines
QDomNodeList snapLineNodes = compositionElem.elementsByTagName( "SnapLine" );
for ( int i = 0; i < snapLineNodes.size(); ++i )

0 comments on commit 2f34300

Please sign in to comment.
You can’t perform that action at this time.