qreal KDReports::SpreadsheetReportLayout::idealWidth()
{
    m_tableLayout.setInitialFontScalingFactor( m_userRequestedFontScalingFactor );
    QAbstractItemModel* model = m_tableLayout.m_model;
    if ( !model )
        return -1;
    m_tableLayout.updateColumnWidths();
    const qreal total = totalWidth();
#ifdef DEBUG_LAYOUT
    qDebug() << "idealWidth:" << total << "pixels";
#endif
    return total;
}
void KDReports::SpreadsheetReportLayout::ensureLayouted()
{
    if ( !m_layoutDirty )
        return;
    if ( m_pageContentSize.isEmpty() ) {
        qWarning( "No paper size specified!" );
        return;
    }

    m_tableLayout.setInitialFontScalingFactor( m_userRequestedFontScalingFactor );
    m_pageRects.clear();

    QAbstractItemModel* model = m_tableLayout.m_model;
    if ( !model )
        return;

    // Here's the whole layouting logic

    // Step 1: determine "ideal" column widths, based on contents

    m_tableLayout.updateColumnWidths();

    // Step 2: based on that and the number of horiz pages wanted,
    //         determine actual column widths (horizontal table breaking)

    KDReports::TableBreakingLogic optimizer;
    optimizer.setColumnWidths( m_tableLayout.m_columnWidths );
    optimizer.setPageCount( m_numHorizontalPages );
    const QVector<int> columnsPerPage = optimizer.columnsPerPage();
    QVector<qreal> widthPerPage = optimizer.widthPerPage( columnsPerPage );
    const int horizPages = columnsPerPage.count();
    bool scaled = false;

    // Step 3: check everything fits horizontally, otherwise calculate font scaling factor for this

    const qreal horizMargins = 0 /*m_leftMargin*/ + 0 /*m_rightMargin*/;
    const qreal verticalMargins = 0 /*m_topMargin*/ + 0 /*m_bottomMargin*/;
    const qreal usablePageWidth = m_pageContentSize.width() - horizMargins;
    const qreal usablePageHeight = m_pageContentSize.height() - verticalMargins - m_tableLayout.hHeaderHeight();

#ifdef DEBUG_LAYOUT
    qDebug() << "usablePageHeight=" << m_pageContentSize.height() << "minus hHeaderHeight" << m_tableLayout.hHeaderHeight();
#endif

    // for each page, if (sum of column widths) > usablePageWidth,
    // then we need to scale everything (font and padding)
    // by the ratio of those two numbers.
    qreal bestScalingFactor = 1000000;
    for ( int page = 0; page < horizPages; ++page ) {
        const qreal width = widthPerPage[page] + m_tableLayout.vHeaderWidth();
        if ( width > usablePageWidth ) {
            const qreal scalingFactor = usablePageWidth / width;
#ifdef DEBUG_LAYOUT
            qDebug() << "page" << page << "sum of column widths:" << width
                     << "usablePageWidth=" << usablePageWidth;
            qDebug() << "scaling factor so that it fits horizontally:" << scalingFactor;
#endif
            bestScalingFactor = qMin(bestScalingFactor, scalingFactor);
            scaled = true;
        }
    }

    if (scaled) {
        m_tableLayout.ensureScalingFactorForWidth( bestScalingFactor );
    }

    // Step 4: check everything fits vertically, otherwise calculate font scaling factor for this

    const int rowCount = m_tableLayout.m_model->rowCount();
    if ( m_numVerticalPages > 0 ) {
        const qreal rowHeight = m_tableLayout.rowHeight();

        // We can't do a global division of heights, it assumes rows can be over page borders, partially truncated
        // const qreal maxTotalHeight = m_numVerticalPages  * usablePageHeight;
        // const qreal maxRowHeight = maxTotalHeight / rowCount;

        // Example: 5 rows over 2 pages, and the usablePageHeight is 100. What do you do?
        // 2.5 rows per page means truncation. 2 rows per page (as in the division above) is not enough.
        // The right solution is qCeil, i.e. max 3 rows per page, and maxRowHeight = 100 / 3 = 33.3.

        const int maxRowsPerPage = qCeil( static_cast<qreal>(rowCount) / m_numVerticalPages );
        const qreal maxRowHeight = usablePageHeight / maxRowsPerPage;
#ifdef DEBUG_LAYOUT
        qDebug() << "usablePageHeight=" << usablePageHeight
                 << "rowHeight=" << rowHeight
                 << "maxRowsPerPage=" << maxRowsPerPage
                 << "maxRowHeight=" << usablePageHeight << "/" << maxRowsPerPage << "=" << maxRowHeight;
#endif
        if ( rowHeight > maxRowHeight ) { // more than authorized maximum
            m_tableLayout.ensureScalingFactorForHeight( maxRowHeight );
            scaled = true;
        }
    }

    // Step 5: update font and calculations based on final font scaling
    if (scaled) {
#ifdef DEBUG_LAYOUT
        qDebug() << "final scaling factor" << m_tableLayout.scalingFactor();
        qDebug() << "final fonts: cells:" << m_tableLayout.scaledFont().pointSizeF()
                << "hHeader:" << m_tableLayout.horizontalHeaderScaledFont().pointSizeF()
                << "vHeader:" << m_tableLayout.verticalHeaderScaledFont().pointSizeF();
#endif
        // With this new scaling factor [when step 4 changed the factor], what should be the column widths?
        // If we just call
        // m_tableLayout.updateColumnWidthsByFactor( m_tableLayout.scalingFactor() / m_userRequestedFontScalingFactor );
        // then we risk truncating column text (because fonts are not proportional).
        // Testcase: LongReport with font size 8, padding 3, 10 columns, 300 rows, and scaleTo(1,10) (or none);
        m_tableLayout.updateColumnWidths();

#ifdef DEBUG_LAYOUT
        qDebug() << "New total width:" << totalWidth();
#endif

#if 0 // not used right now, but could be useful, especially if we want to goto step 3 again, to resize down
        // Update the widthPerPage array
        int column = 0;
        for ( int page = 0; page < horizPages; ++page ) {
            const int numColumnsInPage = columnsPerPage[page];
            widthPerPage[page] = 0;
            for ( int col = column; col < column + numColumnsInPage; ++col) {
                widthPerPage[page] += m_tableLayout.m_columnWidths[col];
            }

            const qreal width = widthPerPage[page] + m_tableLayout.vHeaderWidth();
            if ( width > usablePageWidth ) {
                qWarning() << "Too much width on page" << page;
            }

            column += numColumnsInPage;
        }
        qDebug() << "widthPerPage:" << widthPerPage;
#endif
    }

    const qreal rowHeight = m_tableLayout.rowHeight(); // do it now so that the scaling is included

    // Step 6: determine number of pages for all rows to fit

    const int maxRowsPerPage = qFloor(usablePageHeight / rowHeight); // no qCeil here, the last row would be truncated...
    int verticPages = qCeil( qreal( rowCount ) / qreal( maxRowsPerPage ) );

#ifdef DEBUG_LAYOUT
    qDebug() << "maxRowsPerPage=" << usablePageHeight << "/" << rowHeight << "=" <<  maxRowsPerPage;
    qDebug() << "pages:" << horizPages << "x" << verticPages;
    qDebug() << "verticPages = qCeil(" << rowCount << "/" << maxRowsPerPage << ") =" << verticPages;
#endif

    // avoid rounding problems (or the font not zooming down enough vertically),
    // obey m_numVerticalPages in all cases
    if ( m_numVerticalPages > 0 ) {
        Q_ASSERT( verticPages <= m_numVerticalPages );
        //    verticPages = qMin( m_numVerticalPages, verticPages );
    }

    // Step 7: now we can record all this in terms of cell areas

    if ( m_tableBreakingPageOrder == Report::RightThenDown ) {
        //qDebug() << "Doing right then down layout";
        int row = 0;
        for ( int y = 0; y < verticPages; ++y ) {
            int column = 0;
            const int numRowsInPage = qMin( maxRowsPerPage, rowCount - row );
            for ( int x = 0; x < horizPages; ++x ) {
                const int numColumnsInPage = columnsPerPage[x];
                m_pageRects.append( QRect( column, row, numColumnsInPage, numRowsInPage ) );
                column += numColumnsInPage;
            }
            row += maxRowsPerPage;
        }
    } else {
        //qDebug() << "Doing down then right layout";
        int column = 0;
        for ( int x = 0; x < horizPages; ++x ) {
            int row = 0;
            const int numColumnsInPage = columnsPerPage[x];
            for ( int y = 0; y < verticPages; ++y ) {
                const int numRowsInPage = qMin( maxRowsPerPage, rowCount - row );
                m_pageRects.append( QRect( column, row, numColumnsInPage, numRowsInPage ) );
                row += maxRowsPerPage;
            }
            column += numColumnsInPage;
        }
    }

    m_layoutDirty = false;
}
Esempio n. 3
0
void QgsLayoutTable::recalculateFrameSizes()
{
  mTableSize = QSizeF( totalWidth(), totalHeight() );
  QgsLayoutMultiFrame::recalculateFrameSizes();
}
Esempio n. 4
0
void QgsComposerTableV2::recalculateFrameSizes()
{
  mTableSize = QSizeF( totalWidth(), totalHeight() );
  QgsComposerMultiFrame::recalculateFrameSizes();
}
Esempio n. 5
0
void allocateRespected(SEXP layout, 
		       int *relativeWidths, int *relativeHeights,
		       double *reducedWidthCM, double *reducedHeightCM,
		       LViewportContext parentContext,
		       const pGEcontext parentgc,
		       pGEDevDesc dd,
		       double *npcWidths, double *npcHeights)
{
    int i;
    SEXP widths = layoutWidths(layout);
    SEXP heights = layoutHeights(layout);
    int respect = layoutRespect(layout);
    double sumWidth = totalWidth(layout, relativeWidths, parentContext,
				 parentgc, dd);
    double sumHeight = totalHeight(layout, relativeHeights, parentContext,
				   parentgc, dd);
    double denom, mult;
    double tempWidthCM = *reducedWidthCM;
    double tempHeightCM = *reducedHeightCM;
    if (respect > 0) {
	/* Determine whether aspect ratio of available space is
	 * bigger or smaller than aspect ratio of layout
	 */
	// NB: widths could be zero
	// if ((tempHeightCM / tempWidthCM) > (sumHeight / sumWidth)) {
	if ( tempHeightCM * sumWidth > sumHeight * tempWidthCM) {
	    denom = sumWidth;
	    mult = tempWidthCM;
	}
	else {
	    denom = sumHeight;
	    mult = tempHeightCM;
	}
	/* Allocate respected widths 
	 */
	for (i=0; i<layoutNCol(layout); i++)
	    if (relativeWidths[i])
		if (colRespected(i, layout)) {
		    /* 
		     * Special case of respect, but sumHeight = 0.
		     * Action is to allocate widths as if unrespected.
		     * Ok to test == 0 because will only be 0 if 
		     * all relative heights are actually exactly 0.
		     */
		    if (sumHeight == 0) {
			denom = sumWidth;
			mult = tempWidthCM;
		    }
		    /* Build a unit SEXP with a single value and no data
		     */
		    npcWidths[i] = pureNullUnitValue(widths, i) / 
                        denom*mult;
		    *reducedWidthCM -= npcWidths[i];
		}
	/* Allocate respected heights
	 */
	for (i=0; i<layoutNRow(layout); i++)
	    if (relativeHeights[i])
		if (rowRespected(i, layout)) {
		    /* 
		     * Special case of respect, but sumWidth = 0.
		     * Action is to allocate widths as if unrespected.
		     * Ok to test == 0 because will only be 0 if 
		     * all relative heights are actually exactly 0.
		     */
		    if (sumWidth == 0) {
			denom = sumHeight;
			mult = tempHeightCM;
		    }
		    npcHeights[i] = pureNullUnitValue(heights, i) / 
                        denom*mult;
		    *reducedHeightCM -= npcHeights[i];
		}
    }
}