📄 qtextdocumentlayout.cpp
字号:
painter->restore();}static QFixed flowPosition(const QTextFrame::iterator it){ if (it.atEnd()) return 0; if (it.currentFrame()) { return data(it.currentFrame())->position.y; } else { QTextBlock block = it.currentBlock(); QTextLayout *layout = block.layout(); if (layout->lineCount() == 0) return QFixed::fromReal(layout->position().y()); else return QFixed::fromReal(layout->position().y() + layout->lineAt(0).y()); }}static QFixed firstChildPos(const QTextFrame *f){ return flowPosition(f->begin());}QLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QTextTableCell &cell, QFixed width, int layoutFrom, int layoutTo, QTextTableData *td, QFixed absoluteTableY, bool withPageBreaks){ Q_Q(QTextDocumentLayout); LDEBUG << "layoutCell"; QLayoutStruct layoutStruct; layoutStruct.frame = t; layoutStruct.minimumWidth = 0; layoutStruct.maximumWidth = QFIXED_MAX; layoutStruct.y = 0; if (withPageBreaks) { layoutStruct.frameY = absoluteTableY + td->rowPositions.at(cell.row()) + td->cellPadding; } layoutStruct.x_left = 0; layoutStruct.x_right = width; // we get called with different widths all the time (for example for figuring // out the min/max widths), so we always have to do the full layout ;( // also when for example in a table layoutFrom/layoutTo affect only one cell, // making that one cell grow the available width of the other cells may change // (shrink) and therefore when layoutCell gets called for them they have to // be re-laid out, even if layoutFrom/layoutTo is not in their range. Hence // this line: layoutStruct.pageHeight = QFixed::fromReal(q->document()->pageSize().height()); if (layoutStruct.pageHeight < 0 || !withPageBreaks) layoutStruct.pageHeight = QFIXED_MAX; const int currentPage = layoutStruct.currentPage(); layoutStruct.pageTopMargin = td->effectiveTopMargin + td->cellSpacing + td->border + td->cellPadding; layoutStruct.pageBottomMargin = td->effectiveBottomMargin + td->cellSpacing + td->border + td->cellPadding; layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin; layoutStruct.fullLayout = true; QFixed pageTop = currentPage * layoutStruct.pageHeight + layoutStruct.pageTopMargin - layoutStruct.frameY; layoutStruct.y = qMax(layoutStruct.y, pageTop); const QList<QTextFrame *> childFrames = td->childFrameMap.values(cell.row() + cell.column() * t->rows()); for (int i = 0; i < childFrames.size(); ++i) { QTextFrame *frame = childFrames.at(i); QTextFrameData *cd = data(frame); cd->sizeDirty = true; } layoutFlow(cell.begin(), &layoutStruct, layoutFrom, layoutTo, width); QFixed floatMinWidth; // floats that are located inside the text (like inline images) aren't taken into account by // layoutFlow with regards to the cell height (layoutStruct->y), so for a safety measure we // do that here. For example with <td><img align="right" src="..." />blah</td> // when the image happens to be higher than the text for (int i = 0; i < childFrames.size(); ++i) { QTextFrame *frame = childFrames.at(i); QTextFrameData *cd = data(frame); if (frame->frameFormat().position() != QTextFrameFormat::InFlow) layoutStruct.y = qMax(layoutStruct.y, cd->position.y + cd->size.height); floatMinWidth = qMax(floatMinWidth, cd->minimumWidth); } // constraint the maximumWidth by the minimum width of the fixed size floats, to // keep them visible layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth); // as floats in cells get added to the table's float list but must not affect // floats in other cells we must clear the list here. data(t)->floats.clear();// qDebug() << "layoutCell done"; return layoutStruct;}QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo, QFixed parentY){ LDEBUG << "layoutTable"; QTextTableData *td = static_cast<QTextTableData *>(data(table)); Q_ASSERT(td->sizeDirty); const int rows = table->rows(); const int columns = table->columns(); const QTextTableFormat fmt = table->format(); td->childFrameMap.clear(); { const QList<QTextFrame *> children = table->childFrames(); for (int i = 0; i < children.count(); ++i) { QTextFrame *frame = children.at(i); QTextTableCell cell = table->cellAt(frame->firstPosition()); td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame); } } QVector<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints(); if (columnWidthConstraints.size() != columns) columnWidthConstraints.resize(columns); Q_ASSERT(columnWidthConstraints.count() == columns); const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(fmt.cellSpacing())); td->cellPadding = QFixed::fromReal(scaleToDevice(fmt.cellPadding())); const QFixed leftMargin = td->leftMargin + td->border + td->padding; const QFixed rightMargin = td->rightMargin + td->border + td->padding; const QFixed topMargin = td->topMargin + td->border + td->padding; const QFixed absoluteTableY = parentY + td->position.y; QFixed totalWidth = td->contentsWidth; // two (vertical) borders per cell per column totalWidth -= columns * 2 * td->border; // inter-cell spacing totalWidth -= (columns - 1) * cellSpacing; // cell spacing at the left and right hand side totalWidth -= 2 * cellSpacing; // remember the width used to distribute to percentaged columns QFixed initialTotalWidth = totalWidth; td->widths.resize(columns); td->widths.fill(0); td->minWidths.resize(columns); // start with a minimum width of 0. totally empty // cells of default created tables are invisible otherwise // and therefore hardly editable td->minWidths.fill(1); td->maxWidths.resize(columns); td->maxWidths.fill(QFIXED_MAX); // calculate minimum and maximum sizes of the columns for (int i = 0; i < columns; ++i) { for (int row = 0; row < rows; ++row) { const QTextTableCell cell = table->cellAt(row, i); const int cspan = cell.columnSpan(); if (cspan > 1 && i != cell.column()) continue; // to figure out the min and the max width lay out the cell at // maximum width. otherwise the maxwidth calculation sometimes // returns wrong values QLayoutStruct layoutStruct = layoutCell(table, cell, QFIXED_MAX, layoutFrom, layoutTo, td, absoluteTableY, /*withPageBreaks =*/false); // distribute the minimum width over all columns the cell spans QFixed widthToDistribute = layoutStruct.minimumWidth + 2 * td->cellPadding; for (int n = 0; n < cspan; ++n) { const int col = i + n; QFixed w = widthToDistribute / (cspan - n); td->minWidths[col] = qMax(td->minWidths.at(col), w); widthToDistribute -= td->minWidths.at(col); if (widthToDistribute <= 0) break; } QFixed maxW = td->maxWidths.at(i); if (layoutStruct.maximumWidth != QFIXED_MAX) { if (maxW == QFIXED_MAX) maxW = layoutStruct.maximumWidth + 2 * td->cellPadding; else maxW = qMax(maxW, layoutStruct.maximumWidth + 2 * td->cellPadding); } if (maxW == QFIXED_MAX) continue; widthToDistribute = maxW; for (int n = 0; n < cspan; ++n) { const int col = i + n; QFixed w = widthToDistribute / (cspan - n); td->maxWidths[col] = qMax(td->minWidths.at(col), w); widthToDistribute -= td->maxWidths.at(col); if (widthToDistribute <= 0) break; } } } // set fixed values, figure out total percentages used and number of // variable length cells. Also assign the minimum width for variable columns. QFixed totalPercentage; int variableCols = 0; for (int i = 0; i < columns; ++i) { const QTextLength &length = columnWidthConstraints.at(i); if (length.type() == QTextLength::FixedLength) { td->minWidths[i] = td->widths[i] = qMax(QFixed::fromReal(length.rawValue()), td->minWidths.at(i)); totalWidth -= td->widths.at(i); } else if (length.type() == QTextLength::PercentageLength) { totalPercentage += QFixed::fromReal(length.rawValue()); } else if (length.type() == QTextLength::VariableLength) { variableCols++; td->widths[i] = td->minWidths.at(i); totalWidth -= td->minWidths.at(i); } } // set percentage values { QFixed minimumSlack = QFIXED_MAX; int colsWithSlack = 0; QFixed overhangingPercent; const QFixed totalPercentagedWidth = initialTotalWidth * totalPercentage / 100; for (int i = 0; i < columns; ++i) if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) { const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue()); const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage; if (percentWidth >= td->minWidths.at(i)) { td->widths[i] = percentWidth; if (td->widths.at(i) > td->minWidths.at(i)) { ++colsWithSlack; minimumSlack = qMin(minimumSlack, td->widths.at(i) - td->minWidths.at(i)); } } else { td->widths[i] = td->minWidths.at(i); QFixed effectivePercentage = td->widths.at(i) * 100 / totalPercentagedWidth; overhangingPercent += effectivePercentage - allottedPercentage; } totalWidth -= td->widths.at(i); } // subtract the overhanging percent from columns with slack while (colsWithSlack > 0 && overhangingPercent > 0) { const QFixed slackToSubtract = qMin(minimumSlack, totalPercentagedWidth * overhangingPercent / (colsWithSlack * totalPercentage)); overhangingPercent -= (slackToSubtract * colsWithSlack * 100) / totalPercentagedWidth; minimumSlack = QFIXED_MAX; colsWithSlack = 0; for (int i = 0; i < columns; ++i) if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) { if (td->widths.at(i) > td->minWidths.at(i)) { td->widths[i] -= slackToSubtract; if (td->widths.at(i) > td->minWidths.at(i)) { ++colsWithSlack; minimumSlack = qMin(minimumSlack, td->widths.at(i) - td->minWidths.at(i)); } } } } } // for variable columns distribute the remaining space if (variableCols > 0 && totalWidth > 0) { QVarLengthArray<int> columnsWithProperMaxSize; for (int i = 0; i < columns; ++i) if (columnWidthConstraints.at(i).type() == QTextLength::VariableLength && td->maxWidths.at(i) != QFIXED_MAX) columnsWithProperMaxSize.append(i); QFixed lastTotalWidth = totalWidth; while (totalWidth > 0) { for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) { const int col = columnsWithProperMaxSize[k]; const int colsLeft = columnsWithProperMaxSize.count() - k; const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), totalWidth / colsLeft); td->widths[col] += w; totalWidth -= w; } if (totalWidth == lastTotalWidth) break; lastTotalWidth = totalWidth; } if (totalWidth > 0 // don't unnecessarily grow variable length sized tables && fmt.width().type() != QTextLength::VariableLength) { const QFixed widthPerAnySizedCol = totalWidth / variableCols; for (int col = 0; col < columns; ++col) { if (columnWidthConstraints.at(col).type() == QTextLength::VariableLength) td->widths[col] += widthPerAnySizedCol; } } } td->columnPositions.resize(columns); td->columnPositions[0] = leftMargin /*includes table border*/ + cellSpacing + td->border; for (int i = 1; i < columns; ++i) td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->border + cellSpacing; td->heights.resize(rows); td->heights.fill(0); td->rowPositions.resize(rows); td->rowPositions[0] = topMargin /*includes table border*/ + cellSpacing + td->border; bool haveRowSpannedCells = false; // need to keep track of cell heights for vertical alignment QVector<QFixed> cellHeights; cellHeights.reserve(rows * columns); Q_Q(QTextDocumentLayout); QFixed pageHeight = QFixed::fromReal(q->document()->pageSize().height()); if (pageHeight <= 0) pageHeight = QFIXED_MAX; QVector<QFixed> heightToDistribute; heightToDistribute.resize(columns); td->headerHeight = 0; const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1); const QFixed originalTopMargin = td->effectiveTopMargin; bool hasDroppedTable = false; // now that we have the column w
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -