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; }
void QgsLayoutTable::recalculateFrameSizes() { mTableSize = QSizeF( totalWidth(), totalHeight() ); QgsLayoutMultiFrame::recalculateFrameSizes(); }
void QgsComposerTableV2::recalculateFrameSizes() { mTableSize = QSizeF( totalWidth(), totalHeight() ); QgsComposerMultiFrame::recalculateFrameSizes(); }
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]; } } }